mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-06 02:24:30 +00:00
Moved data source processors into Autopsy
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.cellxml;
|
||||
|
||||
public class CellXMLDef {
|
||||
|
||||
public static final String CLLXML_TYPE_ATTR = "type";
|
||||
public static final String CLLXML_UUID_ATTR = "UUID";
|
||||
public static final String CLLXML_DELETED_ATTR = "deleted";
|
||||
|
||||
public static final String CLLXML_NAME_TAG = "name";
|
||||
public static final String CLLXML_VALUE_TAG = "value";
|
||||
public static final String CLLXML_DOMAIN_TAG = "domain";
|
||||
public static final String CLLXML_PATH_TAG = "path";
|
||||
public static final String CLLXML_CREATED_TAG = "created";
|
||||
public static final String CLLXML_EXPIRES_TAG = "expires";
|
||||
public static final String CLLXML_DIRECTION_TAG = "direction";
|
||||
public static final String CLLXML_TIME_TAG = "time";
|
||||
public static final String CLLXML_TIMESTAMP_TAG = "timeStamp";
|
||||
public static final String CLLXML_DATETIME_TAG = "dateTime";
|
||||
public static final String CLLXML_DESCRIPTION_TAG = "description";
|
||||
public static final String CLLXML_ADDRESS_TAG = "address";
|
||||
public static final String CLLXML_START_TAG = "start";
|
||||
public static final String CLLXML_END_TAG = "end";
|
||||
public static final String CLLXML_URL_TAG = "url";
|
||||
public static final String CLLXML_URL_NAME_TAG = "urlName";
|
||||
public static final String CLLXML_URL_ADDRESS_TAG = "urlAddress";
|
||||
public static final String CLLXML_DEVICE_NAME_TAG = "deviceName";
|
||||
public static final String CLLXML_DEVICE_ADDRESS_TAG = "deviceAddress";
|
||||
public static final String CLLXML_USER_ID_TAG = "userID";
|
||||
public static final String CLLXML_PASSWORD_TAG = "password";
|
||||
|
||||
public static final String CLLXML_CONTACT_TAG = "contact";
|
||||
|
||||
public static final String CLLXML_DISPLAYNAME_TAG = "displayName";
|
||||
public static final String CLLXML_PHONE_NUMBER_TAG = "phoneNumber";
|
||||
public static final String CLLXML_PHONE_FORM_TAG = "phoneForm";
|
||||
public static final String CLLXML_EMAIL_ADDRESS_TAG = "emailAddress";
|
||||
public static final String CLLXML_RFC5322_FORM_TAG = "rfc5322Form";
|
||||
public static final String CLLXML_CALL_TAG = "call";
|
||||
|
||||
public static final String CLLXML_MESSAGE_TAG = "message";
|
||||
public static final String CLLXML_SOURCE_TAG = "source";
|
||||
public static final String CLLXML_DESTINATION_TAG = "destination";
|
||||
public static final String CLLXML_SUBJECT_TAG = "subject";
|
||||
public static final String CLLXML_BODY_TAG = "body";
|
||||
|
||||
public static final String CLLXML_TYPE_HOME = "home";
|
||||
public static final String CLLXML_TYPE_WORK = "work";
|
||||
public static final String CLLXML_TYPE_MOBILE = "mobile";
|
||||
|
||||
public static final String CLLXML_MSG_TYPE_SMS = "sms";
|
||||
public static final String CLLXML_MSG_TYPE_MMS = "mms";
|
||||
public static final String CLLXML_MSG_TYPE_EMAIL = "email";
|
||||
|
||||
public static final String CLLXML_CALENDAR_ENTRY_TAG = "calendarEntry";
|
||||
|
||||
public static final String CLLXML_INTERNET_BOOKMARK_TAG = "internetBookmark";
|
||||
public static final String CLLXML_INTERNET_HISTORY_TAG = "internetHistoryEntry";
|
||||
|
||||
public static final String CLLXML_COOKIE_TAG = "cookie";
|
||||
|
||||
public static final String CLLXML_SPEEDDIAL_ENTRY_TAG = "speedDialEntry";
|
||||
public static final String CLLXML_DIALCODE_TAG = "dialCode";
|
||||
|
||||
public static final String CLLXML_BLUETOOTH_PAIRING_TAG = "bluetoothPairing";
|
||||
|
||||
public static final String CLLXML_GEO_LOCATION_TAG = "geoLocation";
|
||||
public static final String CLLXML_LATITUDE_TAG = "latitude";
|
||||
public static final String CLLXML_LONGITUDE_TAG = "longitude";
|
||||
public static final String CLLXML_ALTITUDE_TAG = "altitude";
|
||||
|
||||
public static final String CLLXML_GPS_FAVORITES_TAG = "gpsFavorites";
|
||||
public static final String CLLXML_GPS_SEARCHES_TAG = "gpsSearches";
|
||||
public static final String CLLXML_GPS_LAST_KNOWN_LOCATION_TAG = "gpsLastKnownLocation";
|
||||
public static final String CLLXML_GPS_TRACKPOINTS_TAG = "gpsTrackpoints";
|
||||
|
||||
public static final String CLLXML_APPLICATION_ACCOUNT_TAG = "applicationAccount";
|
||||
public static final String CLLXML_USER_ACCOUNT_INFO_TAG = "userAccountInfo";
|
||||
public static final String CLLXML_APPLICATION_INFO_TAG = "applicationInfo";
|
||||
public static final String CLLXML_EMAIL_ACCOUNT_INFO_TAG = "emailAccountInfo";
|
||||
|
||||
public static final String CLLXML_APPLICATION_NAME_TAG = "applicationName";
|
||||
public static final String CLLXML_APPLICATION_URL_TAG = "applicationURL";
|
||||
public static final String CLLXML_INSTALL_PATH_TAG = "installPath";
|
||||
|
||||
public static final String CLLXML_MAILBOX_NAME_TAG = "mailboxName";
|
||||
public static final String CLLXML_REPLY_ADDRESS_TAG = "replyToAddress";
|
||||
public static final String CLLXML_MAILSERVER_TAG = "mailServer";
|
||||
|
||||
public static final String CLLXML_EVENTLOG_ENTRY_TAG = "eventLogEntry";
|
||||
public static final String CLLXML_EVENT_DESCRIPTION_TAG = "eventDescription";
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
/*
|
||||
* A runnable that adds the image files from a Cellebrite UFED output folder to
|
||||
* a case database. If SleuthKit fails to find a filesystem in any of input
|
||||
* image files, the file is added to the case as a local/logical file instead.
|
||||
*/
|
||||
class AddCellebriteAndroidImageTask implements Runnable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AddCellebriteAndroidImageTask.class.getName());
|
||||
public static final String MODULE_NAME = "Cellebrite UFED Output Data Source Processor";
|
||||
public static final String TSK_FS_TYPE_UNKNOWN_ERR_MSG = "Cannot determine file system type";
|
||||
private final String deviceId;
|
||||
private final List<String> imageFilePaths;
|
||||
private final String timeZone;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private final Case currentCase;
|
||||
private boolean criticalErrorOccurred;
|
||||
private volatile boolean cancelled;
|
||||
|
||||
/**
|
||||
* Constructs a runnable that adds the image files from a Cellebrite UFED
|
||||
* output folder to a case database. If SleuthKit fails to find a filesystem
|
||||
* in any of input image files, the file is added to the case as a
|
||||
* local/logical file instead.
|
||||
*
|
||||
* @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 imageFilePaths The paths of the Cellebrite output files.
|
||||
* @param timeZone The time zone to use when processing dates and
|
||||
* times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param progressMonitor Progress monitor for reporting progress during
|
||||
* processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
AddCellebriteAndroidImageTask(String deviceId, List<String> imageFilePaths, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
this.deviceId = deviceId;
|
||||
this.imageFilePaths = imageFilePaths;
|
||||
this.timeZone = timeZone;
|
||||
this.callback = callback;
|
||||
this.progressMonitor = progressMonitor;
|
||||
currentCase = Case.getCurrentCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
/*
|
||||
* Try to add the input image files as images.
|
||||
*/
|
||||
List<Content> newDataSources = new ArrayList<>();
|
||||
List<String> localFileDataSourcePaths = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
currentCase.getSleuthkitCase().acquireExclusiveLock();
|
||||
try {
|
||||
progressMonitor.setIndeterminate(true);
|
||||
for (String imageFilePath : imageFilePaths) {
|
||||
if (!cancelled) {
|
||||
addImageToCase(imageFilePath, newDataSources, localFileDataSourcePaths, errorMessages);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
currentCase.getSleuthkitCase().releaseExclusiveLock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to add any input image files that did not have file systems as a
|
||||
* single local/logical files set with the device id as the root virtual
|
||||
* directory name.
|
||||
*/
|
||||
if (!cancelled && localFileDataSourcePaths.size() > 0) {
|
||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||
FileManager.FileAddProgressUpdater progressUpdater = (final AbstractFile newFile) -> {
|
||||
progressMonitor.setProgressText(String.format("Adding: %s as logical file", Paths.get(newFile.getParentPath(), newFile.getName())));
|
||||
};
|
||||
try {
|
||||
LocalFilesDataSource localFilesDataSource = fileManager.addLocalFilesDataSource(deviceId, deviceId, timeZone, localFileDataSourcePaths, progressUpdater);
|
||||
newDataSources.add(localFilesDataSource.getRootDirectory());
|
||||
} catch (TskCoreException | TskDataException ex) {
|
||||
errorMessages.add(String.format("Error adding images without file systems for device %s: %s", deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This appears to be the best that can be done to indicate completion
|
||||
* with the DataSourceProcessorProgressMonitor in its current form.
|
||||
*/
|
||||
progressMonitor.setProgress(0);
|
||||
progressMonitor.setProgress(100);
|
||||
|
||||
/*
|
||||
* Pass the results back via the callback.
|
||||
*/
|
||||
DataSourceProcessorResult result;
|
||||
if (criticalErrorOccurred) {
|
||||
result = DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
} else if (!errorMessages.isEmpty()) {
|
||||
result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
|
||||
} else {
|
||||
result = DataSourceProcessorResult.NO_ERRORS;
|
||||
}
|
||||
callback.done(result, errorMessages, newDataSources);
|
||||
criticalErrorOccurred = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to cancel the processing of the input image files. May result in
|
||||
* partial processing of the input.
|
||||
*/
|
||||
public void cancelTask() {
|
||||
logger.log(Level.WARNING, "AddCellebriteAndroidImageTask cancelled, processing may be incomplete");
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add an input image to the case.
|
||||
*
|
||||
* @param imageFilePath The image file path.
|
||||
* @param newDataSources If the image is added, a data source is
|
||||
* added to this list for eventual return to
|
||||
* the caller via the callback.
|
||||
* @param localFileDataSourcePaths If the image cannot be added because
|
||||
* SleuthKit cannot detect a filesystem, the
|
||||
* image file path is added to this list for
|
||||
* later addition as a part of a
|
||||
* local/logical files data source.
|
||||
* @param errorMessages If there are any error messages, the
|
||||
* error messages are added to this list for
|
||||
* eventual return to the caller via the
|
||||
* callback.
|
||||
*/
|
||||
private void addImageToCase(String imageFilePath, List<Content> newDataSources, List<String> localFileDataSourcePaths, List<String> errorMessages) {
|
||||
/*
|
||||
* Try to add the image to the case database as a data source.
|
||||
*/
|
||||
progressMonitor.setProgressText(String.format("Adding: %s", imageFilePath));
|
||||
SleuthkitCase caseDatabase = currentCase.getSleuthkitCase();
|
||||
SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = caseDatabase.makeAddImageProcess(timeZone, false, false);
|
||||
Thread progressReporterThread = new Thread(new AddImageProgressReportingTask(progressMonitor, addImageProcess));
|
||||
try {
|
||||
progressReporterThread.start();
|
||||
addImageProcess.run(deviceId, new String[]{imageFilePath});
|
||||
} catch (TskCoreException ex) {
|
||||
if (ex.getMessage().contains(TSK_FS_TYPE_UNKNOWN_ERR_MSG)) {
|
||||
/*
|
||||
* If SleuthKit failed to add the image because it did not find
|
||||
* a file system, save the image path so it can be added to the
|
||||
* case as part of a local/logical files data source. All other
|
||||
* errors are critical.
|
||||
*/
|
||||
localFileDataSourcePaths.add(imageFilePath);
|
||||
} else {
|
||||
errorMessages.add(String.format("Critical error adding %s for %s:", imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
/*
|
||||
* Either way, the add image process needs to be reverted.
|
||||
*/
|
||||
try {
|
||||
addImageProcess.revert();
|
||||
} catch (TskCoreException e) {
|
||||
errorMessages.add(String.format("Critical error reverting add image process for %s for %s: %s", imageFilePath, deviceId, e.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
return;
|
||||
} catch (TskDataException ex) {
|
||||
errorMessages.add(String.format("Non-critical error adding %s for %s: %s", imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
} finally {
|
||||
progressReporterThread.interrupt();
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to commit the results of the add image process, retrieve the new
|
||||
* image from the case database, and add it to the list of new data
|
||||
* sources to be returned via the callback.
|
||||
*/
|
||||
try {
|
||||
long imageId = addImageProcess.commit();
|
||||
Image dataSource = caseDatabase.getImageById(imageId);
|
||||
newDataSources.add(dataSource);
|
||||
|
||||
/*
|
||||
* Verify the size of the new image. Note that it may not be what is
|
||||
* expected, but at least part of it was added to the case.
|
||||
*/
|
||||
String verificationError = dataSource.verifyImageSize();
|
||||
if (!verificationError.isEmpty()) {
|
||||
errorMessages.add(String.format("Non-critical error adding %s for device %s: %s", imageFilePath, deviceId, verificationError));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
/*
|
||||
* The add image process commit failed or querying the case database
|
||||
* for the newly added image failed. Either way, this is a critical
|
||||
* error.
|
||||
*/
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s: %s", imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.modules.InstalledFileLocator;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.autopsy.experimental.cellex.cellxml.CellXMLParser;
|
||||
|
||||
/*
|
||||
* A runnable that adds a Cellebrite XML report a case as a local files data
|
||||
* source. The Cellebrite XML report is converted to a DFXML/CellXML report and
|
||||
* the CellXML report is parsed to generate artifacts.
|
||||
*/
|
||||
class AddCellebriteXMLTask implements Runnable {
|
||||
|
||||
public enum CellebriteInputType {
|
||||
|
||||
handset,
|
||||
SIM,
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CellebriteXMLProcessor.class.getName());
|
||||
private static final String MODULE_NAME = "Cellebrite XML Processor";
|
||||
private static final String CONVERTOR_EXE = "CellebriteToDFXMLConv.exe";
|
||||
private final String deviceId;
|
||||
private final String rootVirtualDirectoryName;
|
||||
private final String cellebriteXmlFilePath;
|
||||
private final CellebriteInputType cellebriteXmlFileType;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private volatile boolean cancelled = false;
|
||||
|
||||
/**
|
||||
* Constructs a runnable that adds a Cellebrite XML report a case as a local
|
||||
* files data source. The Cellebrite XML report is converted to a
|
||||
* DFXML/CellXML report and the CellXML report is parsed to generate
|
||||
* artifacts.
|
||||
*
|
||||
* @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 rootVirtualDirectoryName The name to give to the virtual directory
|
||||
* that will represent the data source. Pass
|
||||
* the empty string to get a default name of
|
||||
* the form: LogicalFileSet[N]
|
||||
* @param cellebriteXmlFilePath Path to a Cellebrite report XML file.
|
||||
* @param cellebriteXmlFileType Handset or SIM.
|
||||
* @param progressMonitor Progress monitor for reporting
|
||||
* progressMonitor during processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
AddCellebriteXMLTask(String deviceId, String rootVirtualDirectoryName, String cellebriteXmlFilePath, CellebriteInputType cellebriteXmlFileType, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
this.deviceId = deviceId;
|
||||
this.rootVirtualDirectoryName = rootVirtualDirectoryName;
|
||||
this.cellebriteXmlFilePath = cellebriteXmlFilePath;
|
||||
this.cellebriteXmlFileType = cellebriteXmlFileType;
|
||||
this.callback = callback;
|
||||
this.progressMonitor = progressMonitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Cellebrite XML report a case as a local files data source. The
|
||||
* Cellebrite XML report is converted to a DFXML/CellXML report and the
|
||||
* CellXML report is parsed to generate artifacts.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
List<Content> newDataSources = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
try {
|
||||
progressMonitor.setIndeterminate(true);
|
||||
progressMonitor.setProgressText("Processing: " + cellebriteXmlFilePath);
|
||||
|
||||
/*
|
||||
* Locate the Cellebrite XML to DFXML/CellXML converter.
|
||||
*/
|
||||
final File converterHome = InstalledFileLocator.getDefault().locate(FilenameUtils.removeExtension(CONVERTOR_EXE), AddCellebriteXMLTask.class.getPackage().getName(), false);
|
||||
if (null == converterHome) {
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s: %s not found", cellebriteXmlFilePath, deviceId, CONVERTOR_EXE));
|
||||
return;
|
||||
}
|
||||
String converterExePath = Paths.get(converterHome.getAbsolutePath(), CONVERTOR_EXE).toString();
|
||||
|
||||
/*
|
||||
* Get the file name of the image file sans extension and use it to
|
||||
* create an converter output folder in the module output directory
|
||||
* of the case, adding a time stamp suffix for uniqueness.
|
||||
*
|
||||
* NOTE: The input file name may not have a .xml extension.
|
||||
*/
|
||||
String cellebriteXmlFileNameWithoutExt = FilenameUtils.removeExtension(Paths.get(cellebriteXmlFilePath).getFileName().toString());
|
||||
Path converterOutputDirPath = Paths.get(Case.getCurrentCase().getModuleDirectory(),
|
||||
MODULE_NAME,
|
||||
cellebriteXmlFileNameWithoutExt + "_" + new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS").format(new Date()));
|
||||
try {
|
||||
Files.createDirectories(converterOutputDirPath);
|
||||
} catch (IOException ex) {
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s, cannot create converter output directory %s : %s", cellebriteXmlFilePath, deviceId, converterOutputDirPath, ex.getLocalizedMessage()));
|
||||
return;
|
||||
}
|
||||
String cellXmlFilePath = Paths.get(converterOutputDirPath.toString(), cellebriteXmlFileNameWithoutExt + ".xml").toString();
|
||||
|
||||
/*
|
||||
* Run the converter.
|
||||
*/
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
converterExePath,
|
||||
"-t",
|
||||
cellebriteXmlFileType.toString(),
|
||||
"-i",
|
||||
cellebriteXmlFilePath,
|
||||
"-o",
|
||||
cellXmlFilePath);
|
||||
String logFileName = Paths.get(converterOutputDirPath.toString(), "c2c_stdout.txt").toString();
|
||||
File logFile = new File(logFileName);
|
||||
Path errFileName = Paths.get(converterOutputDirPath.toString(), "c2c_errors.txt");
|
||||
File errFile = new File(errFileName.toString());
|
||||
processBuilder.redirectError(ProcessBuilder.Redirect.appendTo(errFile));
|
||||
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile));
|
||||
try {
|
||||
int exitValue = ExecUtil.execute(processBuilder);
|
||||
if (exitValue != 0) {
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s: %s returned failure code", cellebriteXmlFilePath, deviceId, CONVERTOR_EXE));
|
||||
return;
|
||||
}
|
||||
} catch (IOException | SecurityException ex) {
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s, %s execution exception: %s", cellebriteXmlFilePath, deviceId, CONVERTOR_EXE, ex.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the Cellebrite XML file to the case as a local file data
|
||||
* source.
|
||||
*/
|
||||
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
|
||||
List<String> localFilePaths = new ArrayList<>();
|
||||
localFilePaths.add(cellebriteXmlFilePath);
|
||||
LocalFilesDataSource newDataSource = fileManager.addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, "", localFilePaths, (final AbstractFile newFile) -> {
|
||||
});
|
||||
newDataSources.add(newDataSource.getRootDirectory());
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate artifacts from the DFXML/CellXML file.
|
||||
*/
|
||||
java.io.File cellxmlFile = new java.io.File(cellXmlFilePath);
|
||||
if (cellxmlFile.exists()) {
|
||||
CellXMLParser.getDefault().Process(cellXmlFilePath, newDataSource.getRootDirectory(), MODULE_NAME);
|
||||
} else {
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s: missing CellXML file", cellebriteXmlFilePath, deviceId));
|
||||
}
|
||||
} catch (TskDataException | TskCoreException ex) {
|
||||
errorMessages.add(String.format("Critical error adding %s for device %s: %s", cellebriteXmlFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
} finally {
|
||||
/*
|
||||
* This appears to be the best that can be done to indicate
|
||||
* completion with the DataSourceProcessorProgressMonitor in its
|
||||
* current form.
|
||||
*/
|
||||
progressMonitor.setProgress(0);
|
||||
progressMonitor.setProgress(100);
|
||||
|
||||
/*
|
||||
* Pass the results back via the callback.
|
||||
*/
|
||||
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||
if (!errorMessages.isEmpty()) {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
} else {
|
||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
|
||||
}
|
||||
callback.done(result, errorMessages, newDataSources);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to cancel the processing of the input image file. May result in
|
||||
* partial processing of the input.
|
||||
*/
|
||||
public void cancelTask() {
|
||||
logger.log(Level.WARNING, "AddMPFImageTask cancelled, processing may be incomplete");
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
|
||||
/*
|
||||
* A Runnable that updates a data source processor progress monitor with the
|
||||
* name of the directory currently being processed by a SleuthKit add image
|
||||
* process.
|
||||
*
|
||||
* TODO (JIRA-1578): The sleep code in the run method should be removed. Clients
|
||||
* should use a java.util.concurrent.ScheduledThreadPoolExecutor instead to be
|
||||
* able to control update frequency and cancellation.
|
||||
*/
|
||||
class AddImageProgressReportingTask implements Runnable {
|
||||
|
||||
DataSourceProcessorProgressMonitor progressMonitor;
|
||||
SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess;
|
||||
|
||||
/**
|
||||
* Constructs a Runnable that updates a data source processor progress
|
||||
* monitor with the name of the directory currently being processed by a
|
||||
* SleuthKit add image process.
|
||||
*
|
||||
* @param progressMonitor The progress monitor.
|
||||
* @param addImageProcess An Sleuth add image process.
|
||||
*/
|
||||
AddImageProgressReportingTask(DataSourceProcessorProgressMonitor progressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess) {
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.addImageProcess = addImageProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every two seconds, updates the progress monitor with the name of the
|
||||
* directory currently being processed by the add image process.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
String currDir = addImageProcess.currentDirectory();
|
||||
if (null != currDir && !currDir.isEmpty()) {
|
||||
progressMonitor.setProgressText("Adding: " + currDir);
|
||||
}
|
||||
/*
|
||||
* TODO (JIRA-1578): The sleep should be removed here. Clients
|
||||
* should use a java.util.concurrent.ScheduledThreadPoolExecutor
|
||||
* instead to be able to control update frequency and
|
||||
* cancellation,
|
||||
*/
|
||||
Thread.sleep(2 * 1000);
|
||||
}
|
||||
} catch (InterruptedException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
OpenIDE-Module-Display-Category=Ingest Module
|
||||
OpenIDE-Module-Long-Description=\
|
||||
Extracts application information, lists potentially suspicious databases and SD card folders, and parses known SQLite databases.
|
||||
OpenIDE-Module-Name=Android Triage
|
||||
MPFModuleSimplePanel.jSizeLabel.text=Min Size (MB):
|
||||
MPFModuleSimplePanel.jImgExtCheckBox.text=.img
|
||||
MPFModuleSimplePanel.jNoExtCheckBox.text=\ No extension
|
||||
MPFModuleSimplePanel.jBinExtCheckBox.text=.bin
|
||||
MPFModuleSimplePanel.jMinSizeSpinner.toolTipText=Minimum size of file to be processed by MPF.
|
||||
MPFModuleSimplePanel.jAllFilesRadioButton.text=Yes (will take longer)
|
||||
MPFModuleSimplePanel.jFilterFilesRadioButton.text=No
|
||||
MPFModuleSimplePanel.jLabel2.text=File Extensions:
|
||||
MPFModuleSimplePanel.jLabel3.text=Process files that meet the following criteria:
|
||||
MPFModuleSimplePanel.jLabel4.text=Run on all files in set of logical files?
|
||||
CellebriteXMLFilePanel.browseButton.text=Browse
|
||||
CellebriteXMLFilePanel.pathLabel.text=Browse for a Cellebrite XML file:
|
||||
CellebriteXMLFilePanel.pathTextField.text=
|
||||
CellebriteXMLFilePanel.jLabel1.text=Input type:
|
||||
CellebriteXMLFilePanel.jHandsetRadioButton.text=Handset
|
||||
CellebriteXMLFilePanel.jSIMRadioButton.text=SIM
|
||||
CellebriteAndroidInputPanel.browseButton.text=Browse
|
||||
CellebriteAndroidInputPanel.pathTextField.text=
|
||||
CellebriteAndroidInputPanel.timeZoneLabel.text=Please select the input timezone:
|
||||
CellebriteAndroidInputPanel.pathLabel.text=Browse for a Android phone image folder:
|
||||
OpenIDE-Module-Short-Description=Does triage of Android data.
|
||||
CellebriteAndroidInputPanel.errorLabel.text=Error Label
|
||||
CellebriteXMLFilePanel.errorLabel.text=Error Label
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
|
||||
/**
|
||||
* An Cellebrite UFED output folder 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.
|
||||
*/
|
||||
@ServiceProvider(service = DataSourceProcessor.class)
|
||||
public class CellebriteAndroidImageProcessor implements DataSourceProcessor {
|
||||
|
||||
private static final String DATA_SOURCE_TYPE = "Cellebrite Android";
|
||||
private final CellebriteAndroidInputPanel configPanel;
|
||||
private AddCellebriteAndroidImageTask addImagesTask;
|
||||
|
||||
/**
|
||||
* Contructs a Cellebrite UFED output folder 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 CellebriteAndroidImageProcessor() {
|
||||
configPanel = CellebriteAndroidInputPanel.createInstance(CellebriteAndroidImageProcessor.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 DATA_SOURCE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
configPanel.storeSettings();
|
||||
run(UUID.randomUUID().toString(), configPanel.getContentPaths(), configPanel.getTimeZone(), 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
public void run(String deviceId, String imageFolderPath, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
List<String> imageFilePaths = getImageFilePaths(imageFolderPath);
|
||||
addImagesTask = new AddCellebriteAndroidImageTask(deviceId, imageFilePaths, timeZone, progressMonitor, callback);
|
||||
new Thread(addImagesTask).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() {
|
||||
addImagesTask.cancelTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection and configuration panel for this data source
|
||||
* processor.
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
configPanel.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the paths of the image files in a Cellebrite UFED output folder.
|
||||
*
|
||||
* @param folderPath The path to a Cellebrite UFED output folder
|
||||
*
|
||||
* @return A list of image file paths.
|
||||
*/
|
||||
private static List<String> getImageFilePaths(String folderPath) {
|
||||
List<String> imageFilePaths = new ArrayList<>();
|
||||
File folder = new File(folderPath);
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
for (File file : listOfFiles) {
|
||||
if (file.isFile()) {
|
||||
String fName = file.getName().toLowerCase();
|
||||
int lastPeriod = fName.lastIndexOf('.');
|
||||
if (-1 == lastPeriod) {
|
||||
continue;
|
||||
}
|
||||
String fNameNoExt = fName.substring(0, lastPeriod);
|
||||
String ext = fName.substring(lastPeriod + 1);
|
||||
String filePathName = folderPath + File.separator + fName;
|
||||
if (ext.equalsIgnoreCase("bin")) {
|
||||
// this needs to identify and handle different Cellebrite scenarios:
|
||||
// i single image in a single file
|
||||
// ii. Single image spilt over multiple files - just need to pass the first to TSK and it will combine the split image files.
|
||||
// Note there may be more than than one split images in a single dir,
|
||||
// e.g. blk0_mmcblk0.bin, blk0_mmcblk0(1).bin......, and blk24_mmcblk1.bin, blk24_mmcblk1(1).bin......
|
||||
//iii. Multiple image files - one per volume - need to handle each one separately
|
||||
// e.g. blk0_mmcblk0.bin, mtd0_system.bin, mtd1_cache.bin, mtd2_userdata.bin
|
||||
// if the file name ends with something like (001).bin then its part of a split image,
|
||||
if (!fNameNoExt.matches("\\w+\\(\\d+\\)")) {
|
||||
imageFilePaths.add(filePathName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageFilePaths;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" 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">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="pathLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="pathTextField" min="-2" pref="286" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="timeZoneLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="timeZoneComboBox" min="-2" pref="215" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace 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 max="-2" attributes="0"/>
|
||||
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="timeZoneLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="timeZoneComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="198" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="pathLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteAndroidInputPanel.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="pathTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteAndroidInputPanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="browseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteAndroidInputPanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="timeZoneLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteAndroidInputPanel.timeZoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="timeZoneComboBox">
|
||||
<Properties>
|
||||
<Property name="maximumRowCount" type="int" value="30"/>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="errorLabel">
|
||||
<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="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteAndroidInputPanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.File;
|
||||
import java.util.Calendar;
|
||||
import java.util.SimpleTimeZone;
|
||||
import java.util.TimeZone;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PathValidator;
|
||||
|
||||
public class CellebriteAndroidInputPanel extends JPanel implements DocumentListener {
|
||||
|
||||
private final String PROP_LASTIMAGE_PATH = "LBL_LastImage_PATH";
|
||||
|
||||
private PropertyChangeSupport pcs = null;
|
||||
private JFileChooser fc = new JFileChooser();
|
||||
|
||||
// Externally supplied name is used to store settings
|
||||
private String contextName;
|
||||
|
||||
/**
|
||||
* Creates new form CellebriteAndroidInputPanel
|
||||
*/
|
||||
public CellebriteAndroidInputPanel(String context) {
|
||||
initComponents();
|
||||
|
||||
errorLabel.setVisible(false);
|
||||
|
||||
fc.setDragEnabled(false);
|
||||
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
|
||||
this.contextName = context;
|
||||
pcs = new PropertyChangeSupport(this);
|
||||
|
||||
createTimeZoneList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an instance of a ImageFilePanel.
|
||||
*/
|
||||
public static synchronized CellebriteAndroidInputPanel createInstance(String context) {
|
||||
CellebriteAndroidInputPanel instance = new CellebriteAndroidInputPanel(context);
|
||||
instance.postInit();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
//post-constructor initialization to properly initialize listener support
|
||||
//without leaking references of uninitialized objects
|
||||
private void postInit() {
|
||||
pathTextField.getDocument().addDocumentListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the drop down list for the time zones and then makes the local
|
||||
* machine time zone to be selected.
|
||||
*/
|
||||
public void createTimeZoneList() {
|
||||
// load and add all timezone
|
||||
String[] ids = SimpleTimeZone.getAvailableIDs();
|
||||
for (String id : ids) {
|
||||
TimeZone zone = TimeZone.getTimeZone(id);
|
||||
int offset = zone.getRawOffset() / 1000;
|
||||
int hour = offset / 3600;
|
||||
int minutes = (offset % 3600) / 60;
|
||||
String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id);
|
||||
|
||||
/*
|
||||
* DateFormat dfm = new SimpleDateFormat("z");
|
||||
* dfm.setTimeZone(zone); boolean hasDaylight =
|
||||
* zone.useDaylightTime(); String first = dfm.format(new Date(2010,
|
||||
* 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid
|
||||
* = hour * -1; String result = first + Integer.toString(mid);
|
||||
* if(hasDaylight){ result = result + second; }
|
||||
* timeZoneComboBox.addItem(item + " (" + result + ")");
|
||||
*/
|
||||
timeZoneComboBox.addItem(item);
|
||||
}
|
||||
// get the current timezone
|
||||
TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
|
||||
int thisOffset = thisTimeZone.getRawOffset() / 1000;
|
||||
int thisHour = thisOffset / 3600;
|
||||
int thisMinutes = (thisOffset % 3600) / 60;
|
||||
String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID());
|
||||
|
||||
// set the selected timezone
|
||||
timeZoneComboBox.setSelectedItem(formatted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the user selected folder.
|
||||
*
|
||||
* @return the image path
|
||||
*/
|
||||
public String getContentPaths() {
|
||||
return pathTextField.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path of the images folder.
|
||||
*/
|
||||
public void setContentPath(String s) {
|
||||
pathTextField.setText(s);
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
String tz = timeZoneComboBox.getSelectedItem().toString();
|
||||
return tz.substring(tz.indexOf(")") + 2).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
pathLabel = new javax.swing.JLabel();
|
||||
pathTextField = new javax.swing.JTextField();
|
||||
browseButton = new javax.swing.JButton();
|
||||
timeZoneLabel = new javax.swing.JLabel();
|
||||
timeZoneComboBox = new javax.swing.JComboBox<>();
|
||||
errorLabel = new javax.swing.JLabel();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CellebriteAndroidInputPanel.class, "CellebriteAndroidInputPanel.pathLabel.text")); // NOI18N
|
||||
|
||||
pathTextField.setText(org.openide.util.NbBundle.getMessage(CellebriteAndroidInputPanel.class, "CellebriteAndroidInputPanel.pathTextField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(CellebriteAndroidInputPanel.class, "CellebriteAndroidInputPanel.browseButton.text")); // NOI18N
|
||||
browseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
browseButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(CellebriteAndroidInputPanel.class, "CellebriteAndroidInputPanel.timeZoneLabel.text")); // NOI18N
|
||||
|
||||
timeZoneComboBox.setMaximumRowCount(30);
|
||||
|
||||
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(CellebriteAndroidInputPanel.class, "CellebriteAndroidInputPanel.errorLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(pathLabel)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 286, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(browseButton))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(timeZoneLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(errorLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(pathLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(browseButton))
|
||||
.addGap(8, 8, 8)
|
||||
.addComponent(errorLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(timeZoneLabel)
|
||||
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap(198, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
@SuppressWarnings("deprecation")
|
||||
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||
String oldText = pathTextField.getText();
|
||||
|
||||
// set the current directory of the FileChooser if the ImagePath Field is valid
|
||||
File currentDir = new File(oldText);
|
||||
if (currentDir.exists()) {
|
||||
fc.setCurrentDirectory(currentDir);
|
||||
}
|
||||
|
||||
int retval = fc.showOpenDialog(this);
|
||||
if (retval == JFileChooser.APPROVE_OPTION) {
|
||||
String path = fc.getSelectedFile().getPath();
|
||||
pathTextField.setText(path);
|
||||
}
|
||||
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true);
|
||||
}//GEN-LAST:event_browseButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton browseButton;
|
||||
private javax.swing.JLabel errorLabel;
|
||||
private javax.swing.JLabel pathLabel;
|
||||
private javax.swing.JTextField pathTextField;
|
||||
private javax.swing.JComboBox<String> timeZoneComboBox;
|
||||
private javax.swing.JLabel timeZoneLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Update functions are called by the pathTextField which has this set as
|
||||
* it's DocumentEventListener. Each update function fires a property change
|
||||
* to be caught by the parent panel.
|
||||
*
|
||||
* @param e the event, which is ignored
|
||||
*/
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the focus to the pathTextField.
|
||||
*/
|
||||
public void select() {
|
||||
pathTextField.requestFocusInWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
super.addPropertyChangeListener(pcl);
|
||||
|
||||
if (pcs == null) {
|
||||
pcs = new PropertyChangeSupport(this);
|
||||
}
|
||||
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
super.removePropertyChangeListener(pcl);
|
||||
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we enable the next button of the wizard?
|
||||
*
|
||||
* @return true if a proper image has been selected, false otherwise
|
||||
*/
|
||||
public boolean validatePanel() {
|
||||
errorLabel.setVisible(false);
|
||||
String path = getContentPaths();
|
||||
if (path == null || path.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// display warning if there is one (but don't disable "next" button)
|
||||
warnIfPathIsInvalid(path);
|
||||
|
||||
// check if a folder exists by this name.
|
||||
File f = new File(path);
|
||||
boolean isValid = (f.exists() && f.isDirectory());
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates path to selected data source and displays warning if it is
|
||||
* invalid.
|
||||
*
|
||||
* @param path Absolute path to the selected data source
|
||||
*/
|
||||
private void warnIfPathIsInvalid(String path) {
|
||||
if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) {
|
||||
errorLabel.setVisible(true);
|
||||
errorLabel.setText("Path to multi-user data source is on \"C:\" drive");
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
//reset the UI elements to default
|
||||
pathTextField.setText(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the last used settings
|
||||
*/
|
||||
public void storeSettings() {
|
||||
String imagePathName = getContentPaths();
|
||||
if (null != imagePathName) {
|
||||
String imagePath = imagePathName.substring(0, imagePathName.lastIndexOf(File.separator) + 1);
|
||||
ModuleSettings.setConfigSetting(contextName, PROP_LASTIMAGE_PATH, imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and load the last used settings
|
||||
*/
|
||||
public void readSettings() {
|
||||
String lastImagePath = ModuleSettings.getConfigSetting(contextName, PROP_LASTIMAGE_PATH);
|
||||
if (null != lastImagePath) {
|
||||
if (!lastImagePath.isEmpty()) {
|
||||
pathTextField.setText(lastImagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Component class="javax.swing.ButtonGroup" name="infileTypeButtonGroup">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<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" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="pathTextField" min="-2" pref="286" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<Component id="jHandsetRadioButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="jSIMRadioButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace 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 max="-2" attributes="0"/>
|
||||
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jSIMRadioButton" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jHandsetRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="26" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="pathLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="pathTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="browseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="jHandsetRadioButton">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="infileTypeButtonGroup"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.jHandsetRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jHandsetRadioButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="jSIMRadioButton">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="infileTypeButtonGroup"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.jSIMRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jSIMRadioButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="errorLabel">
|
||||
<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="org/sleuthkit/autopsy/experimental/cellex/datasourceprocessors/Bundle.properties" key="CellebriteXMLFilePanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.util.List;
|
||||
import java.io.File;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PathValidator;
|
||||
|
||||
public class CellebriteXMLFilePanel extends JPanel implements DocumentListener {
|
||||
|
||||
private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH";
|
||||
|
||||
private PropertyChangeSupport pcs = null;
|
||||
private JFileChooser fc = new JFileChooser();
|
||||
|
||||
// Externally supplied name is used to store settings
|
||||
private String contextName;
|
||||
|
||||
/**
|
||||
* Creates new form CellebriteXMLFilePanel
|
||||
*/
|
||||
private CellebriteXMLFilePanel(String context, List<FileFilter> fileChooserFilters) {
|
||||
initComponents();
|
||||
|
||||
errorLabel.setVisible(false);
|
||||
|
||||
fc.setDragEnabled(false);
|
||||
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
|
||||
boolean firstFilter = true;
|
||||
for (FileFilter filter : fileChooserFilters) {
|
||||
if (firstFilter) { // set the first on the list as the default selection
|
||||
fc.setFileFilter(filter);
|
||||
firstFilter = false;
|
||||
} else {
|
||||
fc.addChoosableFileFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
this.contextName = context;
|
||||
pcs = new PropertyChangeSupport(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an instance of a CellebriteXMLFilePanel.
|
||||
*/
|
||||
public static synchronized CellebriteXMLFilePanel createInstance(String context, List<FileFilter> fileChooserFilters) {
|
||||
CellebriteXMLFilePanel instance = new CellebriteXMLFilePanel(context, fileChooserFilters);
|
||||
|
||||
instance.postInit();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
//post-constructor initialization to properly initialize listener support
|
||||
//without leaking references of uninitialized objects
|
||||
private void postInit() {
|
||||
pathTextField.getDocument().addDocumentListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
infileTypeButtonGroup = new javax.swing.ButtonGroup();
|
||||
pathLabel = new javax.swing.JLabel();
|
||||
pathTextField = new javax.swing.JTextField();
|
||||
browseButton = new javax.swing.JButton();
|
||||
jHandsetRadioButton = new javax.swing.JRadioButton();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
jSIMRadioButton = new javax.swing.JRadioButton();
|
||||
errorLabel = new javax.swing.JLabel();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.pathLabel.text")); // NOI18N
|
||||
|
||||
pathTextField.setText(org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.pathTextField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.browseButton.text")); // NOI18N
|
||||
browseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
browseButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
infileTypeButtonGroup.add(jHandsetRadioButton);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jHandsetRadioButton, org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.jHandsetRadioButton.text")); // NOI18N
|
||||
jHandsetRadioButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jHandsetRadioButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.jLabel1.text")); // NOI18N
|
||||
|
||||
infileTypeButtonGroup.add(jSIMRadioButton);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jSIMRadioButton, org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.jSIMRadioButton.text")); // NOI18N
|
||||
jSIMRadioButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jSIMRadioButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(CellebriteXMLFilePanel.class, "CellebriteXMLFilePanel.errorLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 286, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(browseButton))
|
||||
.addComponent(pathLabel)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(16, 16, 16)
|
||||
.addComponent(jHandsetRadioButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(jSIMRadioButton))
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(errorLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(pathLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(browseButton))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(errorLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jLabel1)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSIMRadioButton)
|
||||
.addComponent(jHandsetRadioButton))
|
||||
.addContainerGap(26, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
@SuppressWarnings("deprecation")
|
||||
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||
String oldText = pathTextField.getText();
|
||||
// set the current directory of the FileChooser if the ImagePath Field is valid
|
||||
File currentDir = new File(oldText);
|
||||
if (currentDir.exists()) {
|
||||
fc.setCurrentDirectory(currentDir);
|
||||
}
|
||||
|
||||
int retval = fc.showOpenDialog(this);
|
||||
if (retval == JFileChooser.APPROVE_OPTION) {
|
||||
String path = fc.getSelectedFile().getPath();
|
||||
pathTextField.setText(path);
|
||||
}
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true);
|
||||
}//GEN-LAST:event_browseButtonActionPerformed
|
||||
|
||||
private void jHandsetRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jHandsetRadioButtonActionPerformed
|
||||
// TODO add your handling code here:
|
||||
}//GEN-LAST:event_jHandsetRadioButtonActionPerformed
|
||||
|
||||
private void jSIMRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jSIMRadioButtonActionPerformed
|
||||
// TODO add your handling code here:
|
||||
}//GEN-LAST:event_jSIMRadioButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton browseButton;
|
||||
private javax.swing.JLabel errorLabel;
|
||||
private javax.swing.ButtonGroup infileTypeButtonGroup;
|
||||
private javax.swing.JRadioButton jHandsetRadioButton;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
private javax.swing.JRadioButton jSIMRadioButton;
|
||||
private javax.swing.JLabel pathLabel;
|
||||
private javax.swing.JTextField pathTextField;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
/**
|
||||
* Get the path of the user selected image.
|
||||
*
|
||||
* @return the image path
|
||||
*/
|
||||
public String getImageFilePath() {
|
||||
return pathTextField.getText();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
//reset the UI elements to default
|
||||
pathTextField.setText(null);
|
||||
jHandsetRadioButton.setSelected(true);
|
||||
}
|
||||
|
||||
public boolean isHandsetFile() {
|
||||
return jHandsetRadioButton.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we enable the next button of the wizard?
|
||||
*
|
||||
* @return true if a proper image has been selected, false otherwise
|
||||
*/
|
||||
public boolean validatePanel() {
|
||||
errorLabel.setVisible(false);
|
||||
String path = getImageFilePath();
|
||||
if (path == null || path.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// display warning if there is one (but don't disable "next" button)
|
||||
warnIfPathIsInvalid(path);
|
||||
|
||||
boolean isExist = new File(path).exists();
|
||||
|
||||
return (isExist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates path to selected data source and displays warning if it is
|
||||
* invalid.
|
||||
*
|
||||
* @param path Absolute path to the selected data source
|
||||
*/
|
||||
private void warnIfPathIsInvalid(String path) {
|
||||
if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) {
|
||||
errorLabel.setVisible(true);
|
||||
errorLabel.setText("Path to multi-user data source is on \"C:\" drive");
|
||||
}
|
||||
}
|
||||
|
||||
public void storeSettings() {
|
||||
String inFilePath = getImageFilePath();
|
||||
if (null != inFilePath) {
|
||||
String imagePath = inFilePath.substring(0, inFilePath.lastIndexOf(File.separator) + 1);
|
||||
ModuleSettings.setConfigSetting(contextName, PROP_LASTINPUT_PATH, imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void readSettings() {
|
||||
String inFilePath = ModuleSettings.getConfigSetting(contextName, PROP_LASTINPUT_PATH);
|
||||
if (null != inFilePath) {
|
||||
if (!inFilePath.isEmpty()) {
|
||||
pathTextField.setText(inFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update functions are called by the pathTextField which has this set as
|
||||
* it's DocumentEventListener. Each update function fires a property change
|
||||
* to be caught by the parent panel.
|
||||
*
|
||||
* @param e the event, which is ignored
|
||||
*/
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the focus to the pathTextField.
|
||||
*/
|
||||
public void select() {
|
||||
pathTextField.requestFocusInWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
super.addPropertyChangeListener(pcl);
|
||||
|
||||
if (pcs == null) {
|
||||
pcs = new PropertyChangeSupport(this);
|
||||
}
|
||||
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
super.removePropertyChangeListener(pcl);
|
||||
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
|
||||
/**
|
||||
* A Cellebrite XML report 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.
|
||||
*/
|
||||
@ServiceProvider(service = DataSourceProcessor.class)
|
||||
public class CellebriteXMLProcessor implements DataSourceProcessor {
|
||||
|
||||
private static final String DATA_SOURCE_TYPE = "Cellebrite XML";
|
||||
private static final List<String> CELLEBRITE_EXTS = Arrays.asList(new String[]{".xml"});
|
||||
private static final String CELLEBRITE_DESC = "Cellebrite XML Files (*.xml)";
|
||||
private static final GeneralFilter xmlFilter = new GeneralFilter(CELLEBRITE_EXTS, CELLEBRITE_DESC);
|
||||
private static final List<FileFilter> filtersList = new ArrayList<>();
|
||||
private final CellebriteXMLFilePanel configPanel;
|
||||
private AddCellebriteXMLTask addCellebriteXMLTask;
|
||||
|
||||
static {
|
||||
filtersList.add(xmlFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file extensions supported by this data source processor as a
|
||||
* list of file filters.
|
||||
*
|
||||
* @return List<FileFilter> List of FileFilter objects
|
||||
*/
|
||||
public static final List<FileFilter> getFileFilterList() {
|
||||
return filtersList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a Cellebrite XML report 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 CellebriteXMLProcessor() {
|
||||
configPanel = CellebriteXMLFilePanel.createInstance(CellebriteXMLProcessor.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.
|
||||
*/
|
||||
@Override
|
||||
public String getDataSourceType() {
|
||||
return DATA_SOURCE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
configPanel.storeSettings();
|
||||
String deviceId = UUID.randomUUID().toString();
|
||||
run(deviceId, deviceId, configPanel.getImageFilePath(), configPanel.isHandsetFile(), 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 rootVirtualDirectoryName The name to give to the virtual directory
|
||||
* that will represent the data source. Pass
|
||||
* the empty string to get a default name of
|
||||
* the form: LogicalFileSet[N]
|
||||
* @param cellebriteXmlFilePath Path to a Cellebrite report XML file.
|
||||
* @param isHandsetFile Indicates whether the XML file is for a
|
||||
* handset or a SIM.
|
||||
* @param progressMonitor Progress monitor for reporting progress
|
||||
* during processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
public void run(String deviceId, String rootVirtualDirectoryName, String cellebriteXmlFilePath, boolean isHandsetFile, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
AddCellebriteXMLTask.CellebriteInputType inputType;
|
||||
if (isHandsetFile) {
|
||||
inputType = AddCellebriteXMLTask.CellebriteInputType.handset;
|
||||
} else {
|
||||
inputType = AddCellebriteXMLTask.CellebriteInputType.SIM;
|
||||
}
|
||||
addCellebriteXMLTask = new AddCellebriteXMLTask(deviceId, rootVirtualDirectoryName, cellebriteXmlFilePath, inputType, progressMonitor, callback);
|
||||
new Thread(addCellebriteXMLTask).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() {
|
||||
addCellebriteXMLTask.cancelTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection and configuration panel for this data source
|
||||
* processor.
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
configPanel.reset();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.cellex.datasourceprocessors;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import static java.lang.Math.min;
|
||||
import java.util.Collection;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author flynn
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
public interface Mapper<T1, T2> {
|
||||
|
||||
public T2 apply(T1 x);
|
||||
}
|
||||
|
||||
public static void copyToFile(AbstractFile inputFile, String destPath)
|
||||
throws IOException, TskCoreException {
|
||||
long inputSize = inputFile.getSize();
|
||||
long bufSize = min(inputSize, 65536L);
|
||||
|
||||
byte[] buffer = new byte[(int) bufSize];
|
||||
|
||||
FileOutputStream output = new FileOutputStream(destPath);
|
||||
|
||||
long offset = 0;
|
||||
long bytesLeft = inputSize;
|
||||
|
||||
try {
|
||||
while (bytesLeft > 0) {
|
||||
int bytesRead = inputFile.read(buffer, offset, bufSize);
|
||||
|
||||
if (bytesRead < 0) {
|
||||
throw new IOException("I/O error (rc " + bytesRead + ")");
|
||||
}
|
||||
|
||||
output.write(buffer);
|
||||
|
||||
bytesLeft -= bytesRead;
|
||||
offset += bytesRead;
|
||||
}
|
||||
} finally {
|
||||
// Always always always close output, no matter what.
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getBasename(String path) {
|
||||
String baseName = path;
|
||||
int lastSlash = baseName.lastIndexOf(File.separator);
|
||||
|
||||
if (lastSlash >= 0) {
|
||||
baseName = baseName.substring(lastSlash + 1);
|
||||
}
|
||||
|
||||
return baseName;
|
||||
}
|
||||
|
||||
public static String stripExtension(String path) {
|
||||
String noExt = path;
|
||||
|
||||
int lastPeriod = path.lastIndexOf('.');
|
||||
|
||||
if (lastPeriod > 0) { // Not >=, >. A single "." should be preserved.
|
||||
noExt = path.substring(0, lastPeriod);
|
||||
}
|
||||
|
||||
return noExt;
|
||||
}
|
||||
|
||||
public static String joinPath(String... elements) {
|
||||
return join(File.separator, elements);
|
||||
}
|
||||
|
||||
public static String join(String delim, Mapper<String, String> mapFunc,
|
||||
String... elements) {
|
||||
String joined = "";
|
||||
|
||||
for (String element : elements) {
|
||||
if (mapFunc != null) {
|
||||
element = mapFunc.apply(element);
|
||||
}
|
||||
|
||||
if ((element != null) && (element.length() > 0)) {
|
||||
if (joined.length() > 0) {
|
||||
joined += delim;
|
||||
}
|
||||
|
||||
joined += element;
|
||||
}
|
||||
}
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
public static String join(String delim, Mapper<String, String> mapFunc,
|
||||
Collection<String> elements) {
|
||||
return join(delim, mapFunc,
|
||||
elements.toArray(new String[elements.size()]));
|
||||
}
|
||||
|
||||
public static String join(String delim, String... elements) {
|
||||
return join(delim, null, elements);
|
||||
}
|
||||
|
||||
public static String join(String delim, Collection<String> elements) {
|
||||
return join(delim, null, elements);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user