mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 07:56:16 +00:00
Merge remote-tracking branch 'upstream/develop' into 7261_addHostsToProcess
This commit is contained in:
commit
cfa8700bb5
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -13,3 +13,9 @@ Doxyfile text
|
|||||||
|
|
||||||
*.py text diff=python
|
*.py text diff=python
|
||||||
*.pl text
|
*.pl text
|
||||||
|
|
||||||
|
# ensure solr scripts that are bash scripts not ending with.sh are lf instead of crlf
|
||||||
|
/KeywordSearch/solr/bin/autopsy-solr eol=lf
|
||||||
|
/KeywordSearch/solr/bin/init.d/solr eol=lf
|
||||||
|
/KeywordSearch/solr/bin/post eol=lf
|
||||||
|
/KeywordSearch/solr/bin/solr eol=lf
|
@ -183,11 +183,32 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for this data source.
|
||||||
|
* @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(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
ingestStream = new DefaultIngestStream();
|
ingestStream = new DefaultIngestStream();
|
||||||
readConfigSettings();
|
readConfigSettings();
|
||||||
|
this.host = host;
|
||||||
try {
|
try {
|
||||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||||
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId);
|
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
||||||
final List<String> errors = new ArrayList<>();
|
final List<String> errors = new ArrayList<>();
|
||||||
@ -219,14 +240,47 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
|||||||
@Override
|
@Override
|
||||||
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||||
DataSourceProcessorCallback callBack) {
|
DataSourceProcessorCallback callBack) {
|
||||||
|
runWithIngestStream(null, settings, progress, callBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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. Files found during ingest will be sent directly to the
|
||||||
|
* IngestStream provided. 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, and
|
||||||
|
* should only be called for DSPs that support ingest streams.
|
||||||
|
*
|
||||||
|
* @param host The host for this data source.
|
||||||
|
* @param settings The ingest job settings.
|
||||||
|
* @param progress 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 runWithIngestStream(Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||||
|
DataSourceProcessorCallback callBack) {
|
||||||
|
|
||||||
// Read the settings from the wizard
|
// Read the settings from the wizard
|
||||||
readConfigSettings();
|
readConfigSettings();
|
||||||
|
this.host = host;
|
||||||
|
|
||||||
|
// HOSTTODO - remove once passing in a host
|
||||||
|
try {
|
||||||
|
this.host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("ImageDSProcessor Host");
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
||||||
|
this.host = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the data source before creating the ingest stream
|
// Set up the data source before creating the ingest stream
|
||||||
try {
|
try {
|
||||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||||
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, host);
|
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
||||||
final List<String> errors = new ArrayList<>();
|
final List<String> errors = new ArrayList<>();
|
||||||
@ -273,14 +327,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
|||||||
if (sha256.isEmpty()) {
|
if (sha256.isEmpty()) {
|
||||||
sha256 = null;
|
sha256 = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HOSTTODO - this will come from the config panel
|
|
||||||
try {
|
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("ImageDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
host = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,26 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for this data source.
|
||||||
|
* @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(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
if (!setDataSourceOptionsCalled) {
|
if (!setDataSourceOptionsCalled) {
|
||||||
deviceId = UUID.randomUUID().toString();
|
deviceId = UUID.randomUUID().toString();
|
||||||
drivePath = configPanel.getContentPath();
|
drivePath = configPanel.getContentPath();
|
||||||
@ -148,21 +168,22 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
|||||||
} else {
|
} else {
|
||||||
imageWriterSettings = null;
|
imageWriterSettings = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// HOSTTODO - set to value from config panel
|
|
||||||
try {
|
this.host = host;
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalDiskDSProcessor Host");
|
// HOSTTODO - set to value from config panel
|
||||||
} catch (TskCoreException ex) {
|
try {
|
||||||
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
this.host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalDiskDSProcessor Host");
|
||||||
host = null;
|
} catch (TskCoreException ex) {
|
||||||
}
|
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
||||||
|
this.host = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image image;
|
Image image;
|
||||||
try {
|
try {
|
||||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||||
new String[]{drivePath}, sectorSize,
|
new String[]{drivePath}, sectorSize,
|
||||||
timeZone, null, null, null, deviceId, host);
|
timeZone, null, null, null, deviceId, this.host);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
|
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
|
||||||
final List<String> errors = new ArrayList<>();
|
final List<String> errors = new ArrayList<>();
|
||||||
@ -172,7 +193,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addDiskTask = new AddImageTask(
|
addDiskTask = new AddImageTask(
|
||||||
new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, host, imageWriterSettings),
|
new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, this.host, imageWriterSettings),
|
||||||
progressMonitor,
|
progressMonitor,
|
||||||
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
|
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
|
||||||
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
|
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
|
||||||
|
@ -139,7 +139,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
|||||||
return configPanel.validatePanel();
|
return configPanel.validatePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a data source to the case database using a background task in a
|
* Adds a data source to the case database using a background task in a
|
||||||
* separate thread and the settings provided by the selection and
|
* separate thread and the settings provided by the selection and
|
||||||
* configuration panel. Returns as soon as the background task is started.
|
* configuration panel. Returns as soon as the background task is started.
|
||||||
@ -155,10 +155,29 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
Host host = null;
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for this data source.
|
||||||
|
* @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(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
if (!setDataSourceOptionsCalled) {
|
if (!setDataSourceOptionsCalled) {
|
||||||
|
|
||||||
// HOSTTODO - set to value from config panel
|
// HOSTTODO - use passed in value
|
||||||
try {
|
try {
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalFilesDSProcessor Host");
|
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalFilesDSProcessor Host");
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -61,19 +61,19 @@ final public class TagNameDefinition implements Comparable<TagNameDefinition> {
|
|||||||
private final TskData.FileKnown knownStatus;
|
private final TskData.FileKnown knownStatus;
|
||||||
|
|
||||||
private static final List<TagNameDefinition> STANDARD_TAGS_DEFINITIONS = new ArrayList<>();
|
private static final List<TagNameDefinition> STANDARD_TAGS_DEFINITIONS = new ArrayList<>();
|
||||||
private static final List<String> OLD_CATEGORY_TAG_NAMES = new ArrayList<>();
|
private static final List<String> PROJECT_VIC_NAMES_NO_LONGER_USED = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
STANDARD_TAGS_DEFINITIONS.add(new TagNameDefinition(Bundle.TagNameDefinition_predefTagNames_bookmark_text(), "", TagName.HTML_COLOR.NONE, TskData.FileKnown.UNKNOWN));
|
STANDARD_TAGS_DEFINITIONS.add(new TagNameDefinition(Bundle.TagNameDefinition_predefTagNames_bookmark_text(), "", TagName.HTML_COLOR.NONE, TskData.FileKnown.UNKNOWN));
|
||||||
STANDARD_TAGS_DEFINITIONS.add(new TagNameDefinition(Bundle.TagNameDefinition_predefTagNames_followUp_text(), "", TagName.HTML_COLOR.NONE, TskData.FileKnown.UNKNOWN));
|
STANDARD_TAGS_DEFINITIONS.add(new TagNameDefinition(Bundle.TagNameDefinition_predefTagNames_followUp_text(), "", TagName.HTML_COLOR.NONE, TskData.FileKnown.UNKNOWN));
|
||||||
STANDARD_TAGS_DEFINITIONS.add(new TagNameDefinition(Bundle.TagNameDefinition_predefTagNames_notableItem_text(), "", TagName.HTML_COLOR.NONE, TskData.FileKnown.BAD));
|
STANDARD_TAGS_DEFINITIONS.add(new TagNameDefinition(Bundle.TagNameDefinition_predefTagNames_notableItem_text(), "", TagName.HTML_COLOR.NONE, TskData.FileKnown.BAD));
|
||||||
|
|
||||||
OLD_CATEGORY_TAG_NAMES.add("CAT-1: Child Exploitation (Illegal)");
|
PROJECT_VIC_NAMES_NO_LONGER_USED.add("CAT-1: Child Exploitation (Illegal)");
|
||||||
OLD_CATEGORY_TAG_NAMES.add("CAT-2: Child Exploitation (Non-Illegal/Age Difficult)");
|
PROJECT_VIC_NAMES_NO_LONGER_USED.add("CAT-2: Child Exploitation (Non-Illegal/Age Difficult)");
|
||||||
OLD_CATEGORY_TAG_NAMES.add("CAT-3: CGI/Animation (Child Exploitive)");
|
PROJECT_VIC_NAMES_NO_LONGER_USED.add("CAT-3: CGI/Animation (Child Exploitive)");
|
||||||
OLD_CATEGORY_TAG_NAMES.add("CAT-4: Exemplar/Comparison (Internal Use Only)");
|
PROJECT_VIC_NAMES_NO_LONGER_USED.add("CAT-4: Exemplar/Comparison (Internal Use Only)");
|
||||||
OLD_CATEGORY_TAG_NAMES.add("CAT-5: Non-pertinent");
|
PROJECT_VIC_NAMES_NO_LONGER_USED.add("CAT-5: Non-pertinent");
|
||||||
OLD_CATEGORY_TAG_NAMES.add("CAT-0: Uncategorized");
|
PROJECT_VIC_NAMES_NO_LONGER_USED.add("CAT-0: Uncategorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,7 +259,7 @@ final public class TagNameDefinition implements Comparable<TagNameDefinition> {
|
|||||||
*/
|
*/
|
||||||
static synchronized Set<TagNameDefinition> getTagNameDefinitions() {
|
static synchronized Set<TagNameDefinition> getTagNameDefinitions() {
|
||||||
if (needsVersionUpdate()) {
|
if (needsVersionUpdate()) {
|
||||||
updateTagDefinitions();
|
updatePropertyFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
String tagsProperty = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
|
String tagsProperty = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
|
||||||
@ -311,7 +311,7 @@ final public class TagNameDefinition implements Comparable<TagNameDefinition> {
|
|||||||
/**
|
/**
|
||||||
* Updates the Tag Definition file to the current format.
|
* Updates the Tag Definition file to the current format.
|
||||||
*/
|
*/
|
||||||
private static void updateTagDefinitions() {
|
private static void updatePropertyFile() {
|
||||||
Integer version = getPropertyFileVersion();
|
Integer version = getPropertyFileVersion();
|
||||||
List<TagNameDefinition> definitions = new ArrayList<>();
|
List<TagNameDefinition> definitions = new ArrayList<>();
|
||||||
|
|
||||||
@ -355,18 +355,18 @@ final public class TagNameDefinition implements Comparable<TagNameDefinition> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove the standard and Project VIC tags from the list
|
// Remove the standard and Project VIC tags from the list
|
||||||
List<String> tagStrings = new ArrayList<>();
|
List<String> tagStringsToKeep = new ArrayList<>();
|
||||||
List<String> standardTags = getStandardTagNames();
|
List<String> standardTags = getStandardTagNames();
|
||||||
for (TagNameDefinition def : definitions) {
|
for (TagNameDefinition def : definitions) {
|
||||||
if (!standardTags.contains(def.getDisplayName())
|
if (!standardTags.contains(def.getDisplayName())
|
||||||
&& !OLD_CATEGORY_TAG_NAMES.contains(def.getDisplayName())) {
|
&& !PROJECT_VIC_NAMES_NO_LONGER_USED.contains(def.getDisplayName())) {
|
||||||
tagStrings.add(def.toSettingsFormat());
|
tagStringsToKeep.add(def.toSettingsFormat());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out the version and the new tag list.
|
// Write out the version and the new tag list.
|
||||||
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_SETTING_VERSION_KEY, Integer.toString(TAG_SETTINGS_VERSION));
|
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_SETTING_VERSION_KEY, Integer.toString(TAG_SETTINGS_VERSION));
|
||||||
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, String.join(";", tagStrings));
|
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, String.join(";", tagStringsToKeep));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +88,7 @@ final public class TagSetDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of the defined TagSet objects.
|
* Returns a list of configured TagSets (from the user's config folder)
|
||||||
*
|
*
|
||||||
* @return A list of TagSetDefinition objects or empty list if none were
|
* @return A list of TagSetDefinition objects or empty list if none were
|
||||||
* found.
|
* found.
|
||||||
|
@ -55,7 +55,9 @@ public class TagsManager implements Closeable {
|
|||||||
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
|
||||||
private final SleuthkitCase caseDb;
|
private final SleuthkitCase caseDb;
|
||||||
|
|
||||||
private static String DEFAULT_TAG_SET_NAME = "Project VIC";
|
// NOTE: This name is also hard coded in Image Gallery and Projet Vic module.
|
||||||
|
// They need to stay in sync
|
||||||
|
private static String PROJECT_VIC_TAG_SET_NAME = "Project VIC";
|
||||||
|
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
|
|
||||||
@ -196,7 +198,7 @@ public class TagsManager implements Closeable {
|
|||||||
try {
|
try {
|
||||||
List<TagSet> tagSetList = Case.getCurrentCaseThrows().getSleuthkitCase().getTaggingManager().getTagSets();
|
List<TagSet> tagSetList = Case.getCurrentCaseThrows().getSleuthkitCase().getTaggingManager().getTagSets();
|
||||||
for (TagSet tagSet : tagSetList) {
|
for (TagSet tagSet : tagSetList) {
|
||||||
if (tagSet.getName().equals(DEFAULT_TAG_SET_NAME)) {
|
if (tagSet.getName().equals(PROJECT_VIC_TAG_SET_NAME)) {
|
||||||
for (TagName tagName : tagSet.getTagNames()) {
|
for (TagName tagName : tagSet.getTagNames()) {
|
||||||
tagList.add(tagName.getDisplayName());
|
tagList.add(tagName.getDisplayName());
|
||||||
}
|
}
|
||||||
@ -237,7 +239,7 @@ public class TagsManager implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new TagSetDefinition file.
|
* Creates a new TagSetDefinition file that will be used for future cases
|
||||||
*
|
*
|
||||||
* @param tagSetDef The tag set definition.
|
* @param tagSetDef The tag set definition.
|
||||||
*
|
*
|
||||||
@ -258,23 +260,26 @@ public class TagsManager implements Closeable {
|
|||||||
TagsManager(SleuthkitCase caseDb) {
|
TagsManager(SleuthkitCase caseDb) {
|
||||||
this.caseDb = caseDb;
|
this.caseDb = caseDb;
|
||||||
|
|
||||||
// Add standard tags and the Project VIC default tag set and tags.
|
// Add standard tags and any configured tag sets.
|
||||||
TaggingManager taggingMgr = caseDb.getTaggingManager();
|
TaggingManager taggingMgr = caseDb.getTaggingManager();
|
||||||
try {
|
try {
|
||||||
List<TagSet> setList = taggingMgr.getTagSets();
|
List<TagSet> tagSetsInCase = taggingMgr.getTagSets();
|
||||||
if (setList.isEmpty()) {
|
if (tagSetsInCase.isEmpty()) {
|
||||||
|
|
||||||
|
// add the standard tag names
|
||||||
for (TagNameDefinition def : TagNameDefinition.getStandardTagNameDefinitions()) {
|
for (TagNameDefinition def : TagNameDefinition.getStandardTagNameDefinitions()) {
|
||||||
caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus());
|
caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus());
|
||||||
}
|
}
|
||||||
//Assume new case and add tag sets
|
|
||||||
|
//Assume new case and add all tag sets
|
||||||
for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) {
|
for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) {
|
||||||
List<TagName> tagNameList = new ArrayList<>();
|
List<TagName> tagNamesInSet = new ArrayList<>();
|
||||||
for (TagNameDefinition tagNameDef : setDef.getTagNameDefinitions()) {
|
for (TagNameDefinition tagNameDef : setDef.getTagNameDefinitions()) {
|
||||||
tagNameList.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus()));
|
tagNamesInSet.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tagNameList.isEmpty()) {
|
if (!tagNamesInSet.isEmpty()) {
|
||||||
taggingMgr.addTagSet(setDef.getName(), tagNameList);
|
taggingMgr.addTagSet(setDef.getName(), tagNamesInSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.validator.routines.DomainValidator;
|
import org.apache.commons.validator.routines.DomainValidator;
|
||||||
import org.apache.commons.validator.routines.EmailValidator;
|
import org.apache.commons.validator.routines.EmailValidator;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides functions for normalizing data by attribute type before insertion or
|
* Provides functions for normalizing data by attribute type before insertion or
|
||||||
@ -144,11 +145,11 @@ final public class CorrelationAttributeNormalizer {
|
|||||||
private static String normalizeDomain(String data) throws CorrelationAttributeNormalizationException {
|
private static String normalizeDomain(String data) throws CorrelationAttributeNormalizationException {
|
||||||
DomainValidator validator = DomainValidator.getInstance(true);
|
DomainValidator validator = DomainValidator.getInstance(true);
|
||||||
if (validator.isValid(data)) {
|
if (validator.isValid(data)) {
|
||||||
return data.toLowerCase();
|
return NetworkUtils.extractDomain(data.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
final String validIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
|
final String validIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
|
||||||
if (data.matches(validIpAddressRegex)) {
|
if (data.matches(validIpAddressRegex)) {
|
||||||
return data;
|
return NetworkUtils.extractDomain(data);
|
||||||
} else {
|
} else {
|
||||||
throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid domain: %s", data));
|
throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid domain: %s", data));
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import org.openide.util.NbPreferences;
|
|||||||
import org.python.icu.util.TimeZone;
|
import org.python.icu.util.TimeZone;
|
||||||
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
|
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
|
||||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.TextConverterException;
|
import org.sleuthkit.autopsy.coreutils.TextConverterException;
|
||||||
import org.sleuthkit.autopsy.coreutils.Version;
|
import org.sleuthkit.autopsy.coreutils.Version;
|
||||||
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
|
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
|
||||||
@ -84,7 +85,8 @@ public final class UserPreferences {
|
|||||||
private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true;
|
private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true;
|
||||||
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
|
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
|
||||||
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
|
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
|
||||||
private static final int DEFAULT_SOLR_HEAP_SIZE_MB = 2048;
|
private static final int DEFAULT_SOLR_HEAP_SIZE_MB_64BIT_PLATFORM = 2048;
|
||||||
|
private static final int DEFAULT_SOLR_HEAP_SIZE_MB_32BIT_PLATFORM = 512;
|
||||||
public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize";
|
public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize";
|
||||||
private static final String GEO_TILE_OPTION = "GeolocationTileOption";
|
private static final String GEO_TILE_OPTION = "GeolocationTileOption";
|
||||||
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
|
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
|
||||||
@ -534,12 +536,17 @@ public final class UserPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum JVM heap size (in MB) for the embedded Solr server.
|
* Get the maximum JVM heap size (in MB) for the embedded Solr server. The returned value
|
||||||
|
* depends on the platform (64bit vs 32bit).
|
||||||
*
|
*
|
||||||
* @return Saved value or default (2 GB)
|
* @return Saved value or default (2 GB for 64bit platforms, 512MB for 32bit)
|
||||||
*/
|
*/
|
||||||
public static int getMaxSolrVMSize() {
|
public static int getMaxSolrVMSize() {
|
||||||
return preferences.getInt(SOLR_MAX_JVM_SIZE, DEFAULT_SOLR_HEAP_SIZE_MB);
|
if (PlatformUtil.is64BitJVM()) {
|
||||||
|
return preferences.getInt(SOLR_MAX_JVM_SIZE, DEFAULT_SOLR_HEAP_SIZE_MB_64BIT_PLATFORM);
|
||||||
|
} else {
|
||||||
|
return preferences.getInt(SOLR_MAX_JVM_SIZE, DEFAULT_SOLR_HEAP_SIZE_MB_32BIT_PLATFORM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.corecomponentinterfaces;
|
|||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface implemented by classes that add data sources of a particular type
|
* Interface implemented by classes that add data sources of a particular type
|
||||||
@ -109,6 +110,25 @@ public interface DataSourceProcessor {
|
|||||||
*/
|
*/
|
||||||
void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback);
|
void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for the data source.
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
default void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a data source to the case database using a background task in a
|
* Adds a data source to the case database using a background task in a
|
||||||
* separate thread and the settings provided by the selection and
|
* separate thread and the settings provided by the selection and
|
||||||
@ -131,6 +151,30 @@ public interface DataSourceProcessor {
|
|||||||
DataSourceProcessorCallback callBack) {
|
DataSourceProcessorCallback callBack) {
|
||||||
throw new UnsupportedOperationException("Streaming ingest not supported for this data source processor");
|
throw new UnsupportedOperationException("Streaming ingest not supported for this data source processor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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. Files found during ingest will be sent directly to
|
||||||
|
* the IngestStream provided. 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, and
|
||||||
|
* should only be called for DSPs that support ingest streams. The ingest
|
||||||
|
* settings must be complete before calling this method.
|
||||||
|
*
|
||||||
|
* @param host Host for this data source.
|
||||||
|
* @param settings The ingest job settings.
|
||||||
|
* @param progress 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.
|
||||||
|
*/
|
||||||
|
default void runWithIngestStream(Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||||
|
DataSourceProcessorCallback callBack) {
|
||||||
|
runWithIngestStream(settings, progress, callBack);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this DSP supports ingest streams.
|
* Check if this DSP supports ingest streams.
|
||||||
|
@ -30,6 +30,7 @@ import org.openide.util.NbBundle;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum to represent the six categories in the DHS image categorization scheme.
|
* Enum to represent the six categories in the DHS image categorization scheme.
|
||||||
|
* NOTE: This appears to not be used anywhere anymore after the ImageGallery refactoring
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"Category.one=CAT-1: Child Exploitation (Illegal)",
|
"Category.one=CAT-1: Child Exploitation (Illegal)",
|
||||||
|
@ -389,11 +389,6 @@ public final class FileTypes implements AutopsyVisitableItem {
|
|||||||
return content.newArtifact(artifactTypeID);
|
return content.newArtifact(artifactTypeID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList) throws TskCoreException {
|
|
||||||
return content.newAnalysisResult(artifactType, score, conclusion, configuration, justification, attributesList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
|
public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
|
||||||
return content.newArtifact(type);
|
return content.newArtifact(type);
|
||||||
@ -434,15 +429,6 @@ public final class FileTypes implements AutopsyVisitableItem {
|
|||||||
return content.getAllArtifacts();
|
return content.getAllArtifacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<AnalysisResult> getAllAnalysisResults() throws TskCoreException {
|
|
||||||
return content.getAllAnalysisResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<AnalysisResult> getAnalysisResults(BlackboardArtifact.Type artifactType) throws TskCoreException {
|
|
||||||
return content.getAnalysisResults(artifactType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getHashSetNames() throws TskCoreException {
|
public Set<String> getHashSetNames() throws TskCoreException {
|
||||||
return content.getHashSetNames();
|
return content.getHashSetNames();
|
||||||
@ -468,9 +454,24 @@ public final class FileTypes implements AutopsyVisitableItem {
|
|||||||
return content.getAllArtifactsCount();
|
return content.getAllArtifactsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type type, Score score, String string, String string1, String string2, Collection<BlackboardAttribute> clctn) throws TskCoreException {
|
||||||
|
return content.newAnalysisResult(type, score, string, string1, string2, clctn);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Score getAggregateScore() throws TskCoreException {
|
public Score getAggregateScore() throws TskCoreException {
|
||||||
return content.getAggregateScore();
|
return content.getAggregateScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AnalysisResult> getAnalysisResults(BlackboardArtifact.Type type) throws TskCoreException {
|
||||||
|
return content.getAnalysisResults(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AnalysisResult> getAllAnalysisResults() throws TskCoreException {
|
||||||
|
return content.getAllAnalysisResults();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,10 +138,29 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for the data source.
|
||||||
|
* @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(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
|
|
||||||
// HOSTTODO - replace with a call to configPanel().getHost()
|
// HOSTTODO - use passed in value
|
||||||
Host host;
|
|
||||||
try {
|
try {
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("RawDSProcessor Host");
|
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("RawDSProcessor Host");
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -194,14 +194,33 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
* in isPanelValid().
|
* in isPanelValid().
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the XRY folder that the examiner selected. The heavy lifting is
|
||||||
|
* done off of the EDT, so this function will return while the
|
||||||
|
* path is still being processed.
|
||||||
|
*
|
||||||
|
* This function assumes the calling thread has sufficient privileges to
|
||||||
|
* read the folder and its child content, which should have been validated
|
||||||
|
* in isPanelValid().
|
||||||
|
*
|
||||||
|
* @param host Host for the data source.
|
||||||
|
* @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
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"XRYDataSourceProcessor.noCurrentCase=No case is open."
|
"XRYDataSourceProcessor.noCurrentCase=No case is open."
|
||||||
})
|
})
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
progressMonitor.setIndeterminate(true);
|
progressMonitor.setIndeterminate(true);
|
||||||
|
|
||||||
// HOSTTODO - replace with a call to configPanel().getHost()
|
// HOSTTODO - use passed in value
|
||||||
Host host;
|
|
||||||
try {
|
try {
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("XRYDSProcessor Host");
|
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("XRYDSProcessor Host");
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.discovery.ui;
|
package org.sleuthkit.autopsy.discovery.ui;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -63,6 +64,7 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
|
|||||||
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
||||||
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
||||||
}
|
}
|
||||||
|
setMinimumSize(new Dimension(125, 20));
|
||||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
<NonVisualComponents>
|
<NonVisualComponents>
|
||||||
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="dividerLocation" type="int" value="350"/>
|
<Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
<Property name="resizeWeight" type="double" value="0.1"/>
|
<Connection code="dividerLocation" type="code"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="resizeWeight" type="double" value="0.2"/>
|
||||||
|
<Property name="lastDividerLocation" type="int" value="250"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||||
|
@ -20,8 +20,12 @@ package org.sleuthkit.autopsy.discovery.ui;
|
|||||||
|
|
||||||
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
|
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
import javax.swing.event.ListSelectionListener;
|
import javax.swing.event.ListSelectionListener;
|
||||||
@ -41,13 +45,18 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
private final ArtifactsListPanel listPanel;
|
private final ArtifactsListPanel listPanel;
|
||||||
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
|
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
|
||||||
private AbstractArtifactDetailsPanel rightPanel = null;
|
private AbstractArtifactDetailsPanel rightPanel = null;
|
||||||
|
private int dividerLocation = 300;
|
||||||
|
private final PropertyChangeListener dividerListener;
|
||||||
|
|
||||||
private ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED;
|
private ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED;
|
||||||
private final ListSelectionListener listener = new ListSelectionListener() {
|
private final ListSelectionListener listener = new ListSelectionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void valueChanged(ListSelectionEvent event) {
|
public void valueChanged(ListSelectionEvent event) {
|
||||||
if (!event.getValueIsAdjusting()) {
|
if (!event.getValueIsAdjusting()) {
|
||||||
|
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
rightPanel.setArtifact(listPanel.getSelectedArtifact());
|
rightPanel.setArtifact(listPanel.getSelectedArtifact());
|
||||||
|
mainSplitPane.setDividerLocation(dividerLocation);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -60,12 +69,27 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) {
|
DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
dividerListener = new PropertyChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
|
||||||
|
&& evt.getNewValue() instanceof Integer
|
||||||
|
&& evt.getOldValue() instanceof Integer
|
||||||
|
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())) {
|
||||||
|
dividerLocation = (int) evt.getNewValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
this.artifactType = type;
|
this.artifactType = type;
|
||||||
listPanel = new ArtifactsListPanel(artifactType);
|
listPanel = new ArtifactsListPanel(artifactType);
|
||||||
|
listPanel.setPreferredSize(new Dimension(100, 20));
|
||||||
listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel));
|
listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel));
|
||||||
|
|
||||||
mainSplitPane.setLeftComponent(listPanel);
|
mainSplitPane.setLeftComponent(listPanel);
|
||||||
add(mainSplitPane);
|
add(mainSplitPane);
|
||||||
setRightComponent();
|
setRightComponent();
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
|
dividerLocation = mainSplitPane.getDividerLocation();
|
||||||
listPanel.addSelectionListener(listener);
|
listPanel.addSelectionListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +145,7 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
void setStatus(ArtifactRetrievalStatus status) {
|
void setStatus(ArtifactRetrievalStatus status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
if (status == ArtifactRetrievalStatus.UNPOPULATED) {
|
if (status == ArtifactRetrievalStatus.UNPOPULATED) {
|
||||||
listPanel.clearList();
|
listPanel.clearList();
|
||||||
removeAll();
|
removeAll();
|
||||||
@ -132,6 +157,8 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
removeAll();
|
removeAll();
|
||||||
add(new LoadingPanel(artifactType.getDisplayName()));
|
add(new LoadingPanel(artifactType.getDisplayName()));
|
||||||
}
|
}
|
||||||
|
mainSplitPane.setDividerLocation(dividerLocation);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,6 +171,7 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
|
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
|
||||||
if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) {
|
if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
listPanel.removeSelectionListener(listener);
|
listPanel.removeSelectionListener(listener);
|
||||||
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
|
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
|
||||||
status = ArtifactRetrievalStatus.POPULATED;
|
status = ArtifactRetrievalStatus.POPULATED;
|
||||||
@ -152,6 +180,8 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
listPanel.selectFirst();
|
listPanel.selectFirst();
|
||||||
removeAll();
|
removeAll();
|
||||||
add(mainSplitPane);
|
add(mainSplitPane);
|
||||||
|
mainSplitPane.setDividerLocation(dividerLocation);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
if (artifactresultEvent.shouldGrabFocus()) {
|
if (artifactresultEvent.shouldGrabFocus()) {
|
||||||
focusList();
|
focusList();
|
||||||
}
|
}
|
||||||
@ -188,8 +218,9 @@ final class DomainArtifactsTabPanel extends JPanel {
|
|||||||
|
|
||||||
mainSplitPane = new javax.swing.JSplitPane();
|
mainSplitPane = new javax.swing.JSplitPane();
|
||||||
|
|
||||||
mainSplitPane.setDividerLocation(350);
|
mainSplitPane.setDividerLocation(dividerLocation);
|
||||||
mainSplitPane.setResizeWeight(0.1);
|
mainSplitPane.setResizeWeight(0.2);
|
||||||
|
mainSplitPane.setLastDividerLocation(250);
|
||||||
|
|
||||||
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
|
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
|
||||||
setMinimumSize(new java.awt.Dimension(0, 0));
|
setMinimumSize(new java.awt.Dimension(0, 0));
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.discovery.ui;
|
package org.sleuthkit.autopsy.discovery.ui;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -39,7 +40,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
/**
|
/**
|
||||||
* Panel to display list of artifacts types and descriptions.
|
* Panel to display list of artifacts types and descriptions.
|
||||||
*/
|
*/
|
||||||
class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
|
final class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final TypeDescriptionTableModel tableModel;
|
private final TypeDescriptionTableModel tableModel;
|
||||||
@ -61,6 +62,7 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
|
|||||||
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
||||||
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
||||||
}
|
}
|
||||||
|
setMinimumSize(new Dimension(125, 20));
|
||||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.discovery.ui;
|
package org.sleuthkit.autopsy.discovery.ui;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@ -33,7 +34,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
|
|||||||
/**
|
/**
|
||||||
* Panel to display list of dates and counts.
|
* Panel to display list of dates and counts.
|
||||||
*/
|
*/
|
||||||
class MiniTimelineDateListPanel extends JPanel {
|
final class MiniTimelineDateListPanel extends JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final DateCountTableModel tableModel = new DateCountTableModel();
|
private final DateCountTableModel tableModel = new DateCountTableModel();
|
||||||
@ -49,6 +50,7 @@ class MiniTimelineDateListPanel extends JPanel {
|
|||||||
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
||||||
jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
||||||
}
|
}
|
||||||
|
setMinimumSize(new Dimension(125, 20));
|
||||||
jTable1.getRowSorter().toggleSortOrder(0);
|
jTable1.getRowSorter().toggleSortOrder(0);
|
||||||
jTable1.getRowSorter().toggleSortOrder(0);
|
jTable1.getRowSorter().toggleSortOrder(0);
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
<NonVisualComponents>
|
<NonVisualComponents>
|
||||||
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="dividerLocation" type="int" value="400"/>
|
<Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
<Property name="resizeWeight" type="double" value="0.1"/>
|
<Connection code="mainSplitPaneDividerLocation" type="code"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="resizeWeight" type="double" value="0.2"/>
|
||||||
<Property name="toolTipText" type="java.lang.String" value=""/>
|
<Property name="toolTipText" type="java.lang.String" value=""/>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[0, 0]"/>
|
<Dimension value="[0, 0]"/>
|
||||||
@ -16,7 +18,9 @@
|
|||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
|
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="dividerLocation" type="int" value="198"/>
|
<Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
|
<Connection code="leftSplitPaneDividerLocation" type="code"/>
|
||||||
|
</Property>
|
||||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[0, 0]"/>
|
<Dimension value="[0, 0]"/>
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
package org.sleuthkit.autopsy.discovery.ui;
|
package org.sleuthkit.autopsy.discovery.ui;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
import javax.swing.event.ListSelectionListener;
|
import javax.swing.event.ListSelectionListener;
|
||||||
@ -42,6 +46,9 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
|||||||
private String selectedDomain = null;
|
private String selectedDomain = null;
|
||||||
private final ListSelectionListener artifactListener;
|
private final ListSelectionListener artifactListener;
|
||||||
private final ListSelectionListener dateListener;
|
private final ListSelectionListener dateListener;
|
||||||
|
private int leftSplitPaneDividerLocation = 125;
|
||||||
|
private int mainSplitPaneDividerLocation = 300;
|
||||||
|
private final PropertyChangeListener dividerListener;
|
||||||
|
|
||||||
@NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"})
|
@NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"})
|
||||||
/**
|
/**
|
||||||
@ -62,22 +69,51 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
|||||||
} else {
|
} else {
|
||||||
rightPanel = new GeneralPurposeArtifactViewer();
|
rightPanel = new GeneralPurposeArtifactViewer();
|
||||||
}
|
}
|
||||||
|
leftSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
|
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
mainSplitPane.setRightComponent(rightPanel.getComponent());
|
mainSplitPane.setRightComponent(rightPanel.getComponent());
|
||||||
rightPanel.setArtifact(artifact);
|
rightPanel.setArtifact(artifact);
|
||||||
|
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||||
|
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
validate();
|
validate();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
dividerListener = new PropertyChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
|
||||||
|
&& evt.getNewValue() instanceof Integer
|
||||||
|
&& evt.getOldValue() instanceof Integer
|
||||||
|
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())) {
|
||||||
|
if (evt.getSource().equals(leftSplitPane)) {
|
||||||
|
leftSplitPaneDividerLocation = (int) evt.getNewValue();
|
||||||
|
} else if (evt.getSource().equals(mainSplitPane)) {
|
||||||
|
mainSplitPaneDividerLocation = (int) evt.getNewValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
dateListener = new ListSelectionListener() {
|
dateListener = new ListSelectionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void valueChanged(ListSelectionEvent event) {
|
public void valueChanged(ListSelectionEvent event) {
|
||||||
if (!event.getValueIsAdjusting()) {
|
if (!event.getValueIsAdjusting()) {
|
||||||
artifactListPanel.removeSelectionListener(artifactListener);
|
artifactListPanel.removeSelectionListener(artifactListener);
|
||||||
|
leftSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
|
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
artifactListPanel.clearList();
|
artifactListPanel.clearList();
|
||||||
artifactListPanel.addArtifacts(dateListPanel.getArtifactsForSelectedDate());
|
artifactListPanel.addArtifacts(dateListPanel.getArtifactsForSelectedDate());
|
||||||
artifactListPanel.addSelectionListener(artifactListener);
|
artifactListPanel.addSelectionListener(artifactListener);
|
||||||
artifactListPanel.selectFirst();
|
artifactListPanel.selectFirst();
|
||||||
|
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
validate();
|
validate();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
@ -85,10 +121,16 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
|||||||
};
|
};
|
||||||
dateListPanel.addSelectionListener(dateListener);
|
dateListPanel.addSelectionListener(dateListener);
|
||||||
artifactListPanel.addSelectionListener(artifactListener);
|
artifactListPanel.addSelectionListener(artifactListener);
|
||||||
|
dateListPanel.setPreferredSize(new Dimension(100, 20));
|
||||||
leftSplitPane.setLeftComponent(dateListPanel);
|
leftSplitPane.setLeftComponent(dateListPanel);
|
||||||
|
artifactListPanel.setPreferredSize(new Dimension(100, 20));
|
||||||
leftSplitPane.setRightComponent(artifactListPanel);
|
leftSplitPane.setRightComponent(artifactListPanel);
|
||||||
mainSplitPane.setRightComponent(rightPanel.getComponent());
|
mainSplitPane.setRightComponent(rightPanel.getComponent());
|
||||||
add(mainSplitPane);
|
add(mainSplitPane);
|
||||||
|
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
|
leftSplitPaneDividerLocation = leftSplitPane.getDividerLocation();
|
||||||
|
mainSplitPaneDividerLocation = mainSplitPane.getDividerLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,6 +187,8 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
|||||||
if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) {
|
if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) {
|
||||||
dateListPanel.removeListSelectionListener(dateListener);
|
dateListPanel.removeListSelectionListener(dateListener);
|
||||||
artifactListPanel.removeSelectionListener(artifactListener);
|
artifactListPanel.removeSelectionListener(artifactListener);
|
||||||
|
leftSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
|
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||||
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
|
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
|
||||||
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
|
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
|
||||||
setEnabled(!dateListPanel.isEmpty());
|
setEnabled(!dateListPanel.isEmpty());
|
||||||
@ -156,6 +200,10 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
|||||||
}
|
}
|
||||||
removeAll();
|
removeAll();
|
||||||
add(mainSplitPane);
|
add(mainSplitPane);
|
||||||
|
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||||
|
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
|
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||||
revalidate();
|
revalidate();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
@ -174,12 +222,12 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
|||||||
mainSplitPane = new javax.swing.JSplitPane();
|
mainSplitPane = new javax.swing.JSplitPane();
|
||||||
leftSplitPane = new javax.swing.JSplitPane();
|
leftSplitPane = new javax.swing.JSplitPane();
|
||||||
|
|
||||||
mainSplitPane.setDividerLocation(400);
|
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||||
mainSplitPane.setResizeWeight(0.1);
|
mainSplitPane.setResizeWeight(0.2);
|
||||||
mainSplitPane.setToolTipText("");
|
mainSplitPane.setToolTipText("");
|
||||||
mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
||||||
|
|
||||||
leftSplitPane.setDividerLocation(198);
|
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||||
leftSplitPane.setResizeWeight(0.5);
|
leftSplitPane.setResizeWeight(0.5);
|
||||||
leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
||||||
mainSplitPane.setLeftComponent(leftSplitPane);
|
mainSplitPane.setLeftComponent(leftSplitPane);
|
||||||
|
@ -477,6 +477,9 @@ public final class IngestJobSettings {
|
|||||||
case "Exif Parser": //NON-NLS
|
case "Exif Parser": //NON-NLS
|
||||||
moduleNames.add("Picture Analyzer"); //NON-NLS
|
moduleNames.add("Picture Analyzer"); //NON-NLS
|
||||||
break;
|
break;
|
||||||
|
case "Drone Analyzer":
|
||||||
|
moduleNames.add("DJI Drone Analyzer");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
moduleNames.add(name);
|
moduleNames.add(name);
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,26 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
|||||||
* @param callback Callback that will be used by the background task
|
* @param callback Callback that will be used by the background task
|
||||||
* to return results.
|
* to return results.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for the data source.
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.",
|
"# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.",
|
||||||
"# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}",
|
"# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}",
|
||||||
@ -139,11 +159,10 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
|||||||
"LogicalImagerDSProcessor.noCurrentCase=No current case",
|
"LogicalImagerDSProcessor.noCurrentCase=No current case",
|
||||||
})
|
})
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
|
|
||||||
// HOSTTODO - set to value from config panel
|
// HOSTTODO - set to value from config panel
|
||||||
Host host;
|
|
||||||
try {
|
try {
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LogicalImagerDSProcessor Host");
|
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LogicalImagerDSProcessor Host");
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -41,8 +41,6 @@ import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
|||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.AnalysisResult;
|
|
||||||
import org.sleuthkit.datamodel.AnalysisResultAdded;
|
|
||||||
import org.sleuthkit.datamodel.Blackboard;
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
@ -50,7 +48,6 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
|||||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||||
import org.sleuthkit.datamodel.HashHitInfo;
|
import org.sleuthkit.datamodel.HashHitInfo;
|
||||||
import org.sleuthkit.datamodel.HashUtility;
|
import org.sleuthkit.datamodel.HashUtility;
|
||||||
import org.sleuthkit.datamodel.Score;
|
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
@ -519,7 +516,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
|||||||
private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
|
private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
|
||||||
try {
|
try {
|
||||||
String moduleName = HashLookupModuleFactory.getModuleName();
|
String moduleName = HashLookupModuleFactory.getModuleName();
|
||||||
//BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
|
BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
|
||||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
//TODO Revisit usage of deprecated constructor as per TSK-583
|
//TODO Revisit usage of deprecated constructor as per TSK-583
|
||||||
//BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
|
//BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
|
||||||
@ -527,22 +524,14 @@ public class HashDbIngestModule implements FileIngestModule {
|
|||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
|
||||||
|
|
||||||
SleuthkitCase.CaseDbTransaction trans = this.skCase.beginTransaction();
|
badFile.addAttributes(attributes);
|
||||||
|
|
||||||
AnalysisResultAdded resultAdded = blackboard.newAnalysisResult(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_HASHSET_HIT), abstractFile.getId(), abstractFile.getDataSourceObjectId(), new Score(Score.Significance.MEDIUM, Score.Confidence.HIGH), moduleName, comment, hashSetName, attributes, trans);
|
|
||||||
AnalysisResult badFile = resultAdded.getAnalysisResult();
|
|
||||||
trans.commit();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
* post the artifact which will index the artifact for keyword
|
* post the artifact which will index the artifact for keyword
|
||||||
* search, and fire an event to notify UI of this new artifact
|
* search, and fire an event to notify UI of this new artifact
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
blackboard.postArtifact(badFile, moduleName);
|
blackboard.postArtifact(badFile, moduleName);
|
||||||
|
|
||||||
} catch (Blackboard.BlackboardException ex) {
|
} catch (Blackboard.BlackboardException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + badFile.getArtifactID(), ex); //NON-NLS
|
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + badFile.getArtifactID(), ex); //NON-NLS
|
||||||
MessageNotifyUtil.Notify.error(
|
MessageNotifyUtil.Notify.error(
|
||||||
@ -585,7 +574,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
|||||||
abstractFile.getName() + md5Hash,
|
abstractFile.getName() + md5Hash,
|
||||||
badFile));
|
badFile));
|
||||||
}
|
}
|
||||||
} catch (TskException | Blackboard.BlackboardException ex) {
|
} catch (TskException ex) {
|
||||||
logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
|
logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,10 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
|
|||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
|
if (false == PlatformUtil.is64BitOS()) {
|
||||||
|
throw new IngestModuleException(NbBundle.getMessage(this.getClass(), "AleappAnalyzerIngestModule.not.64.bit.os"));
|
||||||
|
}
|
||||||
|
|
||||||
if (false == PlatformUtil.isWindowsOS()) {
|
if (false == PlatformUtil.isWindowsOS()) {
|
||||||
throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_requires_windows());
|
throw new IngestModuleException(Bundle.ALeappAnalyzerIngestModule_requires_windows());
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
|||||||
ILeappAnalyzerIngestModule.processing.file=Processing file {0}
|
ILeappAnalyzerIngestModule.processing.file=Processing file {0}
|
||||||
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
||||||
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
||||||
|
IleappAnalyzerIngestModule.not.64.bit.os=iLeapp will not run on a 32bit operating system
|
||||||
ALeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
ALeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
||||||
ALeappAnalyzerIngestModule.processing.file=Processing file {0}
|
ALeappAnalyzerIngestModule.processing.file=Processing file {0}
|
||||||
ALeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
ALeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
||||||
ALeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
ALeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
||||||
|
AleappAnalyzerIngestModule.not.64.bit.os=aLeapp will not run on a 32bit operating system
|
@ -22,10 +22,12 @@ ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
|||||||
ILeappAnalyzerIngestModule.processing.file=Processing file {0}
|
ILeappAnalyzerIngestModule.processing.file=Processing file {0}
|
||||||
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
||||||
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
||||||
|
IleappAnalyzerIngestModule.not.64.bit.os=iLeapp will not run on 32bit operating system
|
||||||
ALeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
ALeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
||||||
ALeappAnalyzerIngestModule.processing.file=Processing file {0}
|
ALeappAnalyzerIngestModule.processing.file=Processing file {0}
|
||||||
ALeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
ALeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
||||||
ALeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
ALeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
||||||
|
AleappAnalyzerIngestModule.not.64.bit.os=aLeapp will not run on 32bit operating system
|
||||||
ILeappAnalyzerIngestModule.report.name=iLeapp Html Report
|
ILeappAnalyzerIngestModule.report.name=iLeapp Html Report
|
||||||
ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.
|
ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.
|
||||||
ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp
|
ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp
|
||||||
|
@ -92,6 +92,10 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
|
|||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
|
if (false == PlatformUtil.is64BitOS()) {
|
||||||
|
throw new IngestModuleException(NbBundle.getMessage(this.getClass(), "IleappAnalyzerIngestModule.not.64.bit.os"));
|
||||||
|
}
|
||||||
|
|
||||||
if (false == PlatformUtil.isWindowsOS()) {
|
if (false == PlatformUtil.isWindowsOS()) {
|
||||||
throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_requires_windows());
|
throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_requires_windows());
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
|||||||
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
|
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||||
@ -379,7 +380,9 @@ public final class LeappFileProcessor {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlackboardAttribute attr = (value == null) ? null : getAttribute(colAttr.getAttributeType(), value, fileName);
|
String formattedValue = formatValueBasedOnAttrType(colAttr, value);
|
||||||
|
|
||||||
|
BlackboardAttribute attr = (value == null) ? null : getAttribute(colAttr.getAttributeType(), formattedValue, fileName);
|
||||||
if (attr == null) {
|
if (attr == null) {
|
||||||
logger.log(Level.WARNING, String.format("Blackboard attribute could not be parsed column %s at line %d in file %s. Omitting row.", colAttr.getColumnName(), lineNum, fileName));
|
logger.log(Level.WARNING, String.format("Blackboard attribute could not be parsed column %s at line %d in file %s. Omitting row.", colAttr.getColumnName(), lineNum, fileName));
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@ -394,6 +397,21 @@ public final class LeappFileProcessor {
|
|||||||
return attrsToRet;
|
return attrsToRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check type of attribute and possibly format string based on it.
|
||||||
|
*
|
||||||
|
* @param colAttr Column Attribute information
|
||||||
|
* @param value string to be formatted
|
||||||
|
* @return formatted string based on attribute type if no attribute type found then return original string
|
||||||
|
*/
|
||||||
|
private String formatValueBasedOnAttrType(TsvColumn colAttr, String value) {
|
||||||
|
if (colAttr.getAttributeType().getTypeName().equals("TSK_DOMAIN")) {
|
||||||
|
return NetworkUtils.extractDomain(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format of time stamps in tsv.
|
* The format of time stamps in tsv.
|
||||||
*/
|
*/
|
||||||
|
@ -71,6 +71,7 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.FileSystem;
|
import org.sleuthkit.datamodel.FileSystem;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
import org.sleuthkit.datamodel.Image;
|
import org.sleuthkit.datamodel.Image;
|
||||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||||
import org.sleuthkit.datamodel.Pool;
|
import org.sleuthkit.datamodel.Pool;
|
||||||
@ -1079,12 +1080,20 @@ public class PortableCaseReportModule implements ReportModule {
|
|||||||
BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
|
BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
|
||||||
newContent = copyArtifact(parentId, artifactToCopy);
|
newContent = copyArtifact(parentId, artifactToCopy);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// Get or create the host (if needed) before beginning transaction.
|
||||||
|
Host newHost = null;
|
||||||
|
if (content instanceof DataSource) {
|
||||||
|
Host oldHost = ((DataSource)content).getHost();
|
||||||
|
newHost = portableSkCase.getHostManager().getOrCreateHost(oldHost.getName());
|
||||||
|
}
|
||||||
|
|
||||||
CaseDbTransaction trans = portableSkCase.beginTransaction();
|
CaseDbTransaction trans = portableSkCase.beginTransaction();
|
||||||
try {
|
try {
|
||||||
if (content instanceof Image) {
|
if (content instanceof Image) {
|
||||||
Image image = (Image) content;
|
Image image = (Image) content;
|
||||||
newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
|
newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
|
||||||
new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
|
new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), newHost, trans);
|
||||||
} else if (content instanceof VolumeSystem) {
|
} else if (content instanceof VolumeSystem) {
|
||||||
VolumeSystem vs = (VolumeSystem) content;
|
VolumeSystem vs = (VolumeSystem) content;
|
||||||
newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
|
newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
|
||||||
@ -1108,7 +1117,7 @@ public class PortableCaseReportModule implements ReportModule {
|
|||||||
|
|
||||||
if (abstractFile instanceof LocalFilesDataSource) {
|
if (abstractFile instanceof LocalFilesDataSource) {
|
||||||
LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
|
LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
|
||||||
newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
|
newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), newHost, trans);
|
||||||
} else {
|
} else {
|
||||||
if (abstractFile.isDir()) {
|
if (abstractFile.isDir()) {
|
||||||
newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
|
newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<!-- for viewers -->
|
<!-- for viewers -->
|
||||||
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/>
|
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/>
|
||||||
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.6.0"/>
|
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.7.0"/>
|
||||||
|
|
||||||
<!-- for file search -->
|
<!-- for file search -->
|
||||||
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||||
|
@ -42,8 +42,8 @@ file.reference.javassist-3.12.1.GA.jar=release/modules/ext/javassist-3.12.1.GA.j
|
|||||||
file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4.jar
|
file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4.jar
|
||||||
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
|
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
|
||||||
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
|
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
|
||||||
file.reference.jna-5.6.0.jar=release/modules/ext/jna-5.6.0.jar
|
file.reference.jna-5.7.0.jar=release/modules/ext/jna-5.7.0.jar
|
||||||
file.reference.jna-platform-5.6.0.jar=release/modules/ext/jna-platform-5.6.0.jar
|
file.reference.jna-platform-5.7.0.jar=release/modules/ext/jna-platform-5.7.0.jar
|
||||||
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
|
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
|
||||||
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
|
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
|
||||||
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
|
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
|
||||||
|
@ -923,8 +923,8 @@
|
|||||||
<binary-origin>release/modules/ext/commons-compress-1.18.jar</binary-origin>
|
<binary-origin>release/modules/ext/commons-compress-1.18.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/jna-platform-5.6.0.jar</runtime-relative-path>
|
<runtime-relative-path>ext/jna-platform-5.7.0.jar</runtime-relative-path>
|
||||||
<binary-origin>release\modules\ext\jna-platform-5.6.0.jar</binary-origin>
|
<binary-origin>release\modules\ext\jna-platform-5.7.0.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
|
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
|
||||||
@ -951,8 +951,8 @@
|
|||||||
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
|
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/jna-5.6.0.jar</runtime-relative-path>
|
<runtime-relative-path>ext/jna-5.7.0.jar</runtime-relative-path>
|
||||||
<binary-origin>release\modules\ext\jna-5.6.0.jar</binary-origin>
|
<binary-origin>release\modules\ext\jna-5.7.0.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
|
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
|
||||||
|
@ -119,10 +119,29 @@ public class MemoryDSProcessor implements DataSourceProcessor {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
run(null, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 host Host for the data source.
|
||||||
|
* @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(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
|
|
||||||
// HOSTTODO - replace with a call to configPanel().getHost()
|
// HOSTTODO - replace with a call to configPanel().getHost()
|
||||||
Host host;
|
|
||||||
try {
|
try {
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("MemoryDSProcessor Host");
|
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("MemoryDSProcessor Host");
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -95,8 +95,7 @@ public final class ImageGalleryController {
|
|||||||
private static final Logger logger = Logger.getLogger(ImageGalleryController.class.getName());
|
private static final Logger logger = Logger.getLogger(ImageGalleryController.class.getName());
|
||||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED, IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED, IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED, IngestManager.IngestModuleEvent.FILE_DONE);
|
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED, IngestManager.IngestModuleEvent.FILE_DONE);
|
||||||
|
|
||||||
private static String DEFAULT_TAG_SET_NAME = "Project VIC";
|
|
||||||
/*
|
/*
|
||||||
* The file limit for image gallery. If the selected data source (or all
|
* The file limit for image gallery. If the selected data source (or all
|
||||||
* data sources, if that option is selected) has more than this many files
|
* data sources, if that option is selected) has more than this many files
|
||||||
@ -738,7 +737,7 @@ public final class ImageGalleryController {
|
|||||||
List<TagSet> tagSetList = getCaseDatabase().getTaggingManager().getTagSets();
|
List<TagSet> tagSetList = getCaseDatabase().getTaggingManager().getTagSets();
|
||||||
if (tagSetList != null && !tagSetList.isEmpty()) {
|
if (tagSetList != null && !tagSetList.isEmpty()) {
|
||||||
for (TagSet set : tagSetList) {
|
for (TagSet set : tagSetList) {
|
||||||
if (set.getName().equals(getCategoryTagSetName())) {
|
if (set.getName().equals(ImageGalleryService.PROJECT_VIC_TAG_SET_NAME)) {
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -749,14 +748,6 @@ public final class ImageGalleryController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the category tag set.
|
|
||||||
*
|
|
||||||
* @return Tagset name
|
|
||||||
*/
|
|
||||||
static String getCategoryTagSetName() {
|
|
||||||
return DEFAULT_TAG_SET_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for ingest module application events.
|
* A listener for ingest module application events.
|
||||||
@ -839,8 +830,11 @@ public final class ImageGalleryController {
|
|||||||
Content newDataSource = (Content) event.getNewValue();
|
Content newDataSource = (Content) event.getNewValue();
|
||||||
if (isListeningEnabled()) {
|
if (isListeningEnabled()) {
|
||||||
try {
|
try {
|
||||||
drawableDB.insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN);
|
// If the data source already exists and has a status other than UNKNOWN, don’t overwrite it.
|
||||||
} catch (SQLException ex) {
|
if(drawableDB.getDataSourceDbBuildStatus(newDataSource.getId()) == DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN) {
|
||||||
|
drawableDB.insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN);
|
||||||
|
}
|
||||||
|
} catch (SQLException | TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, String.format("Error updating datasources table (data source object ID = %d, status = %s)", newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN.toString()), ex); //NON-NLS
|
logger.log(Level.SEVERE, String.format("Error updating datasources table (data source object ID = %d, status = %s)", newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN.toString()), ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,20 +44,32 @@ import org.sleuthkit.datamodel.TskData;
|
|||||||
})
|
})
|
||||||
public class ImageGalleryService implements AutopsyService {
|
public class ImageGalleryService implements AutopsyService {
|
||||||
|
|
||||||
private static final String CATEGORY_ONE_NAME = "Child Exploitation (Illegal)";
|
/* Image Gallery has its own definition of Project VIC tag names because
|
||||||
private static final String CATEGORY_TWO_NAME = "Child Exploitation (Non-Illegal/Age Difficult)";
|
* these will be used if the Project Vic module is not installed. These will
|
||||||
private static final String CATEGORY_THREE_NAME = "CGI/Animation (Child Exploitive)";
|
* get added when a case is opened if the tag set is not already defined.
|
||||||
private static final String CATEGORY_FOUR_NAME = "Exemplar/Comparison (Internal Use Only)";
|
*
|
||||||
private static final String CATEGORY_FIVE_NAME = "Non-pertinent";
|
* The following list of names must be kept in sync with the CountryManager
|
||||||
|
* code in the ProjectVic module.
|
||||||
private static final List<TagNameDefinition> DEFAULT_CATEGORY_DEFINITION = new ArrayList<>();
|
*
|
||||||
|
* Autopsy Core Tag code and TSK DataModel upgrade code also have a
|
||||||
|
* references to the "Projet VIC" set name. Be careful changing any of these names.
|
||||||
|
*/
|
||||||
|
static String PROJECT_VIC_TAG_SET_NAME = "Project VIC";
|
||||||
|
private static final String PV_US_CAT0 = "Non-Pertinent";
|
||||||
|
private static final String PV_US_CAT1 = "Child Abuse Material - (CAM)";
|
||||||
|
private static final String PV_US_CAT2 = "Child Exploitive (Non-CAM) Age Difficult";
|
||||||
|
private static final String PV_US_CAT3 = "CGI/Animation - Child Exploitive";
|
||||||
|
private static final String PV_US_CAT4 = "Comparison Images";
|
||||||
|
|
||||||
|
private static final List<TagNameDefinition> PROJECT_VIC_US_CATEGORIES = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DEFAULT_CATEGORY_DEFINITION.add(new TagNameDefinition(CATEGORY_ONE_NAME, "", TagName.HTML_COLOR.RED, TskData.FileKnown.BAD));
|
// NOTE: The colors here are what will be shown in the border
|
||||||
DEFAULT_CATEGORY_DEFINITION.add(new TagNameDefinition(CATEGORY_TWO_NAME, "", TagName.HTML_COLOR.LIME, TskData.FileKnown.BAD));
|
PROJECT_VIC_US_CATEGORIES.add(new TagNameDefinition(PV_US_CAT0, "", TagName.HTML_COLOR.GREEN, TskData.FileKnown.UNKNOWN));
|
||||||
DEFAULT_CATEGORY_DEFINITION.add(new TagNameDefinition(CATEGORY_THREE_NAME, "", TagName.HTML_COLOR.YELLOW, TskData.FileKnown.BAD));
|
PROJECT_VIC_US_CATEGORIES.add(new TagNameDefinition(PV_US_CAT1, "", TagName.HTML_COLOR.RED, TskData.FileKnown.BAD));
|
||||||
DEFAULT_CATEGORY_DEFINITION.add(new TagNameDefinition(CATEGORY_FOUR_NAME, "", TagName.HTML_COLOR.PURPLE, TskData.FileKnown.UNKNOWN));
|
PROJECT_VIC_US_CATEGORIES.add(new TagNameDefinition(PV_US_CAT2, "", TagName.HTML_COLOR.YELLOW, TskData.FileKnown.BAD));
|
||||||
DEFAULT_CATEGORY_DEFINITION.add(new TagNameDefinition(CATEGORY_FIVE_NAME, "", TagName.HTML_COLOR.FUCHSIA, TskData.FileKnown.UNKNOWN));
|
PROJECT_VIC_US_CATEGORIES.add(new TagNameDefinition(PV_US_CAT3, "", TagName.HTML_COLOR.FUCHSIA, TskData.FileKnown.BAD));
|
||||||
|
PROJECT_VIC_US_CATEGORIES.add(new TagNameDefinition(PV_US_CAT4, "", TagName.HTML_COLOR.BLUE, TskData.FileKnown.UNKNOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -91,17 +103,17 @@ public class ImageGalleryService implements AutopsyService {
|
|||||||
|
|
||||||
// Check to see if the Project VIC tag set exists, if not create a
|
// Check to see if the Project VIC tag set exists, if not create a
|
||||||
// tag set using the default tags.
|
// tag set using the default tags.
|
||||||
boolean addDefaultTagSet = true;
|
boolean addProjVicTagSet = true;
|
||||||
List<TagSet> tagSets = context.getCase().getServices().getTagsManager().getAllTagSets();
|
List<TagSet> tagSets = context.getCase().getServices().getTagsManager().getAllTagSets();
|
||||||
for (TagSet set : tagSets) {
|
for (TagSet set : tagSets) {
|
||||||
if (set.getName().equals(ImageGalleryController.getCategoryTagSetName())) {
|
if (set.getName().equals(PROJECT_VIC_TAG_SET_NAME)) {
|
||||||
addDefaultTagSet = false;
|
addProjVicTagSet = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addDefaultTagSet) {
|
if (addProjVicTagSet) {
|
||||||
addDefaultTagSet(context.getCase());
|
addProjetVicTagSet(context.getCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageGalleryController.createController(context.getCase());
|
ImageGalleryController.createController(context.getCase());
|
||||||
@ -134,13 +146,11 @@ public class ImageGalleryService implements AutopsyService {
|
|||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
private void addDefaultTagSet(Case currentCase) throws TskCoreException {
|
private void addProjetVicTagSet(Case currentCase) throws TskCoreException {
|
||||||
List<TagName> tagNames = new ArrayList<>();
|
List<TagName> tagNames = new ArrayList<>();
|
||||||
for (TagNameDefinition def : DEFAULT_CATEGORY_DEFINITION) {
|
for (TagNameDefinition def : PROJECT_VIC_US_CATEGORIES) {
|
||||||
tagNames.add(currentCase.getSleuthkitCase().addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus()));
|
tagNames.add(currentCase.getSleuthkitCase().addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus()));
|
||||||
}
|
}
|
||||||
|
currentCase.getServices().getTagsManager().addTagSet(PROJECT_VIC_TAG_SET_NAME, tagNames);
|
||||||
currentCase.getServices().getTagsManager().addTagSet(ImageGalleryController.getCategoryTagSetName(), tagNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,10 @@ public final class OpenAction extends CallableSystemAction {
|
|||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
ImageGalleryController controller;
|
ImageGalleryController controller;
|
||||||
|
// @@@ This call gets a lock. We shouldn't do this in the UI....
|
||||||
controller = ImageGalleryController.getController(currentCase);
|
controller = ImageGalleryController.getController(currentCase);
|
||||||
|
|
||||||
|
// Display an error if we could not get the controller and return
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
Alert errorDIalog = new Alert(Alert.AlertType.ERROR);
|
Alert errorDIalog = new Alert(Alert.AlertType.ERROR);
|
||||||
errorDIalog.initModality(Modality.APPLICATION_MODAL);
|
errorDIalog.initModality(Modality.APPLICATION_MODAL);
|
||||||
@ -174,6 +177,7 @@ public final class OpenAction extends CallableSystemAction {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the user is aware of Single vs Multi-user behaviors
|
||||||
if (currentCase.getCaseType() == Case.CaseType.MULTI_USER_CASE
|
if (currentCase.getCaseType() == Case.CaseType.MULTI_USER_CASE
|
||||||
&& ImageGalleryPreferences.isMultiUserCaseInfoDialogDisabled() == false) {
|
&& ImageGalleryPreferences.isMultiUserCaseInfoDialogDisabled() == false) {
|
||||||
Alert dialog = new Alert(Alert.AlertType.INFORMATION);
|
Alert dialog = new Alert(Alert.AlertType.INFORMATION);
|
||||||
|
@ -99,9 +99,9 @@ public class CategoryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the number of file with the given {@link DhsImageCategory}
|
* get the number of file with the given tag
|
||||||
*
|
*
|
||||||
* @param cat get the number of files with Category = cat
|
* @param tagName get the number of files with Category = tagName
|
||||||
*
|
*
|
||||||
* @return the number of files with the given Category
|
* @return the number of files with the given Category
|
||||||
*/
|
*/
|
||||||
@ -110,20 +110,18 @@ public class CategoryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* increment the cached value for the number of files with the given
|
* increment the cached value for the number of files with the given tag
|
||||||
* {@link DhsImageCategory}
|
|
||||||
*
|
*
|
||||||
* @param cat the Category to increment
|
* @param tagName the Category to increment
|
||||||
*/
|
*/
|
||||||
synchronized public void incrementCategoryCount(TagName tagName) {
|
synchronized public void incrementCategoryCount(TagName tagName) {
|
||||||
categoryCounts.getUnchecked(tagName).increment();
|
categoryCounts.getUnchecked(tagName).increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* decrement the cached value for the number of files with the given
|
* decrement the cached value for the number of files with the given tag
|
||||||
* DhsImageCategory
|
|
||||||
*
|
*
|
||||||
* @param cat the Category to decrement
|
* @param tagName the Category to decrement
|
||||||
*/
|
*/
|
||||||
synchronized public void decrementCategoryCount(TagName tagName) {
|
synchronized public void decrementCategoryCount(TagName tagName) {
|
||||||
categoryCounts.getUnchecked(tagName).decrement();
|
categoryCounts.getUnchecked(tagName).decrement();
|
||||||
|
@ -207,19 +207,19 @@ public final class DrawableDB {
|
|||||||
*/
|
*/
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
/**
|
/**
|
||||||
* Analyis (an ingest job or image gallery database rebuild) for the
|
* Analysis (an ingest job or image gallery database rebuild) for the
|
||||||
* data source is in progress.
|
* data source is in progress.
|
||||||
*/
|
*/
|
||||||
IN_PROGRESS,
|
IN_PROGRESS,
|
||||||
/**
|
/**
|
||||||
* Analyis (an ingest job or image gallery database rebuild) for the
|
* Analysis (an ingest job or image gallery database rebuild) for the
|
||||||
* data source has been completed and at least one file in the data
|
* data source has been completed and at least one file in the data
|
||||||
* source has a MIME type (ingest filters may have been applied, so some
|
* source has a MIME type (ingest filters may have been applied, so some
|
||||||
* files may not have been typed).
|
* files may not have been typed).
|
||||||
*/
|
*/
|
||||||
COMPLETE,
|
COMPLETE,
|
||||||
/**
|
/**
|
||||||
* Analyis (an ingest job or image gallery database rebuild) for the
|
* Analysis (an ingest job or image gallery database rebuild) for the
|
||||||
* data source has been completed, but the files for the data source
|
* data source has been completed, but the files for the data source
|
||||||
* were not assigned a MIME type (file typing was not enabled).
|
* were not assigned a MIME type (file typing was not enabled).
|
||||||
*/
|
*/
|
||||||
|
@ -187,7 +187,7 @@ Server.query.exception.msg=Error running query: {0}
|
|||||||
Server.query2.exception.msg=Error running query: {0}
|
Server.query2.exception.msg=Error running query: {0}
|
||||||
Server.queryTerms.exception.msg=Error running terms query: {0}
|
Server.queryTerms.exception.msg=Error running terms query: {0}
|
||||||
Server.connect.exception.msg=Failed to connect to Solr server: {0}
|
Server.connect.exception.msg=Failed to connect to Solr server: {0}
|
||||||
Server.openCore.exception.msg=Keyword search service not yet running
|
Server.openCore.exception.msg=Local keyword search service not yet running
|
||||||
Server.openCore.exception.cantOpen.msg=Could not create or open index
|
Server.openCore.exception.cantOpen.msg=Could not create or open index
|
||||||
Server.openCore.exception.noIndexDir.msg=Index directory could not be created or is missing
|
Server.openCore.exception.noIndexDir.msg=Index directory could not be created or is missing
|
||||||
Server.request.exception.exception.msg=Could not issue Solr request
|
Server.request.exception.exception.msg=Could not issue Solr request
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.keywordsearch;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.apache.solr.client.solrj.SolrServerException;
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
import org.openide.modules.ModuleInstall;
|
import org.openide.modules.ModuleInstall;
|
||||||
@ -39,6 +42,7 @@ class Installer extends ModuleInstall {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Installer.class.getName());
|
private static final Logger logger = Logger.getLogger(Installer.class.getName());
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final String KWS_START_THREAD_NAME = "KWS-server-start-%d";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restored() {
|
public void restored() {
|
||||||
@ -46,19 +50,29 @@ class Installer extends ModuleInstall {
|
|||||||
KeywordSearchSettings.setDefaults();
|
KeywordSearchSettings.setDefaults();
|
||||||
|
|
||||||
final Server server = KeywordSearch.getServer();
|
final Server server = KeywordSearch.getServer();
|
||||||
try {
|
|
||||||
server.start();
|
ExecutorService jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(KWS_START_THREAD_NAME).build());
|
||||||
} catch (SolrServerNoPortException ex) {
|
Runnable kwsStartTask = new Runnable() {
|
||||||
logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS
|
public void run() {
|
||||||
if (ex.getPortNumber() == server.getLocalSolrServerPort()) {
|
try {
|
||||||
reportPortError(ex.getPortNumber());
|
server.start();
|
||||||
} else {
|
} catch (SolrServerNoPortException ex) {
|
||||||
reportStopPortError(ex.getPortNumber());
|
logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS
|
||||||
|
if (ex.getPortNumber() == server.getLocalSolrServerPort()) {
|
||||||
|
reportPortError(ex.getPortNumber());
|
||||||
|
} else {
|
||||||
|
reportStopPortError(ex.getPortNumber());
|
||||||
|
}
|
||||||
|
} catch (KeywordSearchModuleException | SolrServerException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS
|
||||||
|
reportInitError(ex.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (KeywordSearchModuleException | SolrServerException ex) {
|
};
|
||||||
logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS
|
|
||||||
reportInitError(ex.getMessage());
|
// start KWS service on the background thread. Currently all it does is start the embedded Solr server.
|
||||||
}
|
jobProcessingExecutor.submit(kwsStartTask);
|
||||||
|
jobProcessingExecutor.shutdown(); // tell executor no more work is coming
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,6 +31,7 @@ import java.io.InputStreamReader;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@ -239,6 +240,8 @@ public class Server {
|
|||||||
private static final String CORE_PROPERTIES = "core.properties";
|
private static final String CORE_PROPERTIES = "core.properties";
|
||||||
private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT);
|
private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT);
|
||||||
private static final int NUM_COLLECTION_CREATION_RETRIES = 5;
|
private static final int NUM_COLLECTION_CREATION_RETRIES = 5;
|
||||||
|
private static final int NUM_EMBEDDED_SERVER_RETRIES = 12; // attempt to connect to embedded Solr server for 1 minute
|
||||||
|
private static final int EMBEDDED_SERVER_RETRY_WAIT_SEC = 5;
|
||||||
|
|
||||||
public enum CORE_EVT_STATES {
|
public enum CORE_EVT_STATES {
|
||||||
|
|
||||||
@ -269,6 +272,8 @@ public class Server {
|
|||||||
*/
|
*/
|
||||||
Server() {
|
Server() {
|
||||||
initSettings();
|
initSettings();
|
||||||
|
|
||||||
|
localSolrServer = getSolrClient("http://localhost:" + localSolrServerPort + "/solr");
|
||||||
|
|
||||||
serverAction = new ServerAction();
|
serverAction = new ServerAction();
|
||||||
File solr8Folder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); //NON-NLS
|
File solr8Folder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); //NON-NLS
|
||||||
@ -664,11 +669,13 @@ public class Server {
|
|||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"Server.status.failed.msg=Local Solr server did not respond to status request. This may be because the server failed to start or is taking too long to initialize.",})
|
"Server.status.failed.msg=Local Solr server did not respond to status request. This may be because the server failed to start or is taking too long to initialize.",})
|
||||||
void startLocalSolr(SOLR_VERSION version) throws KeywordSearchModuleException, SolrServerNoPortException, SolrServerException {
|
synchronized void startLocalSolr(SOLR_VERSION version) throws KeywordSearchModuleException, SolrServerNoPortException, SolrServerException {
|
||||||
|
|
||||||
|
logger.log(Level.INFO, "Starting local Solr " + version + " server"); //NON-NLS
|
||||||
if (isLocalSolrRunning()) {
|
if (isLocalSolrRunning()) {
|
||||||
if (localServerVersion.equals(version)) {
|
if (localServerVersion.equals(version)) {
|
||||||
// this version of local server is already running
|
// this version of local server is already running
|
||||||
|
logger.log(Level.INFO, "Local Solr " + version + " server is already running"); //NON-NLS
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// wrong version of local server is running, stop it
|
// wrong version of local server is running, stop it
|
||||||
@ -712,7 +719,7 @@ public class Server {
|
|||||||
logger.log(Level.INFO, "Starting Solr 8 server"); //NON-NLS
|
logger.log(Level.INFO, "Starting Solr 8 server"); //NON-NLS
|
||||||
localSolrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); //NON-NLS
|
localSolrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); //NON-NLS
|
||||||
curSolrProcess = runLocalSolr8ControlCommand(new ArrayList<>(Arrays.asList("start", "-p", //NON-NLS
|
curSolrProcess = runLocalSolr8ControlCommand(new ArrayList<>(Arrays.asList("start", "-p", //NON-NLS
|
||||||
Integer.toString(localSolrServerPort)))); //NON-NLS
|
Integer.toString(localSolrServerPort)))); //NON-NLS
|
||||||
} else {
|
} else {
|
||||||
// solr4
|
// solr4
|
||||||
localSolrFolder = InstalledFileLocator.getDefault().locate("solr4", Server.class.getPackage().getName(), false); //NON-NLS
|
localSolrFolder = InstalledFileLocator.getDefault().locate("solr4", Server.class.getPackage().getName(), false); //NON-NLS
|
||||||
@ -721,11 +728,10 @@ public class Server {
|
|||||||
Arrays.asList("-Dbootstrap_confdir=../solr/configsets/AutopsyConfig/conf", //NON-NLS
|
Arrays.asList("-Dbootstrap_confdir=../solr/configsets/AutopsyConfig/conf", //NON-NLS
|
||||||
"-Dcollection.configName=AutopsyConfig"))); //NON-NLS
|
"-Dcollection.configName=AutopsyConfig"))); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the Solr server to start and respond to a statusRequest request.
|
// Wait for the Solr server to start and respond to a statusRequest request.
|
||||||
for (int numRetries = 0; numRetries < 6; numRetries++) {
|
for (int numRetries = 0; numRetries < NUM_EMBEDDED_SERVER_RETRIES; numRetries++) {
|
||||||
if (isLocalSolrRunning()) {
|
if (isLocalSolrRunning()) {
|
||||||
localSolrServer = getSolrClient("http://localhost:" + localSolrServerPort + "/solr");
|
|
||||||
final List<Long> pids = this.getSolrPIDs();
|
final List<Long> pids = this.getSolrPIDs();
|
||||||
logger.log(Level.INFO, "New Solr process PID: {0}", pids); //NON-NLS
|
logger.log(Level.INFO, "New Solr process PID: {0}", pids); //NON-NLS
|
||||||
return;
|
return;
|
||||||
@ -734,7 +740,7 @@ public class Server {
|
|||||||
// Local Solr server did not respond so we sleep for
|
// Local Solr server did not respond so we sleep for
|
||||||
// 5 seconds before trying again.
|
// 5 seconds before trying again.
|
||||||
try {
|
try {
|
||||||
TimeUnit.SECONDS.sleep(5);
|
TimeUnit.SECONDS.sleep(EMBEDDED_SERVER_RETRY_WAIT_SEC);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS
|
logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS
|
||||||
}
|
}
|
||||||
@ -767,6 +773,23 @@ public class Server {
|
|||||||
* @param port the port to check for availability
|
* @param port the port to check for availability
|
||||||
*/
|
*/
|
||||||
static boolean isPortAvailable(int port) {
|
static boolean isPortAvailable(int port) {
|
||||||
|
final String osName = PlatformUtil.getOSName().toLowerCase();
|
||||||
|
if (osName != null && osName.toLowerCase().startsWith("mac")) {
|
||||||
|
return isPortAvailableOSX(port);
|
||||||
|
} else {
|
||||||
|
return isPortAvailableDefault(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if a specific port is available.
|
||||||
|
*
|
||||||
|
* NOTE: This is used on non-OS X systems as of right now but could be
|
||||||
|
* replaced with the OS X version.
|
||||||
|
*
|
||||||
|
* @param port the port to check for availability
|
||||||
|
*/
|
||||||
|
static boolean isPortAvailableDefault(int port) {
|
||||||
ServerSocket ss = null;
|
ServerSocket ss = null;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -792,6 +815,48 @@ public class Server {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if a specific port is available.
|
||||||
|
*
|
||||||
|
* NOTE: This is only used on OSX for now, but could replace default
|
||||||
|
* implementation in the future.
|
||||||
|
*
|
||||||
|
* @param port The port to check for availability.
|
||||||
|
* @throws IllegalArgumentException If port is outside range of possible ports.
|
||||||
|
*/
|
||||||
|
static boolean isPortAvailableOSX(int port) {
|
||||||
|
// implementation taken from https://stackoverflow.com/a/435579
|
||||||
|
if (port < 1 || port > 65535) {
|
||||||
|
throw new IllegalArgumentException("Invalid start port: " + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerSocket ss = null;
|
||||||
|
DatagramSocket ds = null;
|
||||||
|
try {
|
||||||
|
ss = new ServerSocket(port);
|
||||||
|
ss.setReuseAddress(true);
|
||||||
|
ds = new DatagramSocket(port);
|
||||||
|
ds.setReuseAddress(true);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
} finally {
|
||||||
|
if (ds != null) {
|
||||||
|
ds.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ss != null) {
|
||||||
|
try {
|
||||||
|
ss.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
/* should not be thrown */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the current solr server port. Only call this after available.
|
* Changes the current solr server port. Only call this after available.
|
||||||
*
|
*
|
||||||
@ -1875,13 +1940,22 @@ public class Server {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private void connectToEmbeddedSolrServer() throws SolrServerException, IOException {
|
private void connectToEmbeddedSolrServer() throws SolrServerException, IOException {
|
||||||
HttpSolrClient solrServer = getSolrClient("http://localhost:" + localSolrServerPort + "/solr");
|
|
||||||
TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check");
|
TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check");
|
||||||
CoreAdminRequest.getStatus(null, solrServer);
|
CoreAdminRequest.getStatus(null, localSolrServer);
|
||||||
HealthMonitor.submitTimingMetric(metric);
|
HealthMonitor.submitTimingMetric(metric);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to connect to the given Solr server, which is running in
|
||||||
|
* SoulrCloud mode. This API does not work for the local Solr which is NOT
|
||||||
|
* running in SolrCloud mode.
|
||||||
|
*
|
||||||
|
* @param host Host name of the remote Solr server
|
||||||
|
* @param port Port of the remote Solr server
|
||||||
|
*
|
||||||
|
* @throws SolrServerException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
void connectToSolrServer(String host, String port) throws SolrServerException, IOException {
|
void connectToSolrServer(String host, String port) throws SolrServerException, IOException {
|
||||||
try (HttpSolrClient solrServer = getSolrClient("http://" + host + ":" + port + "/solr")) {
|
try (HttpSolrClient solrServer = getSolrClient("http://" + host + ":" + port + "/solr")) {
|
||||||
connectToSolrServer(solrServer);
|
connectToSolrServer(solrServer);
|
||||||
@ -1945,47 +2019,7 @@ public class Server {
|
|||||||
throw new KeywordSearchModuleException(
|
throw new KeywordSearchModuleException(
|
||||||
NbBundle.getMessage(this.getClass(), "Server.serverList.exception.msg", solrServer.getBaseURL()));
|
NbBundle.getMessage(this.getClass(), "Server.serverList.exception.msg", solrServer.getBaseURL()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ELTODO leaving this for reference, will delete later
|
|
||||||
private boolean clusterStatusWithCollection(String collectionName) throws IOException, SolrServerException {
|
|
||||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
|
||||||
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
|
|
||||||
params.set("collection", collectionName);
|
|
||||||
SolrRequest request = new QueryRequest(params);
|
|
||||||
request.setPath("/admin/collections");
|
|
||||||
|
|
||||||
NamedList<Object> statusResponse;
|
|
||||||
try {
|
|
||||||
statusResponse = currentSolrServer.request(request);
|
|
||||||
} catch (RemoteSolrException ex) {
|
|
||||||
// collection doesn't exist
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statusResponse == null) {
|
|
||||||
logger.log(Level.SEVERE, "Collections response should not be null"); //NON-NLS
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NamedList<Object> cluster = (NamedList<Object>) statusResponse.get("cluster");
|
|
||||||
if (cluster == null) {
|
|
||||||
logger.log(Level.SEVERE, "Cluster should not be null"); //NON-NLS
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
|
|
||||||
if (cluster == null) {
|
|
||||||
logger.log(Level.SEVERE, "Collections should not be null in cluster state"); //NON-NLS
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (collections.size() == 0) {
|
|
||||||
logger.log(Level.SEVERE, "Collections should not be empty in cluster state"); //NON-NLS
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object collection = collections.get(collectionName);
|
|
||||||
return (collection != null);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
class Collection {
|
class Collection {
|
||||||
|
|
||||||
|
@ -401,7 +401,9 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService {
|
|||||||
throw new AutopsyServiceException(String.format("Failed to close core for %s", context.getCase().getCaseDirectory()), ex);
|
throw new AutopsyServiceException(String.format("Failed to close core for %s", context.getCase().getCaseDirectory()), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.getCase().getSleuthkitCase().unregisterForEvents(this);
|
if (context.getCase().getSleuthkitCase() != null) {
|
||||||
|
context.getCase().getSleuthkitCase().unregisterForEvents(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2020 Basis Technology Corp.
|
* Copyright 2012-2021 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Copyright 2012 42six Solutions.
|
* Copyright 2012 42six Solutions.
|
||||||
*
|
*
|
||||||
@ -487,7 +487,7 @@ class Chromium extends Extract {
|
|||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
((result.get("host_key").toString() != null) ? result.get("host_key").toString() : ""))); //NON-NLS
|
((result.get("host_key").toString() != null) ? result.get("host_key").toString() : ""))); //NON-NLS
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
(Long.valueOf(result.get("last_access_utc").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
|
(Long.valueOf(result.get("last_access_utc").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2019 Basis Technology Corp.
|
* Copyright 2012-2021 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Copyright 2012 42six Solutions.
|
* Copyright 2012 42six Solutions.
|
||||||
* Contact: aebadirad <at> 42six <dot> com
|
* Contact: aebadirad <at> 42six <dot> com
|
||||||
@ -325,17 +325,27 @@ abstract class Extract {
|
|||||||
* @return List of BlackboarAttributes for the passed in attributes
|
* @return List of BlackboarAttributes for the passed in attributes
|
||||||
*/
|
*/
|
||||||
protected Collection<BlackboardAttribute> createCookieAttributes(String url,
|
protected Collection<BlackboardAttribute> createCookieAttributes(String url,
|
||||||
Long creationTime, String name, String value, String programName, String domain) {
|
Long creationTime, Long accessTime, Long endTime, String name, String value, String programName, String domain) {
|
||||||
|
|
||||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||||
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL,
|
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
(url != null) ? url : "")); //NON-NLS
|
(url != null) ? url : "")); //NON-NLS
|
||||||
|
|
||||||
if (creationTime != null) {
|
if (creationTime != null && creationTime != 0) {
|
||||||
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
|
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), creationTime));
|
RecentActivityExtracterModuleFactory.getModuleName(), creationTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (accessTime != null && accessTime != 0) {
|
||||||
|
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||||
|
RecentActivityExtracterModuleFactory.getModuleName(), accessTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(endTime != null && endTime != 0) {
|
||||||
|
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END,
|
||||||
|
RecentActivityExtracterModuleFactory.getModuleName(), endTime));
|
||||||
|
}
|
||||||
|
|
||||||
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME,
|
bbattributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019-2020 Basis Technology Corp.
|
* Copyright 2019-2021 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -659,7 +659,7 @@ final class ExtractEdge extends Extract {
|
|||||||
String url = flipDomain(domain);
|
String url = flipDomain(domain);
|
||||||
|
|
||||||
BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
|
BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
|
||||||
bbart.addAttributes(createCookieAttributes(url, ftime, name, value, this.getName(), NetworkUtils.extractDomain(url)));
|
bbart.addAttributes(createCookieAttributes(url, null, ftime, null, name, value, this.getName(), NetworkUtils.extractDomain(url)));
|
||||||
return bbart;
|
return bbart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ class ExtractIE extends Extract {
|
|||||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), url));
|
RecentActivityExtracterModuleFactory.getModuleName(), url));
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), datetime));
|
RecentActivityExtracterModuleFactory.getModuleName(), datetime));
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : ""));
|
RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : ""));
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2021 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -566,7 +566,7 @@ final class ExtractSafari extends Extract {
|
|||||||
Cookie cookie = iter.next();
|
Cookie cookie = iter.next();
|
||||||
|
|
||||||
BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
|
BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
|
||||||
bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
|
bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), null, cookie.getExpirationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
|
||||||
bbartifacts.add(bbart);
|
bbartifacts.add(bbart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2020 Basis Technology Corp.
|
* Copyright 2012-2021 Basis Technology Corp.
|
||||||
*
|
*
|
||||||
* Copyright 2012 42six Solutions.
|
* Copyright 2012 42six Solutions.
|
||||||
* Contact: aebadirad <at> 42six <dot> com
|
* Contact: aebadirad <at> 42six <dot> com
|
||||||
@ -423,7 +423,7 @@ class Firefox extends Extract {
|
|||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
((host != null) ? host : ""))); //NON-NLS
|
((host != null) ? host : ""))); //NON-NLS
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
(Long.valueOf(result.get("lastAccessed").toString())))); //NON-NLS
|
(Long.valueOf(result.get("lastAccessed").toString())))); //NON-NLS
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
||||||
|
@ -16,7 +16,25 @@ If you are experiencing an error, we encourage you to post on the forum (https:/
|
|||||||
<li>If there were any errors in the \ref troubleshooting_logs "logs"
|
<li>If there were any errors in the \ref troubleshooting_logs "logs"
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
\section troubleshooting_user_folder Deleting the Autopsy User Folder
|
\section troubleshooting_specific_issues Specific Issues
|
||||||
|
|
||||||
|
\subsection troubleshooting_fond_size Font Size Too Small in Windows
|
||||||
|
|
||||||
|
Make the following changes if the application is hard to navigate in High DPI systems:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>Right-click on the application icon on your Desktop, Start Menu, etc.
|
||||||
|
<li>Choose Properties.
|
||||||
|
<li>Go to Compatibility tab.
|
||||||
|
<li>Click "Change high DPI settings" button.
|
||||||
|
<li>Select "Override high DPI scaling behavior".
|
||||||
|
<li>Change the "Scaling performed by:" drop down box to "System".
|
||||||
|
<li>Restart Autopsy.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
\section troubleshooting_general General Troubleshooting
|
||||||
|
|
||||||
|
\subsection troubleshooting_user_folder Deleting the Autopsy User Folder
|
||||||
|
|
||||||
If Autopsy starts behaving strangely, stops loading entirely, or menu items go missing, you probably need to delete your user folder. Doing so essenitally gives you a fresh installation. On Windows the user folder is located in "C:\Users\(user name)\AppData\Roaming\autopsy".
|
If Autopsy starts behaving strangely, stops loading entirely, or menu items go missing, you probably need to delete your user folder. Doing so essenitally gives you a fresh installation. On Windows the user folder is located in "C:\Users\(user name)\AppData\Roaming\autopsy".
|
||||||
|
|
||||||
@ -30,7 +48,7 @@ Note that if you delete this folder you will lose all your Autopsy settings incl
|
|||||||
|
|
||||||
Alternately, you could copy the fresh user folder somewhere, move your old version back, and replace folders until it works again.
|
Alternately, you could copy the fresh user folder somewhere, move your old version back, and replace folders until it works again.
|
||||||
|
|
||||||
\section troubleshooting_logs Viewing the Logs
|
\subsection troubleshooting_logs Viewing the Logs
|
||||||
|
|
||||||
The logs are generally the most helpful in figuring out why an error is occurring. There are two sets of logs - the system logs and the case logs. There is an option in the UI to open the log folder:
|
The logs are generally the most helpful in figuring out why an error is occurring. There are two sets of logs - the system logs and the case logs. There is an option in the UI to open the log folder:
|
||||||
|
|
||||||
@ -70,7 +88,7 @@ Caused by: java.sql.SQLException: ResultSet closed
|
|||||||
|
|
||||||
If the error message doesn't help you solve the problem yourself, please post to the <a href="https://sleuthkit.discourse.group/">forum</a> including the full stack trace (if available).
|
If the error message doesn't help you solve the problem yourself, please post to the <a href="https://sleuthkit.discourse.group/">forum</a> including the full stack trace (if available).
|
||||||
|
|
||||||
\section troubleshooting_stack Creating a Thread Dump
|
\subsection troubleshooting_stack Creating a Thread Dump
|
||||||
|
|
||||||
You can also generate a thread dump of the current state. This is useful if an ingest module or other process seems to be stuck. To generate a thread dump, go to "Help" then "Thread Dump" in the UI.
|
You can also generate a thread dump of the current state. This is useful if an ingest module or other process seems to be stuck. To generate a thread dump, go to "Help" then "Thread Dump" in the UI.
|
||||||
|
|
||||||
|
178
pythonExamples/Aug2015DataSourceTutorial/FindContactsDb_v2.py
Normal file
178
pythonExamples/Aug2015DataSourceTutorial/FindContactsDb_v2.py
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# Sample module in the public domain. Feel free to use this as a template
|
||||||
|
# for your modules (and you can remove this header and take complete credit
|
||||||
|
# and liability)
|
||||||
|
#
|
||||||
|
# Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
|
||||||
|
#
|
||||||
|
# This is free and unencumbered software released into the public domain.
|
||||||
|
#
|
||||||
|
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
# distribute this software, either in source code form or as a compiled
|
||||||
|
# binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
# means.
|
||||||
|
#
|
||||||
|
# In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
# of this software dedicate any and all copyright interest in the
|
||||||
|
# software to the public domain. We make this dedication for the benefit
|
||||||
|
# of the public at large and to the detriment of our heirs and
|
||||||
|
# successors. We intend this dedication to be an overt act of
|
||||||
|
# relinquishment in perpetuity of all present and future rights to this
|
||||||
|
# software under copyright law.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
# OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# Simple data source-level ingest module for Autopsy.
|
||||||
|
# Used as part of Python tutorials from Basis Technology - August 2015
|
||||||
|
#
|
||||||
|
# Looks for files of a given name, opens then in SQLite, queries the DB,
|
||||||
|
# and makes artifacts
|
||||||
|
|
||||||
|
import jarray
|
||||||
|
import inspect
|
||||||
|
from java.lang import Class
|
||||||
|
from java.lang import System
|
||||||
|
from java.util.logging import Level
|
||||||
|
from java.util import ArrayList
|
||||||
|
from java.io import File
|
||||||
|
from org.sleuthkit.datamodel import SleuthkitCase
|
||||||
|
from org.sleuthkit.datamodel import AbstractFile
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestModule
|
||||||
|
from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException
|
||||||
|
from org.sleuthkit.autopsy.ingest import DataSourceIngestModule
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestMessage
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestServices
|
||||||
|
from org.sleuthkit.autopsy.ingest import ModuleDataEvent
|
||||||
|
from org.sleuthkit.autopsy.coreutils import Logger
|
||||||
|
from org.sleuthkit.autopsy.casemodule import Case
|
||||||
|
from org.sleuthkit.datamodel import TskCoreException
|
||||||
|
from org.sleuthkit.datamodel.Blackboard import BlackboardException
|
||||||
|
from org.sleuthkit.autopsy.casemodule import NoCurrentCaseException
|
||||||
|
from org.sleuthkit.datamodel import Account
|
||||||
|
from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper
|
||||||
|
from java.sql import ResultSet
|
||||||
|
from java.sql import SQLException
|
||||||
|
from org.sleuthkit.autopsy.coreutils import AppSQLiteDB
|
||||||
|
|
||||||
|
# Factory that defines the name and details of the module and allows Autopsy
|
||||||
|
# to create instances of the modules that will do the analysis.
|
||||||
|
class ContactsDbIngestModuleFactory(IngestModuleFactoryAdapter):
|
||||||
|
|
||||||
|
# TODO - Replace with your modules name
|
||||||
|
moduleName = "Contacts Db Analyzer"
|
||||||
|
|
||||||
|
def getModuleDisplayName(self):
|
||||||
|
return self.moduleName
|
||||||
|
|
||||||
|
def getModuleDescription(self):
|
||||||
|
return "Sample module that parses contacts.db"
|
||||||
|
|
||||||
|
def getModuleVersionNumber(self):
|
||||||
|
return "1.0"
|
||||||
|
|
||||||
|
def isDataSourceIngestModuleFactory(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def createDataSourceIngestModule(self, ingestOptions):
|
||||||
|
return ContactsDbIngestModule()
|
||||||
|
|
||||||
|
|
||||||
|
# Data Source-level ingest module. One gets created per data source.
|
||||||
|
class ContactsDbIngestModule(DataSourceIngestModule):
|
||||||
|
|
||||||
|
_logger = Logger.getLogger(ContactsDbIngestModuleFactory.moduleName)
|
||||||
|
|
||||||
|
def log(self, level, msg):
|
||||||
|
self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.context = None
|
||||||
|
|
||||||
|
# Where any setup and configuration is done
|
||||||
|
# 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext.
|
||||||
|
# See: http://sleuthkit.org/autopsy/docs/api-docs/latest/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html
|
||||||
|
def startUp(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
# Where the analysis is done.
|
||||||
|
# The 'data_source' object being passed in is of type org.sleuthkit.datamodel.Content.
|
||||||
|
# See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/latest/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html
|
||||||
|
# 'progress_bar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress
|
||||||
|
# See: http://sleuthkit.org/autopsy/docs/api-docs/latest/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html
|
||||||
|
def process(self, data_source, progress_bar):
|
||||||
|
|
||||||
|
# we don't know how much work there is yet
|
||||||
|
progress_bar.switchToIndeterminate()
|
||||||
|
|
||||||
|
# Find files named contacts.db anywhere in the data source.
|
||||||
|
# TODO - replace with your database name and parent path.
|
||||||
|
app_databases = AppSQLiteDB.findAppDatabases(data_source, "contacts.db", True, "")
|
||||||
|
|
||||||
|
num_databases = len(app_databases)
|
||||||
|
progress_bar.switchToDeterminate(num_databases)
|
||||||
|
databases_processed = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Iterate through all the database files returned
|
||||||
|
for app_database in app_databases:
|
||||||
|
|
||||||
|
# Check if the user pressed cancel while we were busy
|
||||||
|
if self.context.isJobCancelled():
|
||||||
|
return IngestModule.ProcessResult.OK
|
||||||
|
|
||||||
|
self.log(Level.INFO, "Processing file: " + app_database.getDBFile().getName())
|
||||||
|
|
||||||
|
# Query the contacts table in the database and get all columns.
|
||||||
|
try:
|
||||||
|
# TODO - replace with your query
|
||||||
|
result_set = app_database.runQuery("SELECT * FROM contacts")
|
||||||
|
except SQLException as e:
|
||||||
|
self.log(Level.INFO, "Error querying database for contacts table (" + e.getMessage() + ")")
|
||||||
|
return IngestModule.ProcessResult.OK
|
||||||
|
|
||||||
|
try:
|
||||||
|
#Get the current case for the CommunicationArtifactsHelper.
|
||||||
|
current_case = Case.getCurrentCaseThrows()
|
||||||
|
except NoCurrentCaseException as ex:
|
||||||
|
self.log(Level.INFO, "Case is closed (" + ex.getMessage() + ")")
|
||||||
|
return IngestModule.ProcessResult.OK
|
||||||
|
|
||||||
|
# Create an instance of the helper class
|
||||||
|
# TODO - Replace with your parser name and Account.Type
|
||||||
|
helper = CommunicationArtifactsHelper(current_case.getSleuthkitCase(),
|
||||||
|
ContactsDbIngestModuleFactory.moduleName, app_database.getDBFile(), Account.Type.DEVICE)
|
||||||
|
|
||||||
|
# Iterate through each row and create artifacts
|
||||||
|
while result_set.next():
|
||||||
|
try:
|
||||||
|
# TODO - Replace these calls with your column names and types
|
||||||
|
# Ex of other types: result_set.getInt("contact_type") or result_set.getLong("datetime")
|
||||||
|
name = result_set.getString("name")
|
||||||
|
email = result_set.getString("email")
|
||||||
|
phone = result_set.getString("phone")
|
||||||
|
except SQLException as e:
|
||||||
|
self.log(Level.INFO, "Error getting values from contacts table (" + e.getMessage() + ")")
|
||||||
|
|
||||||
|
helper.addContact(name, phone, "", "", email)
|
||||||
|
|
||||||
|
app_database.close()
|
||||||
|
databases_processed += 1
|
||||||
|
progress_bar.progress(databases_processed)
|
||||||
|
except TskCoreException as e:
|
||||||
|
self.log(Level.INFO, "Error inserting or reading from the Sleuthkit case (" + e.getMessage() + ")")
|
||||||
|
except BlackboardException as e:
|
||||||
|
self.log(Level.INFO, "Error posting artifact to the Blackboard (" + e.getMessage() + ")")
|
||||||
|
|
||||||
|
# After all databases, post a message to the ingest messages in box.
|
||||||
|
# TODO - update your module name here
|
||||||
|
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
|
||||||
|
ContactsDbIngestModuleFactory.moduleName, "Found %d files" % num_databases)
|
||||||
|
IngestServices.getInstance().postMessage(message)
|
||||||
|
|
||||||
|
return IngestModule.ProcessResult.OK
|
284
pythonExamples/Registry_Example.py
Normal file
284
pythonExamples/Registry_Example.py
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
# Sample module in the public domain. Feel free to use this as a template
|
||||||
|
# for your modules (and you can remove this header and take complete credit
|
||||||
|
# and liability)
|
||||||
|
#
|
||||||
|
# Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
|
||||||
|
#
|
||||||
|
# This is free and unencumbered software released into the public domain.
|
||||||
|
#
|
||||||
|
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
# distribute this software, either in source code form or as a compiled
|
||||||
|
# binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
# means.
|
||||||
|
#
|
||||||
|
# In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
# of this software dedicate any and all copyright interest in the
|
||||||
|
# software to the public domain. We make this dedication for the benefit
|
||||||
|
# of the public at large and to the detriment of our heirs and
|
||||||
|
# successors. We intend this dedication to be an overt act of
|
||||||
|
# relinquishment in perpetuity of all present and future rights to this
|
||||||
|
# software under copyright law.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
# OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# Simple data source-level ingest module for Autopsy.
|
||||||
|
# Search for TODO for the things that you need to change
|
||||||
|
# See http://sleuthkit.org/autopsy/docs/api-docs/4.6.0/index.html for documentation
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import ntpath
|
||||||
|
|
||||||
|
from com.williballenthin.rejistry import RegistryHiveFile
|
||||||
|
from com.williballenthin.rejistry import RegistryKey
|
||||||
|
from com.williballenthin.rejistry import RegistryParseException
|
||||||
|
from com.williballenthin.rejistry import RegistryValue
|
||||||
|
from java.io import File
|
||||||
|
from java.lang import Class
|
||||||
|
from java.lang import System
|
||||||
|
from java.sql import DriverManager, SQLException
|
||||||
|
from java.util.logging import Level
|
||||||
|
from java.util import ArrayList
|
||||||
|
from org.sleuthkit.datamodel import SleuthkitCase
|
||||||
|
from org.sleuthkit.datamodel import AbstractFile
|
||||||
|
from org.sleuthkit.datamodel import ReadContentInputStream
|
||||||
|
from org.sleuthkit.datamodel import BlackboardArtifact
|
||||||
|
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||||
|
from org.sleuthkit.datamodel import TskData
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestModule
|
||||||
|
from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException
|
||||||
|
from org.sleuthkit.autopsy.ingest import DataSourceIngestModule
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestModuleIngestJobSettings
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestModuleIngestJobSettingsPanel
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestMessage
|
||||||
|
from org.sleuthkit.autopsy.ingest import IngestServices
|
||||||
|
from org.sleuthkit.autopsy.ingest import ModuleDataEvent
|
||||||
|
from org.sleuthkit.autopsy.coreutils import Logger
|
||||||
|
from org.sleuthkit.autopsy.coreutils import PlatformUtil
|
||||||
|
from org.sleuthkit.autopsy.casemodule import Case
|
||||||
|
from org.sleuthkit.autopsy.casemodule.services import Services
|
||||||
|
from org.sleuthkit.autopsy.casemodule.services import FileManager
|
||||||
|
from org.sleuthkit.autopsy.datamodel import ContentUtils
|
||||||
|
from org.sleuthkit.autopsy.modules.interestingitems import FilesSetsManager
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Factory that defines the name and details of the module and allows Autopsy
|
||||||
|
# to create instances of the modules that will do the analysis.
|
||||||
|
class RegistryExampleIngestModuleFactory(IngestModuleFactoryAdapter):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.settings = None
|
||||||
|
|
||||||
|
moduleName = "Registy Example Module"
|
||||||
|
|
||||||
|
def getModuleDisplayName(self):
|
||||||
|
return self.moduleName
|
||||||
|
|
||||||
|
def getModuleDescription(self):
|
||||||
|
return "Extract Run Keys To Look For Interesting Items"
|
||||||
|
|
||||||
|
def getModuleVersionNumber(self):
|
||||||
|
return "1.0"
|
||||||
|
|
||||||
|
def hasIngestJobSettingsPanel(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def isDataSourceIngestModuleFactory(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def createDataSourceIngestModule(self, ingestOptions):
|
||||||
|
return RegistryExampleIngestModule(self.settings)
|
||||||
|
|
||||||
|
# Data Source-level ingest module. One gets created per data source.
|
||||||
|
class RegistryExampleIngestModule(DataSourceIngestModule):
|
||||||
|
|
||||||
|
_logger = Logger.getLogger(RegistryExampleIngestModuleFactory.moduleName)
|
||||||
|
|
||||||
|
def log(self, level, msg):
|
||||||
|
self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg)
|
||||||
|
|
||||||
|
def __init__(self, settings):
|
||||||
|
self.context = None
|
||||||
|
|
||||||
|
# Where any setup and configuration is done
|
||||||
|
def startUp(self, context):
|
||||||
|
self.context = context
|
||||||
|
# Hive Keys to parse, use / as it is easier to parse out then \\
|
||||||
|
self.registryNTUserRunKeys = ('Software/Microsoft/Windows/CurrentVersion/Run', 'Software/Microsoft/Windows/CurrentVersion/RunOnce')
|
||||||
|
self.registrySoftwareRunKeys = ('Microsoft/Windows/CurrentVersion/Run', 'Microsoft/Windows/CurrentVersion/RunOnce')
|
||||||
|
self.registryKeysFound = []
|
||||||
|
|
||||||
|
# Where the analysis is done.
|
||||||
|
def process(self, dataSource, progressBar):
|
||||||
|
|
||||||
|
# we don't know how much work there is yet
|
||||||
|
progressBar.switchToIndeterminate()
|
||||||
|
|
||||||
|
# Hive files to extract
|
||||||
|
filesToExtract = ("NTUSER.DAT", "SOFTWARE")
|
||||||
|
|
||||||
|
# Create ExampleRegistry directory in temp directory, if it exists then continue on processing
|
||||||
|
tempDir = os.path.join(Case.getCurrentCase().getTempDirectory(), "RegistryExample")
|
||||||
|
self.log(Level.INFO, "create Directory " + tempDir)
|
||||||
|
try:
|
||||||
|
os.mkdir(tempDir)
|
||||||
|
except:
|
||||||
|
self.log(Level.INFO, "ExampleRegistry Directory already exists " + tempDir)
|
||||||
|
|
||||||
|
# Set the database to be read to the once created by the prefetch parser program
|
||||||
|
skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||||
|
fileManager = Case.getCurrentCase().getServices().getFileManager()
|
||||||
|
|
||||||
|
# Look for files to process
|
||||||
|
for fileName in filesToExtract:
|
||||||
|
files = fileManager.findFiles(dataSource, fileName)
|
||||||
|
numFiles = len(files)
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
|
||||||
|
# Check if the user pressed cancel while we were busy
|
||||||
|
if self.context.isJobCancelled():
|
||||||
|
return IngestModule.ProcessResult.OK
|
||||||
|
|
||||||
|
# Check path to only get the hive files in the config directory and no others
|
||||||
|
if ((file.getName() == 'SOFTWARE') and (file.getParentPath().upper() == '/WINDOWS/SYSTEM32/CONFIG/') and (file.getSize() > 0)):
|
||||||
|
# Save the file locally in the temp folder.
|
||||||
|
self.writeHiveFile(file, file.getName(), tempDir)
|
||||||
|
|
||||||
|
# Process this file looking thru the run keys
|
||||||
|
self.processSoftwareHive(os.path.join(tempDir, file.getName()), file)
|
||||||
|
|
||||||
|
elif ((file.getName() == 'NTUSER.DAT') and ('/USERS' in file.getParentPath().upper()) and (file.getSize() > 0)):
|
||||||
|
# Found a NTUSER.DAT file to process only want files in User directories
|
||||||
|
# Filename may not be unique so add file id to the name
|
||||||
|
fileName = str(file.getId()) + "-" + file.getName()
|
||||||
|
|
||||||
|
# Save the file locally in the temp folder.
|
||||||
|
self.writeHiveFile(file, fileName, tempDir)
|
||||||
|
|
||||||
|
# Process this file looking thru the run keys
|
||||||
|
self.processNTUserHive(os.path.join(tempDir, fileName), file)
|
||||||
|
|
||||||
|
|
||||||
|
# Setup Artifact and Attributes
|
||||||
|
try:
|
||||||
|
artID = skCase.addArtifactType( "TSK_REGISTRY_RUN_KEYS", "Registry Run Keys")
|
||||||
|
except:
|
||||||
|
self.log(Level.INFO, "Artifacts Creation Error, some artifacts may not exist now. ==> ")
|
||||||
|
|
||||||
|
artId = skCase.getArtifactTypeID("TSK_REGISTRY_RUN_KEYS")
|
||||||
|
|
||||||
|
try:
|
||||||
|
attributeIdRunKeyName = skCase.addArtifactAttributeType("TSK_REG_RUN_KEY_NAME", BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, "Run Key Name")
|
||||||
|
except:
|
||||||
|
self.log(Level.INFO, "Attributes Creation Error, TSK_REG_RUN_KEY_NAME, May already exist. ")
|
||||||
|
try:
|
||||||
|
attributeIdRunKeyValue = skCase.addArtifactAttributeType("TSK_REG_RUN_KEY_VALUE", BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, "Run Key Value")
|
||||||
|
except:
|
||||||
|
self.log(Level.INFO, "Attributes Creation Error, TSK_REG_RUN_KEY_VALUE, May already exist. ")
|
||||||
|
try:
|
||||||
|
attributeIdRegKeyLoc = skCase.addArtifactAttributeType("TSK_REG_KEY_LOCATION", BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, "Registry Key Location")
|
||||||
|
except:
|
||||||
|
self.log(Level.INFO, "Attributes Creation Error, TSK_REG_KEY_LOCATION, May already exist. ")
|
||||||
|
|
||||||
|
attributeIdRunKeyName = skCase.getAttributeType("TSK_REG_RUN_KEY_NAME")
|
||||||
|
attributeIdRunKeyValue = skCase.getAttributeType("TSK_REG_RUN_KEY_VALUE")
|
||||||
|
attributeIdRegKeyLoc = skCase.getAttributeType("TSK_REG_KEY_LOCATION")
|
||||||
|
|
||||||
|
moduleName = RegistryExampleIngestModuleFactory.moduleName
|
||||||
|
|
||||||
|
# RefistryKeysFound is a list that contains a list with the following records abstractFile, Registry Key Location, Key Name, Key value
|
||||||
|
for registryKey in self.registryKeysFound:
|
||||||
|
attributes = ArrayList()
|
||||||
|
art = registryKey[0].newArtifact(artId)
|
||||||
|
|
||||||
|
attributes.add(BlackboardAttribute(attributeIdRegKeyLoc, moduleName, registryKey[1]))
|
||||||
|
attributes.add(BlackboardAttribute(attributeIdRunKeyName, moduleName, registryKey[2]))
|
||||||
|
attributes.add(BlackboardAttribute(attributeIdRunKeyValue, moduleName, registryKey[3]))
|
||||||
|
art.addAttributes(attributes)
|
||||||
|
|
||||||
|
# index the artifact for keyword search
|
||||||
|
try:
|
||||||
|
blackboard.indexArtifact(art)
|
||||||
|
except:
|
||||||
|
self._logger.log(Level.WARNING, "Error indexing artifact " + art.getDisplayName())
|
||||||
|
|
||||||
|
#Clean up registryExample directory and files
|
||||||
|
try:
|
||||||
|
shutil.rmtree(tempDir)
|
||||||
|
except:
|
||||||
|
self.log(Level.INFO, "removal of directory tree failed " + tempDir)
|
||||||
|
|
||||||
|
# After all databases, post a message to the ingest messages in box.
|
||||||
|
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
|
||||||
|
"RegistryExample", " RegistryExample Files Have Been Analyzed " )
|
||||||
|
IngestServices.getInstance().postMessage(message)
|
||||||
|
|
||||||
|
return IngestModule.ProcessResult.OK
|
||||||
|
|
||||||
|
def writeHiveFile(self, file, fileName, tempDir):
|
||||||
|
# Write the file to the temp directory.
|
||||||
|
filePath = os.path.join(tempDir, fileName)
|
||||||
|
ContentUtils.writeToFile(file, File(filePath))
|
||||||
|
|
||||||
|
|
||||||
|
def processSoftwareHive(self, softwareHive, abstractFile):
|
||||||
|
# Open the registry hive file
|
||||||
|
softwareRegFile = RegistryHiveFile(File(softwareHive))
|
||||||
|
for runKey in self.registrySoftwareRunKeys:
|
||||||
|
currentKey = self.findRegistryKey(softwareRegFile, runKey)
|
||||||
|
if len(currentKey.getValueList()) > 0:
|
||||||
|
skValues = currentKey.getValueList()
|
||||||
|
for skValue in skValues:
|
||||||
|
regKey = []
|
||||||
|
regKey.append(abstractFile)
|
||||||
|
regKey.append(runKey)
|
||||||
|
skName = skValue.getName()
|
||||||
|
skVal = skValue.getValue()
|
||||||
|
regKey.append(str(skName))
|
||||||
|
regKey.append(skVal.getAsString())
|
||||||
|
self.registryKeysFound.append(regKey)
|
||||||
|
|
||||||
|
|
||||||
|
def processNTUserHive(self, ntuserHive, abstractFile):
|
||||||
|
|
||||||
|
# Open the registry hive file
|
||||||
|
ntuserRegFile = RegistryHiveFile(File(ntuserHive))
|
||||||
|
for runKey in self.registryNTUserRunKeys:
|
||||||
|
currentKey = self.findRegistryKey(ntuserRegFile, runKey)
|
||||||
|
if len(currentKey.getValueList()) > 0:
|
||||||
|
skValues = currentKey.getValueList()
|
||||||
|
for skValue in skValues:
|
||||||
|
regKey = []
|
||||||
|
regKey.append(abstractFile)
|
||||||
|
regKey.append(runKey)
|
||||||
|
skName = skValue.getName()
|
||||||
|
skVal = skValue.getValue()
|
||||||
|
regKey.append(str(skName))
|
||||||
|
regKey.append(skVal.getAsString())
|
||||||
|
self.registryKeysFound.append(regKey)
|
||||||
|
|
||||||
|
def findRegistryKey(self, registryHiveFile, registryKey):
|
||||||
|
# Search for the registry key
|
||||||
|
rootKey = registryHiveFile.getRoot()
|
||||||
|
regKeyList = registryKey.split('/')
|
||||||
|
currentKey = rootKey
|
||||||
|
try:
|
||||||
|
for key in regKeyList:
|
||||||
|
currentKey = currentKey.getSubkey(key)
|
||||||
|
return currentKey
|
||||||
|
except:
|
||||||
|
# Key not found
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -273,19 +273,24 @@ class TestRunner(object):
|
|||||||
|
|
||||||
# Compare output with gold and display results
|
# Compare output with gold and display results
|
||||||
TestResultsDiffer.run_diff(test_data)
|
TestResultsDiffer.run_diff(test_data)
|
||||||
print("Html report passed: ", test_data.html_report_passed)
|
# NOTE: commented out html version items
|
||||||
|
# print("Html report passed: ", test_data.html_report_passed)
|
||||||
print("Errors diff passed: ", test_data.errors_diff_passed)
|
print("Errors diff passed: ", test_data.errors_diff_passed)
|
||||||
print("DB diff passed: ", test_data.db_diff_passed)
|
print("DB diff passed: ", test_data.db_diff_passed)
|
||||||
|
|
||||||
# run time test only for the specific jenkins test
|
# run time test only for the specific jenkins test
|
||||||
if test_data.main_config.timing:
|
if test_data.main_config.timing:
|
||||||
print("Run time test passed: ", test_data.run_time_passed)
|
print("Run time test passed: ", test_data.run_time_passed)
|
||||||
test_data.overall_passed = (test_data.html_report_passed and
|
# NOTE: commented out html version items
|
||||||
test_data.errors_diff_passed and test_data.db_diff_passed)
|
#test_data.overall_passed = (test_data.html_report_passed and
|
||||||
|
#test_data.errors_diff_passed and test_data.db_diff_passed)
|
||||||
|
test_data.overall_passed = (test_data.errors_diff_passed and test_data.db_diff_passed)
|
||||||
# otherwise, do the usual
|
# otherwise, do the usual
|
||||||
else:
|
else:
|
||||||
test_data.overall_passed = (test_data.html_report_passed and
|
# NOTE: commented out html version items
|
||||||
test_data.errors_diff_passed and test_data.db_diff_passed)
|
#test_data.overall_passed = (test_data.html_report_passed and
|
||||||
|
#test_data.errors_diff_passed and test_data.db_diff_passed)
|
||||||
|
test_data.overall_passed = (test_data.errors_diff_passed and test_data.db_diff_passed)
|
||||||
|
|
||||||
Reports.generate_reports(test_data)
|
Reports.generate_reports(test_data)
|
||||||
if(not test_data.overall_passed):
|
if(not test_data.overall_passed):
|
||||||
@ -1009,10 +1014,11 @@ class TestResultsDiffer(object):
|
|||||||
test_data.errors_diff_passed = passed
|
test_data.errors_diff_passed = passed
|
||||||
|
|
||||||
# Compare html output
|
# Compare html output
|
||||||
gold_report_path = test_data.get_html_report_path(DBType.GOLD)
|
# NOTE: commented out html version items
|
||||||
output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
|
# gold_report_path = test_data.get_html_report_path(DBType.GOLD)
|
||||||
passed = TestResultsDiffer._html_report_diff(test_data)
|
# output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
|
||||||
test_data.html_report_passed = passed
|
# passed = TestResultsDiffer._html_report_diff(test_data)
|
||||||
|
# test_data.html_report_passed = passed
|
||||||
|
|
||||||
# Compare time outputs
|
# Compare time outputs
|
||||||
if test_data.main_config.timing:
|
if test_data.main_config.timing:
|
||||||
@ -1070,51 +1076,52 @@ class TestResultsDiffer(object):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _html_report_diff(test_data):
|
# NOTE: commented out html version items
|
||||||
"""Compare the output and gold html reports. Diff util is used for this purpose.
|
# def _html_report_diff(test_data):
|
||||||
Diff -r -N -x <non-textual files> --ignore-matching-lines <regex> <folder-location-1> <folder-location-2>
|
# """Compare the output and gold html reports. Diff util is used for this purpose.
|
||||||
is executed.
|
# Diff -r -N -x <non-textual files> --ignore-matching-lines <regex> <folder-location-1> <folder-location-2>
|
||||||
Diff is recursively used to scan through the HTML report directories. Modify the <regex> to suit the needs.
|
# is executed.
|
||||||
Currently, the regex is set to match certain lines found on index.html and summary.html, and skip (read ignore)
|
# Diff is recursively used to scan through the HTML report directories. Modify the <regex> to suit the needs.
|
||||||
them.
|
# Currently, the regex is set to match certain lines found on index.html and summary.html, and skip (read ignore)
|
||||||
Diff returns 0 when there is no difference, 1 when there is difference, and 2 when there is trouble (trouble not
|
# them.
|
||||||
defined in the official documentation).
|
# Diff returns 0 when there is no difference, 1 when there is difference, and 2 when there is trouble (trouble not
|
||||||
|
# defined in the official documentation).
|
||||||
Args:
|
#
|
||||||
test_data TestData object which contains initialized report_paths.
|
# Args:
|
||||||
|
# test_data TestData object which contains initialized report_paths.
|
||||||
Returns:
|
#
|
||||||
true, if the reports match, false otherwise.
|
# Returns:
|
||||||
"""
|
# true, if the reports match, false otherwise.
|
||||||
gold_report_path = test_data.get_html_report_path(DBType.GOLD)
|
# """
|
||||||
output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
|
# gold_report_path = test_data.get_html_report_path(DBType.GOLD)
|
||||||
try:
|
# output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
|
||||||
# Ensure gold is passed before output
|
# try:
|
||||||
(subprocess.check_output(["diff", '-r', '-N', '-x', '*.png', '-x', '*.ico', '--ignore-matching-lines',
|
# # Ensure gold is passed before output
|
||||||
'HTML Report Generated on \|Autopsy Report for case \|Case:\|Case Number:'
|
# (subprocess.check_output(["diff", '-r', '-N', '-x', '*.png', '-x', '*.ico', '--ignore-matching-lines',
|
||||||
'\|Examiner:\|Unalloc_', gold_report_path, output_report_path]))
|
# 'HTML Report Generated on \|Autopsy Report for case \|Case:\|Case Number:'
|
||||||
print_report("", "REPORT COMPARISON", "The test html reports matched the gold reports")
|
# '\|Examiner:\|Unalloc_', gold_report_path, output_report_path]))
|
||||||
return True
|
# print_report("", "REPORT COMPARISON", "The test html reports matched the gold reports")
|
||||||
except subprocess.CalledProcessError as e:
|
# return True
|
||||||
if e.returncode == 1:
|
# except subprocess.CalledProcessError as e:
|
||||||
Errors.print_error("Error Code: 1\nThe HTML reports did not match.")
|
# if e.returncode == 1:
|
||||||
diff_file = codecs.open(test_data.output_path + "\HTML-Report-Diff.txt", "wb", "utf_8")
|
# Errors.print_error("Error Code: 1\nThe HTML reports did not match.")
|
||||||
diff_file.write(str(e.output.decode("utf-8")))
|
# diff_file = codecs.open(test_data.output_path + "\HTML-Report-Diff.txt", "wb", "utf_8")
|
||||||
return False
|
# diff_file.write(str(e.output.decode("utf-8")))
|
||||||
if e.returncode == 2:
|
# return False
|
||||||
Errors.print_error("Error Code: 2\nTrouble executing the Diff Utility.")
|
# if e.returncode == 2:
|
||||||
diff_file = codecs.open(test_data.output_path + "\HTML-Report-Diff.txt", "wb", "utf_8")
|
# Errors.print_error("Error Code: 2\nTrouble executing the Diff Utility.")
|
||||||
diff_file.write(str(e.output.decode("utf-8")))
|
# diff_file = codecs.open(test_data.output_path + "\HTML-Report-Diff.txt", "wb", "utf_8")
|
||||||
return False
|
# diff_file.write(str(e.output.decode("utf-8")))
|
||||||
except OSError as e:
|
# return False
|
||||||
Errors.print_error("Error: OSError while performing html report diff")
|
# except OSError as e:
|
||||||
Errors.print_error(str(e) + "\n")
|
# Errors.print_error("Error: OSError while performing html report diff")
|
||||||
return False
|
# Errors.print_error(str(e) + "\n")
|
||||||
except Exception as e:
|
# return False
|
||||||
Errors.print_error("Error: Unknown fatal error comparing reports.")
|
# except Exception as e:
|
||||||
Errors.print_error(str(e) + "\n")
|
# Errors.print_error("Error: Unknown fatal error comparing reports.")
|
||||||
logging.critical(traceback.format_exc())
|
# Errors.print_error(str(e) + "\n")
|
||||||
return False
|
# logging.critical(traceback.format_exc())
|
||||||
|
# return False
|
||||||
|
|
||||||
def _run_time_diff(test_data, old_time_path):
|
def _run_time_diff(test_data, old_time_path):
|
||||||
""" Compare run times for this run, and the run previous.
|
""" Compare run times for this run, and the run previous.
|
||||||
@ -1371,7 +1378,8 @@ class Reports(object):
|
|||||||
vars.append( str(len(search_log_set("autopsy", "Stopping ingest due to low disk space on disk", test_data))) )
|
vars.append( str(len(search_log_set("autopsy", "Stopping ingest due to low disk space on disk", test_data))) )
|
||||||
vars.append( make_local_path("gold", test_data.image_name, DB_FILENAME) )
|
vars.append( make_local_path("gold", test_data.image_name, DB_FILENAME) )
|
||||||
vars.append( make_local_path("gold", test_data.image_name, "standard.html") )
|
vars.append( make_local_path("gold", test_data.image_name, "standard.html") )
|
||||||
vars.append( str(test_data.html_report_passed) )
|
# NOTE: commented out html version items
|
||||||
|
# vars.append( str(test_data.html_report_passed) )
|
||||||
vars.append( test_data.ant_to_string() )
|
vars.append( test_data.ant_to_string() )
|
||||||
# Join it together with a ", "
|
# Join it together with a ", "
|
||||||
output = "|".join(vars)
|
output = "|".join(vars)
|
||||||
|
@ -469,9 +469,9 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
|
|||||||
|
|
||||||
# Ignore TIFF size and hash if extracted from PDFs.
|
# Ignore TIFF size and hash if extracted from PDFs.
|
||||||
# See JIRA-6951 for more details.
|
# See JIRA-6951 for more details.
|
||||||
# index -1 = last element in the list, which is extension
|
# index -3 = 3rd from the end, which is extension
|
||||||
# index -3 = 3rd from the end, which is the parent path.
|
# index -5 = 5th from the end, which is the parent path.
|
||||||
if fields_list[-1] == "'tif'" and fields_list[-3].endswith(".pdf/'"):
|
if fields_list[-3] == "'tif'" and fields_list[-5].endswith(".pdf/'"):
|
||||||
fields_list[15] = "'SIZE_IGNORED'"
|
fields_list[15] = "'SIZE_IGNORED'"
|
||||||
fields_list[23] = "'MD5_IGNORED'"
|
fields_list[23] = "'MD5_IGNORED'"
|
||||||
fields_list[24] = "'SHA256_IGNORED'"
|
fields_list[24] = "'SHA256_IGNORED'"
|
||||||
|
BIN
thirdparty/java-libpst/java-libpst-0.9.5-SNAPSHOT.jar
vendored
Normal file
BIN
thirdparty/java-libpst/java-libpst-0.9.5-SNAPSHOT.jar
vendored
Normal file
Binary file not shown.
BIN
thirdparty/java-libpst/java-libpst-1.0-SNAPSHOT.jar
vendored
BIN
thirdparty/java-libpst/java-libpst-1.0-SNAPSHOT.jar
vendored
Binary file not shown.
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<target name="get-thirdparty-jars" description="get third-party jar dependencies">
|
<target name="get-thirdparty-jars" description="get third-party jar dependencies">
|
||||||
<mkdir dir="${ext.dir}"/>
|
<mkdir dir="${ext.dir}"/>
|
||||||
<copy file="${thirdparty.dir}/java-libpst/java-libpst-1.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
<copy file="${thirdparty.dir}/java-libpst/java-libpst-0.9.5-SNAPSHOT.jar" todir="${ext.dir}" />
|
||||||
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-core-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-core-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
||||||
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-dom-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-dom-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
||||||
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
|
||||||
|
@ -7,14 +7,16 @@ file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom
|
|||||||
file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar
|
file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar
|
||||||
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
|
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
|
||||||
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
|
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
|
||||||
file.reference.java-libpst-1.0-SNAPSHOT.jar=release/modules/ext/java-libpst-1.0-SNAPSHOT.jar
|
|
||||||
file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar
|
file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar
|
||||||
|
file.reference.java-libpst-0.9.5-SNAPSHOT.jar=release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar
|
||||||
file.reference.vinnie-2.0.2.jar=release/modules/ext/vinnie-2.0.2.jar
|
file.reference.vinnie-2.0.2.jar=release/modules/ext/vinnie-2.0.2.jar
|
||||||
javac.source=1.8
|
javac.source=1.8
|
||||||
javac.compilerargs=-Xlint -Xlint:-serial
|
javac.compilerargs=-Xlint -Xlint:-serial
|
||||||
javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar
|
javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar
|
||||||
|
javadoc.reference.java-libpst-0.9.5-SNAPSHOT.jar=release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar
|
||||||
license.file=../LICENSE-2.0.txt
|
license.file=../LICENSE-2.0.txt
|
||||||
nbm.homepage=http://www.sleuthkit.org/autopsy/
|
nbm.homepage=http://www.sleuthkit.org/autopsy/
|
||||||
nbm.needs.restart=true
|
nbm.needs.restart=true
|
||||||
source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar
|
source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar
|
||||||
|
source.reference.java-libpst-0.9.5-SNAPSHOT.jar=release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar
|
||||||
spec.version.base=4.0
|
spec.version.base=4.0
|
||||||
|
@ -84,14 +84,14 @@
|
|||||||
<runtime-relative-path>ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</runtime-relative-path>
|
<runtime-relative-path>ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</binary-origin>
|
<binary-origin>release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/java-libpst-1.0-SNAPSHOT.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/java-libpst-1.0-SNAPSHOT.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
|
<runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
|
<binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/java-libpst-0.9.5-SNAPSHOT.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</runtime-relative-path>
|
<runtime-relative-path>ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</binary-origin>
|
<binary-origin>release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</binary-origin>
|
||||||
|
@ -43,7 +43,7 @@ class EmailMessage {
|
|||||||
private String localPath = "";
|
private String localPath = "";
|
||||||
private boolean hasAttachment = false;
|
private boolean hasAttachment = false;
|
||||||
private long sentDate = 0L;
|
private long sentDate = 0L;
|
||||||
private List<Attachment> attachments = new ArrayList<>();
|
private final List<Attachment> attachments = new ArrayList<>();
|
||||||
private long id = -1L;
|
private long id = -1L;
|
||||||
private String messageID = "";
|
private String messageID = "";
|
||||||
private String inReplyToID = "";
|
private String inReplyToID = "";
|
||||||
@ -410,4 +410,16 @@ class EmailMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class AttachedEmailMessage extends Attachment {
|
||||||
|
private final EmailMessage emailMessage;
|
||||||
|
|
||||||
|
AttachedEmailMessage(EmailMessage emailMessage) {
|
||||||
|
this.emailMessage = emailMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmailMessage getEmailMessage() {
|
||||||
|
return emailMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -26,12 +26,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.apache.james.mime4j.dom.BinaryBody;
|
|
||||||
import org.apache.james.mime4j.dom.Body;
|
import org.apache.james.mime4j.dom.Body;
|
||||||
import org.apache.james.mime4j.dom.Entity;
|
import org.apache.james.mime4j.dom.Entity;
|
||||||
import org.apache.james.mime4j.dom.Message;
|
import org.apache.james.mime4j.dom.Message;
|
||||||
|
import org.apache.james.mime4j.dom.MessageWriter;
|
||||||
import org.apache.james.mime4j.dom.Multipart;
|
import org.apache.james.mime4j.dom.Multipart;
|
||||||
import org.apache.james.mime4j.dom.SingleBody;
|
|
||||||
import org.apache.james.mime4j.dom.TextBody;
|
import org.apache.james.mime4j.dom.TextBody;
|
||||||
import org.apache.james.mime4j.dom.address.AddressList;
|
import org.apache.james.mime4j.dom.address.AddressList;
|
||||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||||
@ -39,6 +38,7 @@ import org.apache.james.mime4j.dom.address.MailboxList;
|
|||||||
import org.apache.james.mime4j.dom.field.ContentDispositionField;
|
import org.apache.james.mime4j.dom.field.ContentDispositionField;
|
||||||
import org.apache.james.mime4j.dom.field.ContentTypeField;
|
import org.apache.james.mime4j.dom.field.ContentTypeField;
|
||||||
import org.apache.james.mime4j.message.DefaultMessageBuilder;
|
import org.apache.james.mime4j.message.DefaultMessageBuilder;
|
||||||
|
import org.apache.james.mime4j.message.DefaultMessageWriter;
|
||||||
import org.apache.james.mime4j.stream.Field;
|
import org.apache.james.mime4j.stream.Field;
|
||||||
import org.apache.james.mime4j.stream.MimeConfig;
|
import org.apache.james.mime4j.stream.MimeConfig;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
@ -293,7 +293,7 @@ class MimeJ4MessageParser implements AutoCloseable{
|
|||||||
* @param e
|
* @param e
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({"MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."})
|
@NbBundle.Messages({"MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."})
|
||||||
private static void handleAttachment(EmailMessage email, Entity e, long fileID, int index) {
|
private void handleAttachment(EmailMessage email, Entity e, long fileID, int index) {
|
||||||
String outputDirPath;
|
String outputDirPath;
|
||||||
String relModuleOutputPath;
|
String relModuleOutputPath;
|
||||||
try {
|
try {
|
||||||
@ -322,25 +322,31 @@ class MimeJ4MessageParser implements AutoCloseable{
|
|||||||
String outPath = outputDirPath + uniqueFilename;
|
String outPath = outputDirPath + uniqueFilename;
|
||||||
|
|
||||||
Body body = e.getBody();
|
Body body = e.getBody();
|
||||||
if (body instanceof SingleBody) {
|
if (body != null) {
|
||||||
long fileLength;
|
long fileLength;
|
||||||
try (EncodedFileOutputStream fos = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1)) {
|
try (EncodedFileOutputStream fos = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1)) {
|
||||||
((SingleBody) body).writeTo(fos);
|
|
||||||
|
EmailMessage.Attachment attach;
|
||||||
|
MessageWriter msgWriter = new DefaultMessageWriter();
|
||||||
|
|
||||||
|
if(body instanceof Message) {
|
||||||
|
msgWriter.writeMessage((Message)body, fos);
|
||||||
|
attach = new EmailMessage.AttachedEmailMessage(extractEmail((Message)body, email.getLocalPath(), fileID));
|
||||||
|
} else {
|
||||||
|
msgWriter.writeBody(body, fos);
|
||||||
|
attach = new EmailMessage.Attachment();
|
||||||
|
}
|
||||||
fileLength = fos.getBytesWritten();
|
fileLength = fos.getBytesWritten();
|
||||||
|
attach.setName(filename);
|
||||||
|
attach.setLocalPath(relModuleOutputPath + uniqueFilename);
|
||||||
|
attach.setSize(fileLength);
|
||||||
|
attach.setEncodingType(TskData.EncodingType.XOR1);
|
||||||
|
email.addAttachment(attach);
|
||||||
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.WARNING, "Failed to create file output stream for: " + outPath, ex); //NON-NLS
|
logger.log(Level.WARNING, "Failed to create file output stream for: " + outPath, ex); //NON-NLS
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmailMessage.Attachment attach = new EmailMessage.Attachment();
|
|
||||||
attach.setName(filename);
|
|
||||||
attach.setLocalPath(relModuleOutputPath + uniqueFilename);
|
|
||||||
attach.setSize(fileLength);
|
|
||||||
attach.setEncodingType(TskData.EncodingType.XOR1);
|
|
||||||
email.addAttachment(attach);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,16 +277,30 @@ class PstParser implements AutoCloseable{
|
|||||||
*/
|
*/
|
||||||
private EmailMessage extractEmailMessage(PSTMessage msg, String localPath, long fileID) {
|
private EmailMessage extractEmailMessage(PSTMessage msg, String localPath, long fileID) {
|
||||||
EmailMessage email = new EmailMessage();
|
EmailMessage email = new EmailMessage();
|
||||||
email.setRecipients(msg.getDisplayTo());
|
String toAddress = msg.getDisplayTo();
|
||||||
email.setCc(msg.getDisplayCC());
|
String ccAddress = msg.getDisplayCC();
|
||||||
email.setBcc(msg.getDisplayBCC());
|
String bccAddress = msg.getDisplayBCC();
|
||||||
email.setSender(getSender(msg.getSenderName(), msg.getSenderEmailAddress()));
|
String receivedByName = msg.getReceivedByName();
|
||||||
|
String receivedBySMTPAddress = msg.getReceivedBySMTPAddress();
|
||||||
|
|
||||||
|
if (toAddress.contains(receivedByName)) {
|
||||||
|
toAddress = toAddress.replace(receivedByName, receivedBySMTPAddress);
|
||||||
|
}
|
||||||
|
if (ccAddress.contains(receivedByName)) {
|
||||||
|
ccAddress = ccAddress.replace(receivedByName, receivedBySMTPAddress);
|
||||||
|
}
|
||||||
|
if (bccAddress.contains(receivedByName)) {
|
||||||
|
bccAddress = bccAddress.replace(receivedByName, receivedBySMTPAddress);
|
||||||
|
}
|
||||||
|
email.setRecipients(toAddress);
|
||||||
|
email.setCc(ccAddress);
|
||||||
|
email.setBcc(bccAddress);
|
||||||
|
email.setSender(getSender(msg.getSenderName(), msg.getSentRepresentingSMTPAddress()));
|
||||||
email.setSentDate(msg.getMessageDeliveryTime());
|
email.setSentDate(msg.getMessageDeliveryTime());
|
||||||
email.setTextBody(msg.getBody());
|
email.setTextBody(msg.getBody());
|
||||||
if (false == msg.getTransportMessageHeaders().isEmpty()) {
|
if (false == msg.getTransportMessageHeaders().isEmpty()) {
|
||||||
email.setHeaders("\n-----HEADERS-----\n\n" + msg.getTransportMessageHeaders() + "\n\n---END HEADERS--\n\n");
|
email.setHeaders("\n-----HEADERS-----\n\n" + msg.getTransportMessageHeaders() + "\n\n---END HEADERS--\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
email.setHtmlBody(msg.getBodyHTML());
|
email.setHtmlBody(msg.getBodyHTML());
|
||||||
String rtf = "";
|
String rtf = "";
|
||||||
try {
|
try {
|
||||||
|
@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
|
|||||||
import org.sleuthkit.autopsy.ingest.IngestMonitor;
|
import org.sleuthkit.autopsy.ingest.IngestMonitor;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||||
|
import org.sleuthkit.autopsy.thunderbirdparser.EmailMessage.AttachedEmailMessage;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||||
@ -72,13 +73,14 @@ import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Fil
|
|||||||
* structure and metadata.
|
* structure and metadata.
|
||||||
*/
|
*/
|
||||||
public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
|
private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
|
||||||
private final IngestServices services = IngestServices.getInstance();
|
private final IngestServices services = IngestServices.getInstance();
|
||||||
private FileManager fileManager;
|
private FileManager fileManager;
|
||||||
private IngestJobContext context;
|
private IngestJobContext context;
|
||||||
private Blackboard blackboard;
|
private Blackboard blackboard;
|
||||||
private CommunicationArtifactsHelper communicationArtifactsHelper;
|
private CommunicationArtifactsHelper communicationArtifactsHelper;
|
||||||
|
|
||||||
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
||||||
private Case currentCase;
|
private Case currentCase;
|
||||||
|
|
||||||
@ -89,7 +91,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Messages ({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
|
@Messages({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
|
||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
try {
|
try {
|
||||||
@ -112,8 +114,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//skip unalloc
|
//skip unalloc
|
||||||
if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
|
if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS))
|
||||||
(abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
|
|| (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +126,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
// check its signature
|
// check its signature
|
||||||
boolean isMbox = false;
|
boolean isMbox = false;
|
||||||
boolean isEMLFile = false;
|
boolean isEMLFile = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] t = new byte[64];
|
byte[] t = new byte[64];
|
||||||
if (abstractFile.getSize() > 64) {
|
if (abstractFile.getSize() > 64) {
|
||||||
@ -137,15 +139,15 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
} catch (TskException ex) {
|
} catch (TskException ex) {
|
||||||
logger.log(Level.WARNING, null, ex);
|
logger.log(Level.WARNING, null, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isPstFile = PstParser.isPstFile(abstractFile);
|
boolean isPstFile = PstParser.isPstFile(abstractFile);
|
||||||
boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
|
boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
|
||||||
|
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMbox || isEMLFile || isPstFile || isVcardFile ) {
|
if (isMbox || isEMLFile || isPstFile || isVcardFile) {
|
||||||
try {
|
try {
|
||||||
communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
|
communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
|
||||||
EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL);
|
EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL);
|
||||||
@ -158,7 +160,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
if (isMbox) {
|
if (isMbox) {
|
||||||
return processMBox(abstractFile);
|
return processMBox(abstractFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEMLFile) {
|
if (isEMLFile) {
|
||||||
return processEMLFile(abstractFile);
|
return processEMLFile(abstractFile);
|
||||||
}
|
}
|
||||||
@ -166,11 +168,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
if (isPstFile) {
|
if (isPstFile) {
|
||||||
return processPst(abstractFile);
|
return processPst(abstractFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVcardFile) {
|
if (isVcardFile) {
|
||||||
return processVcard(abstractFile);
|
return processVcard(abstractFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +188,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
String fileName;
|
String fileName;
|
||||||
try {
|
try {
|
||||||
fileName = getTempPath() + File.separator + abstractFile.getName()
|
fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||||
+ "-" + String.valueOf(abstractFile.getId());
|
+ "-" + String.valueOf(abstractFile.getId());
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||||
return ProcessResult.ERROR;
|
return ProcessResult.ERROR;
|
||||||
@ -203,8 +205,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
services.postMessage(msg);
|
services.postMessage(msg);
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (PstParser parser = new PstParser(services)){
|
try (PstParser parser = new PstParser(services)) {
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
|
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@ -214,7 +216,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
PstParser.ParseResult result = parser.open(file, abstractFile.getId());
|
PstParser.ParseResult result = parser.open(file, abstractFile.getId());
|
||||||
|
|
||||||
switch( result) {
|
switch (result) {
|
||||||
case OK:
|
case OK:
|
||||||
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
|
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
|
||||||
if (pstMsgIterator != null) {
|
if (pstMsgIterator != null) {
|
||||||
@ -238,20 +240,20 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
// encrypted pst: Add encrypted file artifact
|
// encrypted pst: Add encrypted file artifact
|
||||||
try {
|
try {
|
||||||
|
|
||||||
BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
|
BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
|
||||||
artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
|
artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// index the artifact for keyword search
|
// index the artifact for keyword search
|
||||||
blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName());
|
blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName());
|
||||||
} catch (Blackboard.BlackboardException ex) {
|
} catch (Blackboard.BlackboardException ex) {
|
||||||
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
|
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
|
||||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
|
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
|
|
||||||
}
|
}
|
||||||
break;
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// parsing error: log message
|
// parsing error: log message
|
||||||
postErrorMessage(
|
postErrorMessage(
|
||||||
@ -262,8 +264,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
|
logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
|
||||||
return ProcessResult.ERROR;
|
return ProcessResult.ERROR;
|
||||||
}
|
}
|
||||||
} catch(Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath()));
|
logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath()));
|
||||||
} finally {
|
} finally {
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
@ -294,7 +296,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
String fileName;
|
String fileName;
|
||||||
try {
|
try {
|
||||||
fileName = getTempPath() + File.separator + abstractFile.getName()
|
fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||||
+ "-" + String.valueOf(abstractFile.getId());
|
+ "-" + String.valueOf(abstractFile.getId());
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||||
return ProcessResult.ERROR;
|
return ProcessResult.ERROR;
|
||||||
@ -313,7 +315,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (abstractFile.getSize() < MBOX_SIZE_TO_SPLIT) {
|
if (abstractFile.getSize() < MBOX_SIZE_TO_SPLIT) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
|
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@ -321,25 +323,25 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try {
|
||||||
processMboxFile(file, abstractFile, emailFolder);
|
processMboxFile(file, abstractFile, emailFolder);
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
}finally {
|
} finally {
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
List<Long> mboxSplitOffsets = new ArrayList<>();
|
List<Long> mboxSplitOffsets = new ArrayList<>();
|
||||||
try{
|
try {
|
||||||
mboxSplitOffsets = findMboxSplitOffset(abstractFile, file);
|
mboxSplitOffsets = findMboxSplitOffset(abstractFile, file);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.WARNING, String.format("Failed finding split offsets for mbox file {0}.", fileName), ex); //NON-NLS
|
logger.log(Level.WARNING, String.format("Failed finding split offsets for mbox file {0}.", fileName), ex); //NON-NLS
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
long startingOffset = 0;
|
long startingOffset = 0;
|
||||||
for (Long mboxSplitOffset : mboxSplitOffsets) {
|
for (Long mboxSplitOffset : mboxSplitOffsets) {
|
||||||
File splitFile = new File(fileName + "-" + mboxSplitOffset);
|
File splitFile = new File(fileName + "-" + mboxSplitOffset);
|
||||||
try {
|
try {
|
||||||
@ -348,55 +350,54 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS
|
logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
try{
|
try {
|
||||||
processMboxFile(splitFile, abstractFile, emailFolder);
|
processMboxFile(splitFile, abstractFile, emailFolder);
|
||||||
startingOffset = mboxSplitOffset;
|
startingOffset = mboxSplitOffset;
|
||||||
} finally {
|
} finally {
|
||||||
splitFile.delete();
|
splitFile.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Long> findMboxSplitOffset(AbstractFile abstractFile, File file) throws IOException {
|
private List<Long> findMboxSplitOffset(AbstractFile abstractFile, File file) throws IOException {
|
||||||
|
|
||||||
List<Long> mboxSplitOffset = new ArrayList<>();
|
List<Long> mboxSplitOffset = new ArrayList<>();
|
||||||
|
|
||||||
byte[] buffer = new byte[7];
|
byte[] buffer = new byte[7];
|
||||||
ReadContentInputStream in = new ReadContentInputStream(abstractFile);
|
ReadContentInputStream in = new ReadContentInputStream(abstractFile);
|
||||||
in.skip(MBOX_SIZE_TO_SPLIT);
|
in.skip(MBOX_SIZE_TO_SPLIT);
|
||||||
int len = in.read(buffer);
|
int len = in.read(buffer);
|
||||||
while (len != -1) {
|
while (len != -1) {
|
||||||
len = in.read(buffer);
|
len = in.read(buffer);
|
||||||
if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114 &&
|
if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114
|
||||||
buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
|
&& buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
|
||||||
mboxSplitOffset.add(in.getCurPosition() - 5 );
|
mboxSplitOffset.add(in.getCurPosition() - 5);
|
||||||
in.skip(MBOX_SIZE_TO_SPLIT);
|
in.skip(MBOX_SIZE_TO_SPLIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mboxSplitOffset;
|
return mboxSplitOffset;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void processMboxFile(File file, AbstractFile abstractFile, String emailFolder) {
|
private void processMboxFile(File file, AbstractFile abstractFile, String emailFolder) {
|
||||||
|
|
||||||
try(MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId())) {
|
try (MboxParser emailIterator = MboxParser.getEmailIterator(emailFolder, file, abstractFile.getId())) {
|
||||||
List<EmailMessage> emails = new ArrayList<>();
|
List<EmailMessage> emails = new ArrayList<>();
|
||||||
if(emailIterator != null) {
|
if (emailIterator != null) {
|
||||||
while(emailIterator.hasNext()) {
|
while (emailIterator.hasNext()) {
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EmailMessage emailMessage = emailIterator.next();
|
EmailMessage emailMessage = emailIterator.next();
|
||||||
if(emailMessage != null) {
|
if (emailMessage != null) {
|
||||||
emails.add(emailMessage);
|
emails.add(emailMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,13 +409,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
abstractFile.getName()), errors);
|
abstractFile.getName()), errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile);
|
processEmails(emails, MboxParser.getEmailIterator(emailFolder, file, abstractFile.getId()), abstractFile);
|
||||||
} catch(Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath()));
|
logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse and extract data from a vCard file.
|
* Parse and extract data from a vCard file.
|
||||||
*
|
*
|
||||||
@ -438,8 +439,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProcessResult processEMLFile(AbstractFile abstractFile) {
|
private ProcessResult processEMLFile(AbstractFile abstractFile) {
|
||||||
try {
|
try {
|
||||||
EmailMessage message = EMLParser.parse(abstractFile);
|
EmailMessage message = EMLParser.parse(abstractFile);
|
||||||
|
|
||||||
@ -450,13 +451,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
List<AbstractFile> derivedFiles = new ArrayList<>();
|
List<AbstractFile> derivedFiles = new ArrayList<>();
|
||||||
|
|
||||||
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
||||||
BlackboardArtifact msgArtifact = addEmailArtifact(message, abstractFile, accountFileInstanceCache);
|
createEmailArtifact(message, abstractFile, accountFileInstanceCache, derivedFiles);
|
||||||
accountFileInstanceCache.clear();
|
accountFileInstanceCache.clear();
|
||||||
|
|
||||||
if ((msgArtifact != null) && (message.hasAttachment())) {
|
|
||||||
derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (derivedFiles.isEmpty() == false) {
|
if (derivedFiles.isEmpty() == false) {
|
||||||
for (AbstractFile derived : derivedFiles) {
|
for (AbstractFile derived : derivedFiles) {
|
||||||
services.fireModuleContentEvent(new ModuleContentEvent(derived));
|
services.fireModuleContentEvent(new ModuleContentEvent(derived));
|
||||||
@ -493,7 +490,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a module output folder.
|
* Get a module output folder.
|
||||||
*
|
*
|
||||||
* @throws NoCurrentCaseException if there is no open case.
|
* @throws NoCurrentCaseException if there is no open case.
|
||||||
*
|
*
|
||||||
* @return the module output folder
|
* @return the module output folder
|
||||||
@ -527,48 +524,43 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
* @param fullMessageIterator
|
* @param fullMessageIterator
|
||||||
* @param abstractFile
|
* @param abstractFile
|
||||||
*/
|
*/
|
||||||
private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
|
private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
|
||||||
AbstractFile abstractFile) {
|
AbstractFile abstractFile) {
|
||||||
|
|
||||||
// Create cache for accounts
|
// Create cache for accounts
|
||||||
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
||||||
|
|
||||||
// Putting try/catch around this to catch any exception and still allow
|
// Putting try/catch around this to catch any exception and still allow
|
||||||
// the creation of the artifacts to continue.
|
// the creation of the artifacts to continue.
|
||||||
try{
|
try {
|
||||||
EmailMessageThreader.threadMessages(partialEmailsForThreading);
|
EmailMessageThreader.threadMessages(partialEmailsForThreading);
|
||||||
} catch(Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex);
|
logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AbstractFile> derivedFiles = new ArrayList<>();
|
List<AbstractFile> derivedFiles = new ArrayList<>();
|
||||||
|
|
||||||
int msgCnt = 0;
|
int msgCnt = 0;
|
||||||
while(fullMessageIterator.hasNext()) {
|
while (fullMessageIterator.hasNext()) {
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmailMessage current = fullMessageIterator.next();
|
EmailMessage current = fullMessageIterator.next();
|
||||||
|
|
||||||
if(current == null) {
|
if (current == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(partialEmailsForThreading.size() > msgCnt) {
|
if (partialEmailsForThreading.size() > msgCnt) {
|
||||||
EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
|
EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
|
||||||
|
|
||||||
if(threaded.getMessageID().equals(current.getMessageID()) &&
|
if (threaded.getMessageID().equals(current.getMessageID())
|
||||||
threaded.getSubject().equals(current.getSubject())) {
|
&& threaded.getSubject().equals(current.getSubject())) {
|
||||||
current.setMessageThreadID(threaded.getMessageThreadID());
|
current.setMessageThreadID(threaded.getMessageThreadID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
createEmailArtifact(current, abstractFile, accountFileInstanceCache, derivedFiles);
|
||||||
BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile, accountFileInstanceCache);
|
|
||||||
|
|
||||||
if ((msgArtifact != null) && (current.hasAttachment())) {
|
|
||||||
derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (derivedFiles.isEmpty() == false) {
|
if (derivedFiles.isEmpty() == false) {
|
||||||
@ -581,6 +573,21 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
context.addFilesToJob(derivedFiles);
|
context.addFilesToJob(derivedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createEmailArtifact(EmailMessage email, AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache, List<AbstractFile> derivedFiles) {
|
||||||
|
BlackboardArtifact msgArtifact = addEmailArtifact(email, abstractFile, accountFileInstanceCache);
|
||||||
|
|
||||||
|
if ((msgArtifact != null) && (email.hasAttachment())) {
|
||||||
|
derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile, msgArtifact));
|
||||||
|
|
||||||
|
for (EmailMessage.Attachment attach : email.getAttachments()) {
|
||||||
|
if (attach instanceof AttachedEmailMessage) {
|
||||||
|
createEmailArtifact(((AttachedEmailMessage) attach).getEmailMessage(), abstractFile, accountFileInstanceCache, derivedFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given attachments as derived files and reschedule them for
|
* Add the given attachments as derived files and reschedule them for
|
||||||
* ingest.
|
* ingest.
|
||||||
@ -592,8 +599,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
* @return List of attachments
|
* @return List of attachments
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message."
|
"ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message."
|
||||||
})
|
})
|
||||||
private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
|
private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
|
||||||
List<AbstractFile> files = new ArrayList<>();
|
List<AbstractFile> files = new ArrayList<>();
|
||||||
List<FileAttachment> fileAttachments = new ArrayList<>();
|
List<FileAttachment> fileAttachments = new ArrayList<>();
|
||||||
@ -611,11 +618,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
DerivedFile df = fileManager.addDerivedFile(filename, relPath,
|
DerivedFile df = fileManager.addDerivedFile(filename, relPath,
|
||||||
size, cTime, crTime, aTime, mTime, true, abstractFile, "",
|
size, cTime, crTime, aTime, mTime, true, abstractFile, "",
|
||||||
EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
|
EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
|
||||||
|
|
||||||
associateAttachmentWithMesssge(messageArtifact, df);
|
associateAttachmentWithMesssge(messageArtifact, df);
|
||||||
|
|
||||||
files.add(df);
|
files.add(df);
|
||||||
|
|
||||||
fileAttachments.add(new FileAttachment(df));
|
fileAttachments.add(new FileAttachment(df));
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
postErrorMessage(
|
postErrorMessage(
|
||||||
@ -626,17 +633,16 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
logger.log(Level.INFO, "", ex);
|
logger.log(Level.INFO, "", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList()));
|
communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList()));
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
postErrorMessage(
|
postErrorMessage(
|
||||||
NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
|
NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
|
||||||
"");
|
"");
|
||||||
logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
|
logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,32 +658,33 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
bba.addAttributes(attributes); //write out to bb
|
bba.addAttributes(attributes); //write out to bb
|
||||||
return bba;
|
return bba;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds and returns a set of unique email addresses found in the input string
|
* Finds and returns a set of unique email addresses found in the input
|
||||||
|
* string
|
||||||
*
|
*
|
||||||
* @param input - input string, like the To/CC line from an email header
|
* @param input - input string, like the To/CC line from an email header
|
||||||
*
|
*
|
||||||
* @return Set<String>: set of email addresses found in the input string
|
* @return Set<String>: set of email addresses found in the input string
|
||||||
*/
|
*/
|
||||||
private Set<String> findEmailAddresess(String input) {
|
private Set<String> findEmailAddresess(String input) {
|
||||||
Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
|
Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
|
||||||
Pattern.CASE_INSENSITIVE);
|
Pattern.CASE_INSENSITIVE);
|
||||||
Matcher m = p.matcher(input);
|
Matcher m = p.matcher(input);
|
||||||
Set<String> emailAddresses = new HashSet<>();
|
Set<String> emailAddresses = new HashSet<>();
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
emailAddresses.add( m.group());
|
emailAddresses.add(m.group());
|
||||||
}
|
}
|
||||||
return emailAddresses;
|
return emailAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a blackboard artifact for the given e-mail message.
|
* Add a blackboard artifact for the given e-mail message.
|
||||||
*
|
*
|
||||||
* @param email The e-mail message.
|
* @param email The e-mail message.
|
||||||
* @param abstractFile The associated file.
|
* @param abstractFile The associated file.
|
||||||
* @param accountFileInstanceCache The current cache of account instances.
|
* @param accountFileInstanceCache The current cache of account instances.
|
||||||
*
|
*
|
||||||
* @return The generated e-mail message artifact.
|
* @return The generated e-mail message artifact.
|
||||||
*/
|
*/
|
||||||
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
|
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
|
||||||
@ -701,35 +708,33 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
List<String> senderAddressList = new ArrayList<>();
|
List<String> senderAddressList = new ArrayList<>();
|
||||||
String senderAddress;
|
String senderAddress;
|
||||||
senderAddressList.addAll(findEmailAddresess(from));
|
senderAddressList.addAll(findEmailAddresess(from));
|
||||||
|
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountFileInstance senderAccountInstance = null;
|
AccountFileInstance senderAccountInstance = null;
|
||||||
|
|
||||||
if (senderAddressList.size() == 1) {
|
if (senderAddressList.size() == 1) {
|
||||||
senderAddress = senderAddressList.get(0);
|
senderAddress = senderAddressList.get(0);
|
||||||
try {
|
try {
|
||||||
senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress);
|
senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
||||||
}
|
}
|
||||||
catch(TskCoreException ex) {
|
} else {
|
||||||
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> recipientAddresses = new ArrayList<>();
|
List<String> recipientAddresses = new ArrayList<>();
|
||||||
recipientAddresses.addAll(findEmailAddresess(to));
|
recipientAddresses.addAll(findEmailAddresess(to));
|
||||||
recipientAddresses.addAll(findEmailAddresess(cc));
|
recipientAddresses.addAll(findEmailAddresess(cc));
|
||||||
recipientAddresses.addAll(findEmailAddresess(bcc));
|
recipientAddresses.addAll(findEmailAddresess(bcc));
|
||||||
|
|
||||||
List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
|
List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
|
||||||
for (String addr : recipientAddresses) {
|
for (String addr : recipientAddresses) {
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
@ -738,56 +743,54 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
try {
|
try {
|
||||||
AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
|
AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
|
||||||
recipientAccountInstances.add(recipientAccountInstance);
|
recipientAccountInstances.add(recipientAccountInstance);
|
||||||
}
|
} catch (TskCoreException ex) {
|
||||||
catch(TskCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
|
logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
|
addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
|
||||||
addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
|
addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
|
||||||
addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
|
addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
|
||||||
addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
|
addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
|
||||||
|
|
||||||
addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
|
addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
|
||||||
addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
|
addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
|
||||||
|
|
||||||
addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
|
addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
|
||||||
|
|
||||||
addArtifactAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
|
addArtifactAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
|
||||||
ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
|
ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
|
||||||
|
|
||||||
addArtifactAttribute(((localPath.isEmpty() == false) ? localPath : ""),
|
addArtifactAttribute(((localPath.isEmpty() == false) ? localPath : ""),
|
||||||
ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
|
ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
|
||||||
|
|
||||||
addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
|
addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
|
||||||
addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
|
addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
|
||||||
addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
|
addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
|
||||||
addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
|
addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
|
bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
|
||||||
bbart.addAttributes(bbattributes);
|
bbart.addAttributes(bbattributes);
|
||||||
|
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add account relationships
|
// Add account relationships
|
||||||
currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
|
currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart, Relationship.Type.MESSAGE, dateL);
|
||||||
|
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// index the artifact for keyword search
|
// index the artifact for keyword search
|
||||||
blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName());
|
blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName());
|
||||||
} catch (Blackboard.BlackboardException ex) {
|
} catch (Blackboard.BlackboardException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
|
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
|
||||||
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
|
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
|
||||||
@ -798,11 +801,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
return bbart;
|
return bbart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an attribute of a specified type to a supplied Collection.
|
* Add an attribute of a specified type to a supplied Collection.
|
||||||
*
|
*
|
||||||
* @param stringVal The attribute value.
|
* @param stringVal The attribute value.
|
||||||
* @param attrType The type of attribute to be added.
|
* @param attrType The type of attribute to be added.
|
||||||
* @param bbattributes The Collection to which the attribute will be added.
|
* @param bbattributes The Collection to which the attribute will be added.
|
||||||
*/
|
*/
|
||||||
@ -814,7 +817,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an attribute of a specified type to a supplied Collection.
|
* Add an attribute of a specified type to a supplied Collection.
|
||||||
*
|
*
|
||||||
* @param stringVal The attribute value.
|
* @param stringVal The attribute value.
|
||||||
* @param attrType The type of attribute to be added.
|
* @param attrType The type of attribute to be added.
|
||||||
* @param bbattributes The Collection to which the attribute will be added.
|
* @param bbattributes The Collection to which the attribute will be added.
|
||||||
@ -824,10 +827,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
|
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an attribute of a specified type to a supplied Collection.
|
* Add an attribute of a specified type to a supplied Collection.
|
||||||
*
|
*
|
||||||
* @param longVal The attribute value.
|
* @param longVal The attribute value.
|
||||||
* @param attrType The type of attribute to be added.
|
* @param attrType The type of attribute to be added.
|
||||||
* @param bbattributes The Collection to which the attribute will be added.
|
* @param bbattributes The Collection to which the attribute will be added.
|
||||||
@ -837,49 +840,51 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
|
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache for storing AccountFileInstance.
|
* Cache for storing AccountFileInstance. The idea is that emails will be
|
||||||
* The idea is that emails will be used multiple times in a file and
|
* used multiple times in a file and we shouldn't do a database lookup each
|
||||||
* we shouldn't do a database lookup each time.
|
* time.
|
||||||
*/
|
*/
|
||||||
static private class AccountFileInstanceCache {
|
static private class AccountFileInstanceCache {
|
||||||
|
|
||||||
private final Map<String, AccountFileInstance> cacheMap;
|
private final Map<String, AccountFileInstance> cacheMap;
|
||||||
private final AbstractFile file;
|
private final AbstractFile file;
|
||||||
private final Case currentCase;
|
private final Case currentCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new cache. Caches are linked to a specific file.
|
* Create a new cache. Caches are linked to a specific file.
|
||||||
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* @param currentCase
|
* @param currentCase
|
||||||
*/
|
*/
|
||||||
AccountFileInstanceCache(AbstractFile file, Case currentCase) {
|
AccountFileInstanceCache(AbstractFile file, Case currentCase) {
|
||||||
cacheMap= new HashMap<>();
|
cacheMap = new HashMap<>();
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.currentCase = currentCase;
|
this.currentCase = currentCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the account file instance from the cache or the database.
|
* Get the account file instance from the cache or the database.
|
||||||
*
|
*
|
||||||
* @param email The email for this account.
|
* @param email The email for this account.
|
||||||
*
|
*
|
||||||
* @return The corresponding AccountFileInstance
|
* @return The corresponding AccountFileInstance
|
||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
AccountFileInstance getAccountInstance(String email) throws TskCoreException {
|
AccountFileInstance getAccountInstance(String email) throws TskCoreException {
|
||||||
if (cacheMap.containsKey(email)) {
|
if (cacheMap.containsKey(email)) {
|
||||||
return cacheMap.get(email);
|
return cacheMap.get(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountFileInstance accountInstance =
|
AccountFileInstance accountInstance
|
||||||
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
|
= currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
|
||||||
EmailParserModuleFactory.getModuleName(), file);
|
EmailParserModuleFactory.getModuleName(), file);
|
||||||
cacheMap.put(email, accountInstance);
|
cacheMap.put(email, accountInstance);
|
||||||
return accountInstance;
|
return accountInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the cache.
|
* Clears the cache.
|
||||||
*/
|
*/
|
||||||
@ -887,10 +892,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
cacheMap.clear();
|
cacheMap.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post an error message for the user.
|
* Post an error message for the user.
|
||||||
*
|
*
|
||||||
* @param subj The error subject.
|
* @param subj The error subject.
|
||||||
* @param details The error details.
|
* @param details The error details.
|
||||||
*/
|
*/
|
||||||
@ -901,7 +906,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the IngestServices object.
|
* Get the IngestServices object.
|
||||||
*
|
*
|
||||||
* @return The IngestServices object.
|
* @return The IngestServices object.
|
||||||
*/
|
*/
|
||||||
IngestServices getServices() {
|
IngestServices getServices() {
|
||||||
@ -912,5 +917,5 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
public void shutDown() {
|
public void shutDown() {
|
||||||
// nothing to shut down
|
// nothing to shut down
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,9 @@ fi
|
|||||||
chmod u+x autopsy/markmckinnon/Export*
|
chmod u+x autopsy/markmckinnon/Export*
|
||||||
chmod u+x autopsy/markmckinnon/parse*
|
chmod u+x autopsy/markmckinnon/parse*
|
||||||
|
|
||||||
|
# allow solr dependencies to execute
|
||||||
|
chmod -R u+x autopsy/solr/bin
|
||||||
|
|
||||||
# make sure it is executable
|
# make sure it is executable
|
||||||
chmod u+x bin/autopsy
|
chmod u+x bin/autopsy
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user