Reading the Body of an Email
On RIM's website, there is a web page explaining how to read the text or HTML body of an email. The method they provide is recursive, which is great because each mail server--Domino, Exchange, etc--encodes MIME messages in a different way. Hence I can't rely on finding the TextBodyPart in the same location all the time.
So I pasted RIM's code into my project and adjusted it for my own needs. Unfortunately, it produced NullPointerExceptions when reading certain types of mail. After a bit of research, I found that RIM had published fragile code. For example:
else if (obj instanceof MimeBodyPart)
{
MimeBodyPart mbp = (MimeBodyPart)obj;
if (mbp.getContentType().indexOf(ContentType.TYPE_TEXT_HTML_STRING) != -1)
{
readEmailBody(mbp);
}
}
The code above looks fine at first glance...it properly casts the 'obj' object into a MimeBodyPart object, then uses it. But notice the if
condition:
if (''mbp.getContentType().indexOf(....)''
The problem with nested calls like this is that the first method might return null. And any method involked on null
will produce a NullPointerException
.
So I added some null checking to RIM's code to make it safer. At the same time, I encapsulated the method in a class. The class takes an descendant of Message class as a parameter. And I added a getText()
method to make it easy to retrieve the text. The class can be used like this:
EmailProcessor emp = new EmailProcessor(SomeMessageObject.getContent());
String text = emp.getText();
The revise code from RIM now looks like this:
private class EmailProcessor
{
private boolean _hasSupportedAttachment = false;
private boolean _hasUnsupportedAttachment = false;
private String _plainTextMessage = null;
private String _htmlMessage = null;
private String status = null;
private EmailProcessor()
{
}// hide the default constructor...force use of 1-arg constructor
public EmailProcessor(Object obj)
{
findEmailBody(obj);
}
/**
* This method returns the body content to the calling program If both plain text and HTML
* are available, the plain text will be returned.
*
* @return
*/
public String getText()
{
if (_plainTextMessage != null)
return _plainTextMessage;
if (_htmlMessage != null)
return _htmlMessage;
return "";
}
/**
* This method is recursive. It reads the incoming object's data type and acts accordingly.
* It populates two class variables that hold the plain text and HTML versions of the
* message. The getText() method can be used to retrieve the text.
*
* @param obj
*/
private void findEmailBody(Object obj)
{
// Reset the attachment flags.
if (obj instanceof Multipart)
{
Multipart mp = (Multipart) obj;
// Extract all of the parts within the Multipart message.
for (int count = 0; count < mp.getCount(); ++count)
{
findEmailBody(mp.getBodyPart(count));
}
}
else if (obj instanceof TextBodyPart)
{
// This message only has a text body.
TextBodyPart tbp = (TextBodyPart) obj;
readEmailBody(tbp);
}
else if (obj instanceof MimeBodyPart)
{
MimeBodyPart mbp = (MimeBodyPart) obj;
String cType = mbp.getContentType();
if (cType != null)
{
if (cType.indexOf(ContentType.TYPE_TEXT_HTML_STRING) != -1)
{
// The message has no attachments.
// Read the email body, which may contain a
// TexBodyPart, MimeBodyPart or both.
readEmailBody(mbp);
}
else if (cType.equals(ContentType.TYPE_MULTIPART_MIXED_STRING) || cType.equals(ContentType.TYPE_MULTIPART_ALTERNATIVE_STRING))
{
// The message has attachments or we are at the
// top level of the message. Dig deeper to find the body.
// Extract all of the parts within the MimeBodyPart message.
findEmailBody(mbp.getContent());
}
}
}
else if (obj instanceof SupportedAttachmentPart)
{
// This is a supported attachment.
_hasSupportedAttachment = true;
}
else if (obj instanceof UnsupportedAttachmentPart)
{
// This is an unsupported attachment.
_hasUnsupportedAttachment = true;
}
}
private void readEmailBody(TextBodyPart tbp)
{
// reality check
if (tbp == null)
return;
// This is the plain text body.
_plainTextMessage = (String) tbp.getContent();
// Determine if all of the text body part is present.
if (tbp.hasMore() && !tbp.moreRequestSent())
{
// It does, request more of the message.
try
{
Transport.more(tbp, true);
status = "Requesting more of the plain text message body. Reopen the screen to view it once more has been received.";
}
catch (Exception ex)
{
//nothing we can do...
//Dialog.alert("Exception: " + ex.toString());
}
}
}
// Displays the HTML or plain text body of the email message.
private void readEmailBody(MimeBodyPart mbp)
{
// reality check
if (mbp == null)
return;
// Extract the content of the message.
Object obj = mbp.getContent();
String mimeType = mbp.getContentType();
String body = null;
// Determine if the data returned is a String or a byte array.
// If the BlackBerry is able to convert the HTML content
// into a String, then a String should be returned. If
// the encoding is not supported a byte array is returned
// to allow your application to work with the raw data.
if (obj instanceof String)
{
body = (String) obj;
}
else if (obj instanceof byte[])
{
body = new String((byte[]) obj);
}
if (mimeType != null)
{
if (mimeType.indexOf(ContentType.TYPE_TEXT_PLAIN_STRING) != -1)
{
// This is the plain text body.
_plainTextMessage = body;
// Determine if all of the text body part is present.
if (mbp!=null && mbp.hasMore() && !mbp.moreRequestSent())
{
// It does, request more of the message.
try
{
Transport.more(mbp, true);
status = "Requesting more of the plain text message body. Reopen the screen to view it once more has been received.";
}
catch (Exception ex)
{
// nothing we can do...
// Dialog.alert("Exception: " + ex.toString());
}
}
}
else if (mimeType.indexOf(ContentType.TYPE_TEXT_HTML_STRING) != -1)
{
// This is the HTML body part of the message.
_htmlMessage = body;
// Determine if all of the HTML body part is present.
if (mbp!=null && mbp.hasMore() && !mbp.moreRequestSent())
{
// It does, request more of the message.
try
{
Transport.more(mbp, true);
status = "Requesting more of the HTML message body. Reopen the screen to view it once more has been received.";
}
catch (Exception ex)
{
// nothing we can do....
// Dialog.alert("Exception: " + ex.toString());
}
}
}
}
}
}