ATL SMTP Classes

Environment: Visual C++ 6.0, ATL 3.0, runs in Win9X/NT/2K

Overview

This is yet another set of lightweight, yet full-featured non-MFC SMTP classes to enable C++ programs to send outgoing mail.

Features

  • Optional SMTP authentication (username/password)
  • Supports multiple recipients using CC and BCC fields
  • Supports multipart mixed, related and alternative MIME types
  • Supports any number of file attachments and alternative message bodies
  • Transfer-encoding using 7bit, 8bit, quoted-printable and base64.
  • Can optionally determine the transfer encoding based on content.
  • Looks up content-type from the registry. When not found, it uses application/octet-stream type.
  • CSmtp is overridable and has several event mechanisms to notify the parent of progress during transactions with the server.
  • UNICODE supported.

The source code is heavily documented, so I will concentrate on the basics of implementing SMTP support in your applications.

Setup

Setup is pretty straightforward.  Just insert the smtp.cpp, base64.cpp, smtp.h and base64.h files into a new ATL project or into your existing ATL project. A very simplistic .cpp file to implement SMTP would look like this:
#include "stdafx.h"
#include "resource.h"
#include "smtp.h"

// ... preamble excluded for clarity
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, 
                                HINSTANCE /* hPrevInstance */, 
                                LPTSTR lpCmdLine, 
                                int /* nShowCmd */)
{  
 CSmtp mail;
 CSmtpMessage msg;
 CSmtpAddress cc;
 CSmtpMessageBody body;
 CSmtpAttachment attach;

 // Initialize winsock  
 WSADATA wsa;
 WSAStartup(MAKEWORD(2,0),&wsa);

 msg.Subject = _T("Testing");

 // Who the message is from
 msg.Sender.Name = _T("My Username");
 msg.Sender.Address = _T("myaddress@myemail.com");

 // Primary recipient
 msg.Recipient.Name = _T("Someone Else");
 msg.Recipient.Address = _T("someoneelse@somewhereelse.com");

 // Uncomment these lines to add a CC address
 //  cc.Name = _T("A CCd User");
 //  cc.Address = _T("ccuser@ccdaddress.com");
 //  msg.CC.Add(cc);

 // Uncomment these lines to add a BCC address
 //  cc.Name = _T("Yet Another CC");
 //  cc.Address = _T("YACC@somewhere.com");
 //  msg.BCC.Add(cc);

 // Assign a value to the message body
 body = _T("This is a test of the CSmtp class and its support classes, \
 CSmtpAddress, CSmtpMessage and CSmtpMessageBody.  The textual portion \
 of this message should be encoded in the \"quoted-printable\" transport \
 encoding scheme, because the lines in the message are more than 76 \
 characters long.\r\n\r\nThe CSmtpMessage class makes a best-guess \
 effort to determine the proper transport encoding to use to ensure the \
 data comes across in the format originally intended.\r\n");

 // Add the message body to the message
 msg.Message.Add(body);

 // Here is an example of an alternate message body
 //  body = _T("<html><body>Hello World...</body></html>");
 //  body.Encoding = _T("text/html");
 //  msg.Message.Add(body);

 // Here are some samples of adding attachments to the message
 //  attach = _T("d:\\temp\\ntregmon.zip");
 //  msg.Attachments.Add(attach);
 //
 //  attach = _T("d:\\src\\smtpapp\\readme.html");
 //  msg.Attachments.Add(attach);

 // Assign these values only if your SMTP server requires authentication
 mail.m_strUser = _T("myusername");
 mail.m_strPass = _T("mypassword");

 // Attempt to connect to the mailserver
 if (mail.Connect(_T("mail.mymailserver.com")))
 {
  // Send the message and close the connection afterwards
  mail.SendMessage(msg);
  
  // Alternate one-shot method of sending a message
  // mail.SendMessage(_T("foo@bar.com"),
                      _T("someone@else.com"),
                      _T("My Subject"),
                      _T("This is the msg body"));
  mail.Close();
 }

 WSACleanup();
 
 return 0;
}

For added control, you can easily implement a class which inherits from the CSmtp class.  By providing your own implementation of SmtpWarning, SmtpError, SmtpCommandResponse and SmtpProgress, you can provide users with some visual indication of what is happening.  Furthermore, the SmtpWarning event is raised when authentication fails, either when the connection is first established, or in response to an outgoing mail request being made.  By overriding this event, you can provide users with a way of supplying credentials to be resumbitted to the SMTP server.

The CSmtpMessage class also supports the multipart/related MIME type, which is used most commonly to embed images and other items into an html message.  Here's an example of how to embed an image into a mail message using multipart/related:

CSmtpMessage msg;
CSmtpMessageBody body;
CSmtpAttachment  attach;

// The MimeType must be explicitly set for this type of message
msg.MimeType = mimeRelated;

body = _T("<html><body>This is an inline image<br> \
<img src="cid:1234567"></body></html>");

body.Encoding = _T("text/html");

attach = _T("c:\\web\\myimage.jpg");
attach.ContentId = _T("1234567");

msg.Message.Add(body);
msg.Attachments.Add(attach);

I'm not sure about compatibility, but you can also reference the attachment by name, in which case, the code would be even simpler:

msg.MimeType = mimeRelated;

body = _T("<html><body>This is an inline image<br> \
<img src="myimage.jpg"></body></html>");

body.Encoding = _T("text/html");

attach = _T("c:\\web\\myimage.jpg");

msg.Message.Add(body);
msg.Attachments.Add(attach);

I tested this on Outlook 2000 and it worked fine.  I'd be curious to note if this works for other e-mail applications.

Version History

  • Version 1.7 - 06/18/2001
    • Modified the code that gets the GMT offset and the code that parses the date/time as per Noa Karsten's suggestions.
    • Added an FD_ZERO(&set) to the last part of SendCmd(), since I use the set twice and only zero it out once. Submitted by Marc Allen.
    • Removed the requirement that a message have a body and/or an attachment in order to send it. This allows you to send a message containing only a header. Submitted by Marc Allen.
  • Version 1.6 - 04/04/2001
    • Apparently older versions of the STL do not have the clear() method for basic_string's. I modified the code to use erase() instead.
    • Added #include <atlbase.h> to the smtp.h file, which will allow any app (even non-ATL apps) to use these classes without problems.
  • Version 1.5 - 03/30/2001
    • Guess I should have checked EncodeQuotedPrintable() as well, since it did the same thing BreakMessage() did in adding an extranneous CRLF to the end of any text it processed. Fixed.
  • Version 1.4 - 03/12/2001
    • BreakMesage() added an extranneous CRLF to the end of any text it processed, which is now fixed. Certainly not a big deal, but it caused text attachments to not be 100% identical to the original.
  • Version 1.3 - 03/11/2001
    • Added a new class, CSmtpMimePart, to which the CSmtpAttachment and CSmtpMessageBody classes now inherit. This was done for future expansion. CSmtpMimePart has a new ContentId string value for optionally assigning a unique content ID value to body parts and attachments. This was done to support the multipart/related enhancement.
    • Added a MessageId member to the CSmtpMessage class to allow users to optionally specify a message ID.
    • Added a boolean Inline member variable to CSmtpAttachment to allow users to specify that the content-disposition is Inline rather than Attachment.
    • Support for multipart/related messages, which can be used for sending html messages with embedded images.
    • Modifed CSmtpMessage, adding a new MimeType member variable so the user can specify a certain type of MIME format to use when coding the message.
    • Fixed a bug where multipart/alternative messages with multiple message bodies were not properly processed when attachments were also included in the message.
    • Some small optimizations during the CSmtpMessage::Parse routine
  • Version 1.2 - 03/10/2001
    • Vastly improved the time it takes to break a message, which was dog slow with large attachments. My bad.
    • Added another overridable, SmtpProgress() which is called during the CSmtp::SendCmd() function when there is a large quantity of data being sent over the wire.
    • Added CMD_BLOCK_SIZE to support the above new feature
    • Added support for UNICODE builds
    • Added the CSmtpAttachment class for better control and expandability for attachments.
    • Added alternative implementations for CSmtp::SendMessage which make it easier to send simple messages via a single function call.
    • Added a constructor to CSmtpAddress for assigning default values during initialization.
    • Added a #pragma comment(lib,"wsock32.lib") to the smtp.h so you don't have to modify your project's linker options

Downloads

Download the source code - 16 Kb