Sun Java System Messaging Server 6 2005Q4 MTA Developer's Reference

Chapter 3 Enqueuing Messages

The MTA SDK provides routines with which to construct a mail message and then submit the message to the MTA. The MTA then effects delivery of the message to its recipients. The act of submitting a message to the MTA for delivery is referred to as “enqueuing a message.” This choice of terminology reflects the fact that each message submitted to the MTA for delivery is placed into one or more message queues. Using its configuration, the MTA determines how to route each message to its destination and which message queues to place each the message into. However, programs enqueuing messages do not need to concern themselves with these details; they merely supply the message’s list of recipients and the message itself. The recipients are specified one-by-one as RFC 2822 conformant Internet email addresses. The message header and content is supplied in the form of an RFC 2822 and MIME conformant email message.

When starting a coding project to enqueue messages to the MTA, always stop to consider whether simply using SMTP will be acceptable. The advantage of using SMTP is that it will work with any MTA SMTP server, making it portable. The disadvantages are poorer performance and lack of flexibility and control.

This chapter covers the following enqueuing topics:

Basic Steps to Enqueue Messages

The basic steps necessary to enqueue one or more messages to the MTA are:

  1. Initialize SDK resources and data structures with mtaInit().

  2. For each message to enqueue:

    1. Specify the message envelope with mtaEnqueueStart() and mtaEnqueueTo().

    2. Specify the message header with mtaEnqueueWrite() or mtaEnqueueWriteLine().

    3. Optionally, if a message body is to be supplied, terminate the message header and start the message body by writing a blank line to the message with mtaEnqueueWrite() or mtaEnqueueWriteLine().

    4. Optionally if a message body is to be supplied, write the message body with mtaEnqueueWrite() or mtaEnqueueWriteLine().

    5. Submit the message with mtaEnqueueFinish().

  3. When you have completed enqueuing messages, deallocate SDK resources and data structures with mtaDone().

In Step 2e, mtaEnqueueFinish() commits the message to disk. As part of the enqueue process, the MTA performs any access checks, size checks, format conversions, address rewritings, and other tasks called for by the site’s MTA configuration. After these steps are completed and the message has been successfully written to disk, mtaEnqueueFinish() returns.

Other MTA processes controlled by the MTA Job Controller then begin processing the new message so as to effect its delivery. In fact, these processes may begin handling the new message before mtaEnqueueFinish() even returns. As such, mtaEnqeueueFinish() doesn’t block waiting on these processes; it returns as soon as all requisite copies of the enqueued message have been safely written to disk. The subsequent handling of the newly enqueued message is performed by other MTA processes, and the program which enqueued the message isn’t left waiting for them.

A message submission can be aborted at any point in Step 2 by calling either mtaEnqueueFinish() with the MTA_ABORT option specified or mtaDone(). Using the first method, mtaEnqueueFinish() aborts only the specified message enqueue context while allowing additional messages to be enqueued. Whereas, mtaDone() aborts all active message enqueue contexts in all threads, and deallocates SDK resources disallowing any further submission attempts until the SDK is again initialized.

Originating Messages

Messages enqueued to the MTA fall into one of two broad classes: new messages being originated and messages which were originated elsewhere and which are being transferred into the MTA. The former are typically the product of a local user agent or utility which uses the MTA SDK. The latter are generated by remote user agents, and received by local programs such as SMTP or HTTP servers which then enqueue them to the MTA for routing or delivery or both. In either case, it is the job of the MTA to route the message to its destination, be it a local message store or a remote MTA.

The only distinction the MTA SDK makes between these two cases occurs when the message’s recipient addresses are specified. For new messages being originated, the recipient addresses should be added to both the message’s header and its envelope. For messages originated elsewhere, the recipient addresses should only be added to the message’s envelope. For a discussion of messages originated elsewhere, see Transferring Messages into the MTA, and Intermediate Processing Channels.

When originating a new message, it is easiest to use the MTA_TO, MTA_CC, and MTA_BCC item codes with mtaEnqueueTo(). That tells the SDK to use the specified addresses as both the envelope recipient list and to put them into the message’s header. When using this approach, do not specify any From:, To:, Cc:, or Bcc: header lines in the supplied message’s header; the SDK will add them automatically.

An example of using this approach is found in the section that follows.

A Simple Example of Enqueuing a Message

The program shown in Example 3–1 demonstrates how to enqueue a simple “Hello World” message. The originator address associated with the message is that of the MTA postmaster. The recipient address can be specified on the invocation command line.

After the Messaging Server product is installed, this program can be found in the following location:

msg_server_base/examples/mtasdk/

Note that certain lines of code have numbered comments immediately preceding them of the format:

/* This generates output line N */

where N corresponds to the numbers found next to certain output lines in the sample output in Enqueuing a Message Example Output.

Refer to Running Your Test Programs for information on how to run the sample program.


Example 3–1 Enqueuing a Message


/* hello_world.c -- A simple "Hello World!" enqueue example */
#include <stdio.h\>
#include <stdlib.h\>
#include "mtasdk.h"

mta_nq_t *ctx = NULL;
static void quit(void);
#define CHECK(x) if(x) quit();

void main(int argc, const char *argv[])
{
     char buf[100];

     /* Initialize the SDK */
     CHECK(mtaInit(0));

     /* Start a new message; From: postmaster*/
     /* This generates output line 1 */
     CHECK(mtaEnqueueStart(&ctx, mtaPostmasterAddress(NULL, NULL,
                           0), 0, 0));

     /* Enqueue the message to argv[1] or root */
     /* This generates output line 2 */
     CHECK(mtaEnqueueTo(ctx, (argv[1] ? argv[1] : "root"), 0, 0));

     /* Date: header line */
     /* This generates output line 3 */
     CHECK(mtaEnqueueWriteLine(ctx, "Date: ", 0, mtaDateTime(buf,
                               NULL, sizeof(buf), 0), 0, NULL))

     /* Subject: header line */
     /* This generates output line 4 */

     CHECK(mtaEnqueueWriteLine(ctx, "Subject: " __FILE__, 0,
                               NULL));

     /* Blank line ending the header, starting the message body */
     /* This generates output line 5 */
     CHECK(mtaEnqueueWriteLine(ctx, "", 0, NULL));

     /* Text of the message body (2 lines) */
     /* This generates output line 6 */
     CHECK(mtaEnqueueWriteLine(ctx, "Hello", 0, NULL));
     /* This generates output line 7 */
     CHECK(mtaEnqueueWriteLine(ctx, " World!", 0, NULL));

     /* Enqueue the message */
     CHECK(mtaEnqueueFinish(ctx, 0));

     /* All done */
     mtaDone();
}

void quit(void)
{
     fprintf(stderr, "The MTA SDK returned the error code %d\n
             %s", mta_errno, mtaStrError(mta_errno, 0));
     if (ctx)
         mtaEnqueueFinish(ctx, MTA_ABORT, 0);
     exit(1);
}

Enqueuing a Message Example Output

The example that follows shows the output generated by the enqueuing example. Comment numbers correspond to the numbered comments in Example 3–1.

Comment Number 

Output Lines 

 

Received:from siroe.com by siroe.com (SunONE Messaging Server 6.0)id

<01GP37SOPRW0A9KZFV@siroe.com\>; Fri, 21 Mar 2003 09:07:32 -0800(PST)

Date: Fri, 21 Mar 2003 09:07:41 -0800 (PST)

From: postmaster@siroe.com

To: root@siroe.com

Subject: enqueuing_example.c Message-id: <01GP37SOPRW2A9KZFV@siroe.com\> Content-type: TEXT/PLAIN; CHARSET=US-ASCII Content-transfer-encoding: 7BIT

Hello

World!

Transferring Messages into the MTA

When transferring a message originated elsewhere into the MTA, programs should use the MTA_ENV_TO item code with mtaEnqueueTo(). This way, each of the recipient addresses will only be added to the message’s envelope, and not to its already constructed header. Additionally, supply the message’s header as-is. Do not remove or add any origination or destination header lines unless necessary. Failure to use the MTA_ENV_TO item code will typically cause the SDK to add Resent- header lines to the message’s header.

A Complex Dequeuing Example, and A Simple Virus Scanner Example both illustrate the use of the MTA_ENV_TO item code.

Intermediate Processing Channels

Like programs which transfer messages into the MTA, intermediate processing channels should also use the MTA_ENV_TO item code with mtaEnqueueTo(). When re-enqueuing a message, intermediate processing channels should also preserve any MTA envelope fields present in the message being re-enqueued. This is done using the MTA_DQ_CONTEXT item code in conjunction with mtaEnqueueStart() and mtaEnqueueTo(). Failure to preserve these envelope fields can result in loss of delivery receipt requests, special delivery flags, and other flags which influence handling and delivery of the message.

A Complex Dequeuing Example and A Simple Virus Scanner Example both illustrate the use of the MTA_ENV_TO and MTA_DQ_CONTEXT item codes. item codes. Both of those examples represent intermediate processing channels that handle previously constructed messages. As such, they do not need to alter the existing message header, and they preserve any MTA envelope fields.

Delivery Processing Options (Envelope Fields)

A variety of delivery processing options may be set through the MTA SDK. These options are then stored in the message’s envelope and are generically referred to as “envelope fields.” Options which pertain to the message as a whole are set with mtaEnqueueStart(). Options which pertain to a specific recipient of the message are set with mtaEnqueueTo(). These options, per message and per recipient, include the following:

Delivery flags 

These flags are used to communicate information between channels. For instance, a scanning channel might set the flag to indicate suspected spam content. A delivery channel could then see that the flag is set and, at delivery time, add a header line indicating potential spam content. These flags may also be set using the deliveryflags channel keyword.

Notification flags 

These flags influence whether delivery or non-delivery notification messages are generated. They can be set on a per recipient basis. Typically, they are used to request a delivery receipt. Another common usage is for bulk mail to request no notifications, neither delivery nor non-delivery. 

Original recipient address 

This field is specified on a per recipient basis. It is used to indicate the original form of the associated recipient’s address. This original address can then be used in any notification messages. Its use allows the recipient of the notification to see the original address they specified rather than its evolved form. For example, the recipient would see the name of the mailing list they posted to rather than the failed address of some member of the list. 

Envelope ID 

Set on a per message basis, this is an RFC 1891 envelope ID and can appear in RFC 1892 - 1894 conformant notifications about the message. 

Fragmentation size 

Set on a per message basis, this controls if and when the message is fragmented into smaller messages using the MIME message/partial mechanism.

For additional information, see the descriptions of mtaEnqueueStart(), and mtaEnqueueTo().

Order Dependencies

When you are constructing programs, there is a calling order for the MTA SDK routines that must be observed; for a given enqueue context, some routines must be called before others.

Figure 3–1 visually depicts the calling order dependency of the message enqueue routines. To the right of each routine name appears a horizontal line segment, possibly broken across a column, for example, mtaEnqueueWrite(). Routines for which two horizontal line segments, one atop the other, appear are required routines; that is, routines that must be called in order to successfully enqueue a message. These routines are mtaEnqueueStart(), mtaEnqueueTo(), and mtaEnqueueFinish(). To determine at which point a routine may be called, start in the leftmost column and work towards the rightmost column. Any routine whose line segment lies in the first (leftmost) column may be called first. Any routine whose line segment falls in the second column may next be called, after which any routine whose line segment falls in the third column may be called, and so forth. When more than one routine appears in the same column, any or all of those routines may be called in any order. Progression from left to right across the columns is mandated by the need to call the required routines. Of the required routines, only mtaEnqueueTo() may be called multiple times for a given message.

Figure 3–1 Calling order Dependency for Message Enqueue Routines

The calling order dependency for message enqueue routines. mtaInit,
mtaEnqueueStart, metEnqueueTo, and mtaEnqueueFinish are required.