Autopsy  4.17.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ThunderbirdMboxFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.thunderbirdparser;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.logging.Level;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35 import org.apache.james.mime4j.MimeException;
36 import org.openide.util.NbBundle;
37 import org.openide.util.NbBundle.Messages;
51 import org.sleuthkit.datamodel.AbstractFile;
52 import org.sleuthkit.datamodel.Account;
53 import org.sleuthkit.datamodel.AccountFileInstance;
54 import org.sleuthkit.datamodel.Blackboard;
55 import org.sleuthkit.datamodel.BlackboardArtifact;
56 import org.sleuthkit.datamodel.BlackboardAttribute;
57 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
58 import org.sleuthkit.datamodel.DerivedFile;
59 import org.sleuthkit.datamodel.ReadContentInputStream;
60 import org.sleuthkit.datamodel.Relationship;
61 import org.sleuthkit.datamodel.TskCoreException;
62 import org.sleuthkit.datamodel.TskData;
63 import org.sleuthkit.datamodel.TskDataException;
64 import org.sleuthkit.datamodel.TskException;
65 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
66 import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
67 import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
68 
74 public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
75  private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
79  private Blackboard blackboard;
80  private CommunicationArtifactsHelper communicationArtifactsHelper;
81 
82  private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
83  private Case currentCase;
84 
89  }
90 
91  @Override
92  @Messages ({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
93  public void startUp(IngestJobContext context) throws IngestModuleException {
94  this.context = context;
95  try {
96  currentCase = Case.getCurrentCaseThrows();
98  } catch (NoCurrentCaseException ex) {
99  logger.log(Level.SEVERE, "Exception while getting open case.", ex);
100  throw new IngestModuleException(Bundle.ThunderbirdMboxFileIngestModule_noOpenCase_errMsg(), ex);
101  }
102  }
103 
104  @Override
105  public ProcessResult process(AbstractFile abstractFile) {
106 
107  blackboard = currentCase.getSleuthkitCase().getBlackboard();
108 
109  // skip known
110  if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
111  return ProcessResult.OK;
112  }
113 
114  //skip unalloc
115  if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
116  (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
117  return ProcessResult.OK;
118  }
119 
120  if ((abstractFile.isFile() == false)) {
121  return ProcessResult.OK;
122  }
123 
124  // check its signature
125  boolean isMbox = false;
126  boolean isEMLFile = false;
127 
128  try {
129  byte[] t = new byte[64];
130  if (abstractFile.getSize() > 64) {
131  int byteRead = abstractFile.read(t, 0, 64);
132  if (byteRead > 0) {
133  isMbox = MboxParser.isValidMimeTypeMbox(t, abstractFile);
134  isEMLFile = EMLParser.isEMLFile(abstractFile, t);
135  }
136  }
137  } catch (TskException ex) {
138  logger.log(Level.WARNING, null, ex);
139  }
140 
141  boolean isPstFile = PstParser.isPstFile(abstractFile);
142  boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
143 
144  if (context.fileIngestIsCancelled()) {
145  return ProcessResult.OK;
146  }
147 
148  if (isMbox || isEMLFile || isPstFile || isVcardFile ) {
149  try {
150  communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
151  EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL);
152  } catch (TskCoreException ex) {
153  logger.log(Level.SEVERE, String.format("Failed to create CommunicationArtifactsHelper for file with object id = %d", abstractFile.getId()), ex);
154  return ProcessResult.ERROR;
155  }
156  }
157 
158  if (isMbox) {
159  return processMBox(abstractFile);
160  }
161 
162  if (isEMLFile) {
163  return processEMLFile(abstractFile);
164  }
165 
166  if (isPstFile) {
167  return processPst(abstractFile);
168  }
169 
170  if (isVcardFile) {
171  return processVcard(abstractFile);
172  }
173 
174  return ProcessResult.OK;
175  }
176 
184  @Messages({"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
185  private ProcessResult processPst(AbstractFile abstractFile) {
186  String fileName;
187  try {
188  fileName = getTempPath() + File.separator + abstractFile.getName()
189  + "-" + String.valueOf(abstractFile.getId());
190  } catch (NoCurrentCaseException ex) {
191  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
192  return ProcessResult.ERROR;
193  }
194  File file = new File(fileName);
195 
196  long freeSpace = services.getFreeDiskSpace();
197  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
198  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
200  NbBundle.getMessage(this.getClass(),
201  "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
202  abstractFile.getName()));
203  services.postMessage(msg);
204  return ProcessResult.OK;
205  }
206 
207  try (PstParser parser = new PstParser(services)){
208  try {
209  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
210  } catch (IOException ex) {
211  logger.log(Level.WARNING, "Failed writing pst file to disk.", ex); //NON-NLS
212  return ProcessResult.OK;
213  }
214 
215  PstParser.ParseResult result = parser.open(file, abstractFile.getId());
216 
217  switch( result) {
218  case OK:
219  Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
220  if (pstMsgIterator != null) {
221  processEmails(parser.getPartialEmailMessages(), pstMsgIterator, abstractFile);
222  if (context.fileIngestIsCancelled()) {
223  return ProcessResult.OK;
224  }
225  } else {
226  // sometimes parser returns ParseResult=OK but there are no messages
227  postErrorMessage(
228  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
229  abstractFile.getName()),
230  NbBundle.getMessage(this.getClass(),
231  "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
232  logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
233  return ProcessResult.ERROR;
234  }
235  break;
236 
237  case ENCRYPT:
238  // encrypted pst: Add encrypted file artifact
239  try {
240 
241  BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
242  artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
243 
244  try {
245  // index the artifact for keyword search
246  blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName());
247  } catch (Blackboard.BlackboardException ex) {
248  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
249  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
250  }
251  } catch (TskCoreException ex) {
252  logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
253  }
254  break;
255  default:
256  // parsing error: log message
257  postErrorMessage(
258  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
259  abstractFile.getName()),
260  NbBundle.getMessage(this.getClass(),
261  "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
262  logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
263  return ProcessResult.ERROR;
264  }
265  } catch(Exception ex) {
266  logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath()));
267  } finally {
268  file.delete();
269  }
270  return ProcessResult.OK;
271  }
272 
280  private ProcessResult processMBox(AbstractFile abstractFile) {
281  String mboxFileName = abstractFile.getName();
282  String mboxParentDir = abstractFile.getParentPath();
283  // use the local path to determine the e-mail folder structure
284  String emailFolder = "";
285  // email folder is everything after "Mail" or ImapMail
286  if (mboxParentDir.contains("/Mail/")) { //NON-NLS
287  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/Mail/") + 5); //NON-NLS
288  } else if (mboxParentDir.contains("/ImapMail/")) { //NON-NLS
289  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/ImapMail/") + 9); //NON-NLS
290  }
291  emailFolder += mboxFileName;
292  emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS
293 
294  String fileName;
295  try {
296  fileName = getTempPath() + File.separator + abstractFile.getName()
297  + "-" + String.valueOf(abstractFile.getId());
298  } catch (NoCurrentCaseException ex) {
299  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
300  return ProcessResult.ERROR;
301  }
302  File file = new File(fileName);
303 
304  long freeSpace = services.getFreeDiskSpace();
305  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
306  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
307  postErrorMessage(
308  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
309  abstractFile.getName()),
310  NbBundle.getMessage(this.getClass(),
311  "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
312  return ProcessResult.OK;
313  }
314 
315  if (abstractFile.getSize() < MBOX_SIZE_TO_SPLIT) {
316 
317  try {
318  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
319  } catch (IOException ex) {
320  logger.log(Level.WARNING, "Failed writing mbox file to disk.", ex); //NON-NLS
321  return ProcessResult.OK;
322  }
323 
324  try{
325  processMboxFile(file, abstractFile, emailFolder);
326  if (context.fileIngestIsCancelled()) {
327  return ProcessResult.OK;
328  }
329  }finally {
330  file.delete();
331  }
332  } else {
333 
334  List<Long> mboxSplitOffsets = new ArrayList<>();
335  try{
336  mboxSplitOffsets = findMboxSplitOffset(abstractFile, file);
337  } catch (IOException ex) {
338  logger.log(Level.WARNING, String.format("Failed finding split offsets for mbox file {0}.", fileName), ex); //NON-NLS
339  return ProcessResult.OK;
340  }
341 
342  long startingOffset = 0;
343  for (Long mboxSplitOffset : mboxSplitOffsets) {
344  File splitFile = new File(fileName + "-" + mboxSplitOffset);
345  try {
346  ContentUtils.writeToFile(abstractFile, splitFile, context::fileIngestIsCancelled, startingOffset, mboxSplitOffset);
347  } catch (IOException ex) {
348  logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS
349  return ProcessResult.OK;
350  }
351  try{
352  processMboxFile(splitFile, abstractFile, emailFolder);
353  startingOffset = mboxSplitOffset;
354  } finally {
355  splitFile.delete();
356  }
357 
358  if (context.fileIngestIsCancelled()) {
359  return ProcessResult.OK;
360  }
361  }
362  }
363 
364  return ProcessResult.OK;
365  }
366 
367  private List<Long> findMboxSplitOffset(AbstractFile abstractFile, File file) throws IOException {
368 
369  List<Long> mboxSplitOffset = new ArrayList<>();
370 
371  byte[] buffer = new byte[7];
372  ReadContentInputStream in = new ReadContentInputStream(abstractFile);
373  in.skip(MBOX_SIZE_TO_SPLIT);
374  int len = in.read(buffer);
375  while (len != -1) {
376  len = in.read(buffer);
377  if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114 &&
378  buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
379  mboxSplitOffset.add(in.getCurPosition() - 5 );
380  in.skip(MBOX_SIZE_TO_SPLIT);
381  }
382  }
383 
384  return mboxSplitOffset;
385 
386  }
387 
388 
389  private void processMboxFile(File file, AbstractFile abstractFile, String emailFolder) {
390 
391  try(MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId())) {
392  List<EmailMessage> emails = new ArrayList<>();
393  if(emailIterator != null) {
394  while(emailIterator.hasNext()) {
395  if (context.fileIngestIsCancelled()) {
396  return;
397  }
398  EmailMessage emailMessage = emailIterator.next();
399  if(emailMessage != null) {
400  emails.add(emailMessage);
401  }
402  }
403 
404  String errors = emailIterator.getErrors();
405  if (!errors.isEmpty()) {
406  postErrorMessage(
407  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
408  abstractFile.getName()), errors);
409  }
410  }
411  processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile);
412  } catch(Exception ex) {
413  logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath()));
414  }
415 
416  }
417 
426  @Messages({
427  "# {0} - file name",
428  "# {1} - file ID",
429  "ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse."
430  })
431  private ProcessResult processVcard(AbstractFile abstractFile) {
432  try {
433  VcardParser parser = new VcardParser(currentCase, context);
434  parser.parse(abstractFile);
435  } catch (IOException | NoCurrentCaseException ex) {
436  logger.log(Level.WARNING, String.format("Exception while parsing the file '%s' (id=%d).", abstractFile.getName(), abstractFile.getId()), ex); //NON-NLS
437  return ProcessResult.OK;
438  }
439  return ProcessResult.OK;
440  }
441 
442  private ProcessResult processEMLFile(AbstractFile abstractFile) {
443  try {
444  EmailMessage message = EMLParser.parse(abstractFile);
445 
446  if (message == null) {
447  return ProcessResult.OK;
448  }
449 
450  List<AbstractFile> derivedFiles = new ArrayList<>();
451 
452  AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
453  BlackboardArtifact msgArtifact = addEmailArtifact(message, abstractFile, accountFileInstanceCache);
454  accountFileInstanceCache.clear();
455 
456  if ((msgArtifact != null) && (message.hasAttachment())) {
457  derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
458  }
459 
460  if (derivedFiles.isEmpty() == false) {
461  for (AbstractFile derived : derivedFiles) {
462  services.fireModuleContentEvent(new ModuleContentEvent(derived));
463  }
464  }
465  context.addFilesToJob(derivedFiles);
466 
467  } catch (IOException ex) {
468  logger.log(Level.WARNING, String.format("Error reading eml file %s", abstractFile.getName()), ex);
469  return ProcessResult.ERROR;
470  } catch (MimeException ex) {
471  logger.log(Level.WARNING, String.format("Error reading eml file %s", abstractFile.getName()), ex);
472  return ProcessResult.ERROR;
473  }
474 
475  return ProcessResult.OK;
476  }
477 
484  static String getTempPath() throws NoCurrentCaseException {
485  String tmpDir = Case.getCurrentCaseThrows().getTempDirectory() + File.separator
486  + "EmailParser"; //NON-NLS
487  File dir = new File(tmpDir);
488  if (dir.exists() == false) {
489  dir.mkdirs();
490  }
491  return tmpDir;
492  }
493 
501  static String getModuleOutputPath() throws NoCurrentCaseException {
502  String outDir = Case.getCurrentCaseThrows().getModuleDirectory() + File.separator
503  + EmailParserModuleFactory.getModuleName();
504  File dir = new File(outDir);
505  if (dir.exists() == false) {
506  dir.mkdirs();
507  }
508  return outDir;
509  }
510 
517  static String getRelModuleOutputPath() throws NoCurrentCaseException {
518  return Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath() + File.separator
519  + EmailParserModuleFactory.getModuleName();
520  }
521 
530  private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
531  AbstractFile abstractFile) {
532 
533  // Create cache for accounts
534  AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
535 
536  // Putting try/catch around this to catch any exception and still allow
537  // the creation of the artifacts to continue.
538  try{
539  EmailMessageThreader.threadMessages(partialEmailsForThreading);
540  } catch(Exception ex) {
541  logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex);
542  }
543 
544  List<AbstractFile> derivedFiles = new ArrayList<>();
545 
546  int msgCnt = 0;
547  while(fullMessageIterator.hasNext()) {
548  if (context.fileIngestIsCancelled()) {
549  return;
550  }
551 
552  EmailMessage current = fullMessageIterator.next();
553 
554  if(current == null) {
555  continue;
556  }
557 
558  if(partialEmailsForThreading.size() > msgCnt) {
559  EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
560 
561  if(threaded.getMessageID().equals(current.getMessageID()) &&
562  threaded.getSubject().equals(current.getSubject())) {
563  current.setMessageThreadID(threaded.getMessageThreadID());
564  }
565  }
566 
567  BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile, accountFileInstanceCache);
568 
569  if ((msgArtifact != null) && (current.hasAttachment())) {
570  derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
571  }
572  }
573 
574  if (derivedFiles.isEmpty() == false) {
575  for (AbstractFile derived : derivedFiles) {
576  if (context.fileIngestIsCancelled()) {
577  return;
578  }
579  services.fireModuleContentEvent(new ModuleContentEvent(derived));
580  }
581  }
582  context.addFilesToJob(derivedFiles);
583  }
594  @NbBundle.Messages({
595  "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message."
596 })
597  private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
598  List<AbstractFile> files = new ArrayList<>();
599  List<FileAttachment> fileAttachments = new ArrayList<>();
600  for (EmailMessage.Attachment attach : attachments) {
601  String filename = attach.getName();
602  long crTime = attach.getCrTime();
603  long mTime = attach.getmTime();
604  long aTime = attach.getaTime();
605  long cTime = attach.getcTime();
606  String relPath = attach.getLocalPath();
607  long size = attach.getSize();
608  TskData.EncodingType encodingType = attach.getEncodingType();
609 
610  try {
611  DerivedFile df = fileManager.addDerivedFile(filename, relPath,
612  size, cTime, crTime, aTime, mTime, true, abstractFile, "",
613  EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
614 
615  associateAttachmentWithMesssge(messageArtifact, df);
616 
617  files.add(df);
618 
619  fileAttachments.add(new FileAttachment(df));
620  } catch (TskCoreException ex) {
621  postErrorMessage(
622  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
623  abstractFile.getName()),
624  NbBundle.getMessage(this.getClass(),
625  "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
626  logger.log(Level.INFO, "", ex);
627  }
628  }
629 
630 
631  try {
632  communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList()));
633  } catch (TskCoreException ex) {
634  postErrorMessage(
635  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
636  "");
637  logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
638  }
639 
640  return files;
641  }
642 
647  private BlackboardArtifact associateAttachmentWithMesssge(BlackboardArtifact message, AbstractFile attachedFile) throws TskCoreException {
648  Collection<BlackboardAttribute> attributes = new ArrayList<>();
649  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, EmailParserModuleFactory.getModuleName(), message.getArtifactID()));
650 
651  BlackboardArtifact bba = attachedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
652  bba.addAttributes(attributes); //write out to bb
653  return bba;
654  }
655 
663  private Set<String> findEmailAddresess(String input) {
664  Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
665  Pattern.CASE_INSENSITIVE);
666  Matcher m = p.matcher(input);
667  Set<String> emailAddresses = new HashSet<>();
668  while (m.find()) {
669  emailAddresses.add( m.group());
670  }
671  return emailAddresses;
672  }
673 
683  @Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
684  private BlackboardArtifact addEmailArtifact(EmailMessage email, AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache) {
685  BlackboardArtifact bbart = null;
686  List<BlackboardAttribute> bbattributes = new ArrayList<>();
687  String to = email.getRecipients();
688  String cc = email.getCc();
689  String bcc = email.getBcc();
690  String from = email.getSender();
691  long dateL = email.getSentDate();
692  String headers = email.getHeaders();
693  String body = email.getTextBody();
694  String bodyHTML = email.getHtmlBody();
695  String rtf = email.getRtfBody();
696  String subject = email.getSubject();
697  long id = email.getId();
698  String localPath = email.getLocalPath();
699  String threadID = email.getMessageThreadID();
700 
701  List<String> senderAddressList = new ArrayList<>();
702  String senderAddress;
703  senderAddressList.addAll(findEmailAddresess(from));
704 
705  if (context.fileIngestIsCancelled()) {
706  return null;
707  }
708 
709  AccountFileInstance senderAccountInstance = null;
710 
711  if (senderAddressList.size() == 1) {
712  senderAddress = senderAddressList.get(0);
713  try {
714  senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress);
715  }
716  catch(TskCoreException ex) {
717  logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
718  }
719  }
720  else {
721  logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS
722  }
723 
724  if (context.fileIngestIsCancelled()) {
725  return null;
726  }
727 
728  List<String> recipientAddresses = new ArrayList<>();
729  recipientAddresses.addAll(findEmailAddresess(to));
730  recipientAddresses.addAll(findEmailAddresess(cc));
731  recipientAddresses.addAll(findEmailAddresess(bcc));
732 
733  List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
734  for (String addr : recipientAddresses) {
735  if (context.fileIngestIsCancelled()) {
736  return null;
737  }
738  try {
739  AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
740  recipientAccountInstances.add(recipientAccountInstance);
741  }
742  catch(TskCoreException ex) {
743  logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
744  }
745  }
746 
747  addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
748  addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
749  addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
750  addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
751 
752  addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
753  addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
754 
755  addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
756 
757  addArtifactAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
758  ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
759 
760  addArtifactAttribute(((localPath.isEmpty() == false) ? localPath : ""),
761  ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
762 
763  addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
764  addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
765  addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
766  addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
767 
768 
769  try {
770  if (context.fileIngestIsCancelled()) {
771  return null;
772  }
773 
774  bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
775  bbart.addAttributes(bbattributes);
776 
777  if (context.fileIngestIsCancelled()) {
778  return null;
779  }
780 
781  // Add account relationships
782  currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
783 
784  if (context.fileIngestIsCancelled()) {
785  return null;
786  }
787 
788  try {
789  // index the artifact for keyword search
790  blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName());
791  } catch (Blackboard.BlackboardException ex) {
792  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
793  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
794  }
795  } catch (TskCoreException | TskDataException ex) {
796  logger.log(Level.WARNING, null, ex);
797  }
798 
799  return bbart;
800  }
801 
809  static void addArtifactAttribute(String stringVal, BlackboardAttribute.Type attrType, Collection<BlackboardAttribute> bbattributes) {
810  if (stringVal.isEmpty() == false) {
811  bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
812  }
813  }
814 
822  static void addArtifactAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
823  if (stringVal.isEmpty() == false) {
824  bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
825  }
826  }
827 
835  static void addArtifactAttribute(long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
836  if (longVal > 0) {
837  bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
838  }
839  }
840 
846  static private class AccountFileInstanceCache {
847  private final Map<String, AccountFileInstance> cacheMap;
848  private final AbstractFile file;
849  private final Case currentCase;
850 
856  AccountFileInstanceCache(AbstractFile file, Case currentCase) {
857  cacheMap= new HashMap<>();
858  this.file = file;
859  this.currentCase = currentCase;
860  }
861 
871  AccountFileInstance getAccountInstance(String email) throws TskCoreException {
872  if (cacheMap.containsKey(email)) {
873  return cacheMap.get(email);
874  }
875 
876  AccountFileInstance accountInstance =
877  currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
878  EmailParserModuleFactory.getModuleName(), file);
879  cacheMap.put(email, accountInstance);
880  return accountInstance;
881  }
882 
886  void clear() {
887  cacheMap.clear();
888  }
889  }
890 
897  void postErrorMessage(String subj, String details) {
898  IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
899  services.postMessage(ingestMessage);
900  }
901 
907  IngestServices getServices() {
908  return services;
909  }
910 
911  @Override
912  public void shutDown() {
913  // nothing to shut down
914  }
915 
916 }
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
void processEmails(List< EmailMessage > partialEmailsForThreading, Iterator< EmailMessage > fullMessageIterator, AbstractFile abstractFile)
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)
void addFilesToJob(List< AbstractFile > files)
void postMessage(final IngestMessage message)
BlackboardArtifact addEmailArtifact(EmailMessage email, AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache)
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
void processMboxFile(File file, AbstractFile abstractFile, String emailFolder)
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
List< AbstractFile > handleAttachments(List< EmailMessage.Attachment > attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact)
static synchronized IngestServices getInstance()

Copyright © 2012-2021 Basis Technology. Generated on: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.