19 package org.sleuthkit.autopsy.thunderbirdparser;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
30 import java.util.logging.Level;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import org.apache.james.mime4j.MimeException;
34 import org.openide.util.NbBundle;
35 import org.openide.util.NbBundle.Messages;
55 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
62 import org.
sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
63 import org.
sleuthkit.datamodel.blackboardutils.FileAttachment;
64 import org.
sleuthkit.datamodel.blackboardutils.MessageAttachments;
88 @Messages ({
"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
95 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
106 if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
111 if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
112 (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
116 if ((abstractFile.isFile() ==
false)) {
121 boolean isMbox =
false;
122 boolean isEMLFile =
false;
125 byte[] t =
new byte[64];
126 if (abstractFile.getSize() > 64) {
127 int byteRead = abstractFile.read(t, 0, 64);
129 isMbox = MboxParser.isValidMimeTypeMbox(t);
130 isEMLFile = EMLParser.isEMLFile(abstractFile, t);
133 }
catch (TskException ex) {
134 logger.log(Level.WARNING, null, ex);
137 boolean isPstFile = PstParser.isPstFile(abstractFile);
138 boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
140 if (isMbox || isEMLFile || isPstFile || isVcardFile ) {
142 communicationArtifactsHelper =
new CommunicationArtifactsHelper(currentCase.
getSleuthkitCase(),
144 }
catch (TskCoreException ex) {
145 logger.log(Level.SEVERE, String.format(
"Failed to create CommunicationArtifactsHelper for file with object id = %d", abstractFile.getId()), ex);
176 @Messages({
"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
180 fileName = getTempPath() + File.separator + abstractFile.getName()
181 +
"-" + String.valueOf(abstractFile.getId());
183 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
186 File file =
new File(fileName);
190 logger.log(Level.WARNING,
"Not enough disk space to write file to disk.");
192 NbBundle.getMessage(this.getClass(),
193 "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
194 abstractFile.getName()));
201 }
catch (IOException ex) {
202 logger.log(Level.WARNING,
"Failed writing pst file to disk.", ex);
206 PstParser parser =
new PstParser(services);
207 PstParser.ParseResult result = parser.open(file, abstractFile.getId());
211 Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
212 if (pstMsgIterator != null) {
213 processEmails(parser.getPartialEmailMessages(), pstMsgIterator , abstractFile);
217 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
218 abstractFile.getName()),
219 NbBundle.getMessage(
this.getClass(),
220 "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
221 logger.log(Level.INFO,
"PSTParser failed to parse {0}", abstractFile.getName());
223 if (file.delete() ==
false) {
224 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
234 BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
235 artifact.addAttribute(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(),
"ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
240 }
catch (Blackboard.BlackboardException ex) {
242 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
244 }
catch (TskCoreException ex) {
245 logger.log(Level.INFO,
"Failed to add encryption attribute to file: {0}", abstractFile.getName());
251 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
252 abstractFile.getName()),
253 NbBundle.getMessage(
this.getClass(),
254 "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
255 logger.log(Level.INFO,
"PSTParser failed to parse {0}", abstractFile.getName());
257 if (file.delete() ==
false) {
258 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
263 if (file.delete() ==
false) {
264 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
278 String mboxFileName = abstractFile.getName();
279 String mboxParentDir = abstractFile.getParentPath();
281 String emailFolder =
"";
283 if (mboxParentDir.contains(
"/Mail/")) {
284 emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/Mail/") + 5);
285 }
else if (mboxParentDir.contains(
"/ImapMail/")) {
286 emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/ImapMail/") + 9);
288 emailFolder += mboxFileName;
289 emailFolder = emailFolder.replaceAll(
".sbd",
"");
293 fileName = getTempPath() + File.separator + abstractFile.getName()
294 +
"-" + String.valueOf(abstractFile.getId());
296 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
299 File file =
new File(fileName);
303 logger.log(Level.WARNING,
"Not enough disk space to write file to disk.");
305 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
306 abstractFile.getName()),
307 NbBundle.getMessage(
this.getClass(),
308 "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
314 }
catch (IOException ex) {
315 logger.log(Level.WARNING,
"Failed writing mbox file to disk.", ex);
319 MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId());
320 List<EmailMessage> emails =
new ArrayList<>();
321 if(emailIterator != null) {
322 while(emailIterator.hasNext()) {
323 EmailMessage emailMessage = emailIterator.next();
324 if(emailMessage != null) {
325 emails.add(emailMessage);
329 String errors = emailIterator.getErrors();
330 if (!errors.isEmpty()) {
332 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
333 abstractFile.getName()), errors);
336 processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile);
338 if (file.delete() ==
false) {
339 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
356 "ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse."
360 VcardParser parser =
new VcardParser(currentCase, context);
361 parser.parse(abstractFile);
363 logger.log(Level.WARNING, String.format(
"Exception while parsing the file '%s' (id=%d).", abstractFile.getName(), abstractFile.getId()), ex);
371 EmailMessage message = EMLParser.parse(abstractFile);
373 if (message == null) {
377 List<AbstractFile> derivedFiles =
new ArrayList<>();
381 if ((msgArtifact != null) && (message.hasAttachment())) {
382 derivedFiles.addAll(
handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
385 if (derivedFiles.isEmpty() ==
false) {
386 for (AbstractFile derived : derivedFiles) {
392 }
catch (IOException ex) {
393 logger.log(Level.WARNING, String.format(
"Error reading eml file %s", abstractFile.getName()), ex);
395 }
catch (MimeException ex) {
396 logger.log(Level.WARNING, String.format(
"Error reading eml file %s", abstractFile.getName()), ex);
412 File dir =
new File(tmpDir);
413 if (dir.exists() ==
false) {
426 static String getModuleOutputPath() throws NoCurrentCaseException {
427 String outDir = Case.getCurrentCaseThrows().getModuleDirectory() + File.separator
428 + EmailParserModuleFactory.getModuleName();
429 File dir =
new File(outDir);
430 if (dir.exists() ==
false) {
442 static String getRelModuleOutputPath() throws NoCurrentCaseException {
443 return Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath() + File.separator
444 + EmailParserModuleFactory.getModuleName();
455 private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator, AbstractFile abstractFile) {
460 EmailMessageThreader.threadMessages(partialEmailsForThreading);
461 }
catch(Exception ex) {
462 logger.log(Level.WARNING, String.format(
"Exception thrown parsing emails from %s", abstractFile.getName()), ex);
465 List<AbstractFile> derivedFiles =
new ArrayList<>();
468 while(fullMessageIterator.hasNext()) {
469 EmailMessage current = fullMessageIterator.next();
471 if(current == null) {
475 if(partialEmailsForThreading.size() > msgCnt) {
476 EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
478 if(threaded.getMessageID().equals(current.getMessageID()) &&
479 threaded.getSubject().equals(current.getSubject())) {
480 current.setMessageThreadID(threaded.getMessageThreadID());
486 if ((msgArtifact != null) && (current.hasAttachment())) {
487 derivedFiles.addAll(
handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
491 if (derivedFiles.isEmpty() ==
false) {
492 for (AbstractFile derived : derivedFiles) {
509 "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message."
511 private List<AbstractFile>
handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
512 List<AbstractFile> files =
new ArrayList<>();
513 List<FileAttachment> fileAttachments =
new ArrayList<>();
514 for (EmailMessage.Attachment attach : attachments) {
515 String filename = attach.getName();
516 long crTime = attach.getCrTime();
517 long mTime = attach.getmTime();
518 long aTime = attach.getaTime();
519 long cTime = attach.getcTime();
520 String relPath = attach.getLocalPath();
521 long size = attach.getSize();
522 TskData.EncodingType encodingType = attach.getEncodingType();
526 size, cTime, crTime, aTime, mTime,
true, abstractFile,
"",
533 fileAttachments.add(
new FileAttachment(df));
534 }
catch (TskCoreException ex) {
536 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
537 abstractFile.getName()),
538 NbBundle.getMessage(
this.getClass(),
539 "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
540 logger.log(Level.INFO,
"", ex);
546 communicationArtifactsHelper.addAttachments(messageArtifact,
new MessageAttachments(fileAttachments, Collections.emptyList()));
547 }
catch (TskCoreException ex) {
549 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
551 logger.log(Level.INFO,
"Failed to add attachments to email message.", ex);
562 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
563 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
EmailParserModuleFactory.getModuleName(), message.getArtifactID()));
565 BlackboardArtifact bba = attachedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
566 bba.addAttributes(attributes);
578 Pattern p = Pattern.compile(
"\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
579 Pattern.CASE_INSENSITIVE);
580 Matcher m = p.matcher(input);
581 Set<String> emailAddresses =
new HashSet<>();
583 emailAddresses.add( m.group());
585 return emailAddresses;
596 @Messages({
"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
597 private BlackboardArtifact
addEmailArtifact(EmailMessage email, AbstractFile abstractFile) {
598 BlackboardArtifact bbart = null;
599 List<BlackboardAttribute> bbattributes =
new ArrayList<>();
600 String to = email.getRecipients();
601 String cc = email.getCc();
602 String bcc = email.getBcc();
603 String from = email.getSender();
604 long dateL = email.getSentDate();
605 String headers = email.getHeaders();
606 String body = email.getTextBody();
607 String bodyHTML = email.getHtmlBody();
608 String rtf = email.getRtfBody();
609 String subject = email.getSubject();
610 long id = email.getId();
611 String localPath = email.getLocalPath();
612 String threadID = email.getMessageThreadID();
614 List<String> senderAddressList =
new ArrayList<>();
615 String senderAddress;
618 AccountFileInstance senderAccountInstance = null;
620 if (senderAddressList.size() == 1) {
621 senderAddress = senderAddressList.get(0);
623 senderAccountInstance = currentCase.
getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress,
EmailParserModuleFactory.getModuleName(), abstractFile);
625 catch(TskCoreException ex) {
626 logger.log(Level.WARNING,
"Failed to create account for email address " + senderAddress, ex);
630 logger.log(Level.WARNING,
"Failed to find sender address, from = {0}", from);
633 List<String> recipientAddresses =
new ArrayList<>();
638 List<AccountFileInstance> recipientAccountInstances =
new ArrayList<>();
639 recipientAddresses.forEach((addr) -> {
641 AccountFileInstance recipientAccountInstance =
642 currentCase.
getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
644 recipientAccountInstances.add(recipientAccountInstance);
646 catch(TskCoreException ex) {
647 logger.log(Level.WARNING,
"Failed to create account for email address " + addr, ex);
651 addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
652 addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
653 addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
654 addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
656 addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
657 addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
659 addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
661 addArtifactAttribute(((
id < 0L) ? NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(
id)),
662 ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
664 addArtifactAttribute(((localPath.isEmpty() ==
false) ? localPath :
""),
665 ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
667 addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
668 addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
669 addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
670 addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
675 bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
676 bbart.addAttributes(bbattributes);
679 currentCase.
getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
684 }
catch (Blackboard.BlackboardException ex) {
685 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + bbart.getArtifactID(), ex);
688 }
catch (TskCoreException | TskDataException ex) {
689 logger.log(Level.WARNING, null, ex);
702 static void addArtifactAttribute(String stringVal, BlackboardAttribute.Type attrType, Collection<BlackboardAttribute> bbattributes) {
703 if (stringVal.isEmpty() ==
false) {
715 static void addArtifactAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
716 if (stringVal.isEmpty() ==
false) {
717 bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
728 static void addArtifactAttribute(
long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
730 bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
740 void postErrorMessage(String subj, String details) {
741 IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
750 IngestServices getServices() {
Set< String > findEmailAddresess(String input)
static final Logger logger
FileManager getFileManager()
String getTempDirectory()
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
void processEmails(List< EmailMessage > partialEmailsForThreading, Iterator< EmailMessage > fullMessageIterator, AbstractFile abstractFile)
BlackboardArtifact addEmailArtifact(EmailMessage email, AbstractFile abstractFile)
final IngestServices services
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
synchronized 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)
BlackboardArtifact associateAttachmentWithMesssge(BlackboardArtifact message, AbstractFile attachedFile)
ProcessResult processMBox(AbstractFile abstractFile)
static final int DISK_FREE_SPACE_UNKNOWN
ProcessResult processVcard(AbstractFile abstractFile)
ProcessResult processEMLFile(AbstractFile abstractFile)
void addFilesToJob(List< AbstractFile > files)
void postMessage(final IngestMessage message)
ProcessResult process(AbstractFile abstractFile)
SleuthkitCase getSleuthkitCase()
ProcessResult processPst(AbstractFile abstractFile)
void startUp(IngestJobContext context)
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
static void error(String title, String message)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
List< AbstractFile > handleAttachments(List< EmailMessage.Attachment > attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact)
CommunicationArtifactsHelper communicationArtifactsHelper
static synchronized IngestServices getInstance()