Internet Protocols Module User’s Guide : PART II Packages of the Internet Protocols Module : Chapter 9 Using the MIME Package : Processing a MIME Part
Processing a MIME Part
Processing a MIME part presents a special problem: when you receive a part, you may not know whether or not the part contains multipart content. This section presents a simple procedure for processing both simple and multipart MIME parts:
1. Receive the part as a string (“Receive the Part as a String”).
2. Create an RWMimePart (“Create a MIME Part”).
3. Fill the MIME part from the string (“Fill the MIME Part From the String”).
4. Retrieve the headers as necessary for your application (“Retrieve the Headers as Necessary”).
5. Process the part body (“Process the Body”).
Receive the Part as a String
Most often, your program receives a MIME part from the network using one of the other packages in the Internet Protocols Module. In other cases, you may read in a file, or even retrieve the message from a database. Wherever the data comes from, store the data in an RWCString.
Create a MIME Part
Create an RWMimePart object, as shown below:
 
RWMimePart message;
Fill the MIME Part From the String
The RWMimePart class provides a fromString() function that parses a MIME message contained within a string and populates the message with the string’s contents. For example:
 
RWCString mimedata;
 
// ... fill mimedata with a MIME message
 
RWMimePart message;
 
try
{
message.fromString(mimedata);
}
catch (const RWMimeParseError& msg)
{
// The fromString function could not parse the complete message.
// ...handle the exception
}
The function extracts as much data as possible from the message. Typically, this is the full message. However, if the function can’t find any way to make sense of the message, the function throws RWMimeParseError. We use this strategy so that the MIME package can process as many messages as possible – even some messages that don’t meet the specification or that were damaged in transit.
Retrieve the Headers as Necessary
An RWMimePart contains a body and an indexed collection of headers. The part provides several functions for working with headers. The -getHeaderCount() function returns the number of headers the part contains. To retrieve a header from the part, use the getHeader() function with the position of the header. If you try to retrieve a header that doesn’t exist (for example, asking for the header at position 10 in a part that only has 3 headers), the getHeader() function throws RWBoundsErr. For example, to retrieve the first header from a message:
 
if (message.getHeaderCount() > 0) // Make sure there’s a header
{
 
RWMimeHeader firstHeader = message.getHeader(0);
 
// ... do something with the first header
 
}
A message also provides the findHeader() function for locating a header with a given label. The function searches through the messages header collection and returns the location of the first header with a matching label. If no headers match, the function returns RW_NPOS. Since MIME header labels aren’t case-sensitive, f-indHeader() uses case-insensitive comparisons to find a match. Once you’ve found the location of a header, use getHeader() to retrieve the header from the collection, as shown below:
 
size_t idPos = message.findHeader(RWMimeContentIdHeader::Label);
 
if (idPos != RW_NPOS)
{
RWMimeContentIdHeader idHeader = message.getHeader(idPos);
// ... do something with the Content-ID header
}
else
{
// ... the message does not contain a Content-ID header
}
Process the Body
A MIME part provides an isMultipart() function for determining if a part contains multipart content, as shown below:
 
if (message.isMultipart())
{
// process a multipart message (See below.)
}
else
{
// process a simple message (See below.)
}
Simple MIME Parts
To process the body of a simple MIME part, use the getBody() function to retrieve the body of the part as a string. For example:
 
RWMimePart message;
 
// ... fill message from a string, determine that the message
// is not a multipart message
 
RWCString body = message.getBody();
The getBody() function returns the body exactly as contained in the original data string. Depending on the Content-Transfer-Encoding of the message, you may need to decode the body. For convenience, the static decode() function of RWMimeUtils accepts all legal MIME encodings. Rather than checking the value of the Content-Transfer-Encoding to determine if decoding is necessary, it’s easier to always decode the message, as shown:
 
RWMimePart message;
 
// ... fill message from a string, determine that message
// is not multipart
 
RWCString body = message.getBody();
 
size_t encPos =
message.findHeader(RWMimeContentTransferEncodingHeader::Label);
 
if (encPos != RW_NPOS)
{
RWMimeContentTransferEncodingHeader cTEHead =
message.getHeader(encPos);
 
try
{
body = RWMimeUtils::decode(body, cTEHead.getEncoding());
}
catch (const RWMimeError& msg)
{
// Invalid content transfer encoding value, or
// the value does not match the format of the body.
}
}
The snippet above skips the decoding step if the part has no Content-Transfer-Encoding header. Since the MIME specification states that parts without a Content-Transfer-Encoding header are unencoded 7-bit ASCII, this approach is safe for any valid MIME message.
Depending on the Content-Type, the body of the part may be in canonical form. This is most common for text messages. To convert a text message from canonical form to a platform-specific format, use the static replaceLineDelimiters() function of RWMimeUtils. For example, to convert text from canonical form to UNIX text:
 
body = RWMimeUtils::replaceLineDelimiter(body, "\n");
Multipart MIME Parts
If the part contains multipart content, create an RWMimeMultipart from the part. This conversion simply creates a new handle to the underlying implementation. For example,
 
if (message.isMultipart())
{
RWMimeMultipart multipart(message);
// ... process multipart
}
An RWMimeMultipart contains an indexed collection of MIME parts. The multipart provides several methods for working the part collection. The getPartCount() function returns the number of parts the multipart contains. To retrieve a part from the multipart, use the getPart() function with the position of the part. If you try to retrieve a part that doesn’t exist (for example, asking for the part at position 10 in a multipart body that only has 3 parts), the getPart() function throws RWBoundsErr. To retrieve every part in a message, use code like the following:
 
// ... message is an RWMimePart that contains a multipart message
 
RWMimeMultipart multipart(message);
 
for (size_t pos = 0; pos < multipart.getPartCount(); ++pos)
{
 
RWMimePart part = multipart.getPart(pos);
 
// ... process the part
 
}
A multipart also provides the findPart() function for locating a part that contains a specific header. The function accepts a header, searches through the multipart body’s header collection, and returns the location of the first part with a matching header. If no parts match, the function returns RW_NPOS. Since MIME header labels aren’t case-sensitive, f-indPart() uses case-insensitive comparisons for header labels. Comparisons for values depend on the header type. Once you’ve found the location of a part, use getPart() to retrieve the part from the collection, as shown below:
 
// ... message is an RWMimePart that contains a multipart message
 
RWMimeMultipart multipart(message);
 
RWMimeContentIdHeader cid("<part004@roguewave.example>");
 
size_t partPos = multipart.findPart(cid);
 
if (partPos != RW_NPOS)
{
RWMimePart part = multipart.getPart(partPos);
// ... process the part
}
else
{
// ... the message does not contain a part with the content id
// <part004@roguewave.example>
}
Notice that the part retrieved from the multipart body is a MIME part that also requires processing. Example 31 in “Program: Processing a Multipart MIME Message” demonstrates one approach for processing multipart bodies.