Skip to main content
April 24, 2009
Answered

Using javax.mail.internet.MimeMessage etc. to create EML file

  • April 24, 2009
  • 1 reply
  • 8299 views

Hi,

I have email messages stored in a MySQL database and their attachments stored as base64-encoded text.  I'm trying to loop through a query and write out a properly formatted .EML file for each message.  I started from some borrowed code that was used to send IMAP messages, and I had no prior experience with Java, but I've since worked through some tutorials and I now know how to write and compile a simple class. But I'm still having trouble (lots!) instantiating the correct Java objects in the correct way and order in ColdFusion.

Here's what sort of works, with problems I'll detail below:

<cfscript>
    clsSession = createObject("Java", "javax.mail.Session");
    objProperties = createObject("Java", "java.util.Properties");


    msg = CreateObject("Java", "javax.mail.internet.MimeMessage");
    mmp = CreateObject("Java", "javax.mail.internet.MimeMultipart");
    mbp = CreateObject("Java", "javax.mail.internet.MimeBodyPart");
    objRecipientType = CreateObject("Java", "javax.mail.Message$RecipientType");


    fds = CreateObject("Java", "javax.activation.FileDataSource");


    fos = CreateObject("Java", "java.io.FileOutputStream");
    oos = CreateObject("Java", "java.io.ObjectOutputStream");

</cfscript>

<cfloop query="getAllMessages">

<!--- Here I also do a query "qGetMsgAtts" to get all attachments by getAllMessages.msg_id ,

      and use valueList(qGetMsgAtts.attIndex) to create "msgAttachmentsList" --->

<!--- initialize a java MimeMessage object --->

<cfscript>
    objProperties.init();

//  (I was actually setting properties here, but I never retrieved them and code works w/o them)

    objSession = clssession.getInstance(objProperties);


// Message
    msg.init(objSession);
    msg.addFrom(add.parse(getAllMessages.msgFrom, false));
    msg.setReplyTo(add.parse(getAllMessages.msgFrom, false));
    msg.addRecipients(objRecipientType.TO, add.parse(getAllMessages.msgTo, false));

// ETC... no problems setting msg properties
    loopCount = loopCount + 1;


// file name
    thisFileName = '#userId#_#myFun.doCountToken(loopCount)#.eml'; // my doCountToken(integer) function simply pads with zeros.


// Message body parts
    mbp.init();
    if (not msgIsHTML)

    {
        mbp.setText(msgBody);
    }
    else
    {
        mbp.setContent(msgBody, "text/html");
    }
    mmp.addBodyPart(mbp);
    msg.setContent(mmp);


    if (len (msgAttachmentsList)) // message has attachments
    try // TRY TO ATTACH FILES
    {
      { // qGetMsgAtts.colnames: msg_Id, attIndex, fileName, fileMIMEType, fileMacCreator, fileMacType, fileContents
          writeOutput('Has attachments ' & msgAttachmentsList & '<br />');
          for (i=1; i is listLen(msgAttachmentsList); i++)
          {

              fds.init(qGetMsgAtts.fileContents); // WORKS

              mbp.init();

              mbp.setDataHandler(dh.init(fds)); // WORKS

              mbp.setFileName(qGetMsgAtts.fileName); // WORKS

              mbp.setContent(qGetMsgAtts.fileContents,qGetMsgAtts.fileMIMEType); // WORKS

              mmp.addBodyPart(mbp);          

          }
          msg.setContent(mmp);
      }
    }
    catch(Any excpt) {
            writeOutput("#excpt.Type# error attaching content. #excpt.Message# #excpt.Detail#<br>");
    }


    aFile = thisFilePath & thisFileName;  // PATH AND FILE NAME
    fos.init(aFile); //
    oos.init(fos); //


try// WRITE THE FILE TO DISK

{
    msg.writeTo(oos); // WORKS ONLY IF MIME TYPE IS text/plain or text/html
    oos.flush(); 
    oos.close();
    fos.close();
}
catch (Any excpt) {
    writeOutput("#excpt.Type# error attaching content. #excpt.Message# #excpt.Detail#<br>");
    oos.flush();
    oos.close();
}

</cfscript>

</cfloop>

Although that writes the correct number of files with the correct file names, there are 2 major problems at this point:

  1. the contents of the file on disk appears to contain all the messages. I'm doing msg.init() atop every loop so I don't understand why. (Also the part boundary appears to be the same in every part in every message.)
  2. I get an error like the following for any MIME type other than text/plain or text/html :
    javax.activation.UnsupportedDataTypeException error attaching content. no object DCH for MIME type application/pdf no object DCH for MIME type application/pdf

I Googled that and I understand that I probably need to use something like
    bads = createObject("java","javax.mail.util.ByteArrayDataSource");
    baos = createObject("Java", "java.io.ByteArrayOutputStream");

...somewhere in the mix, but all my attempts to instantiate such objects have so far failed, usually with "Method Not Found" or "Constructor not found".

I hope that explanation makes sense. Anyone ever write EML files using ColdFusion? I'll be forever grateful for any suggestions or snippets.

Richard

York U CA

This topic has been closed for replies.
Correct answer -__cfSearching__-

This was very helpful, it looks as if I'm almost there --- thanks again! For the record I was using proper variable setting in some places, and most of the places I wasn't were in fact copied from someone else's application. Anyway among other things I changed the database I'm storing the attachments in to longblob fields rather than binaryEncode(file_contents,'Base64') into a longtext field.

The code that follows now appears to do everything I wanted, except that when I open a resulting EML message 1) there's some corruption at the beginning of the headers and at the end of the body, and 2) the attachments don't actually open... Acrobat says they're "damaged" and probably not decoded properly, and similarly Word displays a dialog by which one may change the encoding, but any choice displays only gibberish. The header corruption at the beginning of the first line has the result that it makes the sent date unreadable, and at the end of the body it's usually a w- or a z followed by one or more unicode squares.

So here's what now seems to get me very close to the solution (with extraneous stuff replaced by comments):

Before anything...

<cfscript>

    objProperties = createObject("Java", "java.util.Properties");

    MailSession = createObject("Java", "javax.mail.Session");

    msg = CreateObject("Java", "javax.mail.internet.MimeMessage");
    mmp = CreateObject("Java", "javax.mail.internet.MimeMultipart");
    mbp = CreateObject("Java", "javax.mail.internet.MimeBodyPart");

    objRecipientType = CreateObject("Java", "javax.mail.Message$RecipientType");


    dh = CreateObject("Java", "javax.activation.DataHandler");


    oos = CreateObject("Java", "java.io.ObjectOutputStream");
    fos = CreateObject("Java", "java.io.FileOutputStream");

    baos = CreateObject("Java", "java.io.ByteArrayOutputStream");

</cfscript>

<!--- Query the database to get all messages, then loop through the query: --->

<cfloop query="qGetAllMsgs">
<!--- initialize message field variables etc. --->
<!--- Query the database to get all attachment belonging to this message --->

<cfscript>

    loopCount = loopCount + 1;
    newObjProperties = objProperties.init();

    objSession = newMailSession.getInstance(newObjProperties);

    newMessage = msg.init(objSession);

// set message fields
    newMessage.addFrom(add.parse(
qGetAllMsgs.msgFrom, false));
    newMessage.setReplyTo(add.parse(
qGetAllMsgs.msgFrom, false));
    newMessage.addRecipients(objRecipientType.TO, add.parse(
qGetAllMsgs.msgTo, false));
    newMessage.addRecipients(objRecipientType.CC, add.parse(
qGetAllMsgs.msgCC, false));
    newMessage.addRecipients(objRecipientType.BCC, add.parse(
qGetAllMsgs.msgBcc, false));
    newMessage.setSubject(
qGetAllMsgs.msgSubject);
    newMessage.setSentDate(
qGetAllMsgs.msgSentDate);

    thisFileName = '#qGetAllMsgs.userId#_#myFun.doCountToken(loopCount)#.eml'; // pads loopCount with 0s


// Message body parts
    newMimeMultipart = mmp.init();
    newBodyPart = mbp.init();
    if (not msgIsHTML){
        newBodyPart.setText(msgBody);
    }
    else
    {
        newBodyPart.setContent(msgBody, "text/html");
    }
    newMimeMultipart.addBodyPart(newBodyPart);
    newMessage.setContent(newMimeMultipart);
    if (len (msgAttachmentsList)) // message has attachments
    try // TRY TO ATTACH FILES
    {
      {
         
          for (i=1; i lte listLen(msgAttachmentsList); i++)
          {   

              newBodyPart = mbp.init();
              source = bads.init(qGetMsgAtts.fileContents,qGetMsgAtts.fileMIMEType);
              handler = dh.init(source);
              newBodyPart.setFileName(javaCast('string',qGetMsgAtts.fileName));
              newBodyPart.setContent(qGetMsgAtts.fileContents,qGetMsgAtts.fileMIMEType);
              newBodyPart.setDataHandler(handler);
              newMimeMultipart.addBodyPart(newBodyPart);

          }
          newMessage.setContent(newMimeMultipart);
      }
    }
    catch(Any excpt) {
            writeOutput("#excpt.Type# error attaching content (#qGetMsgAtts.fileName#). #excpt.Message# #excpt.Detail#<br>");
    }

   
    // write eml file to disk
    emlFile = emlFilePath & thisFileName;
    myFileOutputStream = fos.init(
emlFile ); //
    myObjectOutputStream = oos.init(myFileOutputStream); //
try

       {
    newMessage.writeTo(myObjectOutputStream); // WORKS: 'myObjectOutputStream'
    myFileOutputStream.close(); //
    myObjectOutputStream.flush(); //
    myObjectOutputStream.close();
    }
catch (Any excpt)

       {
    writeOutput("#excpt.Type# error attaching content. #excpt.Message# #excpt.Detail#<br>");
    myFileOutputStream.close(); //
    myObjectOutputStream.flush(); //
    myObjectOutputStream.close();
    }

</cfscript>

</cfloop>

The result looks like an email message with the correct body and attachments, but all the files have 9 unicode characters at the top (I think they're triplets that refer to 3 single characters, and I can only paste a few of them in this text area: ¬í) and the attachments will not open in their helper applications.  I'm currently trying to get them on a mail server to see if the server's decoding changes anything.

Thanks again for the help that got me this far!

Richard

York U.


EDUYork wrote:

The code that follows now appears to do everything I wanted, except that when I open a resulting EML message 1) there's some corruption at the beginning of the headers and at the end of the body, and 2) the attachments don't actually open...


       ...

    myFileOutputStream = fos.init(emlFile ); //
    myObjectOutputStream = oos.init(myFileOutputStream); //

Hi Richard,

It looks okay except for the part that writes it to a file.  You only need one output stream here, so just use FileOutputStream. I think ObjectOutputStream is used more for serialization, and is probably what is causing the strange output here.

try
{
    emlFile = emlFilePath & thisFileName;
    outStream = CreateObject("Java", "java.io.FileOutputStream").init( emlFile );
    newMessage.writeTo( outStream );
    outStream.flush();
    outStream.close();
}
catch (Any excpt)
{
    writeOutput("#excpt.Type# error attaching content. #excpt.Message# #excpt.Detail#<br>");

    // only close the stream if it exists
    if ( IsDefined("outStream") )
    {
        outStream.close();
    }
}

BTW, the best reference for javax.email is sun's: http://java.sun.com/developer/onlineTraining/JavaMail/index.html   .So, did you ever find out if there is an easier way to do this?  I am wondering if you could store the whole message (headers, etc..) without the attachements?

1 reply

April 24, 2009

EDUYork wrote:

<snip>

there are 2 major problems at this point:

  1. the contents of the file on disk appears to contain all the messages. I'm doing msg.init() atop every loop so I don't understand why. (Also the part boundary appears to be the same in every part in every message.)
  2. I get an error like the following for any MIME type other than text/plain or text/html :
    javax.activation.UnsupportedDataTypeException error attaching content. no object DCH for MIME type application/pdf no object DCH for MIME type application/pdf

I Googled that and I understand that I probably need to use something like
    bads = createObject("java","javax.mail.util.ByteArrayDataSource");
    baos = createObject("Java", "java.io.ByteArrayOutputStream");

...somewhere in the mix, but all my attempts to instantiate such objects have so far failed, usually with "Method Not Found" or "Constructor not found".

I hope that explanation makes sense. Anyone ever write EML files using ColdFusion? I'll be forever grateful for any suggestions or snippets.

Richard

York U CA

I've answered #1 for myself: I just needed to (proof-read more closely and) re-initialize the MimeMultipart object as well...

// Message body parts
    mmp.init();
    mbp.init();
    if (not msgIsHTML){ // etc., etc.

I'm still seeking the way to create the part object as ByteArrayOutputStream (or similar) so it will properly detect and encode the many other mime types amongst my attachments.

Cheers!

Inspiring
April 24, 2009

I have never tried to reconstruct a message, so I don't know what other information you might need, like headers and such. But have you tried just setting the content?

Though I am wondering if there is a simpler option  (like saving the original file), as this does seems like the long route. ...

April 27, 2009

-==cfSearching==- wrote:

I have never tried to reconstruct a message, so I don't know what other information you might need, like headers and such. But have you tried just setting the content?

Though I am wondering if there is a simpler option  (like saving the original file), as this does seems like the long route. ...

Thanks for the link. I'm using setContent() successfully for text/plain and text/html content types, but it's throwing a DataContentHandler error "no object DCH for MIME type" for anything else.  I've Googled that and learned java.io.ByteArrayOutputStream and/or javax.mail.util.ByteArrayDataSource have the ability to do that correctly for other MIME types but I can't seem to figure out how to instantiate or initialize any working combination.

What works up to a point is (msgBody comes from my query)


    mmp.init(); // javax.mail.internet.MimeMultipart
    mbp.init(); // javax.mail.internet.MimeBodyPart
    if (not msgIsHTML){
        mbp.setText(msgBody);
    }
    else
    {
        mbp.setContent(msgBody, "text/html");
    }
    mmp.addBodyPart(mbp);
    msg.setContent(mmp);

// part not working is here, where I loop through attachments list. //

// file creation works
    fos.init(aFile); // a string, the absolute path and file name: ../userId/eml/abc_xyz.eml
    oos.init(fos); // java.io.ObjectOutputStream
    msg.writeTo(oos); // writeTo method outputs a valid EML file

The resulting EML file even opens in Outlook and/or gets appended properly to an IMAP folder on a live server, and the body content is what it's supposed to be. But attachments other than plain text or HTML appear and have correct file name, but they are 0k empty files.