PP SMTP Client
Volume Number: 12
Issue Number: 11
Column Tag: Programming Workshop
Implementing SMTP with PowerPlant
Create a simple Internet mail sender using PowerPlant’s network
classes
By Christopher Haupt
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
Introduction
Since the Internet’s explosive growth in the early 1990’s, a variety of new and
interesting tools have been developed to explore its resources. Without doubt, the
greatest attention has been placed on the World Wide Web; browser battles are a
constant focus of the media. Many new users want to be able to cruise about, exploring
the vast resources open to them. This attention momentarily diverts people from the
fact that the most frequently used tools on the Internet are file transfer (FTP) and
electronic mail (often SMTP/POP) programs.
If you consider the experiences you have today on the Web, you will note the
solitary nature of that activity. Only now are sites becoming aware of the community
building abilities of this new media. By and large, people want to communicate with
each other, and not just enter a ghost town of information.
Over the past few years, my company has focused on this community building
aspect of net use. In particular, we are interested in building tools that allow kids to
meet one another and form relationships. In the beginning, we hope to encourage these
relationships through forming “pen-pals”, or email friends. To this end, we are
exploring the implementation of simple Internet mail enabled tools. From this
research I present this introduction to the primary mail sending protocol, the Simple
Mail Transport Protocol (RFC821), and explore a simple implementation of this
protocol using Metrowerks’ PowerPlant network class library.
This article does not provide an introduction to TCP/IP development, but rather
the higher level mail protocol. Several good references exist on TCP/IP, of which I
recommend (Stevens 94), (Comer 91), and (Tannenbaum 81) as useful additions to
your library.
The code presented in this article meets several simple requirements. It
implements SMTP using the current PowerPlant network classes. It provides the
ability to send “one-shot” email messages-it doesn’t store messages in a mailbox.
Additionally, it will not receive messages, although implementing a mail receiver
using the Post Office Protocol (RFC1725) can be done using this code as a guide. By
using the latest PowerPlant, we also get the ability to dynamically switch between
MacTCP and OpenTransport without additional code.
the Simple Mail Transport Protocol (SMTP)
The Simple Mail Transport Protocol was designed to be an easily implemented,
reliable mechanism for moving messages from one trusted host to another. This
article includes an overview of the protocol, but the definitive specification is
(RFC821). (Stevens 94) is an excellent treatment of this material.
SMTP is specified independent of a transport service (it can run over any type of
network with a reliable transport layer). This paper describes an SMTP
implementation using TCP, which is the most common transport medium in use today
for SMTP on microcomputers. SMTP is assigned to the permanent TCP port 25.
The SMTP specification describes a lock-step protocol in which the sender and
the receiver transmit specifically formatted ASCII messages to one another, awaiting a
response before continuing. At a high level, the SMTP architecture can be described by
a simple finite state machine which contains three main states (see Figure 1). The
machinery is either checking reply codes, sending new commands, or sending the
message contents. In smart implementations, an error code does not necessarily force
a disconnect, although it is illustrated and implemented this way in this article.
Figure 1. SMTP Finite State Machine
SMTP defines a small required command set, with several optional commands
included for convenience purposes. Table 1 shows the minimal set required for an
SMTP sending client.
HELO - Initial State Identification
MAIL - Mail Sender Reverse Path
RCPT - One Recipient’s Forward Path
DATA - Mail Message Text State
RSET - Abort Transaction and Reset all buffers
NOOP - No Operation
QUIT - Commit Message and Close Channel
Table 1. Minimum SMTP Command Set
Commands may have zero or more parameters. Commands and their parameters
are issued as ASCII plain text strings. A command is terminated with a
carriage-return, line-feed () pair. Commands do not span lines. The
termination pair completes the command line when it is encountered. For instance, the
command to identify the sender of a mail message would be sent as MAIL
FROM:.
Acknowledgment messages are formed by a three digit return code, followed by
optional text. The three digits represent error and success codes. A typical success
message would appear as 250 Requested mail action okay. Note that, within an
acknowledgment message, only the first three digits are significant. The textual
portion of the reply messages is for human understanding and can contain any text.
Messages are grouped by meaning by using the first digit as a key. Messages beginning
with a “2” are success messages, “3”’s are error codes, etc.
Normally, the acknowledging process will send one reply message per command.
Each reply is terminated with the standard token. It is possible that more than
one acknowledgment message may be sent, and this is not prohibited by the protocol
specification. You should consider that some servers may generate more than one line
of response and handle that case accordingly-this occurs most frequently with message
220, the service ready message transmitted on startup from the receiver when the
sender initiates a connection. If you aren’t careful, this can throw your state machine
off. The SMTP specification states that multiline responses should include a hyphen
(“-”) immediately following the result code of each intermediate status code. The
final result line is formatted normally, without the hyphen.
A typical SMTP session can be characterized as shown in Figure 2 and described
here. The sender ([S]) opens a two-way channel to the receiver ([R]). The receiver
can be the final destination or an intermediate node, described in the message’s path
explicitly, or implicitly by network routing tables. At connect time, both hosts are in
the Initial state. [R] sends an acknowledgment that the channel is open. [S] sends a
HELO message, identifying itself to the receiver. Note that authentication is not
required, so it is very easy to spoof sender IP addresses; SMTP is not a secure
messaging protocol. [R] sends back a success or error message, possibly denying
access to the sender. If the HELO was successful, both sides are now in the Send
Command/Response state. [S] sends a MAIL command describing the sending party’s
fully qualified reverse path. [R] acknowledges the successful receipt of the path and
clears all of its transaction buffers. [S] sends one or more RCPT commands describing
the forward path of recipients of the mail message, one recipient per line. [R] accepts
or rejects each address.
Figure 2. Typical SMTP Transaction Flow
[S] now sends a DATA command, instructing the receiver that all following data is
the actual mail message, thereby putting the transaction in the Send Data state.
Transmission of the message text completes when the end-of-message (EOM) sequence
is sent (a . triplet, which looks like a period alone on a line). This
raises the question, “what if the message contains the EOM sequence?” Data
transparency is achieved by stuffing any instance of the EOM sequence occurring
within the body of a message with a period “.” character prefix. The receiver checks
each line for a leading period and removes it before buffering the data. Only when [R]
detects the “real”, tailing EOM, does it send an acknowledgment.
[S] sends a QUIT command to place the transaction in the Commit state. [R]
acknowledges the command and closes the channel. It then delivers the message to the
recipients’ mailboxes or forwards the message on to the next server in the recipients’
forward paths.
You will note that the SMTP protocol does not handle any of the fields you would
associate with a standard mail message (fields such as Subject:, Reply-To:, etc.).
These fields, which make up a message that conforms to (RFC822), are built and
parsed by the mail handling agent on either end of the SMTP transaction. SMTP treats
the mail message in an opaque manner, sending the headers and message body all at once
during the Send Data state. The SMTP code only peeks at the message to ascertain EOM
transparency conditions. SMTP places the path information generated in a transaction
at the front of the message contents. This area is often called the envelope.
Implementing SMTP in PowerPlant
To implement a simple SMTP client for the PigMail project, I chose to create a
rudimentary mail editor and tie it to the SMTP code by using a LSingleDoc derived class
and its associated window member. The implementation started out using a threaded
approach. Before long, debugging of the PowerPlant network classes in the older
versions of CodeWarrior bogged the project down. I muttered “Keep It Simple Stupid”
to myself a couple of times and created the very simple, event-loop based
asynchronous version which is presented here.
A threaded implementation is actually not much more difficult to construct, but it
does add some complexity to the discussion at this introductory level. Because SMTP is
a simple problem domain, it is a great opportunity to experiment with threading. You
could implement the entire SMTP state machine as one thread, to which you hand off all
data and let it rip. Or, you could be creative and implement a two thread approach and
play with the Producer/Consumer model of cooperative processes (Silberschatz 92).
I tried both, and while they work fine, they violated my KISS requirement. The most
important thing I learned with these experiments was the danger of mixing threads
which operate with different PowerPlant drawing contexts; talk about major view foci
problems!
The simple mail sender class displays a window which contains a number of text
edit fields: SMTP host, sender address, destination address, subject, and message body.
It also contains a button to send the message when done and a status field. Figure 3
shows a picture of the simple interface.
Figure 3. Simple Mail Editor
In the event-loop/asynchronous handling implementation below, I began by
creating a LSingleDoc, LAsyncClient class similar to the one shown in Listing 1. To
increase your understanding in the following walk-through, you may want to refer to
the sample source code that is supplied with this article’s distribution.
Listing 1: SMTPSenderDoc.h
SMTPSenderDoc
This class implements all of the machinery necessary to experiment with the SMTP protocol using the
PowerPlant network classes (in async/eventloop mode)
class SMTPSenderDoc : public LSingleDoc,
public LListener,
public LAsyncClient
{
public:
SMTPSenderDoc(LCommander *inSuper,
const LStr255 &inTo = “”,
const LStr255 &inSubj = “”);
virtual ~SMTPSenderDoc();
virtual void ListenToMessage(MessageT inMessage,
void*ioParam);
virtual void Connect();
virtual void Disconnect();
virtual Boolean IsIdle();
virtual Boolean AllowSubRemoval(LCommander* inSub);
protected:
virtual void HandleAsyncMessage(const LAsyncMessage&
inMessage);
void BuildSessionWindow(void);
void SendMailMessage(void);
void RunMachine(char *inDataBuffer,
Uint32 inDataSize);
void SendHELO(Boolean inUseShort = false);
void SendQUIT(void);
void SendRSET(void);
void SendNOOP(void);
void SendMAIL(void);
void SendRCPT(LStr255 &inRecipient);
Boolean SendNext(void);
void SendDATA(void);
void SendBody(void);
void SendHeader(void);
Boolean ParseReply(char *inBuffer, Uint32 inBufferLen,
Uint32& inPos);
MailPreferenceTypeH mMailPrefs;
LStr255 mTo;
LStr255 mSubject;
Handle mBody;