19 package org.sleuthkit.autopsy.thunderbirdparser;
 
   22 import java.io.IOException;
 
   23 import java.util.ArrayList;
 
   24 import java.util.Arrays;
 
   25 import java.util.Collection;
 
   26 import java.util.Collections;
 
   27 import java.util.HashMap;
 
   28 import java.util.HashSet;
 
   29 import java.util.Iterator;
 
   30 import java.util.List;
 
   33 import java.util.concurrent.ConcurrentHashMap;
 
   34 import java.util.concurrent.ConcurrentMap;
 
   35 import java.util.logging.Level;
 
   36 import java.util.regex.Matcher;
 
   37 import java.util.regex.Pattern;
 
   38 import org.apache.james.mime4j.MimeException;
 
   39 import org.openide.util.NbBundle;
 
   40 import org.openide.util.NbBundle.Messages;
 
  102     @Messages({
"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
 
  116             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex);
 
  137         if ((abstractFile.
isFile() == 
false)) {
 
  142         boolean isMbox = 
false;
 
  143         boolean isEMLFile = 
false;
 
  146             byte[] t = 
new byte[64];
 
  147             if (abstractFile.
getSize() > 64) {
 
  148                 int byteRead = abstractFile.
read(t, 0, 64);
 
  150                     isMbox = MboxParser.isValidMimeTypeMbox(t, abstractFile);
 
  151                     isEMLFile = EMLParser.isEMLFile(abstractFile, t);
 
  155             logger.log(Level.WARNING, null, ex);
 
  158         boolean isPstFile = PstParser.isPstFile(abstractFile);
 
  159         boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
 
  165         if (isMbox || isEMLFile || isPstFile || isVcardFile) {
 
  170                 logger.log(Level.SEVERE, String.format(
"Failed to create CommunicationArtifactsHelper for file with object id = %d", abstractFile.
getId()), ex);
 
  201     @Messages({
"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
 
  205             fileName = getTempPath() + File.separator + abstractFile.
getName()
 
  206                     + 
"-" + String.valueOf(abstractFile.
getId());
 
  208             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex); 
 
  211         File file = 
new File(fileName);
 
  215             logger.log(Level.WARNING, 
"Not enough disk space to write file to disk."); 
 
  217                     NbBundle.getMessage(this.getClass(),
 
  218                             "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
 
  224         try (PstParser parser = 
new PstParser(services)) {
 
  227             } 
catch (IOException ex) {
 
  228                 logger.log(Level.WARNING, 
"Failed writing pst file to disk.", ex); 
 
  232             PstParser.ParseResult result = parser.open(file, abstractFile.
getId());
 
  236                     Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
 
  237                     if (pstMsgIterator != null) {
 
  238                         processEmails(parser.getPartialEmailMessages(), pstMsgIterator, abstractFile);
 
  245                                 NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
 
  247                                 NbBundle.getMessage(
this.getClass(),
 
  248                                         "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
 
  249                         logger.log(Level.INFO, 
"PSTParser failed to parse {0}", abstractFile.
getName()); 
 
  258                     String encryptionFileLevel = NbBundle.getMessage(this.getClass(), 
 
  259                                         "ThunderbirdMboxFileIngestModule.encryptionFileLevel");
 
  267                             .getAnalysisResult();
 
  274                         logger.log(Level.SEVERE, 
"Unable to index blackboard artifact " + artifact.
getArtifactID(), ex); 
 
  277                     logger.log(Level.INFO, 
"Failed to add encryption attribute to file: {0}", abstractFile.
getName()); 
 
  283                             NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
 
  285                             NbBundle.getMessage(
this.getClass(),
 
  286                                     "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
 
  287                     logger.log(Level.INFO, 
"PSTParser failed to parse {0}", abstractFile.
getName()); 
 
  290         } 
catch (Exception ex) {
 
  291             logger.log(Level.WARNING, String.format(
"Failed to close temp pst file %s", file.getAbsolutePath()));
 
  306         String mboxFileName = abstractFile.
getName();
 
  309         String emailFolder = 
"";
 
  311         if (mboxParentDir.contains(
"/Mail/")) { 
 
  312             emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/Mail/") + 5); 
 
  313         } 
else if (mboxParentDir.contains(
"/ImapMail/")) { 
 
  314             emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/ImapMail/") + 9); 
 
  316         emailFolder += mboxFileName;
 
  317         emailFolder = emailFolder.replaceAll(
".sbd", 
""); 
 
  321             fileName = getTempPath() + File.separator + abstractFile.
getName()
 
  322                     + 
"-" + String.valueOf(abstractFile.
getId());
 
  324             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex); 
 
  327         File file = 
new File(fileName);
 
  331             logger.log(Level.WARNING, 
"Not enough disk space to write file to disk."); 
 
  333                     NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
 
  335                     NbBundle.getMessage(
this.getClass(),
 
  336                             "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
 
  344             } 
catch (IOException ex) {
 
  345                 logger.log(Level.WARNING, 
"Failed writing mbox file to disk.", ex); 
 
  359             List<Long> mboxSplitOffsets = 
new ArrayList<>();
 
  362             } 
catch (IOException ex) {
 
  363                 logger.log(Level.WARNING, String.format(
"Failed finding split offsets for mbox file {0}.", fileName), ex); 
 
  367             long startingOffset = 0;
 
  368             for (Long mboxSplitOffset : mboxSplitOffsets) {
 
  369                 File splitFile = 
new File(fileName + 
"-" + mboxSplitOffset);
 
  371                     ContentUtils.
writeToFile(abstractFile, splitFile, context::fileIngestIsCancelled, startingOffset, mboxSplitOffset);
 
  372                 } 
catch (IOException ex) {
 
  373                     logger.log(Level.WARNING, 
"Failed writing split mbox file to disk.", ex); 
 
  378                     startingOffset = mboxSplitOffset;
 
  394         List<Long> mboxSplitOffset = 
new ArrayList<>();
 
  396         byte[] buffer = 
new byte[7];
 
  398         in.
skip(MBOX_SIZE_TO_SPLIT);
 
  399         int len = in.
read(buffer);
 
  401             len = in.
read(buffer);
 
  402             if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114
 
  403                     && buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
 
  405                 in.
skip(MBOX_SIZE_TO_SPLIT);
 
  409         return mboxSplitOffset;
 
  415         try (MboxParser emailIterator = MboxParser.getEmailIterator(emailFolder, file, abstractFile.
getId())) {
 
  416             List<EmailMessage> emails = 
new ArrayList<>();
 
  417             if (emailIterator != null) {
 
  418                 while (emailIterator.hasNext()) {
 
  422                     EmailMessage emailMessage = emailIterator.next();
 
  423                     if (emailMessage != null) {
 
  424                         emails.add(emailMessage);
 
  428                 String errors = emailIterator.getErrors();
 
  429                 if (!errors.isEmpty()) {
 
  431                             NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
 
  432                                     abstractFile.
getName()), errors);
 
  435             processEmails(emails, MboxParser.getEmailIterator(emailFolder, file, abstractFile.
getId()), abstractFile);
 
  436         } 
catch (Exception ex) {
 
  437             logger.log(Level.WARNING, String.format(
"Failed to close mbox temp file %s", file.getAbsolutePath()));
 
  453         "ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse." 
  458             parser.parse(abstractFile);
 
  460             logger.log(Level.WARNING, String.format(
"Exception while parsing the file '%s' (id=%d).", abstractFile.
getName(), abstractFile.
getId()), ex); 
 
  468             EmailMessage message = EMLParser.parse(abstractFile);
 
  470             if (message == null) {
 
  474             List<AbstractFile> derivedFiles = 
new ArrayList<>();
 
  477             createEmailArtifact(message, abstractFile, accountFileInstanceCache, derivedFiles);
 
  478             accountFileInstanceCache.clear();
 
  480             if (derivedFiles.isEmpty() == 
false) {
 
  487         } 
catch (IOException ex) {
 
  488             logger.log(Level.WARNING, String.format(
"Error reading eml file %s", abstractFile.
getName()), ex);
 
  490         } 
catch (MimeException ex) {
 
  491             logger.log(Level.WARNING, String.format(
"Error reading eml file %s", abstractFile.
getName()), ex);
 
  507         File dir = 
new File(tmpDir);
 
  508         if (dir.exists() == 
false) {
 
  521     static String getModuleOutputPath() throws NoCurrentCaseException {
 
  522         String outDir = Case.getCurrentCaseThrows().getModuleDirectory() + File.separator
 
  523                 + EmailParserModuleFactory.getModuleName();
 
  524         File dir = 
new File(outDir);
 
  525         if (dir.exists() == 
false) {
 
  537     static String getRelModuleOutputPath() throws NoCurrentCaseException {
 
  538         return Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath() + File.separator
 
  539                 + EmailParserModuleFactory.getModuleName();
 
  550     private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
 
  559             EmailMessageThreader.threadMessages(partialEmailsForThreading);
 
  560         } 
catch (Exception ex) {
 
  561             logger.log(Level.WARNING, String.format(
"Exception thrown parsing emails from %s", abstractFile.
getName()), ex);
 
  564         List<AbstractFile> derivedFiles = 
new ArrayList<>();
 
  567         while (fullMessageIterator.hasNext()) {
 
  572             EmailMessage current = fullMessageIterator.next();
 
  574             if (current == null) {
 
  578             if (partialEmailsForThreading.size() > msgCnt) {
 
  579                 EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
 
  581                 if (threaded.getMessageID().equals(current.getMessageID())
 
  582                         && threaded.getSubject().equals(current.getSubject())) {
 
  583                     current.setMessageThreadID(threaded.getMessageThreadID());
 
  586             createEmailArtifact(current, abstractFile, accountFileInstanceCache, derivedFiles);
 
  589         if (derivedFiles.isEmpty() == 
false) {
 
  600     void createEmailArtifact(EmailMessage email, 
AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache, List<AbstractFile> derivedFiles) {
 
  603         if ((msgArtifact != null) && (email.hasAttachment())) {
 
  604             derivedFiles.addAll(
handleAttachments(email.getAttachments(), abstractFile, msgArtifact));
 
  606             for (EmailMessage.Attachment attach : email.getAttachments()) {
 
  607                 if (attach instanceof AttachedEmailMessage) {
 
  608                     createEmailArtifact(((AttachedEmailMessage) attach).getEmailMessage(), abstractFile, accountFileInstanceCache, derivedFiles);
 
  625         "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message." 
  628         List<AbstractFile> files = 
new ArrayList<>();
 
  629         List<FileAttachment> fileAttachments = 
new ArrayList<>();
 
  630         for (EmailMessage.Attachment attach : attachments) {
 
  631             String filename = attach.getName();
 
  632             long crTime = attach.getCrTime();
 
  633             long mTime = attach.getmTime();
 
  634             long aTime = attach.getaTime();
 
  635             long cTime = attach.getcTime();
 
  636             String relPath = attach.getLocalPath();
 
  637             long size = attach.getSize();
 
  642                         size, cTime, crTime, aTime, mTime, 
true, abstractFile, 
"",
 
  650                         NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
 
  652                         NbBundle.getMessage(
this.getClass(),
 
  653                                 "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
 
  654                 logger.log(Level.INFO, 
"", ex);
 
  662                     NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
 
  664             logger.log(Level.INFO, 
"Failed to add attachments to email message.", ex);
 
  679         Pattern p = Pattern.compile(
"\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
 
  680                 Pattern.CASE_INSENSITIVE);
 
  681         Matcher m = p.matcher(input);
 
  682         Set<String> emailAddresses = 
new HashSet<>();
 
  684             emailAddresses.add(m.group());
 
  686         return emailAddresses;
 
  698     @Messages({
"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
 
  701         List<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  702         String to = email.getRecipients();
 
  703         String cc = email.getCc();
 
  704         String bcc = email.getBcc();
 
  705         String from = email.getSender();
 
  706         long dateL = email.getSentDate();
 
  707         String headers = email.getHeaders();
 
  708         String body = email.getTextBody();
 
  709         String bodyHTML = email.getHtmlBody();
 
  710         String rtf = email.getRtfBody();
 
  711         String subject = email.getSubject();
 
  712         long id = email.
getId();
 
  713         String localPath = email.getLocalPath();
 
  714         String threadID = email.getMessageThreadID();
 
  716         List<String> senderAddressList = 
new ArrayList<>();
 
  717         String senderAddress;
 
  726         if (senderAddressList.size() == 1) {
 
  727             senderAddress = senderAddressList.get(0);
 
  729                 senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress, context);
 
  731                 logger.log(Level.WARNING, 
"Failed to create account for email address  " + senderAddress, ex); 
 
  734             logger.log(Level.WARNING, 
"Failed to find sender address, from  = {0}", from); 
 
  741         List<String> recipientAddresses = 
new ArrayList<>();
 
  746         List<AccountFileInstance> recipientAccountInstances = 
new ArrayList<>();
 
  747         for (String addr : recipientAddresses) {
 
  752                 AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr, context);
 
  753                 recipientAccountInstances.add(recipientAccountInstance);
 
  755                 logger.log(Level.WARNING, 
"Failed to create account for email address  " + addr, ex); 
 
  769         addArtifactAttribute(((
id < 0L) ? NbBundle.getMessage(
this.getClass(), 
"ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(
id)),
 
  773              addArtifactAttribute((email.hasAttachment() ? 
"Yes" : 
""), 
 
  777             logger.log(Level.SEVERE, 
"Unable to create EMAIL_HAS_ATTACHMENT attribute" , ex); 
 
  780         addArtifactAttribute(((localPath.isEmpty() == 
false) ? localPath : 
""),
 
  812                 logger.log(Level.SEVERE, 
"Unable to index blackboard artifact " + bbart.
getArtifactID(), ex); 
 
  816             logger.log(Level.WARNING, null, ex);
 
  829     static void addArtifactAttribute(String stringVal, 
BlackboardAttribute.
Type attrType, Collection<BlackboardAttribute> bbattributes) {
 
  830         if (stringVal.isEmpty() == 
false) {
 
  842     static void addArtifactAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
 
  843         if (stringVal.isEmpty() == 
false) {
 
  844             bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
 
  855     static void addArtifactAttribute(
long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
 
  857             bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
 
  868         private final Map<String, AccountFileInstance> 
cacheMap;
 
  879             cacheMap = 
new HashMap<>();
 
  895             if (cacheMap.containsKey(email)) {
 
  896                 return cacheMap.get(email);
 
  902             cacheMap.put(email, accountInstance);
 
  903             return accountInstance;
 
  920     void postErrorMessage(String subj, String details) {
 
  921         IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
 
  930     IngestServices getServices() {
 
List< Long > findMboxSplitOffset(AbstractFile abstractFile, File file)
static ConcurrentMap< String, BlackboardAttribute.Type > customAttributeCache
Set< String > findEmailAddresess(String input)
static final Logger logger
CommunicationsManager getCommunicationsManager()
void postArtifact(BlackboardArtifact artifact, String moduleName)
FileManager getFileManager()
static final Score SCORE_NOTABLE
Blackboard getBlackboard()
String getTempDirectory()
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
void processEmails(List< EmailMessage > partialEmailsForThreading, Iterator< EmailMessage > fullMessageIterator, AbstractFile abstractFile)
TskData.TSK_DB_FILES_TYPE_ENUM getType()
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
final IngestServices services
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
final Map< String, AccountFileInstance > cacheMap
static final Type TSK_ENCRYPTION_DETECTED
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributesList)
ProcessResult processMBox(AbstractFile abstractFile)
static Object customAttributeCacheLock
static final int DISK_FREE_SPACE_UNKNOWN
TskData.FileKnown getKnown()
synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)
ProcessResult processVcard(AbstractFile abstractFile)
ProcessResult processEMLFile(AbstractFile abstractFile)
void addFilesToJob(List< AbstractFile > files)
void postMessage(final IngestMessage message)
ProcessResult process(AbstractFile abstractFile)
SleuthkitCase getSleuthkitCase()
boolean fileIngestIsCancelled()
BlackboardArtifact addEmailArtifact(EmailMessage email, AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache)
ProcessResult processPst(AbstractFile abstractFile)
void startUp(IngestJobContext context)
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
void processMboxFile(File file, AbstractFile abstractFile, String emailFolder)
static final int MBOX_SIZE_TO_SPLIT
static void error(String title, String message)
synchronized static Logger getLogger(String name)
AccountFileInstance createAccountFileInstance(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile, List< BlackboardAttribute > attributes, Long ingestJobId)
static Case getCurrentCaseThrows()
static final Relationship.Type MESSAGE
List< AbstractFile > handleAttachments(List< EmailMessage.Attachment > attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact)
final int read(byte[] buf, long offset, long len)
void addAttachments(BlackboardArtifact message, MessageAttachments attachments)
CommunicationArtifactsHelper communicationArtifactsHelper
static final Account.Type EMAIL
DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
void addRelationships(AccountFileInstance sender, List< AccountFileInstance > recipients, BlackboardArtifact sourceArtifact, org.sleuthkit.datamodel.Relationship.Type relationshipType, long dateTime)
static synchronized IngestServices getInstance()