Rebate & Sell

ESMTP/SMTP client

Writing a SMTP client from scratch:
how to send an e-mail and an attached binary file

  • Objective and prerequisite
  • SMTP and Mail messages
  • MIME version 1.0
  • Base 64 encoding
  • SMTP client/server conversation
  • The structure of the mail
  • The structure of the program
  • The source code
  • How does SendBuf work
  • A note about SendText
  • Objective and prerequisite

    Objective of this article is to write some C++ code to send an e-mail, together with its binary attachment, without buying any component on the web.

    Prerequisite is being in posses of the DLL described in Build your own ASPI dll. You are certainly asking: what does ASPI have to do with the SMTP client? Nothing. But I just needed some place to put into several win32asm functions that encode Base64 the binary attachment, and I developed and added these functions into the ASPIDLL.DLL for the sake of simplicity.

    The DLL is freeware from this site, and comes complete with full source code. You can also download it already assembled and zipped, in case you are not familiar with MASM32.

    SMTP and Mail messages

    SMTP stands for Simple Mail Transfer Protocol, and is a very simple protocol used to send mail messages from a sender to a recipient. It is one of the first and most used Internet protocols, released in 1982 and described in RFC 821.

    Mail messages, in turn, are messages sent over the Internet according to the SMTP client/server model outlined below. These messages must be formatted according to RFC 822 (Standard for ARPA Internet Text Messages). This makes it evident that mail messages are pure text messages, nothing but normal, plain, character messages.

    Additional note: ESMTP stands for Extended SMTP, and is a successive implementation of SMTP that is described in some RFCs that you can find on the web. Essentially ESMTP adds some features to the SMTP (without altering it) and uses some forms of authentication mechanisms to identify the mail sender and thus reducing the possibility of spamming (see RFC 2554).

    SMTP servers are servers that operate according to RFC 821. SMTP clients are clients that operate according to RFC 821 too.

    Original SMTP did not define any provision for attachments, and for binary attachments less than never, but some elegant *tricks* have been invented since 1982 to accomodate this indispensable feature. Please note that the original SMTP protocol is still in use even when attachments are sent, and the protocol itself remains unaltered.

    These "tricks" are named MIME and Base64 encoding, and are described into the following paragraphs.

    MIME version 1.0

    MIME stands for Multipurpose Internet Mail Extension and is defined in RFC 2045, RFC 2046, RFC 2047, RFC 2048, RFC 2049 (fear not, you don't have to read them all).

    MIME defines the following:

    • how to divide a mail message into several parts
    • how to delimit each part
    • how to describe and characterize each part
    In this project "parts" help us dividing the mail into a text message and a binary attachment.

    Base 64 encoding

    Because mail messages should always be pure text and the attachment is binary, it is necessary to convert the binary attachment into a stream of text characters before it is put into the part of the mail message that will be reserved for the attachment.

    Base64 encoding makes this possible. It is a very simple algorhytm that converts a sequence of binary data into a sequence of characters. It has only one drawback: it expands the attachment by a factor of 4/3. So a 3 kB binary file would convert into a 4 kB text attachment. I think that this overhead is well worth the service it provides, and anycase it is universally accepted.

    You can easily find on the Internet how does the Base64 encoding scheme work. In these web pages you will find a free implementation, see Build your own ASPI dll. I had to put the required assembly functions into a DLL, and I decided to put everything into the DLL already defined in the ASPI programming section of these web pages.

    You have access to the source code, you can build the DLL, or you can download a zipped copy of the already assembled DLL (or you can code your own implementation of a Base64 encoder, it is very simple).

    I did not write the Base64 decoder because I only wanted to send a mail and not to receive it. Base64 decoding will be performed by your Eudora or OutLook as you receive the mail and its attachment.

    SMTP client/server conversation

    The ESMTP/SMTP server works like this:
    • as soon you connect port 25 of the ESMTP/SMTP server, it sends you a welcome message and stands there waiting for your requests
    • the ESMTP/SMTP client (i.e.: your application program) sends some requests, one at a time, and receives responses back
    • both requests and responses are ASCII characters (no strange characters allowed, only plain text characters)
    • for each request you get one (only one) response
    • you can't send another request if the response to the previous request has not been received yet
    • requests and responses togheter form a conversation
    • requests talk about what and where to send e-mail stuff
    • responses *usually* make the ESMTP/SMTP client aware that the ESMTP/SMTP server has correctly received the requests
    Typical requests, the ones that will be used in this project, are:
    • HELO: the client says "hello" to the SMTP server and introduces itself; must be followed by the name of the client; example:

      HELO foobar mail sender

    • EHLO: same as above, but the target server is an ESMTP server (EHLO stands for Extended HELO)
    • AUTH: request for authentication, used only with ESMTP servers (not in use with normal SMTP servers); the only auth request that will be covered here is AUTH LOGIN
    • MAIL: tells the ESMTP/SMTP server who is the mail sender; example:
      MAIL From: <donald.duck@disney.com>

      and it is used as a return address for undeliverable mails
    • RCPT: tells the ESMTP/SMTP server who is the mail recipient; example:
      RCPT To: <mickey.mouse@disney.com>

    • DATA: asks the ESMTP/SMTP server to put itself into a "listening" state since the client is ready to send the body of the mail and its eventual attachments; this requires each mail part to be formatted according to RFC 822 and the whole message to be structured according to the MIME 1.0 RFCs
    • QUIT: says goodbye to the ESMTP/SMTP server
    Typical responses are three cyphers numbers followed by an ASCII descriptive text. Within the application program only the numbers are useful to understand what is happening. Descriptive text is useful only when interactively talking to the SMTP server using telnet to make experiments.

    (When approaching a ESMTP/SMTP server using telnet on port 25 to make experiments it could happen that you receive *more* than one response from the server. Don't worry: during the execution of the application program this will not happen.)

    A final note: within your application program you will receive one, and only one, response to each request.

    The structure of the mail

    The described structure is relative to the objective of this article, that is sending a text message followed by a binary attachment, according to RFC 822 and MIME RFCs. This is the *logical* structure of the mail:
    
    From: <donald.duck@disney.com> CRLF
    To: <mickey.mouse@disney.com> CRLF
    Subject: foobar CRLF
    MIME-Version: 1.0 CRLF
    Content-Type: multipart/mixed; CRLF
     boundary= "KkK170891tpbkKk__FV_KKKkkkjjwq" CRLF
    CRLF
    --KkK170891tpbkKk__FV_KKKkkkjjwq CRLF
    Content-Type: text/plain; charset=US-ASCII CRLF
    CRLF
    here goes the text message CRLF
    CRLF
    --KkK170891tpbkKk__FV_KKKkkkjjwq CRLF
    Content-Type: application/octet-stream CRLF
    Content-Transfer-Encoding: base64 CRLF
    Content-Disposition: attachment; CRLF
     filename= "suggested name of the attachment" CRLF
    CRLF
    here goes the Base64 encoded attachment CRLF
    --KkK170891tpbkKk__FV_KKKkkkjjwq-- CRLF
    CRLF.CRLF
    
    
    Please note the following:
    • CR and LF stand for Carriage Return and Line Feed
    • each line *must* be terminated by a CRLF because the conversation is character based, and CRLF let the receiver understand that a line of text has been completed
    • a CRLF alone means a blank line
    • multipart/mixed means that the mail message is made of more than one part, in this case a plain text message and a binary attachment, and the parts are of different type (mixed)
    • the string KkK170891tpbkKk__FV_KKKkkkjjwq is a casual arbitrary string of my invention (you can invent one of yours) that is declared in the boundary= attribute and used by the mail receiving agent to identify each single part of the mail message
    • each part of the message is contained inbetween two consecutive boundary strings
    • once declared, successive occurrences of the boundary string must be preceeded by two hyphens
    • the last boundary string occurrence is preceeded and followed by two hyphens
    • each part in the mail is made of a content-decriptor header followed by a blank line followed by the actual content
    • in the above structure two parts have been defined:
      • a text/plain mail text message
      • a application/octect-stream mail attachment
    • the words boundary and filename are preceeded by a blank character because they are actually the prosecution of the preceeding line; the line has been splitted in two to make it more readable, but you can send it all in only one line if you want
    • the final dot enclosed between two blank lines (CRLF.CRLF) tells the SMTP server that it can stop listening for the DATA to arrive (in othe words the mail is complete).

    The structure of the program

    Like any client application you should put a client socket component on the form of your Borland C++ Builder project. Socket communication in Win32 is event driven, so the application program should provide some event handlers to implement the SMTP client.

    Furthermore you need a button component to start the process of sending the mail. In the provided code the button is called btMail, and its onClick event is called btMailClick.

    The source code

    Here you will find the source code. It is a mix of true code and comments. You should provide all the things described in the comments. There is also some pseudo-code, that you can easily implement according to your requirements. Pseudo-code is given in angle brackets.

    The code has been arranged in a state machine fashion. This means that at any given time the program stands in one, and only one, well defined program status. Switching between statuses can only happen according to the accomplishment of some requisites.

    Capital letters variables are global, and must be provided before clicking the send mail button. Please note the following:

    • currentProgramStatus is a global variable that defines the current status of the program
    • a static text component should be put on the form to display the current status of the program; the pointer to this component is called stStatus; in makes the user aware of what is happening
    • many Sleep statements you will encounter within the code: these have been placed there because I think that small pauses will make the communication more reliable, without affecting the total length of the process; please consider that the only time consuming cycle is the loop that sends the attachment, but in that loop the Sleep will be executed only in case of effective usefulness
    • a progress bar is useful to display the advancement of the attached file being sent
    • all SMTP conversation response codes (220, 221, 235, etc.) are fully described in a lot of easy catch sites in the web; please refer to any description
    • the Base64 encoded attachment is sent 512 bytes at a time in a while loop, as you can see below. I can't remember why I used SendBuf instead of SendText, but whichever the reason I had to put a carriage return and a line feed after each 512 bytes sent

    How does SendBuf work

    The VCL documentation says that SendBuf returns the actual number of bytes sent, and it is true, but the assertion is made in such a way that the programmer might understand that the buffer can be partially sent, and in this case the return value would be the actual number of sent bytes in the sense that some bytes have been left unsent.

    During my experiments I observed that a buffer is never partially sent. The buffer is always either completely sent or not sent at all. In this latter case SendBuf returns -1.

    According to this the while loop in the source code provided above only checks whether SendBuf returns -1, and in this case the loop stops 30 milliseconds and then repeats the SendBuf. Very simple and effective.

    A note about SendText

    When using TClientSocket -> SendText to send lines of text to a Linux server, also remember to terminate each line with a "newline" (10) and a "zero" (0) (no need to add a "carriage return"), or extra bytes will be sent over the socket.