Click here to Skip to main content
11,486,649 members (74,497 online)
Click here to Skip to main content

MIME Message Composer/Analyser

, 18 Apr 2004 279.3K 4K 94
Rate this:
Please Sign up or sign in to vote.
A C++ implementation of MIME

Introduction

This implementation is designed to provide an easy-to-use, well-encapsulated component for the development of MIME-conformant applications, with less MIME knowledge. However, because MIME is open and extensible, to provide an easy way to support any customized extension is also important. Here are some features of this implementation:

  • Meet the requirements of MIME-conformance.
  • Written in ANSI C++, easy to migrate to UNIX.
  • Full control on all (standard and extended) header fields and body parts. Provide wrappers on raw data to interpret the standard fields.
  • Provide standard encoding mechanism (Quoted-Printable and Base64) and support all the standard media types.
  • Seamless interface to support extended media types and encoding mechanisms.
  • Provide standard header encoding/decoding for non-ASCII text, and folding/unfolding for long line text.

Using the code

Although there're more than 10 classes in the source files, the only 3 or 4 classes you need to work with, include:

CMimeEnvironment The global environment to manage encoding mechanisms and options.
CMimeField Represents a field of a MIME body part header. Provides methods to manage the name, value, parameters and charset of the field.
CMimeBody Represents a body part of a MIME message. It's derived from CMimeHeader class, provides methods to manage the content and header fields of the body part.
CMimeMessage Represents a MIME message. It's derived from CMimeBody class, provides extra functionality to access RFC822 message header fields (e.g. From, To, Date, Subject).

Compose a message

This example demonstrates how to create a complex multipart message with a text part, a file attachment, an attached message, and an embedded multipart entity.

    CMimeMessage mail;

    // Initialize message header
    mail.SetDate(); // set 'Date' field to the current time
    mail.Setversion();
    mail.SetFrom("sender@local.com");
    mail.SetTo("recipient1@server1.com, 
      Nick Name <recipient2@server1.com>, 
      \"Nick Name\" <recipient3@server3.com>");
    mail.SetCc("recipient4@server4.com");
    mail.SetSubject("Test message");
    mail.SetFieldValue("X-Priority", "3 (Normal)"); // extended field
    mail.SetFieldValue("X-My-Field", "My value");   // user-defined field

    // Initialize body part header
    mail.SetContentType("multipart/mixed");
    // generate a boundary delimeter automatically
    // if the parameter is NULL
    mail.SetBoundary(NULL);

    // Add a text body part
    // Content-Type is not specified, so the default
    // value "text/plain" is implicitly used
    // Content-Transfer-Encoding is not specified
    // so the default value "7bit" is implicitly used
    CMimeBody* pBp = mail.CreatePart();
    pBp->SetText("Hi, there");  // set the content of the body part

    // Add a file attachment body part
    pBp = mail.CreatePart();
    pBp->SetContentDescription("enclosed photo");
    pBp->SetTransferEncoding("base64");
    // if Content-Type is not specified, it'll be
    // set to "image/jpeg" by ReadFromFile()
    pBP->ReadFromFile("d:\\myphoto.jpg"); 

    // Generate a simple message
    CMimeMessage mail2;
    mail2.SetFrom("abc@abc.com");
    mail2.SetTo("abc@abc.com");
    mail2.SetSubject("This is an attached message");
    mail2.SetText("Content of attached message.\r\n");

    // Attach the message
    pBp = mail.CreatePart();
    pBp->SetContentDescription("enclosed message");
    pBp->SetTransferEncoding("7bit");
    // if Content-Type is not specified, it'll be
    // set to "message/rfc822" by SetMessage()
    pBp->SetMessage(&mail2); 

    // Add an embeded multipart
    pBp = mail.CreatePart();
    pBp->SetContentType("multipart/alternative");
    pBp->SetBoundary("embeded_multipart_boundary");
    CMimeBody *pBpChild = pBp->CreatePart();
    pBpChild->SetText("Content of Part 1\r\n");
    pBpChild = pBp->CreatePart();
    pBpChild->SetText("Content of Part 2\r\n");

    // Store the message to buffer    
    // fold the long lines in the headers
    CMimeEnvironment::SetAutoFolding(true); 
    int nSize = mail.GetLength();
    char* pBuff = new char[nSize];
    nSize = mail.Store(pBuff, nSize);
    ...
    delete pBuff;

Analyze a message

To analyze a message is the same and simple: call Load() to load the message object from buffer, then call FindFirstPart()/FindNextPart() functions to iterate its child body parts. But the fact that a multipart entity could have embedded multipart entities brings some complexities. We have to call FindFirstPart()/FindNextPart() on each child body part recursively to iterate all the descendant parts. So CMimeBody class provides an alternative way, to retrieve a list of all the descendant body parts, by calling GetBodyPartList().

    CMimeMessage mail;
    int nLoadedSize = mail.Load(pBuff, nDataSize);

    // Analyze the message header
    const char* pszField;
    pszField = mail.GetSubject();
    if (pszField != NULL)
        printf("Subject: %s\r\n", pszField);
    pszField = mail.GetFrom();
    if (pszField != NULL)
        printf("From: %s\r\n", pszField);
    pszField = mail.GetFieldValue("X-Priority");
    if (pszField != NULL)
        printf("X-Priority: %s\r\n", pszField);

    // Iterate all the descendant body parts
    CMimeBody::CBodyList bodies;
    int nCount = mail.GetBodyPartList(bodies);
    CMimeBody::CBodyList::const_iterator it;
    for (it=bodies.begin(); it!=bodies.end(); it++)
    {
        CMimeBody* pBP = *it;

        // Iterate all the header fields of this body part:
        CMimeHeader::CFieldList& fds = pBP->Fields();
        CMimeHeader::CFieldList::const_iterator itfd;
        for (itfd=fds.begin(); itfd!=fds.end(); itfd++)
        {
            const CMimeField& fd = *itfd;
            printf("%s: %s\r\n", fd.GetName(), fd.GetValue());
        }

        if (pBP->IsText())
        {
            string strText;
            pBP->GetText(strText);
            printf("Content: %s\r\n", strText.c_str());
        }
        else if (pBP->IsAttachment())
        {
            string strName = pBP->GetName();
            printf("File name: %s\r\n", strName.c_str());
            printf("File size: %d\r\n", pBP->GetContentLength());
            strName = "d:\\download\\" + strName;
            pBP->WriteToFile(strName.c_str());
        }
    }

Header encoding and folding

The header encoding is to encode any non-ASCII text data in message headers to 'encoded-word'. For example, a field like "Subject: Bonne Année" will be encoded to "Subject: =?iso-8859-1?Q?Bonne=20Ann=E9e?=" and "From: René <sender@local.com>" will be encoded to "From: =?iso-8859-1?Q?Ren=E9?= <sender@local.com>".

During the period of storing (invoking CMimeBody::Store() function), the header encoding is performed automatically if a field contains non-ASCII text, and the 'charset' of this field has been specified. The 'charset' of a field can be specified by calling CMimeBody::SetFieldCharset(), or by passing a charset string to functions like CMimeBody::SetFieldValue(), CMimeMessage::SetSubject(), CMimeMessage::SetFrom(), etc.

Instead of specifying charset for each field repeatedly, you can specify the 'global charset' by calling CMimeEnvironment::SetGlobalCharset(). When the header encoding is being performed, the global charset is used if the field's charset is empty. If the global charset is empty too, the header encoding doesn't occur. So you can leave both charsets empty if you don't need the header encoding.

    mail.SetSubject("Bonne Année", "iso-8859-1");
    or:
    mail.SetFieldValue("Content-Description", "carte bonne année");
    CMimeEnvironment::SetGlobalCharset("iso-8859-1");

Furthermore, header folding is performed automatically too. In general, any header lines longer than 76 bytes will be folded by inserting "\r\n\t" at the position of a space (SPACE or TAB). For address fields (such as From, To, CC, etc.), the folding occurs between addresses, after the separating comma. However, the header folding can be turned on/off by calling CMimeEnvironment::SetAutoFolding() function with bAutoFolding parameter set to true/false.

Support of custom encoding

To support any Content-Transfer-Encoding mechanisms other than Quoted-Printable and Base64, derive a class from CMimeCodeBase, then register this class by invoking REGISTER_MIMECODER().

class CMyCoder : public CMimeCodeBase
{
    DECLARE_MIMECODER(CMyCoder)
    ...
protected:
    virtual int GetEncodeLength() const;
    virtual int GetDecodeLength() const;
    virtual int Encode(unsigned char* pbOutput, int nMaxSize) const;
    virtual int Decode(unsigned char* pbOutput, int nMaxSize);
};

    CMimeMessage mail;
    ...
    REGISTER_MIMECODER("my_coding_name", CMyCoder);
    mail.Store(pbBuff, nSize);
    mail.Load(pbBuff, nSize);

After REGISTER_MIMECODER() being called, for any body parts with Content-Transfer-Encoding specified to my_coding_name, CMyCoder class will be used to encode/decode the content by Store()/Load() function.

Support of extended media type

The use of extended media types is rare but still supported. Similar to support for custom encoding, derive a class from CMimeBody, then register the class by invoking REGISTER_MEDIATYPE().

class CMyBodyPart : public CMimeBody
{
    DECLARE_MEDIATYPE(CMyBodyPart)
    ...
public:
    virtual void Clear();
    virtual int GetLength() const;
    virtual int Store(char* pszData, int nMaxSize) const;
    virtual int Load(const char* pszData, int nDataSize);
};

    CMimeMessage mail;
    ...
    REGISTER_MEDIATYPE("my_media_type", CMyBodyPart);
    CMyBodyPart* pBP = (CMyBodyPart*) mail.CreatePart("my_media_type");
    ...
    mail.Store(pbBuff, nSize);
    mail.Load(pbBuff, nSize);

After REGISTER_MEDIATYPE() being called, for any body parts with Content-Type specified to my_media_type, CMyBodyPart object will be created to represent this body part.

History

  • 17 Feb 2004 - updated download
  • 16 Apr 2004 - updated download

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

nickadams
Web Developer
Canada Canada
Nick Adams is one of my favorite figures in Hemingway's stories. I use it because Jeff Lee has been occupied on Codeproject.

Comments and Discussions

 
BugThanks, but, i got some problems Pin
Xie Jinghui2-Aug-12 23:57
memberXie Jinghui2-Aug-12 23:57 
QuestionSubject decode Pin
piccoola30-Jul-12 2:12
memberpiccoola30-Jul-12 2:12 
GeneralMy vote of 5 Pin
ytfrdfiw16-Jul-12 16:29
memberytfrdfiw16-Jul-12 16:29 
GeneralMy vote of 5 Pin
Xie Jinghui23-Aug-11 0:15
memberXie Jinghui23-Aug-11 0:15 
GeneralMIME Message Composer/Analyser Pin
aaa945260415-Feb-11 6:42
memberaaa945260415-Feb-11 6:42 
Generaljust a dumm question Pin
kampi4-Jan-11 4:08
memberkampi4-Jan-11 4:08 
Questionhow to use utf-8 Pin
die_for_rock_vn4-Dec-10 9:33
memberdie_for_rock_vn4-Dec-10 9:33 
AnswerRe: how to use utf-8 Pin
die_for_rock_vn7-Dec-10 22:19
memberdie_for_rock_vn7-Dec-10 22:19 
Generalit's good Pin
Member 41124855-Aug-10 23:12
memberMember 41124855-Aug-10 23:12 
GeneralLATEST CODE Pin
richardwhitehead23-Sep-09 1:12
memberrichardwhitehead23-Sep-09 1:12 
GeneralRe: LATEST CODE Pin
richardwhitehead8-Mar-10 22:36
memberrichardwhitehead8-Mar-10 22:36 
GeneralRe: LATEST CODE Pin
Jose Schmidt12-Oct-10 6:56
memberJose Schmidt12-Oct-10 6:56 
GeneralRe: LATEST CODE Pin
richardwhitehead12-Oct-10 10:44
memberrichardwhitehead12-Oct-10 10:44 
GeneralRe: LATEST CODE Pin
Jose Schmidt12-Oct-10 12:04
memberJose Schmidt12-Oct-10 12:04 
GeneralVery nice work! Pin
medium21-Sep-09 11:31
membermedium21-Sep-09 11:31 
GeneralLicense Pin
Member 384073731-Aug-08 5:18
memberMember 384073731-Aug-08 5:18 
GeneralUsing string function strstr() may be lead to bug Pin
zhtsh22-Jul-08 21:32
memberzhtsh22-Jul-08 21:32 
QuestionHow to SetFileName Pin
Pelle191110-Jul-08 10:38
memberPelle191110-Jul-08 10:38 
GeneralEncoded or not encoded, that is the question... Pin
Pelle19111-Jul-08 1:32
memberPelle19111-Jul-08 1:32 
Hi all,

I stumbled upon these classes looking for a quick solution to decode MIME-Mail messages (which I have an urgent need for).
In my test-application, things seemed to work great.
In my real application, things seem to work great, too, BUT different...
AND I can't find a way to figure out the difference.

Now here's it:
I "Load" the message, then I look for an attachment.
The attachment is then examined for Encoding. So far, all I care about is BASE64.
With both examples that will be presented later, encoding is reported as BASE64.

Then I do "GetContent" and try to Base64-decode the contents.
This is where the trouble starts.
In my test-application, the content - or more precisely the return from "GetContent" is actually BASE64 encoded. In my real application, I get the decoded data.
Now, since I don't know if the data is already decoded or not, this makes me stumble.

Anyone ever had this problem?

Any hints greatly appreciated!

Regards,
Pelle.
GeneralBig thanks Pin
Jerry Evans13-Mar-08 12:20
memberJerry Evans13-Mar-08 12:20 
GeneralRe: Big thanks Pin
awilbourn22-Mar-08 9:57
memberawilbourn22-Mar-08 9:57 
GeneralRe: Big thanks Pin
Jerry Evans22-Mar-08 10:38
memberJerry Evans22-Mar-08 10:38 
GeneralRe: Big thanks Pin
awilbourn22-Mar-08 14:11
memberawilbourn22-Mar-08 14:11 
GeneralRe: Big thanks Pin
Jerry Evans22-Mar-08 15:19
memberJerry Evans22-Mar-08 15:19 
GeneralRe: Big thanks Pin
Omar.Pessoa24-Sep-08 9:44
memberOmar.Pessoa24-Sep-08 9:44 
GeneralDouble dots in begin of line for plain/text attachments [modified] Pin
CoderCZ17-Oct-07 3:39
memberCoderCZ17-Oct-07 3:39 
QuestionHow to accomodate emails written in chinese language? Pin
AnindyaM29-Aug-07 18:41
memberAnindyaM29-Aug-07 18:41 
AnswerRe: How to accomodate emails written in chinese language? Pin
kanitamildasan25-Sep-07 19:29
memberkanitamildasan25-Sep-07 19:29 
QuestionHow to get the "Received" Field Pin
annewang66315-Aug-07 22:24
memberannewang66315-Aug-07 22:24 
Generaliso-8859-2 in subject Pin
benicky8-Jun-07 8:20
memberbenicky8-Jun-07 8:20 
GeneralRe: iso-8859-2 in subject [modified] Pin
CoderCZ9-Jul-07 0:37
memberCoderCZ9-Jul-07 0:37 
QuestionHow do I handle message/partial content types Pin
peterdelchiappo13-Oct-06 0:03
memberpeterdelchiappo13-Oct-06 0:03 
QuestionLicense terms Pin
xquiky1-Oct-06 13:59
memberxquiky1-Oct-06 13:59 
GeneralGood work Pin
waldermort31-Aug-06 21:05
memberwaldermort31-Aug-06 21:05 
GeneralLoad function is too slow Pin
KFC12330-Jul-06 20:26
memberKFC12330-Jul-06 20:26 
GeneralCMimeBody::ReadFromFile losing last block on large file Pin
rinzai7-Jun-06 6:03
memberrinzai7-Jun-06 6:03 
GeneralRe: CMimeBody::ReadFromFile losing last block on large file Pin
rinzai7-Jun-06 6:10
memberrinzai7-Jun-06 6:10 
GeneralRe: CMimeBody::ReadFromFile losing last block on large file Pin
rinzai7-Jun-06 6:54
memberrinzai7-Jun-06 6:54 
GeneralRe: CMimeBody::ReadFromFile losing last block on large file Pin
gu7-Sep-06 3:58
membergu7-Sep-06 3:58 
GeneralIt need 2 times of program memory for CMimeBody::Load Pin
Eaway11-May-06 0:27
memberEaway11-May-06 0:27 
QuestionCMimeBody::IsAttachment()? Pin
Eaway19-Apr-06 19:18
memberEaway19-Apr-06 19:18 
QuestionVedio? Pin
thenickname10-Jan-06 10:22
memberthenickname10-Jan-06 10:22 
QuestionReadFromFile problem with large files Pin
Robin Minto6-Jan-06 13:44
memberRobin Minto6-Jan-06 13:44 
Generalvery useful Pin
pglidden31-Dec-05 8:25
memberpglidden31-Dec-05 8:25 
QuestionHow to decode japanese characters Pin
juncki15-Dec-05 10:24
memberjuncki15-Dec-05 10:24 
AnswerRe: How to decode japanese characters Pin
juncki15-Dec-05 14:47
memberjuncki15-Dec-05 14:47 
QuestionRe: How to decode japanese characters Pin
Abhinav agrawal14-May-06 5:00
memberAbhinav agrawal14-May-06 5:00 
QuestionHow to use the MIME message, i.e how to send it out? Pin
kezhu8-Dec-05 15:53
memberkezhu8-Dec-05 15:53 
Questionnecessary sample for parsing message ! Pin
jsli@ukr.net28-Nov-05 23:44
memberjsli@ukr.net28-Nov-05 23:44 
Question8bit and CP_UTF8 Pin
dennisV10-Nov-05 12:20
memberdennisV10-Nov-05 12:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150520.1 | Last Updated 19 Apr 2004
Article Copyright 2004 by nickadams
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid