The SMTP protocol (first specified in RFC 821, most recently in SMTP 5321) is a simple text-based protocol which is designed to transfer email across IP networks. Although used by servers to send and receive messages, client devices typically only use SMTP for sending messages and use POP3 or IMAP to retrieve messages from a mail server.
SMTP works over a TCP/IP connection using TCP typically on server port 25.
When the process of sending email is started, by calling email_start_send(), the SMTP state machine is setup to first take the URL of the SMTP server (defined as either a constant or variable string) and perform a DNS query, using the DNS driver.
Once the IP address of the SMTP server is returned the driver then sends an ARP request to retrieve the MAC address to communicate with. As a SMTP server will typically be on the internet and connected to via a gateway router of some form the MAC address returned by the ARP driver will typically be the MAC address of the gateway, which will deal with forwarding communications on.
Once the MAC address is retrieved the driver then attempts to open a TCP connection with the SMTP server. This may take some time, if for instance the server is very busy.
Once the TCP connection is opened the server sends a greeting message. This is a text string that must start with 3 digits to be valid (the remainder of it can be anything). For example:
“220 some.server.name ESMTP”
The leading 2 character is the important one and it indicates the server is happy (3 is used for data transfers, 4 & 5 indicate an error). Once received the driver sends one of two commands, followed by a domain identifier string and then followed by a carriage return and line feed as shown:
“HELO EMB<CR><LF>”
or
“EHLO EMB<CR><LF>”
HELO is the normal command.
EHLO is the alternative command that requests an authenticated login. You may select either when calling the email_start_send() function, with an authenticated login generally required when using a SMTP server that is located remotely from the local internet connection (to protect against spam use). The domain identifier string can be anything.
The server should respond with a string starting with a 3 digit value. The first character should be “2” to indicate that the server is happy. If the 3 digit number is followed by a hyphen “-“ character then there are more lines to be read, each of which will start with the 3 digit number. Each line will be indicating a capability of the server and the driver reads each line until the final line, indicated by no trailing “-“ is read. For example:
“250-the.server.name<CR><LF>”
“250-AUTH=LOGIN CRAM-MD5 PLAIN<CR><LF>”
“250 8BITMIME<CR><LF>”
The driver will now jump over the following login, username and password stages if an authenticated login has not been selected, or if it has it will proceed as follows.
The driver first sends the AUTH LOGIN command, as shown:
“AUTH LOGIN<CR><LF>”
It waits to receive the response string, which should start with a “3” to indicate the server is in data transfer mode (4 & 5 indicate an error). For example:
“334 VXNlcm5hbWU7<CR><LF>”
The driver then sends the username encoded using BASE64. For example:
“ZGV2ZWxALnVrWHX<CR><LF>”
It waits to receive the response string, which should start with a “3” to indicate the server is still in data transfer mode and happy. For example:
“334 UGFzc3YAq8<CR><LF>”
The driver then sends the password encoded using BASE64. For example:
“C2VJlNkj3FuQ<CR><LF>”
It waits to receive the response string, which should start with a “2” to indicate the server is happy. For example:
“235 go ahead<CR><LF>”
Now that the login has been completed the driver continues on in the same way if either authenticated login was select or not. The driver starts the send process with the MAIL FROM command, followed by a space character and then the email address the email is being sent from enclosed in angle brackets, for example:
“MAIL FROM: <embedded-device1@my-domain.com><CR><LF>”
It waits to receive the response string, which should start with a “2” to indicate the server is happy. For example:
“235 ok<CR><LF>”
The driver now sends the RCPT TO command, followed by a space character and then the destination email address enclosed in angle brackets, for example:
“RCPT TO: <john@yahoo.com><CR><LF>”
It waits to receive the response string, which should start with a “2” to indicate the server is happy. For instance:
“235 ok<CR><LF>”
The driver now sends the DATA command:
“DATA<CR><LF>”
It waits to receive the response string, which should start with a “3” to indicate the server is now in data transfer mode. For example:
“354 go ahead<CR><LF>”
The email message is now transferred to the server as a continuous block of data until it is complete. No response is given by the server until the transfer is complete (apart from TCP acknowledgements).
The first section of the message is the headers, which includes the FROM email address, which is the senders email address again (these fields will be read and used by the receiver of the email), the TO email address, which is the recipients email address again, and the email subject.
After this the email body is sent, the start of which is indicated by a blank line. As this driver allows a file to be attached emails are sent MIME encoded. Therefore first of all a constant MIME header block is sent to indicate the start of the text portion of the email. Once this has been sent a special user application function is called to request each byte of the email text. As TCP packets may need to be resent should a packet be lost, a variable is passed to the user application which is normally zero, but if not indicates that the application needs to be move back the specified number of bytes as the driver is resending the previous packet. Once the user application has passed all of the bytes of the email text it returns a value to indicate that it is complete.
If the user flagged that the email would include a file attachment when email_start_send() was originally called the driver now sends a MIME header for the file attachment portion of the email body. Once this has been sent the special user function is now called to request each byte of the file. Any type of file may be sent and the driver automatically BASE64 encodes the file so that binary data is suitable for sending via SMTP. Again, as TCP packets may need to be resent should a packet be lost, a variable is passed to the user application which is normally zero, but if not indicates that the application needs to be move back the specified number of bytes as the driver is resending the previous packet of the file data. Once the user application has passed all of the bytes of the file it returns a value to indicate that it is complete.
The driver sends the final MIME block and completes the email with the <CR><LF>.<CR><LF> marker.
It waits to receive the response string, which should start with a “2” to indicate the server is happy. For instance:
“250 ok<CR><LF>”
Finally the driver sends the QUIT command:
“QUIT<CR><LF>”
It waits for the response before then closing the TCP connection to the SMTP server and returning the SMTP driver state machine to the idle state.
Should an error occur at any point, for instance due to a timeout or unexpected response the TCP connection is closed and the SMTP driver state machine is returned to the idle state.
If the user application wants to follow the progress of the SMTP process it can either monitor the state of the sm_smtp variable or an optional string pointer is provided (selected using the DO_SMTP_PROGRESS_STRING define) which is loaded with a user changeable string at each stage of the email send process. This may be used for example to display progress on a user screen.