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.
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.
Of course, the API will change as well, but we aim to preserve compatibility with the original semantics where possible.
These facilities aren't comprehensive - they don't necessarily mean you never have to generate your own non-delivery reports, for example - but they can greatly simplify your recipient and message queueing requirements.
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.
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.
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.
As far as the API is concerned, the tasks performed by a gateway or other client program consist of:
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.
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.
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.
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).
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.
Examples of client types and descriptions are:
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.
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.
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 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.
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:
This function is of most use to clients whose major purpose is to redirect mail.
This function is of most use to mailing list servers.
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.
Call this function once for every recipient.
Call this function once for every header field. (In API version 2, you can instead use Avm_PutContentLine to add preformatted header fields and their terminating blank line exactly as you want them to appear in the message, omitting any calls to Avm_PutHeaderField.)
Call this function once for every content line.
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.
To provide an easy migration path as the mail system is developed, any given set of AltaVista Mail libraries may support multiple versions of the API specification. This specification describes version 1, so to get the behaviour described here, use the value 1.
A client type and client description are the names that distinguish your program from the other kinds of client that may be integrated with AltaVista Mail.
The full file specification of your client's executable program.
The directory that serves as the message storage area for a mailbox. If you wish, you can store your own data in the directories your client serves. Don't use any filename that ends in '.821' or '.tmp', though, and don't use any filenames used by AltaVista Mail for its own operation. (All AltaVista Mail's data files have names that start with an underscore - '_' - so if you pick some other leading character you should be safe.)
The Address parameter is the trailing part of the addresses served by a mailbox. That is, if Avm_ScanMailboxes returns an Address parameter with any given mailbox, any recipient address that ends with the value returned will be delivered to that mailbox.
The Address parameter provides a convenient way for your client to construct Internet email addresses. Suppose a gateway to some foreign mail system serves a mailbox whose Address value is '%foreign@local.host.name'. For each user of the foreign mail system, it can append this value to the foreign recipient addresses - 'Fred.Bloggs' becomes 'Fred.Bloggs%foreign@local.host.name'. A message sent to this email address will normally be delivered to the gateway's mailbox.
Note that the Address parameter simply gives access to any wildcard usernames associated with the mailbox. Since it's a wildcard, it can be overridden by a more specific wildcard or completely-specified username on another mailbox. This shouldn't be a problem under normal circumstances, but you should be aware that another client can claim part of your client's username space.
A Context parameter is used by functions that scan lists of items. A scan function retrieves only one item per call, so multiple calls are needed to traverse an entire list. The Context parameter keeps track of the progress of the scan between calls.
To start a scan, assign a NULL value to a pointer variable, and call the scan function to retrieve your first result, passing the address of your pointer variable as the Context parameter. For subsequent calls, don't change the Context parameter or the variable it points to.
The scan function returns 1 for each item it retrieves. When there are no more it returns zero and assigns a NULL value to your Context variable. If you want to abort the scan, pass a NULL value for the scan function's output parameters: the scan will then skip the remaining values and return zero with a NULL Context variable as though you'd reached the end of the list.
The Main parameter provides the address of a main routine you want to call. The Parameter parameter provides a pointer to pass to that routine.
The filename of a message file in a mailbox.
A Message parameter is an opaque handle on a message being fetched from a mailbox or built for submission to the SMTP server. All operations on messages are performed using API functions.
A message's return path is the Internet email address to which any reports will be sent if the message is non-delivered. If empty, the message is itself a report.
A foreign return path is a foreign mail system's representation of a return path. It's used by gateways, to record the correspondence between an Internet email address and the foreign address to or from which it was translated: since the translation may not be obvious, the foreign version must be logged to aid in message tracing.
A ReceivedId is a unique identifier given to a specific instance of a message. It's the ID component of the message's first (most recent) Received: field. It is used within the AltaVista Mail logs to correlate all information about local operations on messages: using the Received-ID, you can work out exactly what's been done to any message in the local SMTP server.
A Received: field is created (and its corresponding ID field assigned) when a message enters the SMTP server. Most operations on the message leave the field intact: so, for example, when a message's recipients are served by distinct targets, the message is split to each target, but each split retains the originally-assigned Received: field.
A Received: field does change when the message replaces any of its recipients. When a recipient is redirected or expanded to a mailing list, a new Received: field is added to the new message to record the original recipient (and return path, if it's been replaced). When a message is non-delivered, the non-delivery report gets a complete new Received: list (it's actually a complete new message). In each case, an event is logged to keep track of the old and new Received-ID.
A ForeignId is used by a gateway to a foreign mail system to express the foreign system's closest equivalent to a Received-ID: some kind of transaction identifier if possible, or a message ID if not. A transaction identifier is preferable because it's likely to keep track of individual instances of messages - a given message ID will typically appear in multiple messages (this is not an error, and there are good reasons why this will happen) so it isn't very good at working out the exact history of individual messages.
A Recipient parameter is a message recipient in RFC 821 format: user@host.name.
A ForeignRecipient parameter is a foreign mail system's representation of a message recipient. It's used by gateways, to record the correspondence between an Internet email address and the foreign address to or from which it was translated: since the translation may not be obvious, the foreign version must be logged to aid in message tracing.
A Reason parameter is a text string describing why a recipient is being non-delivered. A Term parameter is a text string containing the part of the recipient's address that's in error.
The size you've allocated to hold a header field.
An RFC 822 header field name and its value.
A single line of non-header message content.
The API handles the SMTP termination and transparency mechanisms. When writing content data, don't add a dot to lines that begin with a dot, and don't finish your input with a line containing a single dot; and when reading, don't remove any leading dot or treat a single dot as the end of content. The API does this for you.
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.
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.
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 */
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 */
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 */
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 */
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.)
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 */
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.)
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.)
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.
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.