MIME++ "How To"
Note: This is a work in progress, which is being continually updated. The latest version is available on the web:
If you have any suggestions for tasks that should be added to the How-To document, please send your suggestions to Doug Sauder <doug@hunnysoft.com>.
Create a simple message
Creating a new message is very simple. When you create a new message, though, you should be sure to create all the standard header fields, which include From, To, Subject, and Date. The following code shows how easy it is to do this:
// Create a new DwMessage object DwMessage msg; // Set the From, To, Subject, and Date header fields msg.Headers().From().FromString("john@example.com"); msg.Headers().To().FromString("mary@example.com"); msg.Headers().Subject().FromString("Call me"); msg.Headers().Date().FromCalendarTime(time(0)); // Set the message body msg.Body().FromString("Call me when you can.\n"); // Finally, assemble the message into a string msg.Assemble(); cout << msg.AsString();
Of course, there are other header fields that you may consider setting. Some of these include Message-Id, Content-Type, Cc, Bcc, Reply-To, and possibly others. Also, you may want to set text into these header fields in a charset other than ASCII. To do that, you may find it easier to use the EmailMessage class found in the examples/email directory. The following code shows how to use the EmailMessage class:
// Create a new EmailMessage object EmailMessage msg; // Set the From header field EmailAddress fromAddr("mueller@example.com", "J\374rgen M\374ller", "ISO-8859-1"); msg.Originator(fromAddr); // Set the To header field EmailAddress toAddr1("fuchs@example.com", "Andreas Fuchs", "ISO-8859-1"); msg.ToRecipients().Add(toAddr1); EmailAddress toAddr2("braun@example.com", "Martin Braun", "ISO-8859-1"); msg.ToRecipients().Add(toAddr2); // Set the Cc header field msg.CcRecipients().Add( EmailAddress("renate@example.com", "Renate M\374ller", "ISO-8859-1")); // Set the Bcc header field msg.BccRecipients().Add( EmailAddress("mueller@example.com", "J\374rgen M\374ller", "ISO-8859-1")); // Set the Subject header field const char *subj = "Spielen wir heute nachmittag Fu\337ball?"; msg.Subject(EmailText(subj, "ISO-8859-1")); // Set the message body const char *memo = "Spielen wir heute nachmittag Fu\337ball? " "Was meint ihr dazu?\n"; msg.MemoText(EmailText(memo, "ISO-8859-1")); // Serialize the message to a string DwString msgString; msg.SerializeTo(msgString); // See how it looks cout << msgString;
Parse a simple message
This example shows how to parse a simple message to get the basic header information, such as the originator, the recipients, the subject, the date, and the message body.
// Create a new DwMessage object DwMessage msg; // Set string form of the message DwString msgStr = ... // Somehow set the string's value (not shown) msg.FromString(msgStr); // Parse the message msg.Parse(); // Show the originator, recipients, etc cout << msg.Headers().From().AsString() << endl; cout << msg.Headers().To().AsString() << endl; cout << msg.Headers().Subject().AsString() << endl; cout << msg.Headers().Date().AsString() << endl; // Show the message body cout << msg.Body().AsString() << endl;
Send a message using SMTP
This example shows how to send a message using the DwSmtpClient class. It shows how to handle errors in the protocol, which are caused by a problem at the SMTP server, and not a program error. The function log_error is your own error handler function.
int SendMail( const char* server, const char* from, const char* to, const char* message) { // Create an SMTP client. This constructor opens a connection to TCP // port 25, which is the well-known port for SMTP, and which is also // the default argument for this constructor. (Note: There is work // underway to standardize on port 557 for message submission using // SMTP.) DwSmtpClient smtp; smtp.Open(server, 25); // Check the server's greeting. The response code for success is 250. if ((smtp.ReplyCode()/100%10) == 2) { // Send HELO command. The response code for succes is 250. smtp.Helo(); if ((smtp.ReplyCode()/100%10) == 2) { // Send MAIL command smtp.Mail(from); if ((smtp.ReplyCode()/100%10) == 2) { // Send RCPT command smtp.Rcpt(to); if ((smtp.ReplyCode()/100%10) == 2) { // Send DATA command smtp.Data(); if ((smtp.ReplyCode()/100%10) == 3) { // Send the message int messageLen = strlen(message); smtp.SendData(message, messageLen); if ((smtp.ReplyCode()/100%10) == 2) { // Send QUIT command smtp.Quit(); // Close the connection. (This is optional, since the // destructor will close the connection if it is still // open.) smtp.Close(); // Return here on success return 0; } } } } } // We arrive here if there was an error. Log the error. (log_error() // is your own function.) log_error(smtp.ReplyCode(), smtp.Response().c_str()); // Send QUIT command to terminate the connection nicely. smtp.Quit(); // Close the connection. (This is optional, since the destructor will // close the connection if it is still open.) smtp.Close(); } else { log_error(smtp.ReplyCode(), smtp.Response().c_str()); } // Return here on failure return -1; }
Search All the Message Headers
In many situations, you can just use the shortcut functions in DwHeaders to access the most common header fields, such as the Subject field, the Date field, or various others. However, there are times when you may want to access all the header fields. It's reasonable that you might want to do this, because some header fields may be present more than once in the headers. (RFC 822 allowed this for the To field, for example, although the most popular implementations generate only a single To field, and the latest version of the standard, RFC 2822, allows only a single To field.) When a field is present more than once, the shortcut function in DwHeaders will only access the first field present. You can access all the header fields by avoiding the shortcut functions in DwHeaders. The following code shows how to display all the headers.
void PrintAllHeaderFields(const DwHeaders& headers) { int numFields = headers.NumFields; for (int i=0; i < numFields; ++i) { const DwField& field = headers.FieldAt(i); cout << field.FieldNameStr() << ": " << field.FieldBody().AsString() << endl; } }
If you want to process the header fields in some way, you may want to know what kind of field body is attached to a field. That is, you may want to know what subclass of DwFieldBody is referenced by the DwField object. The following code shows the simplest way to process the different field bodies differently.
void ProcessHeaderFields(const DwHeaders& headers) { int numFields = headers.NumFields; for (int i=0; i < numFields; ++i) { const DwField& field = headers.FieldAt(i); int classId = field.ClassId(); switch (classId) { case DwMessageComponent::kCidText: // ... break; case DwMessageComponent::kCidAddressList: // ... break; case DwMessageComponent::kCidMailboxList: // ... break; case DwMessageComponent::kCidDateTime: // ... break; // etc, ... } } }
Get the Date and Time a Message Was Received
The Date header field in a message provides the date and time when a message was written. There are times when you may want to know the time when a message was received. You can do this by looking at the Received header fields. Whenever the message passes through an SMTP server, the server must add a new Received header field to the beginning of the message. In this way, you can look at the record of what SMTP servers have processed the message since it was submitted for delivery. Each Received header field also contains a time stamp, which is how you can check for when a message was submitted and when it was received. The following code example shows how you can iterate over the Received header fields and extract the time stamp from each one. Because new Received header fields are always added at the beginning of the message, they are ordered with the most recent time stamp first.
void PrintTimeStamps(const DwHeaders& headers) { int numFields = headers.NumFields(); for (int i=0; i < numFields; ++i) { const DwField& field = headers.FieldAt(i); if (field.ClassId() == DwMessageComponent::kCidStamp) { const DwStamp& stamp = (const DwStamp&) field; cout << stamp.Date().AsString() << endl; } } }
Sending a Text Message with Unicode Characters
Windows developer, especially, may be interested in sending Unicode characters, since Unicode is native the Windows NT and Windows 2000. This example shows how to do that.
We make this really simple. The text we want to send is "Hi, Mom! :-)", with the text smiley turned into the unicode character "Hi, Mom! \u263A", which contains the unicode character with code point U+263A (WHITE SMILING FACE).
First, suppose we use UTF-16. The following code shows how to send the message using this 16-bit encoding. The code assumes that wchar_t is a 16-bit character type. While this is true on Windows, it is not true on many other operating systems (where it's a 32-bit character type). If you are developing for a system with 32-bit wchar_t, then you will have to convert to 16-bit characters somehow.
// This code assumes that wchar_t is a 16-bit char type. // This assumption is probably true only for Microsoft Windows. On Linux // and most Unix systems, wchar_t is a 32-bit char, and this code must be // modified. DwMessage msg; // Set the header fields: To, From, Subject, etc // [code not shown] // Set the message body string. Note that DwString handles any kind of // content, even binary content. wchar_t* s = L"Hi, Mom! \x263A"; size_t sLen = wcslen(s); DwString bodyStr((const char*) s, 2*sLen); // Encode using base64 DwEncodeBase64(bodyStr, bodyStr); // Set the content-type for the message // For Intel processors, use UTF-16LE (little endian) // For Sparc processors, use UTF-16BE (big endian) DwString contentType = "text/plain; charset=UTF-16LE"; msg.Headers().ContentType().FromString(contentType); // Set the content-transfer-encoding msg.Headers().ContentTransferEncoding().FromString("base64"); // Set this text into the message body msg.Body().FromString(bodyStr); // Finally, see how it looks msg.Assemble(); cout << msg.AsString() << endl;
The output I get from running this code looks like this:
Content-Type: text/plain; charset=UTF-16LE Content-Transfer-Encoding: base64 SABpACwAIABNAG8AbQAhACAAOiY=
An alternative way to send Unicode characters is to use the UTF-8 encoding. One nice feature of UTF-8, is that there are no issues associated with byte order (little endian versus big endian). The following code shows how to send the same message with Unicode characters in the UTF-8 encoding.
DwMessage msg; // Set the header fields: To, From, Subject, etc // [code not shown] // Set the message body string char* s = "Hi, Mom! \xe2\x98\xba"; DwString bodyStr = s; // Encode using quoted-printable (alternatively, base64 could be used) DwEncodeQuotedPrintable(bodyStr, bodyStr); // Set the content-type for the message DwString contentType = "text/plain; charset=UTF-8"; msg.Headers().ContentType().FromString(contentType); // Set the content-transfer-encoding msg.Headers().ContentTransferEncoding().FromString("quoted-printable"); // Set this text into the message body msg.Body().FromString(bodyStr); // Finally, see how it looks msg.Assemble(); cout << msg.AsString() << endl;
The output from running this code exactly as shown looks like this:
Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi, Mom! =E2=98=BA