Merge remote-tracking branch 'upstream/develop' into 7282_portCaseHost

This commit is contained in:
apriestman 2021-02-09 13:37:13 -05:00
commit 26d2538c4b
37 changed files with 510 additions and 375 deletions

6
.gitattributes vendored
View File

@ -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

View File

@ -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));
} }
/** /**

View File

@ -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.

View File

@ -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);
} }
} }
} }

View File

@ -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));
} }

View File

@ -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);
}
} }
/** /**

View File

@ -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)",

View File

@ -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();
}
} }
} }

View File

@ -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);
} }

View File

@ -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
} }
} }

View File

@ -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());
} }

View File

@ -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

View File

@ -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

View File

@ -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());
} }

View File

@ -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.
*/ */

View File

@ -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"/>

View File

@ -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

View File

@ -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>

View File

@ -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, dont 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
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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();

View File

@ -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).
*/ */

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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);
}
} }
/** /**

View File

@ -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

View File

@ -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(),

View File

@ -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;
} }

View File

@ -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 : ""));

View File

@ -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);
} }
} }

View File

@ -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,

View File

@ -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;
}
}
} }

View File

@ -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);
} }
} }
/** /**

View File

@ -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
} }
} }

View File

@ -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