Program: Processing a Multipart MIME Message
Example 31 uses the MIME package to process a multipart MIME message and print the component parts to standard output. The message this program contains is essentially identical to the message created in Example 29. Although we’ve kept the example simple for demonstration purposes, notice that a recursive approach to processing MIME parts works well for multipart messages. Source for this program is in MultipartMimeMessageDecomposition.cpp in the examples\mime directory.
NOTE: Sample programs are located in the examples directory created for your installation. For more information, see Installing and Building Your SourcePro Products and Building Your Applications.
Example 31 – Processing a multipart MIME message
#include <rw/mime/RWMimePart.h>
#includwe <rw/mime/RWMimeMultipart.h>
#include <rw/mime/RWMimeContentTransferEncodingHeader.h>
#include <rw/mime/RWMimeParseError.h>
#include <rw/mime/RWMimeUtils.h>
 
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
 
// This is roughly equivalent to the message generated in
// the BasicMimeMessageComposition example.
 
const RWCString messageStr(
"MIME-Version: 1.0\r\n"
"To: <developer@yourcompany.com>\r\n"
"From: <developer@roguewave.com>\r\n"
"Subject: Sample MIME Multipart Message from Rogue Wave\r\n"
"Content-Type: multipart/mixed; \r\n"
" boundary=\"_=_next_part_279429471_=_\"\r\n"
"\r\n"
"This is a MIME 1.0 compliant message.\r\n"
"\r\n"
"If you are able to see this message, your\r\n"
"user agent is not MIME compliant. You will\r\n"
"need an alternative mail viewer to read this\r\n"
"message in its entirety.\r\n"
"\r\n"
"--_=_next_part_279429471_=_\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"This is a part in a (multipart) message generated with the Rogue Wave "
"SourcePro Net product.\r\n"
"\r\n"
"All of the parts of this message are identical, however they've been "
"encoded for transport using different methods.\r\n"
"\r\n"
"--_=_next_part_279429471_=_\r\n"
"Content-Type: text/plain\r\n"
"Content-Transfer-Encoding: base64\r\n"
"\r\n"
"VGhpcyBpcyBhIHBhcnQgaW4gYSAobXVsdGlwYXJ0KSBtZXNzYWdlIGdlbmVyYXRlZCB3aXRo\r\n"
"IHRoZSBSb2d1ZSBXYXZlIFNvdXJjZVBybyBOZXQgcHJvZHVjdC4NCg0KQWxsIG9mIHRoZSBw\r\n"
"YXJ0cyBvZiB0aGlzIG1lc3NhZ2UgYXJlIGlkZW50aWNhbCwgaG93ZXZlciB0aGV5J3ZlIGJl\r\n"
"ZW4gZW5jb2RlZCBmb3IgdHJhbnNwb3J0IHVzaW5nIGRpZmZlcmVudCBtZXRob2RzLg0K\r\n"
"--_=_next_part_279429471_=_\r\n"
"Content-Type: text/plain\r\n"
"Content-Transfer-Encoding: quoted-printable\r\n"
"\r\n"
"This is a part in a (multipart) message generated with the Rogue Wave Sour=\r\n"
"cePro Net product.\r\n"
"\r\n"
"All of the parts of this message are identical, however they've been encod=\r\n"
"ed for transport using different methods.\r\n"
"\r\n"
"--_=_next_part_279429471_=_--\r\n"); //1
 
void decomposePart(const RWMimePart& part, const RWCString& indent = "");
{
cout << indent << "MIME Message Extraction" << endl << endl;
// Show header information for the part.
cout << indent << "...Headers" << endl << endl;
 
for (size_t pos = 0; pos < part.getHeaderCount(); ++pos) //3
{
RWMimeHeader header = part.getHeader(pos); //4
cout << indent << header.asString() << endl; //5
}
// Check and see if this is a multipart part, if it is, then
// we'll break it apart, decode, and show the contents of
// each part separately.
if (part.isMultipart()) //6
{
// It is a multipart, extract each part, and pass it to
// this function to be printed to the screen.
cout << indent << "EXTRACTING MULTIPART BODY" << endl;
RWMimeMultipart multipart = part; //7
 
for (size_t pos = 0; pos < multipart.getPartCount(); ++pos) //8
{
cout << endl << indent << "Extracting Part " << pos+1 << " (of "
<< multipart.getPartCount() << ")..." << endl << endl;
 
decomposePart(multipart.getPart(pos), indent+" "); //9
}
}
else
{
RWCString body = part.getBody(); //10
// Decode the body of the part (if it's encoded) if it
// isn't, then we can assume that it is already in 7-bit
// text.
size_t pos =
part.findHeader(RWMimeContentTransferEncodingHeader::Label); //11
 
if (pos != RW_NPOS)
{
// The part is encoded. Let's decode it.
try
{
RWMimeContentTransferEncodingHeader header =
part.getHeader(pos); //12
body = RWMimeUtils::decode(body, header.getEncoding()); //13
}
catch(const RWMimeParseError& error)
{
cerr << endl << indent << RWCString("[ERROR] ")+error.why() << endl;
}
}
// Convert trailing CRLF characters to local new line
// conventions (and add an indention for output purposes.)
RWCString linebreak = RWCString("\n")+indent;
body = RWMimeUtils::replaceLineDelimiter(body, linebreak); //14
// Display the body of the part.
cout << endl << indent << "...Body" << endl << endl << indent<< body
<< endl; //15
}
}
 
int main(void)
{
// Create a RWMimePart object.
RWMimePart message;
// Extract the message from the string.
message.fromString(messageStr);
decomposePart(message);
return 0;
}
//1 Creates a constant string that contains the multipart MIME message.
//2 The decomposePart() function contains all the code necessary to decompose either a simple MIME part or a multipart MIME part. For a simple MIME part, the function processes and displays the parts. For a multipart MIME part, the function extracts each part, then calls itself with the extracted subpart as the first argument.
//3 The for loop on this line increments pos to index each header in the part. To find a specific header, you could also use the findHeader() function of RWMimePart.
//4 Retrieves the header at position pos.
//5 Prints the header label and value to standard output.
//6 Checks to see if the part being processed has a multipart body. Lines through //9 process a multipart body. Line //10 and subsequent lines process a simple body.
//7 Converts the RWMimePart to an RWMimeMultipart. The assignment on this line implicitly calls the conversion constructor of RWMimeMultipart to create the multipart.
//8 Increments pos to index each part in the multipart.
//9 Calls decomposePart(), passing the MIME part at pos as the first argument. The second argument increases the output indentation for the part by 5 spaces.
//10 Retrieves the body of the part as an RWCString.
//11 Locates the Content-Transfer-Encoding header of the part.
//12 Retrieves the Content-Transfer-Encoding header.
//13 Decodes the body string, using the value of the Content-Transfer-Encoding header as the encoding style. It’s convenient to always decode() the body of any part with a Content-Transfer-Encoding header. The decode() function of RWMimeUtils returns the body string unchanged for the encoding styles 7bit, 8bit, and binary, so there’s no risk of corrupting the data.
//14 Converts the body string from canonical form to use single newlines as line separators. The example does this because the message contains only text parts. A production application would only do this conversion for body strings which have a text Content-Type.
//15 Print the body string to standard output.
This program produces the following output (note that the longer lines are wrapped here):
 
MIME Message Extraction
 
...Headers
 
MIME-Version: 1.0
To: <developer@yourcompany.com>
From: <developer@roguewave.com>
Subject: Sample MIME Multipart Message from Rogue Wave
Content-Type: multipart/mixed; boundary="_=_next_part_279429471_=_"
EXTRACTING MULTIPART BODY
 
Extracting Part 1 (of 3)...
 
MIME Message Extraction
 
...Headers
 
Content-Type: text/plain
 
...Body
 
This is a part in a (multipart) message generated with the Rogue Wave
SourcePro Net product.
All of the parts of this message are identical, however they've been
encoded for transport using different methods.
 
Extracting Part 2 (of 3)...
 
MIME Message Extraction
 
...Headers
 
Content-Type: text/plain
Content-Transfer-Encoding: base64
 
...Body
 
This is a part in a (multipart) message generated with the Rogue Wave
SourcePro Net product.
All of the parts of this message are identical, however they've been
encoded for transport using different methods.
 
Extracting Part 3 (of 3)...
 
MIME Message Extraction
 
...Headers
 
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable
 
...Body
 
This is a part in a (multipart) message generated with the Rogue Wave
SourcePro Net product.
All of the parts of this message are identical, however they've been
encoded for transport using different methods.