![]() |
General Programming »
Internet / Network »
Email & SMTP
Intermediate
MIME Message Composer/AnalyserBy nickadamsA C++ implementation of MIME |
VC6, VC7, VC7.1Win2K, WinXP, Win2003, Visual Studio, MFC, STL, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
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:
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). |
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;
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());
}
}
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.
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.
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.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 18 Apr 2004 Editor: Nishant Sivakumar |
Copyright 2004 by nickadams Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |