Sending Mail with SMTP
Table of Contents | Previous | Next | Index

Messaging Access SDK Guide
Part 1. Using the Messaging Access SDK


Chapter 2
Sending Mail with SMTP

This chapter is an overview of using SMTP (Simple Mail Transfer Protocol) to create and send email messages.

[Top]

The SMTP Protocol

SMTP (Simple Mail Transport Protocol) allows clients to deliver mail messages to SMTP servers. To retrieve these messages, the client uses the IMAP4 or POP3 protocol. Servers can use SMTP to move messages from one server to another before delivering them to a mailbox.

The SMTP client always starts the session, but either client or server can end it. The client starts the session by connecting to the server. The server acknowledges the message with a greeting. The client responds, and, in subsequent commands, specifies the message sender and recipients and sends the message.

SMTP commands are made up of a keyword, followed by any parameters the function has. Commands receive a three-digit response code, described in SMTP Response Codes. SMTP commands include only the U.S. ASCII character set, a subset of ASCII that includes the values 00h-7Fh (0d-127d).

The responses returned by SMTP commands are made up of a three-digit numeric code followed by descriptive text. The client application can detect and handle the response or display the message to the user for interpretation. For more information, see SMTP Response Codes.

If your server supports Extended SMTP (ESMTP), which is provided in an update to the existing SMTP specification, your mail application can take advantage of ESMTP elements, such as pipelining, the bdat command, data chunking, and DSN. For more information, see Determining ESMTP Support.

During a single SMTP session, the client can send multiple unrelated, independently addressed messages. Because of this, the SMTP client can increase efficiency by batching messages and sending them together using pipelining. For more information, see Pipelining Commands.

The SMTP server waits for SMTP messages on the "well-known" TCP port 25. Many mail applications allow the user to specify a different port.

For a table of SDK-supported SMTP protocol commands, see Supported SMTP Internet Protocol Commands. For detailed information about SMTP, consult one of the RFCs listed, with links, in SMTP RFCs.

[Top]

Steps in an SMTP Session

Generally, a messaging application follows standard steps when using SMTP to send mail. These steps are listed below with links to more detailed descriptions.

Step Section with details

Initialize the response sink.

Creating a Response Sink

Initialize the client.

Creating a Client

Connect to the server.

Connecting to a Server

Determine Extended SMTP (ESMTP)
features supported by the server.

Determining ESMTP Support

Set the mailer.

Setting the Mailer

Set the recipients.

Setting the Recipient

Send the message.

Sending the Message

End the SMTP session.

Ending the Session

[Top]

SMTP Response Codes

When the client sends an SMTP command, the response that comes back contains a standard three-digit response code followed by descriptive text. This section is an overview of SMTP responses. For detailed information, see RFC 821.

The response contains the three-digit code, a space, and one or more lines of text that describes the response. If the response is multi-line, each subsequent line also contains the three digit code, a hyphen, and text. The final line contains the code, a space, and text. The three-digit response code and the rest of the text is made available through the callback functions. See smtpSink_t.

This table lists some of the most common SMTP reply codes. In general, response codes in the 100 to 300 range are considered successful; those in the 400 to 500 range are considered unsuccessful.

Table 2.1 SMTP Reply Codes

Code Text of Response

211

system status, or system help reply

214

help message

220

<domain> service ready

221

<domain> service closing transmission channel

250

request mail action okay, completed

251

user not local, will forward to <forward-path >

354

start mail input; and with <CRLF>.<CRLF>

421

<domain> servers not available, closing transmission channel

450

requested mail action not taken: mailbox unavailable

451

requested action aborted: local error in processing

452

requested action not taken: insufficient system storage

500

syntax error, command unrecognized

501

syntax error in parameters or arguments

502

command not implemented

503

bad sequence of commands

504

command parameter not implemented

550

requested action not taken: mailbox unavailable

551

user not local; please try <forward-path>

552

requested mail action aborted: exceeded storage allocation

553

requested action not taken: mailbox name not allowed

554

transaction failed

The first digit of the SMTP reply code tells whether the response is positive or negative.

Table 2.2 SMTP Reply Codes, Digit 1

Digit 1 Meaning
1yz

Positive Preliminary Reply

2yz

Positive Completion Reply

3yz

Positive Intermediate Reply

4yz

Transient Negative Completion Reply

5yz

Permanent Negative Completion Reply

The information described by the second and third digits is noted here. For the meanings of specific numbers, see RFC 821.

[Top]

SMTP Function Callback Mapping

Callbacks are associated with many SMTP functions. For general information about the response sink and callbacks, see SDK Response Sinks for C.

The smtpSink_t structure contains callbacks for each client call. The SMTP client's smtp_processResponses call invokes the corresponding interface function. This function reads in responses from the server and invokes the appropriate response sink functions. It thus invokes the callback functions provided by the user for all responses that are available at the time of execution.

If a time-out occurs, the user can continue by calling smtp_processResponses again.

Functions with multi-line responses map to more than one callback. The second callback provides a notification that the operation is complete.

If a server error occurs, the error callback is invoked.

Table 2.3 shows which SMTP functions are mapped to callbacks in the smtpSink_t structure. Table 2.4 shows which do not map to callbacks.

Table 2.3 Functions with Callbacks

Functions Possible Responses, Mapped to Callbacks
smtp_bdat
bdat, error 
smtp_connect 
connect, error 
smtp_data
data, error 
smtp_ehlo
ehlo, ehloComplete, error 
smtp_expand
expand, expandComplete, error 
smtp_help
help, helpComplete, error 
smtp_mailFrom
mailFrom, error
smtp_noop
noop, error
smtp_quit
quit, error 
smtp_rcptTo 
rcptTo, error 
smtp_reset
reset, error 
smtp_send
send, error
smtp_sendCommand
sendCommand, sendCommandComplete, error
smtp_sendStream
send, error 
smtp_verify
verify, error
Table 2.4 Functions without Callbacks

Functions Without Callbacks
smtp_disconnect 
smtp_free 
smtp_get_option 
smtp_initialize 
smtp_processResponses 
smtp_setChunkSize 
smtp_setPipelining 
smtp_setResponseSink 
smtp_setTimeout 
smtp_set_option 
smtpSink_free 
smtpSink_initialize 
[Top] [SMTP Function Callback Mapping]


Creating a Response Sink

The first step in starting an SMTP session is to initialize the SMTP response sink, which is defined by the smtpSink_t structure. The response sink is made up of function pointers and opaque data. Initializing the sink sets its function pointers and opaque data to null. For general information about the response sink, see SDK Response Sinks for C.

To initialize and allocate the response sink, call the smtpSink_initialize function and supply the sink you want the SMTP client to use. If successful, this function returns NSMAIL_OK. Use this syntax:

int smtpSink_initialize( smtpSink_t ** out_ppsmtpSink );
The following section of code initializes the response sink and sets the sink function pointer.

int l_retCode = 0; 
l_retCode = smtpSink_initialize(&l_psmtpSink); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
setSinkFunctions(l_psmtpSink); 
After you create the response sink, the next step is Creating a Client.

When a session is finished, you must free any memory associated with the response sink. To free the response sink and its data members, use smtpSink_free:

void smtpSink_free( smtpSink_t ** in_ppsmtpSink );
When this function returns, the response sink is set to null. The user must free any opaque data.

[Top] [Creating a Response Sink]

Creating a Client

The SMTP client uses the smtpClient_t structure to communicate with an SMTP server. To initialize and allocate the smtpClient_t structure and set the response sink for the client's use, call the smtp_initialize function.

When you create the client structure, you supply the address of the pointer to the SMTP client you are creating, along with an initialized response sink, as described in Creating a Response Sink. Use this syntax:

int smtp_initialize( smtpClient_t ** out_ppSMTP,
                     smtpSink_t * in_psmtpSink );
The following section of code creates a client.

/* Initialize sink first as described in Creating a Response Sink */
l_retCode = smtp_initialize(&l_psmtpClient, l_psmtpSink); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
When a session is finished, you must free any memory associated with the client. To free the client structure and its data members, use this function:

void smtp_free( smtpClient_t ** in_psmtp_Client );
The in_psmtp_Client parameter represents the SMTP client. When this function returns, the client structure is set to null.

After you initialize the client, the next step is Connecting to a Server.

[Top]

Connecting to a Server

Before sending mail, the client must connect with the server through a service port. To connect to the server, use the smtp_connect function.

This function requires the identifier of the SMTP client that wants to connect with the server. If the server is using the default port for the SMTP protocol (port 25), you can pass 0 as the value of the port parameter. Use this syntax:

int smtp_connect(smtp_t * in_pSMTP, 
                const char * in_server,
                unsigned short in_port );
On connecting, the server sends a greeting message to the client. The client responds by identifying itself with the EHLO command.

NOTE: For this function's callback mapping, see SMTP Function Callback Mapping. §
The following section of code connects the client to the server.

/* After Creating a Response Sink and Creating a Client */
int l_retCode = 0; 
char* l_szServer = "smtpServer.com"; 
l_retCode = smtp_connect(l_psmtpClient, l_szServer, 25); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
During the connect process, you can enable pipelining if your server supports it. See Pipelining Commands. To find out which extensions are supported by the server, see Determining ESMTP Support.

After connecting to the server, the next step is Setting the Mailer.

When a session is finished, you must free any memory associated with the client. To disconnect the client from the server and close the socket connection, use this function.

int smtp_disconnect( smtpClient_t * in_pSMTP );
The in_pSMTP parameter represents the SMTP client. You can use this function as part of a Cancel operation while retrieving a message. Remember that you do not call smtp_processResponses after smtp_disconnect.

[Top]

Determining ESMTP Support

To retrieve a listing of extensions that are supported by the server, call the smtp_ehlo function. This function returns a multiline message listing the Extended SMTP (ESMTP) features, such as pipelining or DSN, that the server supports. This is similar to the functionality of the IMAP4 capability command. Use this syntax:

int smtp_ehlo( smtpClient_t * in_pSMTP, 
               const char * in_domain );
This function calls the EHLO SMTP protocol command, which can be issued in any session state, but is usually issued after connecting to the server.

NOTE: For this function's callback mapping, see SMTP Function Callback Mapping. §
The following section of code finds out which Extended SMTP (ESMTP) features the server supports.

/* After Connecting to a Server */
int l_retCode = 0; 
l_retCode = smtp_ehlo(l_psmtp, l_szDomainName); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
You can enable pipelining if your server supports this extension. See Pipelining Commands.

[Top]

Pipelining Commands

Pipelining allows you to group, or batch, functions for execution rather than executing each separately. If pipelining is enabled on your server, commands are stored internally in the client as they are issued. All commands begin to execute when triggered in one of three ways: if the smtp_processResponses function is called, if the internal storage area is full, or if a function that cannot be pipelined is issued.

You can enable pipelining anywhere, but it may make sense to do this after invoking the smtp_ehlo function. Pipelining may then be enabled if the server supports it. If not, the way the network works does not change. The ehlo callback indicates whether pipelining is supported.

Use this syntax to attempt to enable pipelining:

int smtp_setPipelining( smtpClient_t * in_pSMTP, 
                        boolean in_enablePipelining );
The in_pSMTP parameter represents the client. The in_enablePipelining parameter is a Boolean value that tells the server to attempt to enable pipelining. This function sends the PIPELINING SMTP protocol command.

NOTE: For this function's callback mapping, see SMTP Function Callback Mapping. §
Some functions continue to add to the pipelining list. These are smtp_bdat, smtp_mailFrom, smtp_rcptTo, and smtp_sendStream. Calling any other function causes the commands on the pipelining list to be sent.

For example, you could call smtp_mailFrom, followed by one or more calls to smtp_rcptTo. These functions are added to the pipelining list and are not executed. If you then call another function, such as smtp_noop, the commands are sent to the server.

For details about using pipelining, refer to RFC 1854, "SMTP Service Extension for Command Pipelining."

[Top]

Setting the Mailer

Setting the mailer starts the process of delivering a message. Supply the identifier of the SMTP client that is sending the mail. If your server supports Extended SMTP, you can pass ESMTP elements in the in_esmtpParams parameter. Use this function:

int smtp_mailFrom(smtpClient_t * in_pSMTP, 
                  const char * in_reverseAddress,
                  const char * in_esmtpParams );
The function identifies the sender and provides the sender's fully qualified domain name in the in_reverseAddress parameter. It sends the MAIL FROM: SMTP protocol command.

NOTE: For this function's callback mapping, see SMTP Function Callback Mapping. §
The following section of code sets the mailer.

/* After Connecting to a Server */
int l_retCode = 0; 
char* l_szServer = "smtpServer.com"; 
char* l_szDomainName = "netscape.com"; 
char* l_szMailer = "derekt@netscape.com";
l_retCode = smtp_mailFrom(l_psmtp, l_szMailer, NULL); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
After you set the mailer, the next step is Setting the Recipient.

[Top]

Setting the Recipient

After setting the mailer, the next step is to set the recipient. Use this function:

int smtp_rcptTo(smtpClient_t * in_pSMTP, 
                const char * in_forwardAddress,
                const char * in_esmtpParams );
This function sets a single recipient, so you must call it again for each recipient of a message. The in_pSMTP parameter represents the client. The in_forwardAddress parameter supplies the recipient's address. If your server supports Extended SMTP, you can pass ESMTP elements in the in_esmtpParams parameter.

smtp_ rcptTo sends the RCPT TO SMTP protocol command.

NOTE: For this function's callback mapping, see SMTP Function Callback Mapping. §
The following section of code sets the recipient.

/* After Setting the Mailer */
int l_retCode = 0; 
char* l_szRecipient = "alterego@netscape.com"; 
smtpClient_t * l_psmtpClient = NULL; 
l_retCode = smtp_rcptTo(l_psmtp, l_szRecipient, NULL); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
After you set the recipient, the next step is Sending the Message.

[Top]

Sending the Message

After setting all of the message recipients, the client can send the message data. To send a message, use smtp_data, followed by either the smtp_send or the smtp_sendStream function. First, use this function:

int smtp_data( smtpClient_t * in_pSMTP );
Supply the identifier of the SMTP client that is sending the mail. The server responds with a success or failure reply code. See SMTP Response Codes.

The smtp_send and smtp_sendStream functions both deliver data to the server. smtp_send sends data in a single chunk, while smtp_sendStream sends the data in a series of smaller chunks. If you use either of these functions, you must send data with the smtp_data function and not with smtp_bdat.

smtp_data and smtp_bdat are interchangeable, but cannot be used together. The smtp_bdat function, which can deliver binary data, is not supported on the Netscape Messaging Server and some other servers.

After the data function, call smtp_send, which sends a message to the server:

int smtp_send( smtpClient_t * in_pSMTP, const char * in_data );
Supply the identifier of the SMTP client that is sending the mail and the identifier for the data to send. When the server responds that it is ready, the client sends the RFC 822 message data line by line.

You can set data chunk size with smtp_setChunkSize, or you can use the default (1 K). You can set this to specify the chunk sizes used by the smtp_sendStream function.

The smtp_sendStream function, like smtp_send, sends a message to the server.

int smtp_sendStream( smtpClient_t * in_pSMTP, 
                     nsmail_inputstream_t * in_inputStream );
Supply the identifier of the SMTP client that is sending the mail and the identifier for the input stream.

NOTE: For the callback mapping for these functions, see SMTP Function Callback Mapping. §
The following section of code uses smtp_data and smtp_send to send a message.

smtpClient_t * l_psmtpClient = NULL; 
/*Sending the Message*/ 
/* Start by sending identifier of sender */
l_retCode = smtp_data(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
/*Send the Message with smtp_send */ 
l_retCode = smtp_send(l_psmtpClient, "Hi how are you today?"); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
After you send the message and perform any other SMTP operations you need for the session, the next step is Ending the Session.

[Top]

Ending the Session

When the client wants to close the session, the messaging application must free the response sink, free the client, and close the socket connection with the server. For these operations, see Connecting to a Server, Creating a Client, and Connecting to a Server.

The client calls smtp_quit to notify the server. The server closes the TCP connection and returns a response code. It is preferable to end a session with smtp_quit instead of just closing the connection. This function sends the QUIT SMTP protocol command:

int smtp_quit( smtpClient_t * in_pSMTP );
Supply the identifier of the SMTP client that wants to end the session.

NOTE: For this function's callback mapping, see SMTP Function Callback Mapping. §
The following section of code notifies the server that the client is terminating the session.

int l_retCode = 0; 
smtpClient_t * l_psmtpClient = NULL; 
l_retCode = smtp_quit(l_psmtp); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
l_retCode = smtp_processResponses(l_psmtpClient); 
if (l_retCode != NSMAIL_OK) { /*Deal with error*/ } 
[Top]

SMTP Functions by Task

To help you find the information you need more quickly, look for the task you want to perform in the column on the left. Then you can click the function name for further information.

Table 2.5 SMTP Functions by Task

Task Function for the task

Deliver data in binary data chunks to the server.

smtp_bdat

Connect to the server using the default port.

smtp_connect 

Deliver data to the server.

smtp_data

Close the socket connection.

smtp_disconnect 

Send the EHLO command to the server.

smtp_ehlo

Expand a mailing list.

smtp_expand

Free the SMTP client structure and its data members.

smtp_free 

Get the IO models.

smtp_get_option 

Asks the server for help on a specified topic.

smtp_help

Initialize and allocate the smtpClient_t structure and set the sink.

smtp_initialize 

Initiate sending message; supplies the message's reverse path.

smtp_mailFrom

Get a positive server response without affecting the SMTP session.

smtp_noop

Process the server responses for API commands.

smtp_processResponses 

Close the connection with the server.

smtp_quit

Specify a message recipient's address.

smtp_rcptTo 

Reset the state of the server and discard any sender and recipient information.

smtp_reset

Send message data to the server.

smtp_send

Send an unsupported command to the server.

smtp_sendCommand

Sends an SMTP command that is not otherwise supported by this API.

smtp_sendStream

Set the size of data chunks to send.

smtp_setChunkSize 

Set IO and threading models.

smtp_set_option 

Attempt to enable pipelining.

smtp_setPipelining 

Set a new response sink.

smtp_setResponseSink 

Set the amount of time allowed to wait before returning control to the user.

smtp_setTimeout 

Verify a username.

smtp_verify

Free the SMTP response sink and its data members.

smtpSink_free 

Initialize and allocate the SMTP response sink structure.

smtpSink_initialize 
[Top] [SMTP Functions by Task]


Table of Contents | Previous | Next | Index

Last Updated: June 3, 1998

Copyright © 1998 Netscape Communications Corporation