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