Skip to content

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());
                        }
                    }
                }
            }
        }
    }