Saturday, February 13, 2010

Processing Javamail emails for specific mime types

I recently had to write some software that received an email and processed it for iCal events. I wanted my program to be tolerant of receiving the iCal events within the body or as a attachment. Furthermore if the attachment was a mail message then I wanted to look inside that email for the event and so on...

When I thought about the problem initially I thought that processing just the attachments of an email would be sufficient. That's not the case given Microsoft Outlook though. I quickly learnt that Outlook sends its iCal events within the body of an email and not as an attachment.

I ended up with the following bit of Javamail/iCal4j/Java code that should generally be useful for processing mail messages. In my case I'm looking for a text/calendar object but it could be anything of course. The addCalendarDataToCalendars function can be called recursively and adds any calendar objects it finds to the collection passed in. Enjoy.


/**
* Process a data handler's content looking for a
* calendar object.
*
* @param calendars
* the collection of calendars that we
* must add any found calendar to.
* @param dataHandler
* the data handler to use.
*/
private void addCalendarDataToCalendars(
Collection calendars,
DataHandler dataHandler) {
MimeType mimeType;
try {
mimeType =
new MimeType(dataHandler.getContentType());

if (mimeType.getBaseType()
.equals("text/calendar")) {
// Calendar data is what we're ultimately
// searching for.
CalendarBuilder builder =
new CalendarBuilder();
Calendar calendar = builder.build(
dataHandler.getInputStream());
calendars.add(calendar);

} else if (mimeType.getBaseType()
.equals("message/rfc822")) {
// Found an object representing an email
// address in its entirety.
Message mailMessage = (Message) dataHandler
.getContent();
addCalendarDataToCalendars(calendars,
mailMessage.getDataHandler());

} else if (mimeType.getBaseType()
.startsWith("multipart/")) {
// Found a bunch of attachments so
// process them each.
Multipart multipart =
(Multipart) dataHandler.getContent();
for (int i = 0, n = multipart.getCount();
i < n; ++i) {
Part part = multipart.getBodyPart(i);

String disposition =
part.getDisposition();

if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)
|| (disposition.equals(Part.INLINE))))) {
addCalendarDataToCalendars(calendars,
part.getDataHandler());

} else if (disposition == null) {
MimeBodyPart mimeBodyPart =
(MimeBodyPart) part;
addCalendarDataToCalendars(calendars,
mimeBodyPart.getDataHandler());

}
}
}

} catch (MimeTypeParseException e) {
logger
.warn("Ignoring attachment - could not " +
"parse the mime type of: "
+ dataHandler.getContentType()
+ " - " + e);
} catch (IOException e) {
logger.error(e);
} catch (ParserException e) {
logger
.warn("Ignoring attachment - could not " +
"interpret the calendar object: "
+ dataHandler.getName() + " - " + e);
} catch (MessagingException e) {
logger
.warn("Ignoring attachment - could not " +
"decode the calendar object: "
+ MimeUtility.getEncoding(dataHandler)
+ " - " + e);
}
}

No comments: