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.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
29 import java.util.logging.Level;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 import org.apache.james.mime4j.MimeException;
33 import org.openide.util.NbBundle;
34 import org.openide.util.NbBundle.Messages;
54 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
83 @Messages ({
"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
90 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
101 if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
106 if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
107 (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
111 if ((abstractFile.isFile() ==
false)) {
116 boolean isMbox =
false;
117 boolean isEMLFile =
false;
120 byte[] t =
new byte[64];
121 if (abstractFile.getSize() > 64) {
122 int byteRead = abstractFile.read(t, 0, 64);
124 isMbox = MboxParser.isValidMimeTypeMbox(t);
125 isEMLFile = EMLParser.isEMLFile(abstractFile, t);
128 }
catch (TskException ex) {
129 logger.log(Level.WARNING, null, ex);
140 if (PstParser.isPstFile(abstractFile)) {
144 if (VcardParser.isVcardFile(abstractFile)) {
158 @Messages({
"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
162 fileName = getTempPath() + File.separator + abstractFile.getName()
163 +
"-" + String.valueOf(abstractFile.getId());
165 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
168 File file =
new File(fileName);
172 logger.log(Level.WARNING,
"Not enough disk space to write file to disk.");
174 NbBundle.getMessage(this.getClass(),
175 "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
176 abstractFile.getName()));
183 }
catch (IOException ex) {
184 logger.log(Level.WARNING,
"Failed writing pst file to disk.", ex);
188 PstParser parser =
new PstParser(services);
189 PstParser.ParseResult result = parser.open(file, abstractFile.getId());
193 processEmails(parser.getPartialEmailMessages(), parser.getEmailMessageIterator(), abstractFile);
200 BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
201 artifact.addAttribute(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(),
"ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
206 }
catch (Blackboard.BlackboardException ex) {
208 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
210 }
catch (TskCoreException ex) {
211 logger.log(Level.INFO,
"Failed to add encryption attribute to file: {0}", abstractFile.getName());
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());
225 if (file.delete() ==
false) {
226 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
240 String mboxFileName = abstractFile.getName();
241 String mboxParentDir = abstractFile.getParentPath();
243 String emailFolder =
"";
245 if (mboxParentDir.contains(
"/Mail/")) {
246 emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/Mail/") + 5);
247 }
else if (mboxParentDir.contains(
"/ImapMail/")) {
248 emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/ImapMail/") + 9);
250 emailFolder = emailFolder + mboxFileName;
251 emailFolder = emailFolder.replaceAll(
".sbd",
"");
255 fileName = getTempPath() + File.separator + abstractFile.getName()
256 +
"-" + String.valueOf(abstractFile.getId());
258 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
261 File file =
new File(fileName);
265 logger.log(Level.WARNING,
"Not enough disk space to write file to disk.");
267 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
268 abstractFile.getName()),
269 NbBundle.getMessage(
this.getClass(),
270 "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
276 }
catch (IOException ex) {
277 logger.log(Level.WARNING,
"Failed writing mbox file to disk.", ex);
281 MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId());
282 List<EmailMessage> emails =
new ArrayList<>();
283 if(emailIterator != null) {
284 while(emailIterator.hasNext()) {
285 EmailMessage emailMessage = emailIterator.next();
286 if(emailMessage != null) {
287 emails.add(emailMessage);
291 String errors = emailIterator.getErrors();
292 if (!errors.isEmpty()) {
294 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
295 abstractFile.getName()), errors);
298 processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile);
300 if (file.delete() ==
false) {
301 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
318 "ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse."
322 VcardParser parser =
new VcardParser(currentCase, context);
323 parser.parse(abstractFile);
325 logger.log(Level.WARNING, String.format(
"Exception while parsing the file '%s' (id=%d).", abstractFile.getName(), abstractFile.getId()), ex);
333 EmailMessage message = EMLParser.parse(abstractFile);
335 if (message == null) {
339 List<AbstractFile> derivedFiles =
new ArrayList<>();
343 if ((msgArtifact != null) && (message.hasAttachment())) {
344 derivedFiles.addAll(
handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
347 if (derivedFiles.isEmpty() ==
false) {
348 for (AbstractFile derived : derivedFiles) {
354 }
catch (IOException ex) {
355 logger.log(Level.WARNING, String.format(
"Error reading eml file %s", abstractFile.getName()), ex);
357 }
catch (MimeException ex) {
358 logger.log(Level.WARNING, String.format(
"Error reading eml file %s", abstractFile.getName()), ex);
374 File dir =
new File(tmpDir);
375 if (dir.exists() ==
false) {
388 static String getModuleOutputPath() throws NoCurrentCaseException {
389 String outDir = Case.getCurrentCaseThrows().getModuleDirectory() + File.separator
390 + EmailParserModuleFactory.getModuleName();
391 File dir =
new File(outDir);
392 if (dir.exists() ==
false) {
404 static String getRelModuleOutputPath() throws NoCurrentCaseException {
405 return Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath() + File.separator
406 + EmailParserModuleFactory.getModuleName();
417 private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator, AbstractFile abstractFile) {
421 EmailMessageThreader.threadMessages(partialEmailsForThreading);
422 }
catch(Exception ex) {
423 logger.log(Level.WARNING, String.format(
"Exception thrown parsing emails from %s", abstractFile.getName()), ex);
426 List<AbstractFile> derivedFiles =
new ArrayList<>();
429 while(fullMessageIterator.hasNext()) {
430 EmailMessage current = fullMessageIterator.next();
432 if(current == null) {
436 if(partialEmailsForThreading.size() > msgCnt) {
437 EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
439 if(threaded.getMessageID().equals(current.getMessageID()) &&
440 threaded.getSubject().equals(current.getSubject())) {
441 current.setMessageThreadID(threaded.getMessageThreadID());
447 if ((msgArtifact != null) && (current.hasAttachment())) {
448 derivedFiles.addAll(
handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
452 if (derivedFiles.isEmpty() ==
false) {
453 for (AbstractFile derived : derivedFiles) {
469 private List<AbstractFile>
handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
470 List<AbstractFile> files =
new ArrayList<>();
471 for (EmailMessage.Attachment attach : attachments) {
472 String filename = attach.getName();
473 long crTime = attach.getCrTime();
474 long mTime = attach.getmTime();
475 long aTime = attach.getaTime();
476 long cTime = attach.getcTime();
477 String relPath = attach.getLocalPath();
478 long size = attach.getSize();
479 TskData.EncodingType encodingType = attach.getEncodingType();
483 size, cTime, crTime, aTime, mTime,
true, messageArtifact,
"",
486 }
catch (TskCoreException ex) {
488 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
489 abstractFile.getName()),
490 NbBundle.getMessage(
this.getClass(),
491 "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
492 logger.log(Level.INFO,
"", ex);
506 Pattern p = Pattern.compile(
"\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
507 Pattern.CASE_INSENSITIVE);
508 Matcher m = p.matcher(input);
509 Set<String> emailAddresses =
new HashSet<>();
511 emailAddresses.add( m.group());
513 return emailAddresses;
524 @Messages({
"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
525 private BlackboardArtifact
addEmailArtifact(EmailMessage email, AbstractFile abstractFile) {
526 BlackboardArtifact bbart = null;
527 List<BlackboardAttribute> bbattributes =
new ArrayList<>();
528 String to = email.getRecipients();
529 String cc = email.getCc();
530 String bcc = email.getBcc();
531 String from = email.getSender();
532 long dateL = email.getSentDate();
533 String headers = email.getHeaders();
534 String body = email.getTextBody();
535 String bodyHTML = email.getHtmlBody();
536 String rtf = email.getRtfBody();
537 String subject = email.getSubject();
538 long id = email.getId();
539 String localPath = email.getLocalPath();
540 String threadID = email.getMessageThreadID();
542 List<String> senderAddressList =
new ArrayList<>();
543 String senderAddress;
546 AccountFileInstance senderAccountInstance = null;
548 if (senderAddressList.size() == 1) {
549 senderAddress = senderAddressList.get(0);
551 senderAccountInstance = currentCase.
getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress,
EmailParserModuleFactory.getModuleName(), abstractFile);
553 catch(TskCoreException ex) {
554 logger.log(Level.WARNING,
"Failed to create account for email address " + senderAddress, ex);
558 logger.log(Level.WARNING,
"Failed to find sender address, from = {0}", from);
561 List<String> recipientAddresses =
new ArrayList<>();
566 List<AccountFileInstance> recipientAccountInstances =
new ArrayList<>();
567 recipientAddresses.forEach((addr) -> {
569 AccountFileInstance recipientAccountInstance =
570 currentCase.
getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
572 recipientAccountInstances.add(recipientAccountInstance);
574 catch(TskCoreException ex) {
575 logger.log(Level.WARNING,
"Failed to create account for email address " + addr, ex);
579 addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
580 addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
581 addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
582 addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
584 addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
585 addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
587 addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
589 addArtifactAttribute(((
id < 0L) ? NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(
id)),
590 ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
592 addArtifactAttribute(((localPath.isEmpty() ==
false) ? localPath :
""),
593 ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
595 addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
596 addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
597 addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
598 addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
603 bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
604 bbart.addAttributes(bbattributes);
607 currentCase.
getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
612 }
catch (Blackboard.BlackboardException ex) {
613 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + bbart.getArtifactID(), ex);
616 }
catch (TskCoreException | TskDataException ex) {
617 logger.log(Level.WARNING, null, ex);
630 static void addArtifactAttribute(String stringVal, BlackboardAttribute.Type attrType, Collection<BlackboardAttribute> bbattributes) {
631 if (stringVal.isEmpty() ==
false) {
643 static void addArtifactAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
644 if (stringVal.isEmpty() ==
false) {
645 bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
656 static void addArtifactAttribute(
long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
658 bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
668 void postErrorMessage(String subj, String details) {
669 IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
678 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)
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)
static synchronized IngestServices getInstance()