19 package org.sleuthkit.autopsy.thunderbirdparser;
 
   21 import java.io.BufferedReader;
 
   23 import java.io.FileOutputStream;
 
   24 import java.io.IOException;
 
   25 import java.util.ArrayList;
 
   26 import java.util.List;
 
   27 import java.util.UUID;
 
   28 import java.util.logging.Level;
 
   29 import org.apache.james.mime4j.dom.BinaryBody;
 
   30 import org.apache.james.mime4j.dom.Body;
 
   31 import org.apache.james.mime4j.dom.Entity;
 
   32 import org.apache.james.mime4j.dom.Message;
 
   33 import org.apache.james.mime4j.dom.Multipart;
 
   34 import org.apache.james.mime4j.dom.TextBody;
 
   35 import org.apache.james.mime4j.dom.address.AddressList;
 
   36 import org.apache.james.mime4j.dom.address.Mailbox;
 
   37 import org.apache.james.mime4j.dom.address.MailboxList;
 
   38 import org.apache.james.mime4j.dom.field.ContentDispositionField;
 
   39 import org.apache.james.mime4j.dom.field.ContentTypeField;
 
   40 import org.apache.james.mime4j.message.DefaultMessageBuilder;
 
   41 import org.apache.james.mime4j.stream.Field;
 
   42 import org.apache.james.mime4j.stream.MimeConfig;
 
   43 import org.openide.util.NbBundle;
 
   53 class MimeJ4MessageParser {
 
   55     private static final Logger logger = Logger.getLogger(MimeJ4MessageParser.class.getName());
 
   60     private static final String HTML_TYPE = 
"text/html"; 
 
   61     private DefaultMessageBuilder messageBuilder = null;
 
   62     private final List<String> errorList = 
new ArrayList<>();
 
   67     private String localPath;
 
   69     DefaultMessageBuilder getMessageBuilder() {
 
   70         if (messageBuilder == null) {
 
   71             messageBuilder = 
new DefaultMessageBuilder();
 
   72             MimeConfig config = MimeConfig.custom().setMaxLineLen(-1).setMaxHeaderLen(-1).setMaxHeaderCount(-1).build();
 
   74             messageBuilder.setMimeEntityConfig(config);
 
   77         return messageBuilder;
 
   85     final void setLocalPath(String localPath) {
 
   86         this.localPath = localPath;
 
   94     String getLocalPath() {
 
  106         for (String msg : errorList) {
 
  107             result += 
"<li>" + msg + 
"</li>";
 
  117     void addErrorMessage(String msg) {
 
  129     EmailMessage extractEmail(Message msg, String localPath, 
long sourceFileID) {
 
  130         EmailMessage email = 
new EmailMessage();
 
  132         email.setSender(getAddresses(msg.getFrom()));
 
  133         email.setRecipients(getAddresses(msg.getTo()));
 
  134         email.setBcc(getAddresses(msg.getBcc()));
 
  135         email.setCc(getAddresses(msg.getCc()));
 
  136         email.setSubject(msg.getSubject());
 
  137         email.setSentDate(msg.getDate());
 
  138         email.setLocalPath(localPath);
 
  139         email.setMessageID(msg.getMessageId());
 
  141         Field field = msg.getHeader().getField(
"in-reply-to"); 
 
  142         String inReplyTo = null;
 
  145             inReplyTo = field.getBody();
 
  146             email.setInReplyToID(inReplyTo);
 
  149         field = msg.getHeader().getField(
"references");
 
  151             List<String> references = 
new ArrayList<>();
 
  152             for (String 
id : field.getBody().split(
">")) {
 
  153                 references.add(
id.trim() + 
">");
 
  156             if (!references.contains(inReplyTo)) {
 
  157                 references.add(inReplyTo);
 
  160             email.setReferences(references);
 
  164         if (msg.isMultipart()) {
 
  165             handleMultipart(email, (Multipart) msg.getBody(), sourceFileID);
 
  167             handleTextBody(email, (TextBody) msg.getBody(), msg.getMimeType(), msg.getHeader().getFields());
 
  181     EmailMessage extractPartialEmail(Message msg) {
 
  182         EmailMessage email = 
new EmailMessage();
 
  183         email.setSubject(msg.getSubject());
 
  184         email.setMessageID(msg.getMessageId());
 
  186         Field field = msg.getHeader().getField(
"in-reply-to"); 
 
  187         String inReplyTo = null;
 
  190             inReplyTo = field.getBody();
 
  191             email.setInReplyToID(inReplyTo);
 
  194         field = msg.getHeader().getField(
"references");
 
  196             List<String> references = 
new ArrayList<>();
 
  197             for (String 
id : field.getBody().split(
">")) {
 
  198                 references.add(
id.trim() + 
">");
 
  201             if (!references.contains(inReplyTo)) {
 
  202                 references.add(inReplyTo);
 
  205             email.setReferences(references);
 
  219     private void handleMultipart(EmailMessage email, Multipart multi, 
long fileID) {
 
  220         List<Entity> entities = multi.getBodyParts();
 
  221         for (
int index = 0; index < entities.size(); index++) {
 
  222             Entity e = entities.get(index);
 
  223             if (e.isMultipart()) {
 
  224                 handleMultipart(email, (Multipart) e.getBody(), fileID);
 
  225             } 
else if (e.getDispositionType() != null
 
  226                     && e.getDispositionType().equals(ContentDispositionField.DISPOSITION_TYPE_ATTACHMENT)) {
 
  227                 handleAttachment(email, e, fileID, index);
 
  228             } 
else if (e.getMimeType().equals(HTML_TYPE)
 
  229                     || e.getMimeType().equals(ContentTypeField.TYPE_TEXT_PLAIN)) {
 
  230                 handleTextBody(email, (TextBody) e.getBody(), e.getMimeType(), e.getHeader().getFields());
 
  247     private void handleTextBody(EmailMessage email, TextBody tb, String type, List<Field> fields) {
 
  250             r = 
new BufferedReader(tb.getReader());
 
  251             StringBuilder bodyString = 
new StringBuilder();
 
  252             StringBuilder headersString = 
new StringBuilder();
 
  254             while ((line = r.readLine()) != null) {
 
  255                 bodyString.append(line).append(
"\n");
 
  258             headersString.append(
"\n-----HEADERS-----\n");
 
  259             for (Field field : fields) {
 
  260                 String nextLine = field.getName() + 
": " + field.getBody();
 
  261                 headersString.append(
"\n").append(nextLine);
 
  263             headersString.append(
"\n\n---END HEADERS--\n\n");
 
  265             email.setHeaders(headersString.toString());
 
  268                 case ContentTypeField.TYPE_TEXT_PLAIN:
 
  269                     email.setTextBody(bodyString.toString());
 
  272                     email.setHtmlBody(bodyString.toString());
 
  278         } 
catch (IOException ex) {
 
  279             logger.log(Level.WARNING, 
"Error getting text body of mbox message", ex); 
 
  290     @NbBundle.Messages({
"MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."})
 
  291     private static void handleAttachment(EmailMessage email, Entity e, 
long fileID, 
int index) {
 
  292         String outputDirPath;
 
  293         String relModuleOutputPath;
 
  295             outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
 
  296             relModuleOutputPath = ThunderbirdMboxFileIngestModule.getRelModuleOutputPath() + File.separator;
 
  297         } 
catch (NoCurrentCaseException ex) {
 
  298             logger.log(Level.SEVERE, Bundle.MimeJ4MessageParser_handleAttch_noOpenCase_errMsg(), ex); 
 
  301         String filename = FileUtil.escapeFileName(e.getFilename());
 
  305         if (filename.length() > 64) {
 
  306             filename = UUID.randomUUID().toString();
 
  309         String uniqueFilename = fileID + 
"-" + index + 
"-" + email.getSentDate() + 
"-" + filename;
 
  310         String outPath = outputDirPath + uniqueFilename;
 
  311         EncodedFileOutputStream fos;
 
  314             fos = 
new EncodedFileOutputStream(
new FileOutputStream(outPath), TskData.EncodingType.XOR1);
 
  315         } 
catch (IOException ex) {
 
  316             logger.log(Level.WARNING, 
"Failed to create file output stream for: " + outPath, ex); 
 
  321             Body b = e.getBody();
 
  322             if (b instanceof BinaryBody) {
 
  328         } 
catch (IOException ex) {
 
  329             logger.log(Level.WARNING, 
"Failed to write mbox email attachment to disk.", ex); 
 
  334             } 
catch (IOException ex) {
 
  335                 logger.log(Level.WARNING, 
"Failed to close file output stream", ex); 
 
  339         EmailMessage.Attachment attach = 
new EmailMessage.Attachment();
 
  340         attach.setName(filename);
 
  341         attach.setLocalPath(relModuleOutputPath + uniqueFilename);
 
  342         attach.setSize(
new File(outPath).length());
 
  343         attach.setEncodingType(TskData.EncodingType.XOR1);
 
  344         email.addAttachment(attach);
 
  356     private static String getAddresses(MailboxList mailboxList) {
 
  357         if (mailboxList == null) {
 
  360         StringBuilder addresses = 
new StringBuilder();
 
  361         for (Mailbox m : mailboxList) {
 
  362             addresses.append(m.toString()).append(
"; ");
 
  364         return addresses.toString();
 
  376     private static String getAddresses(AddressList addressList) {
 
  377         return (addressList == null) ? 
"" : getAddresses(addressList.flatten());