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