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.List;
28 import java.util.logging.Level;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import org.openide.util.NbBundle;
32 import org.openide.util.NbBundle.Messages;
53 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
82 @Messages ({
"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
89 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
100 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
105 if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
110 if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
111 (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
115 if ((abstractFile.isFile() ==
false)) {
120 boolean isMbox =
false;
122 byte[] t =
new byte[64];
123 if (abstractFile.getSize() > 64) {
124 int byteRead = abstractFile.read(t, 0, 64);
126 isMbox = MboxParser.isValidMimeTypeMbox(t);
129 }
catch (TskException ex) {
130 logger.log(Level.WARNING, null, ex);
137 if (PstParser.isPstFile(abstractFile)) {
141 if (VcardParser.isVcardFile(abstractFile)) {
155 @Messages({
"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
159 fileName = getTempPath() + File.separator + abstractFile.getName()
160 +
"-" + String.valueOf(abstractFile.getId());
162 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
165 File file =
new File(fileName);
169 logger.log(Level.WARNING,
"Not enough disk space to write file to disk.");
171 NbBundle.getMessage(this.getClass(),
172 "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
173 abstractFile.getName()));
180 }
catch (IOException ex) {
181 logger.log(Level.WARNING,
"Failed writing pst file to disk.", ex);
185 PstParser parser =
new PstParser(services);
186 PstParser.ParseResult result = parser.parse(file, abstractFile.getId());
188 if (result == PstParser.ParseResult.OK) {
192 }
else if (result == PstParser.ParseResult.ENCRYPT) {
195 BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
196 artifact.addAttribute(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(),
"ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
203 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
207 }
catch (TskCoreException ex) {
208 logger.log(Level.INFO,
"Failed to add encryption attribute to file: {0}", abstractFile.getName());
213 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
214 abstractFile.getName()),
215 NbBundle.getMessage(
this.getClass(),
216 "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
217 logger.log(Level.INFO,
"PSTParser failed to parse {0}", abstractFile.getName());
221 if (file.delete() ==
false) {
222 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
225 String errors = parser.getErrors();
226 if (errors.isEmpty() ==
false) {
228 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2",
229 abstractFile.getName()), errors);
243 String mboxFileName = abstractFile.getName();
244 String mboxParentDir = abstractFile.getParentPath();
246 String emailFolder =
"";
248 if (mboxParentDir.contains(
"/Mail/")) {
249 emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/Mail/") + 5);
250 }
else if (mboxParentDir.contains(
"/ImapMail/")) {
251 emailFolder = mboxParentDir.substring(mboxParentDir.indexOf(
"/ImapMail/") + 9);
253 emailFolder = emailFolder + mboxFileName;
254 emailFolder = emailFolder.replaceAll(
".sbd",
"");
258 fileName = getTempPath() + File.separator + abstractFile.getName()
259 +
"-" + String.valueOf(abstractFile.getId());
261 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
264 File file =
new File(fileName);
268 logger.log(Level.WARNING,
"Not enough disk space to write file to disk.");
270 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
271 abstractFile.getName()),
272 NbBundle.getMessage(
this.getClass(),
273 "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
279 }
catch (IOException ex) {
280 logger.log(Level.WARNING,
"Failed writing mbox file to disk.", ex);
284 MboxParser parser =
new MboxParser(services, emailFolder);
285 List<EmailMessage> emails = parser.parse(file, abstractFile.getId());
288 if (file.delete() ==
false) {
289 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
292 String errors = parser.getErrors();
293 if (errors.isEmpty() ==
false) {
295 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
296 abstractFile.getName()), errors);
313 "ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse."
318 fileName = getTempPath() + File.separator + abstractFile.getName()
319 +
"-" + String.valueOf(abstractFile.getId());
321 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
324 File file =
new File(fileName);
328 logger.log(Level.WARNING, String.format(
"Not enough disk space to write file '%s' (id=%d) to disk.",
329 abstractFile.getName(), abstractFile.getId()));
331 Bundle.ThunderbirdMboxFileIngestModule_errorMessage_outOfDiskSpace(abstractFile.getName(), abstractFile.getId()));
338 }
catch (IOException ex) {
339 logger.log(Level.WARNING, String.format(
"Failed writing the vCard file '%s' (id=%d) to disk.",
340 abstractFile.getName(), abstractFile.getId()), ex);
345 VcardParser parser =
new VcardParser(currentCase, context);
346 parser.parse(file, abstractFile);
348 logger.log(Level.WARNING, String.format(
"Exception while parsing the file '%s' (id=%d).", file.getName(), abstractFile.getId()), ex);
352 if (file.delete() ==
false) {
353 logger.log(Level.INFO,
"Failed to delete temp file: {0}", file.getName());
368 File dir =
new File(tmpDir);
369 if (dir.exists() ==
false) {
382 static String getModuleOutputPath() throws NoCurrentCaseException {
383 String outDir = Case.getCurrentCaseThrows().getModuleDirectory() + File.separator
384 + EmailParserModuleFactory.getModuleName();
385 File dir =
new File(outDir);
386 if (dir.exists() ==
false) {
398 static String getRelModuleOutputPath() throws NoCurrentCaseException {
399 return Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath() + File.separator
400 + EmailParserModuleFactory.getModuleName();
410 private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) {
411 List<AbstractFile> derivedFiles =
new ArrayList<>();
416 EmailMessageThreader.threadMessages(emails, String.format(
"%d", abstractFile.getId()));
417 }
catch(Exception ex) {
418 logger.log(Level.WARNING, String.format(
"Exception thrown parsing emails from %s", abstractFile.getName()), ex);
421 for (EmailMessage email : emails) {
424 if ((msgArtifact != null) && (email.hasAttachment())) {
425 derivedFiles.addAll(
handleAttachments(email.getAttachments(), abstractFile, msgArtifact ));
429 if (derivedFiles.isEmpty() ==
false) {
430 for (AbstractFile derived : derivedFiles) {
448 private List<AbstractFile>
handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
449 List<AbstractFile> files =
new ArrayList<>();
450 for (EmailMessage.Attachment attach : attachments) {
451 String filename = attach.getName();
452 long crTime = attach.getCrTime();
453 long mTime = attach.getmTime();
454 long aTime = attach.getaTime();
455 long cTime = attach.getcTime();
456 String relPath = attach.getLocalPath();
457 long size = attach.getSize();
458 TskData.EncodingType encodingType = attach.getEncodingType();
462 size, cTime, crTime, aTime, mTime,
true, messageArtifact,
"",
465 }
catch (TskCoreException ex) {
467 NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
468 abstractFile.getName()),
469 NbBundle.getMessage(
this.getClass(),
470 "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
471 logger.log(Level.INFO,
"", ex);
485 Pattern p = Pattern.compile(
"\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
486 Pattern.CASE_INSENSITIVE);
487 Matcher m = p.matcher(input);
488 Set<String> emailAddresses =
new HashSet<>();
490 emailAddresses.add( m.group());
492 return emailAddresses;
503 @Messages({
"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
504 private BlackboardArtifact
addEmailArtifact(EmailMessage email, AbstractFile abstractFile) {
505 BlackboardArtifact bbart = null;
506 List<BlackboardAttribute> bbattributes =
new ArrayList<>();
507 String to = email.getRecipients();
508 String cc = email.getCc();
509 String bcc = email.getBcc();
510 String from = email.getSender();
511 long dateL = email.getSentDate();
512 String headers = email.getHeaders();
513 String body = email.getTextBody();
514 String bodyHTML = email.getHtmlBody();
515 String rtf = email.getRtfBody();
516 String subject = email.getSubject();
517 long id = email.getId();
518 String localPath = email.getLocalPath();
519 String threadID = email.getMessageThreadID();
521 List<String> senderAddressList =
new ArrayList<>();
522 String senderAddress;
525 AccountFileInstance senderAccountInstance = null;
527 if (senderAddressList.size() == 1) {
528 senderAddress = senderAddressList.get(0);
530 senderAccountInstance = currentCase.
getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress,
EmailParserModuleFactory.getModuleName(), abstractFile);
532 catch(TskCoreException ex) {
533 logger.log(Level.WARNING,
"Failed to create account for email address " + senderAddress, ex);
537 logger.log(Level.WARNING,
"Failed to find sender address, from = {0}", from);
540 List<String> recipientAddresses =
new ArrayList<>();
545 List<AccountFileInstance> recipientAccountInstances =
new ArrayList<>();
546 recipientAddresses.forEach((addr) -> {
548 AccountFileInstance recipientAccountInstance =
549 currentCase.
getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
551 recipientAccountInstances.add(recipientAccountInstance);
553 catch(TskCoreException ex) {
554 logger.log(Level.WARNING,
"Failed to create account for email address " + addr, ex);
558 addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
559 addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
560 addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
561 addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
563 addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
564 addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
566 addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
568 addArtifactAttribute(((
id < 0L) ? NbBundle.getMessage(
this.getClass(),
"ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(
id)),
569 ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
571 addArtifactAttribute(((localPath.isEmpty() ==
false) ? localPath :
"/foo/bar"),
572 ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
574 addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
575 addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
576 addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
577 addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
582 bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
583 bbart.addAttributes(bbattributes);
586 currentCase.
getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
592 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + bbart.getArtifactID(), ex);
595 }
catch (TskCoreException | TskDataException ex) {
596 logger.log(Level.WARNING, null, ex);
609 static void addArtifactAttribute(String stringVal, BlackboardAttribute.Type attrType, Collection<BlackboardAttribute> bbattributes) {
610 if (stringVal.isEmpty() ==
false) {
622 static void addArtifactAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
623 if (stringVal.isEmpty() ==
false) {
624 bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
635 static void addArtifactAttribute(
long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
637 bbattributes.add(
new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
647 void postErrorMessage(String subj, String details) {
648 IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
657 IngestServices getServices() {
Set< String > findEmailAddresess(String input)
static final Logger logger
FileManager getFileManager()
String getTempDirectory()
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
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
void processEmails(List< EmailMessage > emails, AbstractFile abstractFile)
ProcessResult processVcard(AbstractFile abstractFile)
void addFilesToJob(List< AbstractFile > files)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
ProcessResult process(AbstractFile abstractFile)
SleuthkitCase getSleuthkitCase()
ProcessResult processPst(AbstractFile abstractFile)
void startUp(IngestJobContext context)
Blackboard getBlackboard()
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
static void error(String title, String message)
synchronized void indexArtifact(BlackboardArtifact artifact)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
List< AbstractFile > handleAttachments(List< EmailMessage.Attachment > attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact)
static synchronized IngestServices getInstance()