AltaVista Mail API


Digital (TM) Equipment Corporation makes no representations that the use of its products in the manner described in this publication will not infringe on existing or future patent rights, nor do the descriptions contained in this publication imply the granting of licenses to make, use, or sell equipment or software in accordance with the descriptions.

Possession, use, or copying of the AltaVista Mail software described in this publication is authorized only pursuant to a valid written license from Digital or an authorized sublicensor.

Copyright © Digital Equipment Corporation 1996.


This document describes the application programming interface supported by the AltaVista Mail SMTP and POP3 server.

API version:1, 2
Software version:1.0, 2.0
Document revision number:2.0
Document revision date:16 September 1996

If you intend to use this document to help in integrating an application with AltaVista Mail, we strongly encourage you to contact the AltaVista Mail engineering support address, altavista-mail@digital.com, to discuss your requirements. We'll be happy to offer advice, example programs, and any updates to this reference information.


Contents


  1. Introduction
  2. Client model
    1. Mailboxes, usernames and delivery
    2. Users, clients and client types
  3. Client operations
    1. Installing and deinstalling the client
    2. Setting up the client's mailboxes
      1. Per-user or shared mailboxes
      2. Picking mailbox names
      3. Setting mailbox usernames
      4. Setting the Client Type attribute
      5. Creating the mailboxes
    3. Starting the client process
    4. Finding the client's mailboxes
    5. Fetching messages from a mailbox
    6. Processing the recipients of a fetched message
    7. Submitting messages to the SMTP server
  4. Reference
    1. Parameters
    2. Errors
    3. Functions
      1. Avm_Init
      2. Avm_RegisterClientType
      3. Avm_DeregisterClientType
      4. Avm_StartClientType
      5. Avm_ScanMailboxes
      6. Avm_CreateThread
      7. Avm_ScanMessages
      8. Avm_GetMessage
      9. Avm_GetRecipient
      10. Avm_AcceptRecipient
      11. Avm_RejectRecipient
      12. Avm_RedirectRecipient
      13. Avm_ExpandRecipient
      14. Avm_RetryRecipient
      15. Avm_GetHeaderField
      16. Avm_GetContentLine
      17. Avm_PutMessage
      18. Avm_PutRecipient
      19. Avm_PutHeaderField
      20. Avm_PutContentLine
      21. Avm_EndMessage
      22. Avm_AbortMessage

Introduction


The AltaVista Mail SMTP and POP3 server includes a simple application programming interface (API) intended for use by gateways and mail forwarding agents located on the same computer as the server.

The API exists because there is no standardized method for retrieving messages from an SMTP system that will preserve the recipient information required by a gateway. Retrieval would normally be performed using the POP3 protocol, but this protocol discards the actual recipients of a message, leaving only the To: and Cc: fields to which the message was (or may have been) originally directed, not the recipients the local system is actually responsible for. This means that POP3 can be used only for final delivery to a single user. (This hasn't stopped people from writing programs that try to distribute mail further, but such programs cannot work correctly in all cases and can cause message loss, repeated delivery and even indefinite routing loops.)

It is possible to build a gateway colocated with the AltaVista Mail server that does not use this API. We haven't published the file and message structures used by the server, but if you take a look at them you'll discover that they're text-based and extremely simple. You could very easily integrate your own program with the server with no further help.

However, the API offers some substantial benefits.

In general, none of the things the API does is difficult or clever; but if you use the API you'll find you've avoided having to write some extremely messy and error-prone code.


Client model


Mailboxes, usernames and delivery

When AltaVista Mail performs local delivery, it does so by leaving messages in a mailbox. This is a file system directory, and the delivered messages appear there as files, one message per file.

AltaVista Mail perform message delivery to a specific mailbox when the message's recipient address matches one of the usernames associated with that mailbox. A mailbox can have any number of associated usernames. Each username can be a simple string (say, 'User.Name'), or it can include a domain name component (say, 'User.Name@some.domain.name'). If present, the domain name must match the one in the recipient address for delivery to take place.

A username can include a single leading wildcard. This is an asterisk (*) character at the start of the name. It matches any string: a mailbox whose usernames include the value '*abc' will take delivery of any messages whose recipient addresses end with 'abc' before the domain name component (eg 'abc@host.name', 'xxxabc@host.name. and so on). A domain name can again be included (say, '*abc@domain.name'); again, the message recipient's domain name must match for delivery to take place.

Usernames without wildcards take precedence over usernames with wildcards, and more specific wildcards take precedence over less specific. Consider mailboxes A, B and C with usernames of '*', '*a' and 'cba' respectively. A message sent to 'xxx@host.name' will be delivered to mailbox A; one sent to 'aaa@host.name' will be delivered to B; and one sent to 'cba@host.name' will be delivered to C.


Users, clients and client types

Each mailbox serves some number of users - the users whose usernames are associated with the mailbox. Any message delivered to the mailbox is on its way to one or more of those users. The message's next step is to be fetched out of the mailbox by some kind of client program.

To determine the behaviour appropriate to a mailbox, each mailbox has an associated client type. So, for example, a mailbox of type 'POP3' tells the server to offer access to the mailbox using the POP3 protocol; and a mailbox of type 'list' tells the server to forward messages delivered to the mailbox to the mailing list members associated with the mailbox.

When you write a gateway or other client program, you are defining a new client type. Installing or running your gateway will make the type known to the AltaVista Mail server, and mailboxes of your client type can be created, either by your own setup program or by an AltaVista Mail administrator. These mailboxes become your gateway's responsibility: you must fetch and process any messages delivered there. (The API includes a service to find all the mailboxes you must serve, so it's not difficult to do.)

The ability to serve multiple mailboxes may not seem necessary to your application, but it's very useful. In general, you should use a single mailbox to denote a single user community or a single distinct configuration. (A mailbox has its own directory and you can store configuration files there if you wish - don't give them a file extension of '.821' and the server won't think they're messages.)

For example, a cc:Mail gateway might use a single mailbox to denote a connection to a single cc:Mail post office. A single gateway can then support connections to multiple cc:Mail post offices by serving multiple mailboxes.

Another example might be a redirection server that uses a mailbox per organization served. Then if an organization is no longer to be served, it can be deleted as a unit without having to weed out individual redirection entries.

Many gateways have similar properties, but even if it isn't particularly useful to collect your gateway's user community into groups, you should at least serve all your mailboxes, treating them all in the same way: then if an administrator makes a configuration error and gives another mailbox your client type you don't stop serving the mailbox you did intend to serve.


Client operations


As far as the API is concerned, the tasks performed by a gateway or other client program consist of:


Installing and deinstalling the client

To install your client and have it automatically started on system startup, call Avm_RegisterClientType. This registers your client program as a Windows NT service running under AltaVista Mail's own Windows NT account, and makes its client type known to the server.

To deinstall your client, call Avm_DeregisterClientType. This deletes the Windows NT service and removes its client type from AltaVista Mail's list of known client types.

If the provided startup mechanism is inconvenient, for example because your client must run under a specific account, you don't have to call these services. If you don't, you are responsible for starting and stopping your client process, and must ensure that the account you do use has sufficient privilege to access the AltaVista Mail directories.


Setting up the client's mailboxes

All local delivery is performed through mailboxes, so if your client will be responsible for accepting mail from the Internet, you'll have to set up one or more mailboxes to receive it. At the very least, you'll have to tell the AltaVista Mail administrator how to set them up.

Per-user or shared mailboxes

If your client knows all its users individually, you can set up a mailbox for each user in advance of their being able to use the client. Mail for a user is then queued to the user's own mailbox, and you can retrieve it from there. Each user can choose their own username(s), and you can if necessary store per-user information in each mailbox. Any messages sent to multiple users will be processed by your client multiple times - every message you see will be bound for a single user, even if it appears to have multiple recipients.

If your client can't be expected to know all its users, for example because it's a gateway to a foreign mail system and you can't or don't want to resolve changes in the user base, you can set up a shared mailbox. This typically serves an entire user community. While you can still allow users to choose their own usernames (you just add all the usernames to the list associated with the mailbox), it's usually more convenient to have all your users share an address stub (a common trailing part of all email addresses), and register that stub as a wildcard username on the mailbox.

Picking mailbox names

Mailboxes are named within an hierarchical structure that reflects the file system directories used to implement the mailbox storage. The top level of the structure is initially set up by AltaVista Mail itself.

You should pick a mailbox naming scheme sympathetic to that already present. If you serve individual end users, put their mailboxes under the 'Users' hierarchy; if you're building a gateway, put them under 'Gateways'; if a list server, under 'Mailing lists'. If you will routinely support multiple mailboxes, consider grouping yours under a common dummy mailbox (one with no usernames, so no delivery will be made there, and an Undefined client type).

Setting mailbox usernames

The usernames you assign to your mailboxes completely determine which messages are delivered there. For a mailbox that serves one or more named end users, assign those users' usernames. For a mailbox serving an indefinite number of users, pick a wildcard username. For example, if you assign a username of '*%mygate' to a mailbox, all recipients whose email addresses end in '%mygate@local.host.name' will be delivered to the mailbox.

The percent (%) sign in the username above is quite a good delimiter character. Internet users are familiar with a percent sign being used to access gateways, as it's commonly used to invoke the source routing mechanism that operates many gateways.

Setting the Client Type attribute

Your client is known to AltaVista Mail by its client type and description. You need to choose suitable values for these. A client type is typically one or two short words, and a description up to five or six words; combined, they must be recognisable from the Client Type drop-down box in the administration GUI. Use only alphanumeric characters in your client type string.

Examples of client types and descriptions are:

Assign this colon-separated form of the client type and description to the Client Type attribute of each of your mailboxes.

Creating the mailboxes

To create your mailboxes, you can if necessary provide specific instructions to the AltaVista Mail administrator. This might be appropriate for per-user mailboxes - the task is similar to creating POP3 mailboxes.

If your gateway will typically serve only a small and fixed number of mailboxes, it'd be a much better solution if you could create the mailbox yourself from your setup code. To do this, use the AltaVista Mail administration protocol. This is easier than it sounds, and you can telnet to port 1655 to try it for yourself: here, for example, is the complete protocol exchange to create a mailbox. (Commands to the server are shown in italic type, responses in Roman.)

+OK AltaVista Mail F1.0/1.0 BL17 Admin <41.833299608.168@local.host.name>
login clear postmaster really-bad-password
+OK Postmaster
create mailbox my-gateway@Gateways
+OK Mailbox created
set mailbox my-gateway@Gateways
+OK <Attribute>: <Value>?  '.' to finish
ClientType: MyGate: My own gateway
+OK <Attribute>: <Value>?  '.' to finish
Usernames: *%mygate
+OK <Attribute>: <Value>?  '.' to finish
.
+OK Done
quit
+OK Administration server signing off

Here, a mailbox called my-gateway has been created subordinate to the existing (dummy) Gateways mailbox. (Position within the hierarchy is not significant to AltaVista Mail - it's arranged at the administrator's convenience.) Its client type is 'MyGate', client description is 'My own gateway', and it receives mail for all addresses that end with '%mygate@local.host.name'.

The AltaVista Mail administration protocol is documented separately.


Starting the client process

If you've used Avm_RegisterClientType to install your client program as a Windows NT service, the program will be run automatically on system startup. If you haven't, you must make your own arrangements to start the program.

In either case, you must make two calls to initialize the API.

First call Avm_Init. This tells the API which version of the API specification your calls will be following: any given set of API libraries may support multiple versions. To use the calls described in this specification, ask for version 1 or 2: version 1 if your application will need to work against an AltaVista Mail version 1.0 server, and version 2 if you require behaviour added in version 2.0 of the server. Except where otherwise noted, this document describes behaviour common to both versions. The major differences in the API versions are in the Avm_GetContentLine and Avm_PutContentLine functions.

Next, call Avm_StartClientType. This tells the API which client type your process is serving.

Call these routines as soon as you can after your process starts up. If you've used Avm_RegisterClientType to start up as an NT service, your call to Avm_StartClientType will contact the NT Service Control Manager to confirm that the process has been created successfully, and a long delay will make Windows NT believe there's something wrong. So if you have lengthy tasks to perform during process initialization, don't wait for them to complete before making these calls. If a later phase of initialization fails, you're free to let the process die.


Finding the client's mailboxes

Your client is potentially responsible for fetching messages from multiple mailboxes. To find the mailboxes you serve, call Avm_ScanMailboxes until it says there are no more.

For most gateways, it's convenient to write a routine that serves a single mailbox, and then create a thread running that routine for each mailbox you find. To create a thread, call Avm_CreateThread.

You don't have to make your gateway a multithreaded program, and even if you do, you don't have to use Avm_CreateThread. It's provided as a convenience in case you wouldn't otherwise have been writing multithreaded code.


Fetching messages from a mailbox

Fetching messages involves using two API functions. Call Avm_ScanMessages to get the filenames of the messages in the mailbox; and for each of these filenames, call Avm_GetMessage to start processing the message.

Scanning for message filenames is handled separately from opening the messages to give you some control over how and when you process specific messages. Suppose you have problems in handling a message, perhaps because some remote resource required by the message is unavailable. If you remember its filename, you can skip over it when you see it on subsequent scans, and try processing it again only when enough time has elapsed to make a retry worthwhile.

To fetch the various items of message data, use the following API calls in the order given:

If during processing you find that you can't handle the message, call Avm_AbortMessage. This closes the message but leaves it in the mailbox, so future Avm_ScanMessages loops will see it again. Any stored log information and messages generated during recipient processing are discarded. If any of the above API functions fails (returns a negative value), Avm_AbortMessage has already been called: you need to call it yourself only for problems that you detect in your own code.


Processing the recipients of a fetched message

Fetching a message means taking responsibility for the message's transfer, and hence taking responsibility for processing each of the message's recipients. In general, recipients must be handled independently of each other: processing some may succeed while others fail, and some failures will be temporary and others permanent. The AltaVista Mail API provides services to make it easy to handle these different outcomes.

Whenever you finish processing a recipient returned by Avm_GetRecipient, call one of the following functions:

If you make your next call to Avm_GetRecipient without having made any of the above calls, you are assumed to have accepted the recipient. Note, though, that you won't have had the opportunity to report the translated recipient address, as you would if you'd called Avm_AcceptRecipient.


Submitting messages to the SMTP server

Submitting messages to the SMTP server involves a sequence of calls similar to fetching and processing messages in a mailbox. For each message you want to submit, call the following functions in the order given:

If while submitting the message you encounter a problem, you can abort the submission by calling Avm_AbortMessage. This deletes the message you're constructing and discards any log information associated with it. If any of the above API functions fails (returns a negative value), Avm_AbortMessage has already been called: you need to call it yourself only for problems that you detect in your own code.


Reference


Parameters

Individual descriptions for parameters to the API functions are collected here. All string parameters are null-terminated.

Errors

All the AltaVista Mail API functions indicate an error by returning a negative value. The precise cause of any error is logged in the Errors log, which you can monitor using the administration GUI or a Web browser (point it to http://client.host.name:1656/ ). The error cause is not made available to your calling code.

When a function that reads or writes message data encounters an error and returns a negative value, it has already called Avm_AbortMessage. There's no need for you to repeat this call (but it does no harm if you do). The functions that do this are Avm_GetMessage, Avm_GetRecipient, Avm_AcceptRecipient, Avm_RejectRecipient, Avm_RedirectRecipient, Avm_ExpandRecipient, Avm_RetryRecipient, Avm_GetHeaderField, Avm_GetContentLine, Avm_PutMessage, Avm_PutRecipient, Avm_PutHeaderField, Avm_PutContentLine, and Avm_EndMessage.


Functions

Avm_Init

Initialize the API.

int Avm_Init                            /* 2=high, 1=OK, 0-low, -ve=error */
        (int Version);                  /* API version required */

For the semantics described by this version of the specification, pass a Version of 1 (if your application needs to work against an AltaVista Mail version 1.0 server) or 2.

Avm_RegisterClientType

Make a client type known to the system. Call this function from your setup program. You'll need to run setup from an administrator's account.

int Avm_RegisterClientType              /* -ve=error */
        (const char *ClientType,        /* Type identifier */
        const char *ClientDescription,  /* Descriptive name for type */
        const char *Program);           /* Program serving client type */

Avm_DeregisterClientType

Remove the system's knowledge of a client type. Call this function from your deinstallation program. You'll need to do this from an administrator's account.

int Avm_DeregisterClientType            /* -ve=error */
        (const char *ClientType,        /* Type identifier */
        const char *ClientDescription); /* Descriptive name for type */

Avm_StartClientType

Initialize this process as the server for a client type. You can serve only one client type per process, and one process per client type.

int Avm_StartClientType                 /* -ve=error */
        (const char *ClientType,        /* Client's type identifier */
        const char *ClientDescription); /* Descriptive name for type */

Avm_ScanMailboxes

Find the mailboxes served by a specific client type.

int Avm_ScanMailboxes                   /* 1=OK, 0=no more, -ve=error */
        (const char *ClientType,        /* Type identifier */
        const char *ClientDescription,  /* Descriptive name for type */
        char *Directory,                /* Mailbox directory */
        char *Address,                  /* Mailbox address stub */
        void **Context);                /* Context, initially &NULL */

Avm_CreateThread

Spawn a detached thread.

typedef int (*AvmThread) (void *Parameter);
int Avm_CreateThread			/* -ve=error */
	(AvmThread Main,		/* Thread's main routine */
	void *Parameter);		/* Parameter to main routine */

This function calls the main routine you pass to it with the parameter you provide in a thread detached from the current thread of execution.

Note that such a detached thread runs completely independently, so it cannot share the values of any local variables with your calling code, or even parameters passed to the calling routine. If you wish to pass parameters to the new thread, allocate a block of virtual memory, copy the parameters into the block, and pass the address of the block to the thread. Get the thread to deallocate the memory when it's finished with the parameters. (If the call to Avm_CreateThread fails, you will have to deallocate the memory in the calling routine: the thread can't do so because it didn't get created.)

Avm_ScanMessages

Find the messages delivered to a mailbox.

int Avm_ScanMessages                    /* 1=OK, 0=no more, -ve=error */
        (const char *ClientType,        /* Client's type identifier */
        const char *ClientDescription,  /* Descriptive name for type */
        const char *Directory,          /* Mailbox directory */
        char *Filename,                 /* Message filename; NULL=abort */
        void **Context);                /* Context, initially &NULL */

Avm_GetMessage

Open a message in a mailbox.

int Avm_GetMessage                      /* 1=OK, 0=gone/locked, -ve=error */
        (const char *ClientType,        /* Client's type identifier */
        const char *ClientDescription,  /* Descriptive name for type */
        const char *Directory,          /* Mailbox directory */
        const char *Address,            /* Mailbox address stub */
        const char *Filename,           /* Message filename */
        AvmMessage *Message,            /* Handle on message */
        char *ReturnPath,               /* Originator, usually */
        char *ReceivedId);              /* Message Received-ID */

To find the message filenames to pass to Avm_GetMessage, use Avm_ScanMessages.

If Avm_GetMessage returns zero, the message is either no longer present in the mailbox, or it's currently locked by another process. This is not normally a serious problem, so you should not treat it as an error.

Avm_GetRecipient

Get the next recipient of a message retrieved from a mailbox.
int Avm_GetRecipient                    /* 1=OK, 0=no more, -ve=error */
        (AvmMessage *Message,           /* from _GetMessage */
        char *Recipient);               /* Next recipient */

Call Avm_GetRecipient immediately after your call to Avm_GetMessage, and continue calling it until it returns zero (or negative).

Avm_AcceptRecipient

Report acceptance of a recipient.

int Avm_AcceptRecipient                 /* -ve=error */
        (AvmMessage *Message,           /* from _GetMessage */
        const char *ForeignRecipient);  /* Recipient's foreign address */

Call Avm_AcceptRecipient immediately after the Avm_GetRecipient call that returned the recipient you want to accept. You do not have to call this function, but if you don't the translated address won't be logged.

Avm_RejectRecipient

Request non-delivery of a recipient.

int Avm_RejectRecipient                 /* -ve=error */
        (AvmMessage *Message,           /* from _GetMessage */
        const char *Term,               /* Offending address term */
        const char *Reason);            /* Rejection reason */

Call Avm_RejectRecipient immediately after the Avm_GetRecipient call that returned the recipient you want to reject.

Avm_RedirectRecipient

Redirect a recipient to another email address.

int Avm_RedirectRecipient               /* -ve=error */
        (AvmMessage *Message,           /* from _GetMessage */
        const char *ReturnPath,         /* New return path; NULL=no change */
        const char *Recipient);         /* New recipient */

Call Avm_RedirectRecipient immediately after the Avm_GetRecipient call that returned the recipient you want to redirect. You can provide a new return path to change the destination for non-delivery reports, but if the existing return path is null (ie this message is itself a report) it will be ignored.

Avm_ExpandRecipient

Redirect a recipient to a list of email addresses.

int Avm_ExpandRecipient                 /* -ve=error */
        (AvmMessage *Message,           /* from _GetMessage */
        const char *ReturnPath,         /* New return path; NULL=no change */
        const char *Recipient);         /* New recipient */

Call Avm_ExpandRecipient immediately after the Avm_GetRecipient call that returned the recipient you want to expand, once for each new recipient.

Do not call this routine unless you know you're expanding a list. If your system doesn't distinguish between redirection and list expansion, make this call only if you have more than one replacement recipient. If you don't, the logging will be wrong.

You can provide a new return path to change the destination for non-delivery reports, but if the existing return path is null (ie this message is itself a report) it will be ignored. Also, you must provide the new return path for a given recipient on the first call for that recipient; values given on subsequent calls will be ignored.

Avm_RetryRecipient

Send a recipient back into the mailbox for retry at some later point.

int Avm_RetryRecipient                  /* -ve=error */
        (AvmMessage *Message);          /* from _GetMessage */

Call Avm_RetryRecipient immediately after the Avm_GetRecipient call that returned the recipient you want to retry.

Avm_GetHeaderField

Get the next RFC 822 header field.

int Avm_GetHeaderField			/* 1=OK, 0=no more, -ve=error */
	(AvmMessage *Message,		/* from _GetMessage */
	int FieldValueBufferSize,	/* Size of field value buffer */
	char *FieldName,		/* Field name */
	char *FieldValue);		/* Field value */

Call this routine immediately after Avm_GetRecipient returns zero, and continue calling it until it returns zero (or negative). (In API version 2, you can instead use Avm_GetContentLine to retrieve header fields exactly as they appear in the message: just omit the calls to Avm_GetHeaderField.)

If the header field is split across multiple lines, Avm_GetHeaderField joins the lines back into a single value parameter. Extremely long header fields will be truncated; you can avoid this by passing a value buffer large enough to contain any reasonable field size.

If you're not interested in the actual message you've retrieved because you've rejected, redirected, expanded or retried all of the message recipients, you can skip this and subsequent stages and make your final call to Avm_EndMessage. The header and content data will be transcribed into the output messages without your having to read it.

Avm_GetContentLine

Get the next line of content data.

int Avm_GetContentLine                  /* 1=OK, 0=no more, -ve=error */
        (AvmMessage *Message,           /* from _GetMessage */
        char *Line);                    /* Line of data.  Allow 1K. */

Call Avm_GetContentLine after Avm_GetHeaderField returns zero, and continue calling it until it returns zero (or negative). (In API version 2, you can also use this function to retrieve header field data, including the header's terminating blank line: to do this, start your calls after Avm_GetRecipient returns zero.)

Content lines should normally limited to 80 characters, and it's illegal under SMTP to ship lines longer than 1000 characters. Although AltaVista Mail will accept and transfer illegally long lines, the Avm_GetContentLine function will truncate them. You should allow 1024 characters for a line buffer.

Avm_PutMessage

Create a new message bound for the SMTP system.

int Avm_PutMessage                      /* -ve=error */
        (const char *ClientType,        /* Client's type identifier */
        const char *ClientDescription,  /* Descriptive name for type */
        const char *Directory,          /* Mailbox directory */
        const char *Address,            /* Mailbox address stub */
        const char *ReturnPath,         /* Originator, usually */
        AvmMessage *Message,            /* Handle on new message */
        char *ReceivedId);              /* Received-ID that will be allocated */

The mailbox directory and address stub parameters are used only for logging purposes - messages are given to the SMTP server itself, not a mailbox.

Avm_PutRecipient

Add a recipient to an SMTP message.

int Avm_PutRecipient                    /* -ve=error */
        (AvmMessage *Message,           /* from _PutMessage */
        const char *Recipient,          /* Envelope recipient */
        const char *ForeignRecipient);  /* Recipient's foreign address */

Call Avm_PutRecipient immediately after Avm_PutMessage, once for each recipient.

Avm_PutHeaderField

Add a header field to an SMTP message.

int Avm_PutHeaderField                  /* -ve=error */
        (AvmMessage *Message,           /* from _PutMessage */
        const char *FieldName,          /* Field name */
        const char *FieldValue);        /* Field value (no CRLFs) */

Call Avm_PutHeaderField immediately after your last call to Avm_PutRecipient, once for each header field. (In API version 2, you can instead use Avm_PutContentLine to add preformatted header fields: just omit any calls to Avm_PutHeaderField.) Long header fields will automatically be split onto multiple lines. (You may not like where this happens, so avoid this if you can. For example, when adding a Content-type: field for a multipart MIME message, use the shortest suitable Boundary value you can generate.)

Avm_PutContentLine

Add a content line to an SMTP message.

int Avm_PutContentLine                  /* -ve=error */
        (AvmMessage *Message,           /* from _PutMessage */
        const char *Line);              /* Content line (no CRLFs) */

Call Avm_PutContentLine after the last call to Avm_PutHeaderField, once for each line of content data. Aim to limit your lines to 80 characters. If you don't, Avm_PutContentLine will split the line at a place of its choosing. (In API version 2, you can use this function to add preformatted header field data, including the header's terminating blank line: to do this, start calling Avm_PutContentLine immediately after your last call to Avm_PutRecipient.)

Avm_EndMessage

Finish processing a message.

int Avm_EndMessage                      /* -ve=error */
        (AvmMessage *Message,           /* from _GetMessage/_PutMessage */
        const char *ForeignReturnPath,  /* Return path's foreign address */
        const char *ForeignId);         /* Foreign message-ID */

When processing a fetched message, call Avm_EndMessage after your last call to Avm_GetContentLine (or your last recipient-processing function if you weren't actually looking at the data) to delete the message from the mailbox and release any derived messages and log information.

When creating a new message, call Avm_EndMessage after your last call to Avm_PutContentLine to release it and any derived log information to the SMTP server.

Avm_AbortMessage

Give up processing a message.

int Avm_AbortMessage                    /* -ve=error */
        (AvmMessage *Message);          /* from _GetMessage/_PutMessage */

When processing a fetched message, call Avm_AbortMessage to give up, returning the message to the mailbox and discarding any derived messages and log information.

When creating a new message, call Avm_AbortMessage to give up, discarding the message structures and log information created so far.