Extending ATL Server s SMTP Support: Sending HTML Messages


In this example, we demonstrate how you can extend the SMTP support in ATL Server to send HTML messages rather than just plain text messages.

ATL Server uses implementations of the CMimeBodyPart class to encapsulate the various parts of a MIME message. A MIME message is typically made up of several of these parts. Plain text and file attachments are good examples of MIME message parts . These two attachment types are implemented with the CMimeText and CFileAttachment classes, respectively.

A limitation of CMimeText is that it allows you to only send plain text. This, however, does provide us with a good opportunity to demonstrate how to extend ATL Server s SMTP functionality. To do so, you ll create a new class called CMimeTextEx that can support sending different types of text messages. Later on, you ll see how to use this class in a MIME message. First, you ll take a closer look at the common base class that all MIME parts, including the base class CMimeText , implement.

CMimeBodyPart is defined as shown in Listing 24-3.

Listing 24.3: Definition of CMimeBodyPart
start example
 class CMimeBodyPart  {  public:    virtual ~CMimeBodyPart() = 0 {}    // WriteData - pure virtual method to dump the data for a body part.    virtual BOOL WriteData( HANDLE  hFile,    LPOVERLAPPED  pOverlapped,    LPCSTR  szBoundary,    DWORD   dwFlags = 0) = 0;    // GetContentType - pure virtual method to get the content of a body part    virtual LPCSTR GetContentType() = 0;   // GetCharset - virtual method to get the character set of a body part   // (defaults to ATLSMTP_DEFAULT_CSET).    virtual LPCSTR GetCharset()    {      return ATLSMTP_DEFAULT_CSET;    }    virtual CMimeBodyPart* Copy() = 0;  protected:    // MakeMimeHeader - pure virtual method to create a MIME header for a    // body part.    virtual BOOL MakeMimeHeader(CStringA&  header,     LPCSTR  szBoundary) = 0;  }; 
end example
 

You ll use CMimeText as your base class and just inherit most of the implementation from this class. Your class will only differ in the value returned by GetContentType and the way you build the header in MakeMimeHeader .

Implementing your CMimeTextEx class is actually quite simple. Listing 24-4 presents the class definition.

Listing 24.4: Definition of CMimeTextEx
start example
 1 class CMimeTextEx : public CMimeText  2 {  3 protected:  4    CStringA m_strContentType;  5  6 public:  7     CMimeTextEx(LPCSTR contentType)  8   {  9     m_strContentType = contentType;  10  }  11  12  virtual inline LPCSTR GetContentType() throw()  13  {  14    return m_strContentType;  15  }  16  17  virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )  18  {  19    CAutoPtr<CMimeTextEx> pNewText;  20    ATLTRY(pNewText.Attach(new CMimeTextEx(this->m_strContentType)));  21    if (pNewText)  22      *pNewText = *this;  23  24    return pNewText.Detach();  25  }  26  27    const CMimeTextEx& operator=(const CMimeTextEx& that) throw( ... )  28    {  29       if (this != &that)  30       {  31         *(CMimeText*)this = *(CMimeText*)&that;  32          m_strContentType  = that.m_strContentType;  33        }  34  35        return *this;  36    }  37  38    // Make the MIME header  39    virtual inline BOOL MakeMimeHeader(CStringA& header,  40                                       LPCSTR    szBoundary) throw()  41    {  42      char szBegin[ATL_MIME_BOUNDARYLEN+8];  43      if (*szBoundary)  44      {  45        // this is not the only body part  45        memcpy(szBegin, "\r\n\r\n--", 6);  46        memcpy(szBegin+6, szBoundary, ATL_MIME_BOUNDARYLEN);  47        *(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = ' 
 1 class CMimeTextEx : public CMimeText 2 { 3 protected: 4 CStringA m_strContentType; 5 6 public: 7 CMimeTextEx(LPCSTR contentType) 8 { 9 m_strContentType = contentType; 10 } 11 12 virtual inline LPCSTR GetContentType() throw() 13 { 14 return m_strContentType; 15 } 16 17 virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... ) 18 { 19 CAutoPtr<CMimeTextEx> pNewText; 20 ATLTRY(pNewText.Attach(new CMimeTextEx(this->m_strContentType))); 21 if (pNewText) 22 *pNewText = *this; 23 24 return pNewText.Detach(); 25 } 26 27 const CMimeTextEx& operator=(const CMimeTextEx& that) throw( ... ) 28 { 29 if (this != &that) 30 { 31 *(CMimeText*)this = *(CMimeText*)&that; 32 m_strContentType = that.m_strContentType; 33 } 34 35 return *this; 36 } 37 38 // Make the MIME header 39 virtual inline BOOL MakeMimeHeader(CStringA& header, 40 LPCSTR szBoundary) throw() 41 { 42 char szBegin[ATL_MIME_BOUNDARYLEN+8]; 43 if (*szBoundary) 44 { 45 // this is not the only body part 45 memcpy(szBegin, "\r\n\r\n--", 6); 46 memcpy(szBegin+6, szBoundary, ATL_MIME_BOUNDARYLEN); 47 *(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = '\0'; 48 } 49 else 50 { 51 // this is the only body part, so output the full MIME header 52 memcpy (szBegin, "MIME-Version: 1.0", sizeof("MIME-Version: 1.0")); 53 } 54 55 _ATLTRY 56 { 57 header.Format("%s\r\nContent-Type:\ 58 %s;\r\n\tcharset=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", \ 59 szBegin, m_strContentType, m_szCharset); 60 return TRUE; 61 } 62 _ATLCATCHALL() 63 { 64 return FALSE; 65 } 66 } 67 }; 
'; 48 } 49 else 50 { 51 // this is the only body part, so output the full MIME header 52 memcpy(szBegin, "MIME-Version: 1.0", sizeof("MIME-Version: 1.0")); 53 } 54 55 _ATLTRY 56 { 57 header.Format("%s\r\nContent-Type:\ 58 %s;\r\n\tcharset=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", \ 59 szBegin, m_strContentType, m_szCharset); 60 return TRUE; 61 } 62 _ATLCATCHALL() 63 { 64 return FALSE; 65 } 66 } 67 };
end example
 

The implementation of this class is quite simple. The constructor takes the content type of the text part and uses it to set the content type of the header file when the message is being sent. You can use the Initialize method inherited from CMimeText to set the text of the message that you want to send.

As you saw earlier in this chapter, instances of CMimeBodyPart are added to a CMimeMessage object, which you can send using the CSMTPConnection class. CMimeMessage is implemented to accept only a finite set of MIME parts. This limitation gives us another opportunity to extend ATL Server s functionality. Listing 24-5 shows a class that derives from CMimeMessage that allows you to insert your HTML text part into the MIME message.

Listing 24.5: Extending CMimeMessageEx to Allow HTML
start example
 1  class CMimeMessageEx : public CMimeMessage  2  {  3   public:  4   // Add some custom part to the message at position nPos in the body parts list  5   // pPart - the part  6   // bCopy - copy the part or add the pointer itself  7   // nPos  - the position in the message at which to insert the text (optional)  8   inline BOOL AddCustomBodyPart(CMimeBodyPart *pPart,  9                                 BOOL          bCopy = TRUE,  10                                int           nPos  = 1) throw()  11  {  12    BOOL bRet = TRUE;  13  14    if( !pPart )  15      return FALSE;  16  17    if (nPos < 1)  18    {  19      nPos = 1;  20    }  21  22    _ATLTRY  23    {  24      CAutoPtr<CMimeBodyPart> spNewComponent;  25      if( bCopy )  26        spNewComponent.Attach( pPart->Copy() );  27      else  28        spNewComponent.Attach( pPart );  29  30      if( !spNewComponent )  31      {  32        return FALSE;  33      }  34  35      POSITION currPos = m_BodyParts.FindIndex(nPos-1);  36  37      if (!currPos)  38      {  39      if (!m_BodyParts.AddTail(spNewComponent))  40          bRet = FALSE;  41      }  42      else  43      {  44       if (!m_BodyParts.InsertBefore(currPos, spNewComponent))  45         bRet = FALSE;  46      }  47      }  48      _ATLCATCHALL()  49      {  50        bRet = FALSE;  51      }  52  53      return bRet;  54   }  55 }; 
end example
 

Your extension to CMimeMessage adds a new method that allows you to add a custom CMimeBody part to the message. You ll use this method to insert instances of your CMimeTextEx class into your message.

Listing 24-6 puts your two extension classes together into a full example.

Listing 24.6: The Complete Example
start example
 1 CMimeMessageEx htmlMsg;  2  3 htmlMsg.SetSender("bogdanc@microsoft.com");  4 htmlMsg.SetSenderName("Bogdan Crivat");  5 htmlMsg.AddRecipient("bogdanc@microsoft.com",  "Bogdan Crivat");  6 htmlMsg.SetPriority(ATL_MIME_NORMAL_PRIORITY);  7 htmlMsg.SetSubject("Hello World!");  8  9 char *pText = "<html>  10               <body>\  11                 <h1>Hello!</h1>\  12                 <br><br>\  13                 <p>Test</p>\  14                 <img href='http://msimg.com/m/r/logo/msft/logo.gif'>  15                    Hello World!\  16                 </img>\  17               </body>\  18               </html>";  19  20 CMimeTextEx htmlText("text/html");  21 htmlText.Initialize(pText, strlen(pText), NULL, 0);  22  23 htmlMsg.AddCustomBodyPart(&htmlText);  24 smtpConnection.SendMessage(htmlMsg); 
end example
 

Let s have a look at Listing 24-6 line-by-line :

  • Lines 1 through 7: You initialize CMimeTextEx in the same way as you would CMimeText .

  • Lines 9 through 18: This is contrived, but you ll statically declare some HTML to send in your message.

  • Lines 20 and 21: You ve defined the CMimeTextEx constructor to take a content type as a parameter. Because you re sending HTML, you ll use text/html as your content type. The Initialize method allows you to specify the text that you want to send.

  • Lines 23 and 24: Calling AddCustomBodyPart adds your HTML to the message. You can call this method repeatedly to add multiple text parts to the message. As usual, SendMessage will send your MIME message.

As you can see, extending ATL Server to support sending HTML messages over SMTP isn t a difficult task. In the next section you ll look at a few ways of minimizing the performance impact of sending SMTP messages in your application.




ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net