Building and Parsing MIME Messages
[Contents] [Previous] [Next] [Index] 

Chapter 3
Building and Parsing MIME Messages


This chapter is an overview of using the MIME (Multipurpose Internet Mail Extension) API of the Messaging Access SDK to encode, decode, and parse mail messages, and handle text and non-text attachments.

[Top]


The MIME Protocol

The MIME (Multipurpose Internet Mail Extension) protocol is the solution for sending multipart, multimedia, and binary data over the Internet. MIME is the standard for sending a variety of data types, including video, audio, images, programs, formatted documents, and text, in email messages.

The MIME protocol is made up of the extensions to the Internet mail format documented in RFC 822, "Standard for the Format of ARPA Internet Text Messages," August 1982. The MIME protocol, documented in a series of MIME RFCs, adds these features:

MIME messages can include attachments and non-ASCII data. To conform with RFC 822, which requires mail message characters to be in ASCII, MIME uses an encoding algorithm to convert binary data to ASCII characters. For content that requires encoding, MIME specifies two encoding types, either Quoted-Printable or BASE64, which are described more fully in MIME Encoding Types.

In addition to the ability to build multi-media messages in MIME format, the MIME API of the Messaging Access SDK provides a parsing facility for messages. This generic MIME parser takes a MIME-encoded email message and decodes all or parts of it, depending on the preferences of the application. The MIME parser is described in Parsing MIME Messages.

For detailed information about MIME, consult one of the RFCs listed, with links, in MIME RFCs.

[Top]


MIME Encoding Types

MIME messages can include attachments and non-ASCII data. RFC 822 requires mail message characters to be in ASCII, so MIME uses an encoding algorithm to convert binary data to ASCII characters. MIME uses one of two encoding types, Quoted-Printable and BASE64 encoding.

Quoted printable encoding handles content that is mostly composed of ASCII characters, with only a small number that are non-ASCII (for example, Scandinavian characters in the ISO-8859-1 character set). This text is mostly readable on the client before it is encoded. The encoding process ignores ASCII characters and encodes the rest, using a set of rules for representing characters, line breaks, and tabs, and limiting line length.

BASE64 encoding handles binary data. This algorithm works by encoding sets of a octets into encoded characters, and produces 33 percent data expansion.

The MIME API also supports a non-encoding option. For example, no encoding is required for text messages.

For detailed information about MIME encoding, consult one of the RFCs listed, with links, in MIME RFCs.

[Top]


MIME Content Types

MIME types typically have three parts, a type, a subtype and optional content-type parameters. The type is the general content category; the subtype is the specific data format, as shown in these examples: This table lists the valid MIME content types. A valid subtype can be of any data format type, including numerous experimental formats.

Table 3.1 MIME Content Types
Type  Description  Subtypes 
Text  Information in raw text form. Has optional character set (default: us-ascii). plain: includes no formatting information
Audio Message body contains audio data. basic
Image Message body contains an image. image format name, for
example: gif, jpeg
Video Message body contains a time-varying-picture image, possibly with color and sound. image format name, 
for example: mpeg
Application Uninterpreted binary data or information to be processed by an application. octet-stream, postscript
Multipart Messages with multiple attachments of potentially different media. Subtypes describe how the sub-parts relate. mixed, alternative, 
digest, parallel
Message Identifies a message. rfc822, partial, 
external-body
 
The MIME implementation of the Messaging Access SDK provides methods that create these content types, add them to messages, and encode or decode them.

For detailed information about MIME content types, subtypes, and content parameters, consult one of the RFCs listed, with links, in MIME RFCs.

[Top]


Structure of a MIME Message

A MIME message has two main parts, the header and the body.

Figure 3.1    Parts of a MIME message 

The message header consists of lines that describe the sender, subject, recipient, date, version of MIME in use, and a variety of other types of information, depending on the needs of the messaging application. This example shows the header lines of a message.
Return-Path:  <Prasad@netscape.com>
Received:  from netscape.com ([205.217.229.85])by dredd.mcom.com (Netscape Messaging Server 3.0) with ESMTP id AAA24896; Wed, 4 Feb 1998 20:08:19 -0800
Sender:  prasad
Message-ID:  <34D93795.C1F48C83@netscape.com>
Date:  Wed, 04 Feb 1998 19:52:53 -0800
From:  Prasad Yendluri <Prasad@netscape.com>
X-Mailer:  Mozilla 4.03C-NSCP [en] (X11; U; SunOS 5.5.1 sun4u)
MIME-Version:  1.0
To:  sharonw@netscape.com
Subject:  Information about MIME
Content-Type: multipart/mixed; boundary = 
"------------ BFA9E722569728E3111F0326"
 
For more information, see Adding Message Headers.

The message body consists of body parts of different types, depending on the demands of the data in the message.

For more information, see Building the MIME Message.

[Top]


MIME in the Messaging Access SDK

The MIME class hierarchy is made up of the following classes.

Top]


Steps in a MIME Session

The basic MIME operations focus on preparing a message to be sent and translating a received message into readable form for a mail application. Before the MIME message can be sent, the message and its attachments must be built and encoded in MIME format. When a message is received, it must be parsed and decoded.

Generally, a messaging application follows these steps when using MIME to build a message.

Figure 3.2    Translating a MIME Message 

[Top]


Building the MIME Message

Before sending a MIME message, you must build the message and its attachments and encode them in MIME format. This section describes the steps in building a message.

You can build MIME messages from RFC headers and simple text or attachments or both. You have the option of either building the message part by part, or using one of the convenience methods provided by the API.

There are two stages in building a message.

First, create the message object. A MIME message is an instance of the MIMEMessage class. The methods of this class create the message, add headers and body parts, return information about message attributes, and encode the message.

To create a MIMEMessage object, call one of the MIMEMessage class constructors.

Then, add two components, in either order. You can also create a message with ImTransport.sendDocuments, a convenience method for building and mailing documents. This method builds a MIME message, given headers, host, recipients, and other basic message information, then connects to the SMTP transport and submits the message. For more information, see Sending Documents with the Convenience API.

One of the sample applications in the examples directory of the SDK illustrates building a message using this method.

The following section of code demonstrates using MIMEMessage.MIMEMessage to build a MIME message with text from an input stream, a data buffer, and the default encoding type.

/* Get an inputStream to user entered text */
bins = new ByteArrayInputStream (textMsg.getBytes());
/* Create a new Multipart MIMEMessage with the above text and the file passed; -1 selects default encoding */
mmsg = new MIMEMessage(bins, fullfilename, -1);
Next, add the headers to the message. See Adding Message Headers.

[Top]


Adding Message Headers

After building a message, add the message headers. A MIME header is an instance of the Header class. The methods of this class create the header, and get and set header information.

To create a Header object, call one of the Header class constructors. These methods create a header entry as a name:value pair.

This method creates a header entry. You supply the header name and value. This method creates a header entry as a name:value pair.

public Header(String name,
         String value) throws MIMEException
Alternatively, you can add a header to a message, using either the MIMEMessage.addHeader or the MIMEMessage.setHeader method. The addHeader method adds the specified header to the message. If a header with the specified name already exists, it appends the value to the current header value.

public void addHeader(String name,
         String value) throws MIMEException
The MIMEMessage.setHeader method sets any RFC 822 headers including X-headers. If a header exists, it overwrites the existing value.

public void setHeader(String name,
         String value) throws MIMEException
The following section of code creates RFC 822-compliant headers for a MIME message.

/* Set user-entered RFC822 headers to a message (mmsg). */
mmsg.setHeader ("From", sender);
mmsg.setHeader ("Reply-To", sender);
mmsg.setHeader ("To", To);
mmsg.setHeader ("Subject", subject);
/* Add any other desired headers. */
mmsg.setHeader ("X-MsgSdk-Header", "This is a Text Message");
Next, you can add other message parts or attachments as needed to the message. See Adding Content to the Message.

[Top]


Adding Content to the Message

To add content to a message, you first create a message body part that contains data, then use MIMEMessage methods to add this part to the message. A MIME message can include the following types of body parts:

Creating the Basic Part and Adding Data

Before you can add a basic part, you first create the basic part object. A MIME basic part is an instance of the MIMEBasicPart class. This is the common structure for the leaf parts, text, audio, video, image, and application. Use one of the two class constructors. After you create the basic part object and its attributes, you can add the body data. To add data to an existing basic part, use one of the MIMEBasicPart.setBodyData methods, depending on the source of the data. The following section of code demonstrates adding data to a body part.

byte() bodyData = userTxt.getBytes() 
/* userTxt is a user-entered text string */
MIMEBasicPart bp1 = new MIMEBasicPart (MIMEBasicPart.Text);
bp1.setBodyData(bodyData);
Now you can add this part to the message. See Adding Parts to the Message.

[Top]


Adding Body Parts to a Multipart

If a message has two or more attachments, you must create a multipart that includes them before you can add them the message. To add an existing body part to a multipart, use this MIMEMultiPart method:

public int addBodyPart(MIMEBodyPart part,
                   boolean clone) throws MIMEException
Supply the body part type you are adding, either a MIMEBasicPart, a MIMEMultiPart, or a MIMEMessagePart.

The following section of code demonstrates adding a body part to a multipart.

/* Create the basic part */
MIMEBasicPart bp1 = new MIMEBasicPart();
MIMEBasicPart bp2 = new MIMEBasicPart(MIMEBasicPart.AUDIO);
/* Set bodyData of bp1 and bp2; set attributes of bp1 and bp2 */
/* See Creating the Basic Part and Adding Data for details */
/* Create the multipart */
MIMEMultiPart mp = new MIMEMultiPart();
mp.addBodyPart(bp1, false);
mp.addBodyPart(bp2, false);
/* Set attributes of multipart */
mp.setContentSubType ("Mixed");
After creating and assembling the multipart, the next step is to add the multipart to the message. See Adding Parts to the Message.

[Top]


Creating a Message Part

When you forward a message, it becomes the content of another message. This means that the mail application must do two things: To make a message part from the message structure, use the MIMEMessagePart constructor:

public MIMEMessagePart( MIMEMessage msg) throws MIMEException
For the msg parameter, supply the MIMEMessage object for the message to be forwarded. Alternatively, you can create a message part and then add the message to be forwarded to it, as shown here:

MIMEMessagePart msgPart = new MIMEMessagePart();
msgPart.setMessage(msg);
public void setMessage(MIMEMessage msg,
               boolean clone) throws MIMEException
Supply the message. The clone parameter should contain true if the function should clone a copy of the message or false if it should store a reference to the passed object.

[Top]


Adding Parts to the Message

After you create the MIME message object and the body parts you want it to include, use MIMEMessage methods to add the parts to the message. For information about creating the message, see Adding Content to the Message.

When a basic part, multipart, or message part is complete and includes data, you can add it to the message with the MIMEMessage.setBody method:

public void setBody(MIMEBodyPart part,
            boolean clone) throws MIMEException
You supply the body part type, either MIMEBasicPart, MIMEMultiPart, or MIMEMessagePart, and this part becomes the body of the message. For information about constructing a basic part, see Creating the Basic Part and Adding Data.

You can simplify the process of building and adding content to a message by using the MIMEMessage constructor that takes a new stream and file name. You can also use the ImTransport.sendDocuments Convenience API to build and send MIME messages from files and memory-based buffers. You supply message content and other attributes. The method creates a message, then connects to the SMTP transport and submits it. For more information, see Sending Documents with the Convenience API.

[Top]


Deleting Parts of a Message

If you want to delete the message, a body part, or parts of it, after it is built, use one of the MIME delete methods. You can delete the entire message, the body, or a message part, as needed.

[Top]


Encoding the Message

After building a message, the next step is to encode it. You can encode an entire message, with headers and message attachments, in one operation with the MIMEMessage.putByteStream method. Encoding generates a byte stream in MIME canonical form, so that it can be transmitted over SMTP and other transport methods.

public void putByteStream(OutputStream os) 
               throws IOException, MIMEException
This method encodes the data and writes it to the specified MIME output stream.

If you need to encode only a message body part, use the putByteStream method for that part. MIMEMessage.putByteStream internally invokes the putByteStream method for each constituent body part as needed.

The following section of code demonstrates using the putByteStream method.

FileOutputStream fos = new fileInputStream("<fileName>");
mmsg.putByteStream (fos);
[Top]

Encoding and Decoding Utilities

The MIMEHelper class provides a number of utility methods for encoding and decoding. These methods are used by other MIME API methods internally, and are also made available to developers to use in their applications.

The MIMEMessage.putByteStream method could call either of two encoding utility methods, MIMEHelper.encodeBase64 or MIMEHelper.encodeQP, based on whether the requested encoding type is Base64 (default for non-text types) or Quoted Printable. These MIMEHelper utility methods each provide a single form of encoding. Like MIMEMessage.putByteStream, these methods take an input stream and encode it. If an application requires only Base64 or QP-encoded data, you can use one of these methods in place of MIMEMessage.putByteStream.

The MIMEParser.parseEntireMessage method, which parses and decodes encoded messages, could call either of two decoding utility methods, MIMEHelper.decodeBase64 or MIMEHelper.decodeQP, based on the requested encoding type. These MIMEHelper utility methods each provide a single form of decoding, and could be used instead of parseEntireMessage if an application only needs to decode Base64 or QP-encoded data.

[Top]


Encoding and Decoding Headers

Two utility methods allow you to encode and decode only the headers of a message.

[Top]


Sending Documents with the Convenience API

The Messaging Access SDK provides convenience APIs that combine several message-handling operations in one step. These can be helpful when you are mail-enabling an otherwise mail-ignorant application.

ImTransport.sendDocuments is a convenience method for mailing documents. This method builds a MIME message with the specified parameters by automatically detecting the MIME types. It then connects to the SMTP transport at the specified host, and submits the message. If the message has more than one attachment, it is sent as a MIME message of multipart/mixed type.

public String[] sendDocuments(String host,
                              String sender,
                              String recipients[],
                              String subject,
                              String[] msgHeaderNames,
                              String[] msgHeaderValues,
                              IMAttachment [] attachments, 
                              boolean fUseTempFiles)
                              throws IMException
You provide the names of the host and sender, the email addresses of the recipients, the subject of the message, header information, any message attachments. If you set the fUseTempFiles parameter to true, sendDocuments uses temporary intermediate files for some internal processing, for better performance. If you don't want to create temporary files, set this flag to false.

For other purposes, or for more sophisticated email requirements, use the ImTransport.sendMessage method in association with the Netscape MIME API or other Messaging APIs provided by Netscape.

The Messaging Access SDK also provides an API for sending a message that is already in MIME format in a single step. For more information, see Sending the Message with Convenience APIs.

[Top]


Parsing MIME Messages

For parsing encoded messages, the Messaging Access SDK provides these options:

[Top]


Parsing the Entire Message

You can use MIMEParser class methods to parse and decode encoded messages retrieved through email protocol APIs, such as POP3 and IMAP4. First, create a MIMEParser object; then call MIMEParser.parseEntireMessage.

This method parses an entire MIME message in one operation and returns the parsed message:

public MIMEMessage parseEntireMessage(
            InputStream input) throws MIMEException
Supply the identifier of the input stream for the message.

The following section of code uses MIMEParser.parseEntireMessage as part of a routine that parses an entire file.

MIMEMessagePart msg = parseEntireMessage(inputStream);
[Top]

Dynamic Parsing

This section describes the steps involved in using the dynamic parser. Dynamic parsing contrasts with standard MIME parsing, as described in Parsing the Entire Message, in several ways.

[Top]


Steps in Dynamic Parsing

Using the dynamic parser involves these operations: All dynamic parser methods are defined in the MIMEDynamicParser class.

[Top]


MIME Data Sink Callbacks

Callbacks operate in the same way in MIME as they do in other Messaging Access SDK protocols. However, for SMTP, IMAP4, and POP3, callbacks are tied to server responses to individual functions.

The dynamic parser data sink differs from the response sink in that, with a response sink, the mail application sends a command and gets a server response. In the data sink, when a callback takes place, the data is passed to the sink in callbacks. The callback is dependent upon the data in the input stream.

The MIME data sink contains one callback prototype for each piece of information that the parser can return. The dynamic parser makes callbacks based on the kind of data it finds in its input stream. For example, if the parser finds a header, the result is a header callback. As the parser encounters data, it returns information to the caller through callbacks in the data sink.

For general information about the data sink, response sinks, and callbacks, see SDK Sink Classes for Java.

[Top]


Creating a Data Sink

The first step in starting the MIME parser session is to create and initialize the MIME data sink. To do this, extend the MIMEDataSink abstract class. The MIMEDataSink class contains null body callbacks for all methods. For general information about the data sink, see SDK Sink Classes for Java.

After creating the data sink, the application passes it to the parser. As the parser encounters information, it sends this on to the caller through callbacks in the data sink. The MIME data sink contains a call for each piece of information that the parser can return.

The following section of code demonstrates creating a data sink.

public class myDataSink extends MIMEDataSink 
{ 
   public MIMEMessage m_mimeMessage; 
   /* Constructor */
   public myDataSink() 
   { 
      super();
   public void header( Object callbackObject, 
               byte[] name, byte[] value ) 
   { 
      show ("header name = " + new String(name) + 
               "value = " + new String(value)); 
   }
   public void contentType( Object callbackObject, 
               int nContentType ) 
   { 
      show("contentType()=" + nContentType); 
   }
   public void contentSubType( Object callbackObject, 
               byte[] contentSubType )
   { 
      show("contentSubType()=" + new String (contentSubType)); 
   }
   public void contentTypeParams( Object callbackObject, 
               byte[] contentTypeParams ) 
   {
      show("contentTypeParams()" + new String(contentTypeParams));
   }
   public void contentID( Object callbackObject, 
               byte[] contentID ) 
   {
      show("contentID()" + new String(contentID));
   } 
/* Processing continues... */
}
After the data sink is created, the mail application can pass it to the parser when this object is created. As the parser encounters information, it goes through the data sink, returning information to the caller through callbacks.

You can create parsers with different data sinks, based on what you want the messaging application to do. For example, you can define data sinks that create a brief header, a normal header, or list all header lines, each of which can be invoked with a different parser invocation. To add headers, define the ones your application requires within the data sink structure.

After you create the data sink, the next step is Creating the Dynamic Parser.

[Top]


Creating the Dynamic Parser

After creating the data sink, the next step is to create a dynamic parser. You can use the MIMEDynamicParser class constructor to create a new parser and identify the data sink to use.
public MIMEDynamicParser(MIMEDataSink dataSink) throws MIMEException
Supply the identifier for the data sink.

The following section of code creates a dynamic parser.

/* Initialize sink first, as described in Creating a Data Sink */
MIMEDynamicParser mdp = new MIMEDynamkcParser(myDataSink);
After you create the dynamic parser, the next step is Running the Parser.

[Top]


Running the Parser

After the dynamic parser object has been created, as described in Creating the Dynamic Parser, parsing can begin. The parsing operation should continue until no more data is available, then signal that the parsing is complete.

To begin parsing, use the MIMEDynamicParser.beginParse method:

public void beginParse() throws MIMEException
This method starts a new parse cycle and resets the parser's internal data structures.

To continue parsing, use the MIMEDynamicParser.parse method:

public void parse(InputStream input) throws MIMEException
This method requires the input stream for the data to parse. Continue to call this method until there is no more data left.

When no more data remains to be parsed, call this method to indicate that parsing is complete. The parser ends the parse operation.

public void endParse() throws MIMEException
To initiate another parsing cycle, you can call beginParse again.

The following section of code creates a dynamic parser, parses data from an input stream, and ends the parse operation.

Class myDataSink extends MIMEDataSink;
   /* Implement the methods of myDataSink as needed */
   myDataSink dataSink = new myDataSink();
   /* Create the dynamic parser; see Creating the Dynamic Parser */
   MIMEDynamicParser mdp = new MIMEDynamicParser(dataSink);
   /* Start dynamic parsing */
   mdp.beginParse();
   /* Continue dynamic parsing until no more data remains */
   while (not done)
   {
      mdp.parse (data to parse);
   }
   /* When data is finished, stop the dynamic parser */
mdp.endParse ();
[Top]


[Contents] [Previous] [Next] [Index]
Last Updated: 04/10/98 09:30:13 
Copyright © 1997 Netscape Communications Corporation