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.Body;
 
   30 import org.apache.james.mime4j.dom.Entity;
 
   31 import org.apache.james.mime4j.dom.Message;
 
   32 import org.apache.james.mime4j.dom.MessageWriter;
 
   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.message.DefaultMessageWriter;
 
   42 import org.apache.james.mime4j.stream.Field;
 
   43 import org.apache.james.mime4j.stream.MimeConfig;
 
   44 import org.openide.util.NbBundle;
 
   54 class MimeJ4MessageParser 
implements AutoCloseable{
 
   56     private static final Logger logger = Logger.getLogger(MimeJ4MessageParser.class.getName());
 
   61     private static final String HTML_TYPE = 
"text/html"; 
 
   62     private DefaultMessageBuilder messageBuilder = null;
 
   63     private final List<String> errorList = 
new ArrayList<>();
 
   68     private String localPath;
 
   70     DefaultMessageBuilder getMessageBuilder() {
 
   71         if (messageBuilder == null) {
 
   72             messageBuilder = 
new DefaultMessageBuilder();
 
   73             MimeConfig config = MimeConfig.custom().setMaxLineLen(-1).setMaxHeaderLen(-1).setMaxHeaderCount(-1).build();
 
   75             messageBuilder.setMimeEntityConfig(config);
 
   78         return messageBuilder;
 
   86     final void setLocalPath(String localPath) {
 
   87         this.localPath = localPath;
 
   95     String getLocalPath() {
 
  107         for (String msg : errorList) {
 
  108             result += 
"<li>" + msg + 
"</li>";
 
  118     void addErrorMessage(String msg) {
 
  130     EmailMessage extractEmail(Message msg, String localPath, 
long sourceFileID) {
 
  131         EmailMessage email = 
new EmailMessage();
 
  133         email.setSender(getAddresses(msg.getFrom()));
 
  134         email.setRecipients(getAddresses(msg.getTo()));
 
  135         email.setBcc(getAddresses(msg.getBcc()));
 
  136         email.setCc(getAddresses(msg.getCc()));
 
  137         email.setSubject(msg.getSubject());
 
  138         email.setSentDate(msg.getDate());
 
  139         email.setLocalPath(localPath);
 
  140         email.setMessageID(msg.getMessageId());
 
  142         Field field = msg.getHeader().getField(
"in-reply-to"); 
 
  143         String inReplyTo = null;
 
  146             inReplyTo = field.getBody();
 
  147             email.setInReplyToID(inReplyTo);
 
  150         field = msg.getHeader().getField(
"references");
 
  152             List<String> references = 
new ArrayList<>();
 
  153             for (String 
id : field.getBody().split(
">")) {
 
  154                 references.add(
id.trim() + 
">");
 
  157             if (!references.contains(inReplyTo)) {
 
  158                 references.add(inReplyTo);
 
  161             email.setReferences(references);
 
  165         if (msg.isMultipart()) {
 
  166             handleMultipart(email, (Multipart) msg.getBody(), sourceFileID);
 
  168             if(msg.getBody() instanceof TextBody) {
 
  169                 handleTextBody(email, (TextBody) msg.getBody(), msg.getMimeType(), msg.getHeader().getFields());
 
  171                handleAttachment(email, msg, sourceFileID, 1);
 
  186     EmailMessage extractPartialEmail(Message msg) {
 
  187         EmailMessage email = 
new EmailMessage();
 
  188         email.setSubject(msg.getSubject());
 
  189         email.setMessageID(msg.getMessageId());
 
  191         Field field = msg.getHeader().getField(
"in-reply-to"); 
 
  192         String inReplyTo = null;
 
  195             inReplyTo = field.getBody();
 
  196             email.setInReplyToID(inReplyTo);
 
  199         field = msg.getHeader().getField(
"references");
 
  201             List<String> references = 
new ArrayList<>();
 
  202             for (String 
id : field.getBody().split(
">")) {
 
  203                 references.add(
id.trim() + 
">");
 
  206             if (!references.contains(inReplyTo)) {
 
  207                 references.add(inReplyTo);
 
  210             email.setReferences(references);
 
  224     private void handleMultipart(EmailMessage email, Multipart multi, 
long fileID) {
 
  225         List<Entity> entities = multi.getBodyParts();
 
  226         for (
int index = 0; index < entities.size(); index++) {
 
  227             Entity e = entities.get(index);
 
  228             if (e.isMultipart()) {
 
  229                 handleMultipart(email, (Multipart) e.getBody(), fileID);
 
  230             } 
else if (e.getDispositionType() != null
 
  231                     && e.getDispositionType().equals(ContentDispositionField.DISPOSITION_TYPE_ATTACHMENT)) {
 
  232                 handleAttachment(email, e, fileID, index);
 
  233             } 
else if ((e.getMimeType().equals(HTML_TYPE) && (email.getHtmlBody() == null || email.getHtmlBody().isEmpty()))
 
  234                     || (e.getMimeType().equals(ContentTypeField.TYPE_TEXT_PLAIN) && (email.getTextBody() == null || email.getTextBody().isEmpty()))) {
 
  235                     handleTextBody(email, (TextBody) e.getBody(), e.getMimeType(), e.getHeader().getFields());
 
  237                 handleAttachment(email, e, fileID, index);
 
  252     private void handleTextBody(EmailMessage email, TextBody tb, String type, List<Field> fields) {
 
  255             r = 
new BufferedReader(tb.getReader());
 
  256             StringBuilder bodyString = 
new StringBuilder();
 
  257             StringBuilder headersString = 
new StringBuilder();
 
  259             while ((line = r.readLine()) != null) {
 
  260                 bodyString.append(line).append(
"\n");
 
  263             headersString.append(
"\n-----HEADERS-----\n");
 
  264             for (Field field : fields) {
 
  265                 String nextLine = field.getName() + 
": " + field.getBody();
 
  266                 headersString.append(
"\n").append(nextLine);
 
  268             headersString.append(
"\n\n---END HEADERS--\n\n");
 
  270             email.setHeaders(headersString.toString());
 
  273                 case ContentTypeField.TYPE_TEXT_PLAIN:
 
  274                     email.setTextBody(bodyString.toString());
 
  277                     email.setHtmlBody(bodyString.toString());
 
  283         } 
catch (IOException ex) {
 
  284             logger.log(Level.WARNING, 
"Error getting text body of mbox message", ex); 
 
  295     @NbBundle.Messages({
"MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."})
 
  296     private void handleAttachment(EmailMessage email, Entity e, 
long fileID, 
int index) {
 
  297         String outputDirPath;
 
  298         String relModuleOutputPath;
 
  300             outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
 
  301             relModuleOutputPath = ThunderbirdMboxFileIngestModule.getRelModuleOutputPath() + File.separator;
 
  302         } 
catch (NoCurrentCaseException ex) {
 
  303             logger.log(Level.SEVERE, Bundle.MimeJ4MessageParser_handleAttch_noOpenCase_errMsg(), ex); 
 
  306         String filename = e.getFilename();
 
  308         if (filename == null) {
 
  309             filename = 
"attachment" + e.hashCode();
 
  310             logger.log(Level.WARNING, String.format(
"Attachment has no file name using '%s'", filename));
 
  313         filename = FileUtil.escapeFileName(filename);
 
  317         if (filename.length() > 64) {
 
  318             filename = UUID.randomUUID().toString();
 
  321         String uniqueFilename = fileID + 
"-" + index + 
"-" + email.getSentDate() + 
"-" + filename;
 
  322         String outPath = outputDirPath + uniqueFilename;
 
  324         Body body = e.getBody();
 
  327             try (EncodedFileOutputStream fos = 
new EncodedFileOutputStream(
new FileOutputStream(outPath), TskData.EncodingType.XOR1)) {
 
  329                 EmailMessage.Attachment attach;
 
  330                 MessageWriter msgWriter = 
new DefaultMessageWriter();
 
  332                 if(body instanceof Message) {
 
  333                     msgWriter.writeMessage((Message)body, fos);
 
  334                     attach = 
new EmailMessage.AttachedEmailMessage(extractEmail((Message)body, email.getLocalPath(), fileID));
 
  336                     msgWriter.writeBody(body, fos);
 
  337                     attach = 
new EmailMessage.Attachment();
 
  339                 fileLength = fos.getBytesWritten();
 
  340                 attach.setName(filename);
 
  341                 attach.setLocalPath(relModuleOutputPath + uniqueFilename);
 
  342                 attach.setSize(fileLength);
 
  343                 attach.setEncodingType(TskData.EncodingType.XOR1);
 
  344                 email.addAttachment(attach);
 
  346             } 
catch (IOException ex) {
 
  347                 logger.log(Level.WARNING, 
"Failed to create file output stream for: " + outPath, ex); 
 
  361     private static String getAddresses(MailboxList mailboxList) {
 
  362         if (mailboxList == null) {
 
  365         StringBuilder addresses = 
new StringBuilder();
 
  366         for (Mailbox m : mailboxList) {
 
  367             addresses.append(m.toString()).append(
"; ");
 
  369         return addresses.toString();
 
  381     private static String getAddresses(AddressList addressList) {
 
  382         return (addressList == null) ? 
"" : getAddresses(addressList.flatten());
 
  386     public void close() throws IOException{