Document last modified: 3/4/2003 (samc)
LAFmessage API documentation

Table of contents


History

For everyone keeping score at home, LAFmessage is the third (and hopefully last) implementation of a reusable email library in C++ at lookandfeel.

The first implementation was the COM object LAFcommon.Sendmail, started on 10/13/1999. The reason for its creation was the author's complete lack of ability to make CDONTS/MAPI perform as documented. Despite spending more than a full day devoted to reading every knowledge base article in MSDN and trying every trick possible, CDONTS would not send a mail message. LAFcommon.Sendmail was therefore born more out of frustration than planning.

LAFcommon.Sendmail worked just fine for sending plain text messages to any number of recipients. It had the ability to queue messages and deliver them in the background. Unfortunately, it lacked many desirable features, including (but not limited to):

LAFcommon.Sendmail was RFC-822 compliant and was partially RFC-821 compliant.

On 10/13/2000 (an interestingly coincidental date), the second implementation was begun. It was a ground-up rewrite of LAFcommon.Sendmail. Fortunately, it started with slightly better planning and more ambitious goals. It was generally referred to as "SMTPMail" and its core code lived in two files within the LAFcommon project: reusables_SMTP.cpp and reusables_SMTP.h. It also had a COM interface named LAFcommon.SMTPMail.

SMTPMail was intended to be more capable than LAFcommon.Sendmail and more reusable. Because it sported a completely independent C++ API in addition to the API presented by its COM interface, it could be reused within C++ projects at the source code level. SMTPMail was designed to handle sending attachments and HTML email. SMTPMail separated the concepts of SMTP authentication and message envelope content, allowing the caller more freedom when specifying the sender's address. SMTPMail allowed the caller to specify bogus "To" and "From" addresses in the message envelope. SMTPMail provided separate queues so unrelated callers within the same process space would not have to throw their messages in together.

SMTPMail was also designed to implement the concept of "accountability". This was the answer to LAFcommon.Sendmail's inability to report on the status of queued messages. SMTPMail was designed to retry delivery failures an arbitrary number of times and to save messages after attempting delivery so the caller could determine which had been delivered and which had been rejected. Unfortunately, this facility was never fully implemented. SMTPMail's shortcomings included:

SMTPMail was RFC-821 and RFC-822 compliant and partially RFC-2045(/6/7/8/9) compliant.

Now, started on 8/27/2001, LAFmessage was created. It incorporates a completely new, elegant, easy to use API and addresses all of the problems LAFcommon.Sendmail and SMTPMail suffered.


Overview

LAFmessage is a complete message-handling component. Unlike its predecessors (see above), it is not limited to just sending email. Through an object-oriented interface, it represents a generic message that can be passed to other objects for handling. For example, a message object can be constructed and passed to an SMTP object for delivery. A POP3 object can return message objects that can be programatically examined and passed to other objects. The system is designed to be flexible and extendable so, in the future, other facilities may be added for IMAP, filesystem interaction or database interaction.

When working with LAFmessage, it is important to keep separate the concepts of "message generation", "message delivery" and "message fetching". Message generation is the construction of a message by assembling its component parts (text sections, attachments and headers). Once message generation is complete, message delivery can begin -- the act of sending the message. Message fetching is the opposite of message delivery -- it is the act of constructing a message by reading data from a remote source.

Four C++ classes within LAFmessage provide message generation facilities: LAFmessage_Attachment, LAFmessage_Header, LAFmessage_Message and LAFmessage_Text (the COM classes are LAFmessage.Attachment, LAFmessage.Header, LAFmessage.Message and LAFmessage.Text, respectively).

One C++ class within LAFmessage provides message delivery facilities: LAFmessage_SMTP (the COM class is LAFmessage.SMTP).

One C++ class within LAFmessage provides message fetching facilities: LAFmessage_POP3 (the COM class is LAFmessage.POP3).

These classes are discussed in detail below.


C++ Overview

The biggest "gotcha" in the C++ interface is the object sharing and caching. When using the C++ API, each LAFmessage object keeps track of the number of references to itself. When its reference count drops to zero, the object is destroyed by a worker thread according to the rules for its class (e.g. LAFmessage_Message is destroyed (almost) immediately while LAFmessage_SMTP must remain idle for a preset amount of time). Fetching an existing object with a known ID is possible and will return a pointer to the existing object. Requesting a new object will create the object and automatically insert it into the cache.

Because of this, all of LAFmessage's C++ classes are designed to be completely thread-safe.

In order to make the reference counting system work properly, all callers using the C++ interface must be very careful to obey the reference counting rules at all times. If the rules are not followed and reference counts are not incremented when objects are retained, pointers may become invalid when they are destroyed by other threads. Conversely, if reference counts are not decremented, memory leaks may result.

Fortunately, the reference counting rules are simple.

Remember: it is the callee's responsibility to increment an object's reference count if it is going to copy a pointer passed as a parameter. For example, an LAFmessage_Attachment pointer may be passed to LAFmessage_Message::cmd_add(). The reference count of the passed pointer will be updated by LAFmessage_Message::cmd_add() -- it is not necessary for the caller to anticipate the need for that update.

Also, it is the callee's responsibility to increment an object's reference count if it is going to return a copy of a pointer. For example, LAFmessage_Message::get_attachments() returns an array of LAFmessage_Attachment pointers. The reference count of each returned pointer will be updated by LAFmessage_Message::get_attachments() before it returns. The caller may safely save those pointers without updating their reference counts again. However, the caller must decrement the pointers' reference counts if they are to be discarded.

Some of these rules may be a little confusing -- hopefully the examples (below) will help to clarify the rules. The reference-count-updating behavior of the functions that accept or return LAFmessage pointers is documented below.


Most of the member functions that return strings accept a parameter of type LAFmessage_enum::display_types*. This parameter can be used to force the called function to canonicalize the string before returning it. Four types of canonicalization are possible: HTML, TEXTAREA, SQL and Javascript. They use the functions canonicalize_html(), canonicalize_html_no_break(), canonicalize_sql() and canonicalize_javascript(), respectively.

The parameter must be a single-dimensional array of enumerated values, terminated by the value LAFmessage_enum::LAFMESSAGE_DISPLAY_none. Each value in the array will cause the string to be passed through the corresponding function in the order given in the array. For example, the array { LAFmessage_enum::LAFMESSAGE_DISPLAY_javascript, LAFmessage_enum::LAFMESSAGE_DISPLAY_javascript, LAFmessage_enum::LAFMESSAGE_DISPLAY_html, LAFmessage_enum::LAFMESSAGE_DISPLAY_none } will cause the string to be passed first to canonicalize_javascript(). The output will be passed to canonicalize_javascript(). The last output will be passed to canonicalize_html() and the final output will be returned.


All of the LAFmessage functions that accept pointers-to-pointers(-to-pointers) as parameters (e.g. char**) allocate memory, dereference the parameter and set it to point to the allocated memory. In all of these cases, it is the caller's responsibility to properly dispose of LAFmessage objects (i.e. pass them to put_object()) and free any other allocated memory when it is no longer needed. All allocated memory returned by the C++ interface is allocated with malloc() (as opposed to new) and should be deallocated with free() (as opposed to delete). This is done because using malloc() also allows the use of realloc(), which is just too handy to ignore.


Most of the member functions on LAFmessage C++ objects return a LAFmessage_enum::return_codes value. The possible values and their meanings are:

Value Meaning
LAFMESSAGE_success The operation completed successfully.
LAFMESSAGE_fail The operation failed but did not generate an error.
LAFMESSAGE_fail_queue_paused The message cannot be synchronously processed at this time because the queue is currently paused. The message has been queued and will be processed when the queue is unpaused.
LAFMESSAGE_fail_invalid_state The requested method cannot operate while the object is in its current state. Please check the documentation for a description of this object's states.
LAFMESSAGE_fail_file_exists The operation was specified to proceed without overwriting an existing file. The given filename already exists, so the operation has failed.
LAFMESSAGE_error_not_implemented The called method, while part of the API definition, has not yet been implemented. Bug the author to complete the implementation.
LAFMESSAGE_error_internal An internal error occurred. This process may need to be restarted.
LAFMESSAGE_error_string_too_long The given string could not be used because it exceeds the maximum length. See the documentation for maximum lengths of properties and parameters.
LAFMESSAGE_error_invalid_value The given value was not valid for the property or parameter it was given for. See the documentation for a list of valid values for each property and parameter.
LAFMESSAGE_error_mutex_timeout The operation failed while attempting to obtain an exclusive lock over a resource. The system is probably just too busy -- try again later.
LAFMESSAGE_error_invalid_object The object pointer passed as a parameter was either NULL or of the wrong type. See the documentation for details on the correct object types for each property and parameter.
LAFMESSAGE_error_not_found The referenced object or value could not be found.
LAFMESSAGE_error_no_such_constant A constant with the given name does not exist. Please check spelling and versions.
LAFMESSAGE_error_bad_bstr A string could not be extracted from the given BSTR.
LAFMESSAGE_error_bad_array An array operation failed, most likely due to a passed array having the wrong shape/size or perhaps because a new array could not be created or filled. See the documentation for details on the correct array types for each property and parameter.
LAFMESSAGE_error_queryinterface_failed A COM object could not be instantiated internally. This process should probably be restarted.
LAFMESSAGE_error_bad_variant The given VARIANT did not contain the expected data type. See the documentation for details of the expected types of each property and parameter.
LAFMESSAGE_error_bad_object The given object could not be cast to the correct type. See the documentation for details of the expected types of each property and parameter.
LAFMESSAGE_error_bad_address A bad internet address was given.
LAFMESSAGE_error_connection_lost The connection to the remote server was unexpectedly lost after it was established.
LAFMESSAGE_error_connection_refused The attempt to establish a connection to the remote server was refused. This indicates that the machine is alive but is not accepting messages, possibly because it is too busy.
LAFMESSAGE_error_connection_failed The connection to the remote server failed.
LAFMESSAGE_error_network_down The networking subsystem is down on this host. Check to ensure this machine has a network route to the remote server.
LAFMESSAGE_error_out_of_space This host is out of memory -- kill some processes to free up enough memory to process mail.
LAFMESSAGE_error_host_not_found No DNS record for the given remote host could be found.
LAFMESSAGE_error_not_initialized A subsystem was not initialized correctly, most likely the networking library.
LAFMESSAGE_error_try_again_later The networking subsystem on this host is too busy to deliver mail right now. Try again later.
LAFMESSAGE_error_cant_happen You shouldn't ever see this message. Report it to the author immediately along with all the details you have about what caused it.
LAFMESSAGE_error_already_paused The queue is already paused and cannot be paused again without being restarted first. If the intent is to change the amount of time the queue stays paused, set the resume delay using the unpause property instead.
LAFMESSAGE_error_not_paused The queue is not currently paused; it cannot be unpaused at this time.
LAFMESSAGE_error_id_in_use The object's ID cannot be changed to the given ID; the given ID is already in use.
LAFMESSAGE_error_cannot_open_file The attempt to open the file for reading or writing failed. The possible reasons are too numerous to list but the most likely causes are insufficient permissions and malformed filenames (UNCs are not allowed).
LAFMESSAGE_error_delivery_failure The message could not be delivered -- the remote server returned an unexpected response.
LAFMESSAGE_error_remote_timeout The operation timed out because a response was not received from the remote server in time. This might indicate a network problem or excessive load on the server.
LAFMESSAGE_error_missing_sender_address Message delivery cannot proceed without a sender address to authenticate to the remote server.
LAFMESSAGE_error_bad_greeting The remote server did not send a valid greeting banner. The connection attempt has been aborted.
LAFMESSAGE_error_bad_response_format The remote server responded to the command with a success indicator but gave a response that does not match the format specified in the protocol.
LAFMESSAGE_error_bad_message_format The message cannot be parsed and split into its representative objects -- its format does not match the specifications closely enough to be decyphered.
LAFMESSAGE_error_bad_header_format The header cannot be parsed and split into its representative parts -- its format does not match the specifications closely enough to be decyphered.
LAFMESSAGE_error_bad_attachment_format The attachment cannot be parsed and split into its representative parts -- its format does not match the specifications closely enough to be decyphered.
LAFMESSAGE_error_missing_server_address Message delivery cannot proceed without a server address.


C++ interface: LAFmessage_MIME

LAFmessage_MIME is an abstract base class for LAFmessage_Attachment, LAFmessage_Header, LAFmessage_Text and LAFmessage_Message. It provides the following functions to its decendants:

LAFmessage_enum::return_codes get_id(char **return_code, LAFmessage_enum::display_types *display = NULL)
If successful, get_id() allocates memory, copies the object's unique ID into it and points *return_code to the new memory address. The string is formatted according to the values given in display. Each object's ID is a short GUID.

LAFmessage_enum::return_codes get_type(LAFmessage_enum::mime_types *return_value)
If successful, get_type() sets *return_value equal to the object's MIME type.

LAFmessage_enum::return_codes get_encoding(LAFmessage_enum::mime_encoding_types *return_value)
If successful, get_encoding() sets *return_value equal to the object's encoding type.

LAFmessage_enum::return_codes set_type(LAFmessage_enum::mime_types new_type)
set_type() sets the object's MIME type to new_type. The new value must be appropriate for the object type (e.g. a LAFmessage_Message object cannot have a type of LAFmessage_enum::LAFMESSAGE_HEADER_from). See below for a complete list of valid types for each class.

LAFmessage_enum::return_codes set_encoding(LAFmessage_enum::mime_encoding_types new_encoding)
set_encoding() sets the object's MIME encoding type to new_encoding. Each MIME type has "minimum" and "maximum" encoding levels that set_encoding() cannot override. When an object's type is changed using set_type(), the encoding level is automatically updated to the minimum level (if the current level is below the minimum) or the maximum level (if the current level is above the maximum). Because of this, explicitly setting the encoding level is usually not necessary. The minimum and maximum levels are listed below:
LAFmessage_enum::mime_types value Minimum encoding level Maximum encoding level
LAFMESSAGE_MIME_text_plain 7bit base64
LAFMESSAGE_MIME_text_xaol quoted-printable base64
LAFMESSAGE_MIME_text_html quoted-printable base64
LAFMESSAGE_MIME_image_gif base64 base64
LAFMESSAGE_MIME_image_jpeg base64 base64
LAFMESSAGE_MIME_application_octetstream base64 base64
LAFMESSAGE_MIME_multipart_alternative 7bit 7bit
LAFMESSAGE_MIME_multipart_related 7bit 7bit
LAFMESSAGE_MIME_multipart_mixed 7bit 7bit
LAFMESSAGE_MIME_HEADER_return_path 7bit 7bit
LAFMESSAGE_MIME_HEADER_received 7bit 7bit
LAFMESSAGE_MIME_HEADER_date 7bit 7bit
LAFMESSAGE_MIME_HEADER_from 7bit 7bit
LAFMESSAGE_MIME_HEADER_subject 7bit 7bit
LAFMESSAGE_MIME_HEADER_sender 7bit 7bit
LAFMESSAGE_MIME_HEADER_to 7bit 7bit
LAFMESSAGE_MIME_HEADER_cc 7bit 7bit
LAFMESSAGE_MIME_HEADER_x 7bit 7bit
LAFMESSAGE_MIME_HEADER_bcc 7bit 7bit
LAFMESSAGE_MIME_HEADER_comments 7bit 7bit
LAFMESSAGE_MIME_HEADER_encrypted 7bit 7bit
LAFMESSAGE_MIME_HEADER_in_reply_to 7bit 7bit
LAFMESSAGE_MIME_HEADER_keywords 7bit 7bit
LAFMESSAGE_MIME_HEADER_message_id 7bit 7bit
LAFMESSAGE_MIME_HEADER_references 7bit 7bit
LAFMESSAGE_MIME_HEADER_reply_to 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_bcc 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_cc 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_date 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_from 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_message_id 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_reply_to 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_sender 7bit 7bit
LAFMESSAGE_MIME_HEADER_resent_to 7bit 7bit
LAFMESSAGE_MIME_HEADER_content_disposition 7bit 7bit
LAFMESSAGE_MIME_HEADER_content_type 7bit 7bit
LAFMESSAGE_MIME_HEADER_content_transfer_encoding 7bit 7bit
LAFMESSAGE_MIME_HEADER_content_id 7bit 7bit
LAFMESSAGE_MIME_HEADER_mime_version 7bit 7bit
LAFMESSAGE_MIME_HEADER_content_description 7bit 7bit
LAFMESSAGE_MIME_HEADER_unknown 7bit 7bit
LAFMESSAGE_MIME_none 7bit 7bit
LAFMESSAGE_MIME_TEXT 7bit base64
LAFMESSAGE_MIME_ATTACHMENT base64 base64
LAFMESSAGE_MIME_MESSAGE 7bit 7bit
LAFMESSAGE_MIME_HEADER 7bit 7bit

virtual LAFmessage_enum::return_codes cmd_clone(LAFmessage_MIME **return_value) = 0
If successful, cmd_clone() returns a pointer to an exact copy of the object -- all of the data values have been copied but the object itself is discrete and its ID is different from the original. Any internal pointers are copied as well (and their reference counts updated) but the objects pointed to are not cloned. For example:
LAFmessage_Message *original_message,
                   *cloned_message;
LAFmessage_Text **original_text_object_array,
                **cloned_text_object_array;
char *returned_content;

// NOTE: This is only an example.  Ignoring return
// codes is not good practice in real applications.
LAFmessage_Message::get_object(&original_message, LAFmessage_enum::LAFMESSAGE_MIME_MESSAGE);
original_message->cmd_add_text(LAFmessage_enum::LAFMESSAGE_MIME_text_plain, "Original text content");
original_message->cmd_clone(&cloned_message);

cloned_message->get_texts(&cloned_text_object_array);
cloned_text_object_array[0]->set_content("New text content");

original_message->get_texts(&original_text_object_array);
original_text_object_array[0]->get_content(&returned_content);
printf("%s\n", returned_content);

The string written to stdout will be "New text content". The cloned LAFmessage_Message object contains a pointer to the same LAFmessage_Text object as the original -- when the content is updated, both the original and the clone reflect it.

An object returned by cmd_clone() should be treated the same as an object returned by get_object() where reference counting is concerned.

LAFmessage_enum::return_codes update_ref_count(long update_amount, long *return_value = NULL)
If successful, update_ref_count() will update the object's internal reference counter by adding update_amount to it. To decrement the reference counter, update_amount may be negative. If return_value is not equal to NULL, *return_value will be set to the value of the reference counter after the update.

Generally speaking, update_ref_count() should only be called to increment an object's reference counter, not to decrement it; use put_object() for that. Decrementing an object's reference counter using update_ref_count() should only be done under the condition that the reference counter will be greater than zero after the update is complete. In some rare situations, this condition is guaranteed and update_ref_count() is appropriate. When in doubt, use put_object().

LAFmessage_enum::return_codes init()
init() initializes the object's internal data members to their default states. It does not free any allocated memory or properly dereference any objects. init() is normally only called by the object's constructor.

LAFmessage_enum::return_codes empty()
empty() frees any allocated memory contained by the object and properly dereferences any pointers it contains. empty() is normally only called by the object's destructor.

static LAFmessage_enum::return_codes get_object(LAFmessage_MIME **return_value, LAFmessage_enum::mime_types target_type, char *target_id = NULL)
If successful, get_object() sets *return_value to point to an object of the type given by target_type. If target_id is not equal to NULL, the object cache is searched for an object of the type given by target_type with an ID equal to target_id. If the object cannot be found, LAFmessage_enum::LAFMESSAGE_error_not_found is returned and *return_value is not set. If target_id is equal to NULL, a new object of the type given by target_type is created, inserted into the object cache and *return_value is set to its address.

static LAFmessage_enum::return_codes put_object(LAFmessage_MIME **object)
If successful, put_object() decrements the object's internal reference counter and marks it for deletion if the reference counter is zero. Whether successful or not, put_object always sets *object equal to NULL. This makes debugging easier when pointers are used after they are passed to put_object().


C++ interface: LAFmessage_Attachment

LAFmessage_Attachment encapsulates the functionality required for a MIME message attachment. Attachments can be used to transmit arbitrary data within messages that may not be suitable for direct display to the user.


The "inline flag" referenced below affects how the attachment is embedded in the message. "Inline" attachment content may be included directly within the displayed message and is embedded in multipart/related blocks instead of multipart/mixed blocks. All inline attachments must have names; these names can be referenced using a "cid:" tag in a text body. For example, if an inline attachment named "foo.gif" is part of a message, an HTML text section may contain the following tag: <IMG SRC="cid:foo.gif">. This will cause the image file to be loaded into the HTML document from the attachment content without requiring internet access.

Unfortunately, Microsoft Outlook is not compliant with RFC 2387 and does not implement this feature. It does not recognize multipart/related MIME blocks and interprets them as multipart/mixed (in compliance with RFC 2045). Because of this, the presence of an inline attachment will cause Outlook to display a completely blank message that appears to have a single attachment -- a message. Opening the "attached" message will display the message as it was originally intended; the referenced inline attachments are inserted into the document where appropriate.

That's correct -- Outlook will insert the attachment content referenced by the "cid:" tag and it will not correctly recognize multipart/related MIME blocks. In direct violation of RFC 2387, Outlook will include any attachment's content that is referenced by name with a "cid:" tag; it does not require that the attachment be marked inline.

On the other hand, Netscape Communicator does implement RFC 2387 correctly and will not recognize "cid:" tags that reference non-inline attachments. This means that if you want to use "cid:" tags, you must choose which mail application to target -- Outlook or EveryoneElseTM. One of them will not see the email as you intended.

NOTE: The above only applies to inline attachments. There is no problem with sending HTML email that links to files and images on remote servers.


The following members of LAFmessage_enum::mime_types are valid attachments and may be passed to set_type():
Value Meaning
LAFMESSAGE_MIME_text_plain Plain text attachment.
LAFMESSAGE_MIME_text_xaol AOL rich media attachment (only required for AOL 5.0 or earlier).
LAFMESSAGE_MIME_text_html HTML text attachment.
LAFMESSAGE_MIME_image_gif GIF image file attachment.
LAFMESSAGE_MIME_image_jpeg JPEG image file attachment.
LAFMESSAGE_MIME_application_octetstream Any other binary file attachment.


In addition to the member functions inherited from LAFmessage_MIME (above), LAFmessage_Attachment provides the following member functions:

LAFmessage_enum::return_codes get_content(unsigned char **return_value, long *return_value_length = NULL)
If successful, get_content() makes a copy of the attachment's content and sets *return_value to point to its address. If return_value_length is not equal to NULL, *return_value_length is set equal to the length of *return_value in bytes. This is especially useful if the attachment's content consists of binary data that may contain embedded null characters.

Because the attachment's content may be binary, get_content() does not accept a parameter of type LAFmessage_enum::display_types* to canonicalize the output.

LAFmessage_enum::return_codes get_inline(int *return_value)
If successful, get_inline() sets *return_value equal to the attachment's inline flag value (0 or 1).

LAFmessage_enum::return_codes get_mime_section_content(char **return_value, unsigned int *strlen_return = NULL, LAFmessage_enum::display_types *display = NULL)
If successful, get_mime_section_content() generates the body of the MIME message block for the attachment, canonicalizes it according to display and sets *return_value to point to it. If strlen_return is not equal to NULL, *strlen_return is set equal to the number of characters in the returned string.

This function is not particularly useful for practical purposes -- it is more for debugging. It may be removed in a later version.

LAFmessage_enum::return_codes get_mime_section_header(char **return_value, unsigned int *strlen_return = NULL, LAFmessage_enum::display_types *display = NULL)
If successful, get_mime_section_header() generates the header of the MIME message block for the attachment, canonicalizes it according to display and sets *return_value to point to it. If strlen_return is not equal to NULL, *strlen_return is set equal to the number of characters in the returned string.

This function is not particularly useful for practical purposes -- it is more for debugging. It may be removed in a later version.

LAFmessage_enum::return_codes get_mime_section(char **return_value, unsigned int *strlen_return = NULL, LAFmessage_enum::display_types *display = NULL)
If successful, get_mime_section_header() generates the MIME message block for the attachment, canonicalizes it according to display and sets *return_value to point to it. If strlen_return is not equal to NULL, *strlen_return is set equal to the number of characters in the returned string.

LAFmessage_enum::return_codes get_name(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, get_name() sets *return_value to the name of the attachment and canonicalizes it according to display.

LAFmessage_enum::return_codes set_content(unsigned char *new_content, unsigned int new_content_length)
If successful, set_content() copies new_content_length bytes from new_content and saves them as the attachment's content.

LAFmessage_enum::return_codes set_inline(int new_inline_flag)
If successful, set_inline() sets the attachment's inline flag equal to new_inline_flag.

LAFmessage_enum::return_codes set_mime_section(char *new_section)
If successful, set_mime_section() parses the MIME message block given in new_section and decodes the name, inline flag and content from it.

LAFmessage_enum::return_codes set_name(char *new_name)
If successful, set_name() copies the text from new_name into the attachment's name field.

LAFmessage_enum::return_codes cmd_load_content(char *file_path)
If successful, cmd_load_content() opens the file named file_path and loads the entire file into the attachment's content field. It is the caller's responsibility to ensure the process owner has sufficient permission to read the file and that the path is correct. Windows UNC paths are not supported.

LAFmessage_enum::return_codes cmd_save_content(char *file_path, LAFmessage_enum::save_options save_option = LAFMESSAGE_DEFAULT_ATTACHMENT_SAVE_OPTION)
If successful, cmd_save_content() opens the file named file_path and saves the attachment's content field to it. It is the caller's responsibility to ensure the process owner has sufficient permission to create or save the file and that the path is correct. Windows UNC paths are not supported.

If save_option is given, it must be one of the following two values:

LAFmessage_enum::return_codes cmd_clone(LAFmessage_MIME **new_clone)
cmd_clone() is documented in LAFmessage_MIME (above).

LAFmessage_enum::return_codes cmd_clone(LAFmessage_Attachment **new_clone)
This version of cmd_clone() is a wrapper for the above version so the caller does not have to cast the parameter to LAFmessage_MIME**.

static LAFmessage_enum::return_codes get_object(LAFmessage_Attachment **return_value, LAFmessage_enum::mime_types target_type = LAFMESSAGE_DEFAULT_MIME_ATTACHMENT_TYPE, char *target_id = NULL)
This version of get_object() is a wrapper for the version documented in LAFmessage_MIME (above) that does not require the caller to cast the parameter to LAFmessage_MIME**.

static LAFmessage_enum::return_codes put_object(LAFmessage_Attachment **object)
This version of put_object() is a wrapper for the version documented in LAFmessage_MIME (above) that does not require the caller to cast the parameter to LAFmessage_MIME**.


C++ interface: LAFmessage_Header

LAFmessage_Header encapsulates all of the functionality necessary to represent a header of a MIME message or a MIME message block. Headers typically consist of a type and a value, though two types of header also have names.


The following members of LAFmessage_enum::mime_types are valid headers and may be passed to set_type():
Value Meaning
LAFMESSAGE_MIME_HEADER_return_path A "Return-Path" field. From RFC 822: The field is intended to contain definitive information about the address and route back to the message's originator. While the syntax indicates that a route specification is optional, every attempt should be made to provide that information in this field.
LAFMESSAGE_MIME_HEADER_received A "Recieved" field. From RFC 822: A copy of this field is added by each transport service that relays the message. The information in the field can be quite useful for tracing transport problems.
LAFMESSAGE_MIME_HEADER_date A "Date" field. The date the message was generated.
LAFMESSAGE_MIME_HEADER_from A "From" field. From RFC 822: This field contains the identity of the person(s) who wished this message to be sent. The message-creation process should default this field to be a single, authenticated machine address, indicating the AGENT (person, system or process) entering the message.
LAFMESSAGE_MIME_HEADER_subject A "Subject" field. From RFC 822: This is intended to provide a summary, or indicate the nature, of the message.
LAFMESSAGE_MIME_HEADER_sender A "Sender" field. From RFC 822: This field contains the authenticated identity of the AGENT (person, system or process) that sends the message. It is intended for use when the sender is not the author of the message, or to indicate who among a group of authors actually sent the message. If the contents of the "Sender" field would be completely redundant with the "From" field, then the "Sender" field need not be present and its use is discouraged (though still legal). In particular, the "Sender" field MUST be present if it is NOT the same as the "From" Field.
LAFMESSAGE_MIME_HEADER_to A "To" field. From RFC 822: This field contains the identity of the primary recipients of the message.
LAFMESSAGE_MIME_HEADER_cc A "Cc" field. From RFC 822: This field contains the identity of the secondary (informational) recipients of the message.
LAFMESSAGE_MIME_HEADER_x A user-defined field. User-defined fields must have names and are automatically prefixed with "X-".
LAFMESSAGE_MIME_HEADER_bcc A "Bcc" field. From RFC 822: This field contains the identity of additional recipients of the message.
LAFMESSAGE_MIME_HEADER_comments A "Comments" field. From RFC 822: Permits adding text comments onto the message without disturbing the contents of the message's body.
LAFMESSAGE_MIME_HEADER_encrypted An "Encrypted" field. From RFC 822: If the body of a message has been encrypted, to keep its contents private, the "Encrypted" field can be used to note the fact and to indicate the nature of the encryption.
LAFMESSAGE_MIME_HEADER_in_reply_to An "In-Reply-To" field. From RFC 822: The contents of this field identify previous correspon- dence which this message answers. Note that if message identifiers are used in this field, they must use the msg-id specification format.
LAFMESSAGE_MIME_HEADER_keywords A "Keywords" field. From RFC 822: This field contains keywords or phrases, separated by commas.
LAFMESSAGE_MIME_HEADER_message_id A "Message-ID" field. From RFC 822: This field contains a unique identifier (the local-part address unit) which refers to THIS version of THIS message. The uniqueness of the message identifier is guaranteed by the host which generates it.
LAFMESSAGE_MIME_HEADER_references A "References:" field. From RFC 822: The contents of this field identify other correspondence which this message references. Note that if message identifiers are used, they must use the msg-id specification format.
LAFMESSAGE_MIME_HEADER_reply_to A "Reply-To" field: From RFC 822: This field provides a general mechanism for indicating any mailbox(es) to which responses are to be sent.
LAFMESSAGE_MIME_HEADER_resent_bcc A "Resent-Bcc:" field. From RFC 822: This field contains the identity of additional recipients of the message.
LAFMESSAGE_MIME_HEADER_resent_cc A "Resent-Cc" field. From RFC 822: This field contains the identity of the secondary (informational) recipients of the message.
LAFMESSAGE_MIME_HEADER_resent_date A "Resent-Date" field. The date the resent message was generated.
LAFMESSAGE_MIME_HEADER_resent_from A "Resent-From" field. From RFC 822: This field contains the identity of the person(s) who wished this message to be sent. The message-creation process should default this field to be a single, authenticated machine address, indicating the AGENT (person, system or process) entering the message.
LAFMESSAGE_MIME_HEADER_resent_message_id A "Resent-Message-ID" field. From RFC 822: This field contains a unique identifier (the local-part address unit) which refers to THIS version of THIS message. The uniqueness of the message identifier is guaranteed by the host which generates it.
LAFMESSAGE_MIME_HEADER_resent_reply_to A "Resent-Reply-To" field: From RFC 822: This field provides a general mechanism for indicating any mailbox(es) to which responses are to be sent.
LAFMESSAGE_MIME_HEADER_resent_sender A "Resent-Sender" field. From RFC 822: This field contains the authenticated identity of the AGENT (person, system or process) that sends the message. It is intended for use when the sender is not the author of the message, or to indicate who among a group of authors actually sent the message. If the contents of the "Sender" field would be completely redundant with the "From" field, then the "Sender" field need not be present and its use is discouraged (though still legal). In particular, the "Sender" field MUST be present if it is NOT the same as the "From" Field.
LAFMESSAGE_MIME_HEADER_resent_to A "Resent-To" field. From RFC 822: This field contains the identity of the primary recipients of the message.

From RFC 822: Whenever the string "Resent-" begins a field name, the field has the same semantics as a field whose name does not have the prefix. However, the message is assumed to have been forwarded by an original recipient who attached the "Resent-" field. This new field is treated as being more recent than the equivalent, original field. For example, the "Resent-From", indicates the person that forwarded the message, whereas the "From" field indicates the original author.

The following members of LAFmessage_enum::mime_types are valid headers but may not be passed to set_type():
Value Meaning
LAFMESSAGE_MIME_HEADER_content_disposition A "Content-Disposition" field. This field conveys presentational information about a MIME message block, as defined in RFC 2183.
LAFMESSAGE_MIME_HEADER_content_type A "Content-Type" field. From RFC 2045: The purpose of the Content-Type field is to describe the data contained in the body fully enough that the receiving user agent can pick an appropriate agent or mechanism to present the data to the user, or otherwise deal with the data in an appropriate manner.
LAFMESSAGE_MIME_HEADER_content_transfer_encoding A "Content-Transfer-Encoding" field. From RFC 2045: Many media types which could be usefully transported via email are represented, in their "natural" format, as 8bit character or binary data. Such data cannot be transmitted over some transfer protocols. It is necessary, therefore, to define a standard mechanism for encoding such data into a 7bit short line format. This document specifies that such encodings will be indicated by a new "Content-Transfer-Encoding" header field.
LAFMESSAGE_MIME_HEADER_content_id A "Content-ID" field. From RFC 2045: In constructing a high-level user agent, it may be desirable to allow one body to make reference to another. Accordingly, bodies may be labelled using the "Content-ID" header field, which is syntactically identical to the "Message-ID" header field. Like the Message-ID values, Content-ID values must be generated to be world-unique.
LAFMESSAGE_MIME_HEADER_mime_version A "MIME-Version" field. From RFC 2045: "MIME-Version" is to be used to declare the version of the Internet message body format standard in use.
LAFMESSAGE_MIME_HEADER_content_description A "Content-Description" field. From RFC 2045: The ability to associate some descriptive information with a given body is often desirable. Such text may be placed in the Content-Description header field.
LAFMESSAGE_MIME_HEADER_unknown A header field that does not match any of the above fields. Its name field will be parsed into the LAFmessage_Header object's name field.


Header content is much more complex than it should be; casual users should beware. For full details, please refer to RFC 822 section 3. In a nutshell, the standard allows headers to contain comments, quoted strings, span multiple lines and defines strict limits on allowed characters inside and outside comments and quoted strings.

For now, LAFmessage_Header "parses" and "formats" headers by obeying RFC 822 "folding" rules (word-wrapping long lines). The more advanced format specifications are up to the caller to enforce. Specifically, illegal character sequences and requirements that some fields only contain email addresses are not enforced. Expect this to change in a future version.


In addition to the member functions inherited from LAFmessage_MIME (above), LAFmessage_Header provides the following member functions:

LAFmessage_enum::return_codes get_content(char **return_value, unsigned int *strlen_return = NULL, LAFmessage_enum::display_types *display = NULL)
If successful, get_content() allocates memory and copies the header content into *return_value, canonicalizing it according to display. If strlen_return is not equal to NULL, is set equal to the length of *return_value in bytes.

LAFmessage_enum::return_codes get_mime_section(char **return_value, unsigned int *strlen_return = NULL, LAFmessage_enum::display_types *display = NULL)
If successful, get_mime_section() formats the name and content into a header line for inclusion at the top of a message or MIME section, canonicalizes it according to display and copies it to *return_value. If strlen_return is not equal to NULL, *strlen_return is set equal to the length of *return_value in bytes.

LAFmessage_enum::return_codes get_name(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, get_content() allocates memory and copies the header name into *return_value, canonicalizing it according to display. If strlen_return is not equal to NULL, is set equal to the length of *return_value in bytes.

LAFmessage_enum::return_codes set_content(char *new_content)
If successful, set_content() copies the text from new_content into the header's content field.

LAFmessage_enum::return_codes set_mime_section(char *new_section)
If successful, set_mime_section() parses the MIME message block given in new_section and decodes the header type, name and content from it.

LAFmessage_enum::return_codes set_name(char *new_name)
If successful, set_name() copies the text from new_name into the header's name field.

LAFmessage_enum::return_codes cmd_clone(LAFmessage_MIME **new_clone)
cmd_clone() is documented in LAFmessage_MIME (above).

LAFmessage_enum::return_codes cmd_clone(LAFmessage_Header **new_clone)
This version of cmd_clone() is a wrapper for the above version so the caller does not have to cast the parameter to LAFmessage_MIME**.

static LAFmessage_enum::return_codes get_object(LAFmessage_Header **return_value, LAFmessage_enum::mime_types target_type = LAFMESSAGE_DEFAULT_MIME_HEADER_TYPE, char *target_id = NULL)
This version of get_object() is a wrapper for the version documented in LAFmessage_MIME (above) that does not require the caller to cast the parameter to LAFmessage_MIME**.

static LAFmessage_enum::return_codes put_object(LAFmessage_Header **object)
This version of put_object() is a wrapper for the version documented in LAFmessage_MIME (above) that does not require the caller to cast the parameter to LAFmessage_MIME**.


C++ interface: LAFmessage_Message

LAFmessage_Message is primarily a container of LAFmessage_Attachment, LAFmessage_Header and LAFmessage_Text objects to be assembled as an RFC 2045-compliant message.

When LAFmessage_Message::get_mime_message() assembles its text, it takes several actions that cannot be overridden.

In addition to the functions inherited from LAFmessage_MIME (above), LAFmessage_Message provides the following member functions:

LAFmessage_enum::return_codes get_attachments(LAFmessage_Attachment ***return_value, LAFmessage_enum::mime_types type = LAFMESSAGE_DEFAULT_MIME_TYPE)
If successful, get_attachments() copies its internal array of LAFmessage_Attachment pointers into a new, NULL-terminated array allocated for *return_value and calls update_ref_count() on each one. If type is given, only objects whose type is equal to the value given are included in the returned array. Each of the pointers returned via *return_value must be passed to put_object() to be properly disposed of.

LAFmessage_enum::return_codes get_headers(LAFmessage_Header ***return_value, LAFmessage_enum::mime_types type = LAFMESSAGE_DEFAULT_MIME_TYPE)
If successful, get_attachments() copies its internal array of LAFmessage_Header pointers into a new, NULL-terminated array allocated for *return_value and calls update_ref_count() on each one. If type is given, only objects whose type is equal to the value given are included in the returned array. Each of the pointers returned via *return_value must be passed to put_object() to be properly disposed of.

LAFmessage_enum::return_codes get_mime_message(char **return_value, unsigned int *strlen_return = NULL, LAFmessage_enum::display_types *display = NULL)
If successful, get_mime_message() formats the message contents (attachments, headers and texts) into an RFC 2045-compatible message and sets *return_value to point to the the full text of the message (headers and body), canonicalizing it according to display. If strlen_return is not NULL, *strlen_return is set equal to the length of *return_value in bytes.

LAFmessage_enum::return_codes get_texts(LAFmessage_Text ***return_value, LAFmessage_enum::mime_types type = LAFMESSAGE_DEFAULT_MIME_TYPE)
If successful, get_attachments() copies its internal array of LAFmessage_Text pointers into a new, NULL-terminated array allocated for *return_value and calls update_ref_count() on each one. If type is given, only objects whose type is equal to the value given are included in the returned array. Each of the pointers returned via *return_value must be passed to put_object() to be properly disposed of.

LAFmessage_enum::return_codes get_header_contents(LAFmessage_enum::mime_types target_header_type, char ***return_value, LAFmessage_enum::display_types *display = NULL)
If successful, get_header_contents() finds all of the contained LAFmessage_Header objects whose types match target_header_type, allocates a NULL-terminated array of strings and stores the output of each matching LAFmessage_Header's get_content() in order. Each string is canonicalized according to display.

get_header_contents() is provided for the convenience of callers who are displaying a message to a user and may only want to know (for example) what the subject of a message is without needing to manipulate the LAFmessage_Header object that contains it.

LAFmessage_enum::return_codes set_attachments(LAFmessage_Attachment *new_attachment)
If successful, set_attachments() properly disposes of all the attachments stored within the message and stores new_attachment as the only attachment. set_attachments() calls update_ref_count() on its parameter before storing it. Calling set_attachments() with a NULL parameter will remove all the message's attachments.

LAFmessage_enum::return_codes set_attachments(LAFmessage_Attachment **new_attachments)
If successful, set_attachments() properly disposes of all the attachments stored within the message and stores the attachments in the NULL-terminated array new_attachments as the message's attachments. set_attachments() calls update_ref_count() on each attachment in new_attachments before storing it.

LAFmessage_enum::return_codes set_headers(LAFmessage_Header *new_header)
If successful, set_headers() properly disposes of all the headers stored within the message and stores new_header as the only header. set_headers() calls update_ref_count() on its parameter before storing it. Calling set_headers() with a NULL parameter will remove all the message's headers.

LAFmessage_enum::return_codes set_headers(LAFmessage_Header **new_headers)
If successful, set_headers() properly disposes of all the headers stored within the message and stores the headers in the NULL-terminated array new_headers as the message's headers. set_headers() calls update_ref_count() on each header in new_headers before storing it.

LAFmessage_enum::return_codes set_mime_message(char *new_message)
If successful, set_mime_message() parses the RFC 822 or RFC 2045-compliant message in new_message, extracts the headers, text sections and attachments from it and creates LAFmessage_Header objects, LAFmessage_Text objects and LAFmessage_Attachment objects, respectively. The message's previous contents are disposed of before the new objects are stored.

LAFmessage_enum::return_codes set_texts(LAFmessage_Text *new_text)
If successful, set_texts() properly disposes of all the text sections stored within the message and stores new_text as the only text section. set_texts() calls update_ref_count() on its parameter before storing it. Calling set_texts() with a NULL parameter will remove all the message's text sections.

LAFmessage_enum::return_codes set_texts(LAFmessage_Text **new_texts)
If successful, set_texts() properly disposes of all the text sections stored within the message and stores the text sections in the NULL-terminated array new_texts as the message's text sections. set_texts() calls update_ref_count() on each text section in new_headers before storing it.

LAFmessage_enum::return_codes cmd_add(LAFmessage_Attachment *new_attachment)
If successful, cmd_add() appends new_attachment to the message's list of contained attachments. NOTE: The same object may be contained within a single message multiple times. cmd_add() makes no attempts to prevent duplicates.

LAFmessage_enum::return_codes cmd_add(LAFmessage_Header *new_header)
If successful, cmd_add() appends new_header to the message's list of contained headers. NOTE: The same object may be contained within a single message multiple times. cmd_add() makes no attempts to prevent duplicates.

LAFmessage_enum::return_codes cmd_add(LAFmessage_Text *new_text)
If successful, cmd_add() appends new_text to the message's list of contained text sections. NOTE: The same object may be contained within a single message multiple times. cmd_add() makes no attempts to prevent duplicates.

LAFmessage_enum::return_codes cmd_load(char *file_path)
If successful, cmd_load() opens the file named file_path, loads the entire file and passes its contents to set_mime_message() (above). It is the caller's responsibility to ensure the process owner has sufficient permission to read the file and that the path is correct. Windows UNC paths are not supported.

LAFmessage_enum::return_codes cmd_save(char *file_path, LAFmessage_enum::save_options save_option = LAFMESSAGE_DEFAULT_MESSAGE_SAVE_OPTION)
If successful, cmd_save() opens the file named file_path and saves the output of get_mime_message() (above) to it. It is the caller's responsibility to ensure the process owner has sufficient permission to create or save the file and that the path is correct. Windows UNC paths are not supported.

If save_option is given, it must be one of the following two values:

LAFmessage_enum::return_codes cmd_add_attachment(LAFmessage_enum::mime_types new_type, unsigned char *new_content, unsigned int new_content_length, LAFmessage_enum::mime_encoding_types new_encoding = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE, char *new_name = NULL, int new_inline = LAFMESSAGE_DEFAULT_ATTACHMENT_INLINE_FLAG)
If successful, cmd_add_attachment() creates a new LAFmessage_Attachment object, sets its type to new_type, copies new_content_length bytes from new_content into its content field, sets its encoding type to new_encoding, sets its name to new_name, sets its inline flag to new_inline and appends it to the message's list of stored attachments.

cmd_add_attachment() is provided for the convenience of callers who wish to add an attachment but do not need to manipulate a LAFmessage_Attachment object. The only advantage to using it is the ability to add a new attachment in a single call.

LAFmessage_enum::return_codes cmd_add_header(LAFmessage_enum::mime_types new_type, char *new_content, LAFmessage_enum::mime_encoding_types new_encoding = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE, char *new_name = NULL)
If successful, cmd_add_header() creates a new LAFmessage_Header object, sets its type to new_type, copies its content from new_content, sets its encoding type to new_encoding, sets its name to new_name and appends it to the message's list of stored headers.

cmd_add_header() is provided for the convenience of callers who wish to add a header but do not need to manipulate a LAFmessage_Header object. The only advantage to using it is the ability to add a new header in a single call.

LAFmessage_enum::return_codes cmd_add_text(LAFmessage_enum::mime_types new_type, char *new_content, LAFmessage_enum::mime_encoding_types new_encoding = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE)
If successful, cmd_add_text() creates a new LAFmessage_Text object, sets its type to new_type, copies its content from new_content, sets its encoding type to new_encoding and appends it to the message's list of stored text sections.

cmd_add_text() is provided for the convenience of callers who wish to add a text section but do not need to manipulate a LAFmessage_Text object. The only advantage to using it is the ability to add a new text section in a single call.

LAFmessage_enum::return_codes cmd_clone(LAFmessage_MIME **new_clone)
cmd_clone() is documented in LAFmessage_MIME (above).

LAFmessage_enum::return_codes cmd_clone(LAFmessage_Message **new_clone)
This version of cmd_clone() is a wrapper for the above version so the caller does not have to cast the parameter to LAFmessage_MIME**.

static LAFmessage_enum::return_codes get_object(LAFmessage_Message **return_value, LAFmessage_enum::mime_types target_type = LAFMESSAGE_DEFAULT_MIME_MESSAGE_TYPE, char *target_id = NULL)
This version of get_object() is a wrapper for the version documented in LAFmessage_MIME (above) that does not require the caller to cast the parameter to LAFmessage_MIME**.

static LAFmessage_enum::return_codes put_object(LAFmessage_Message **object)
This version of put_object() is a wrapper for the version documented in LAFmessage_MIME (above) that does not require the caller to cast the parameter to LAFmessage_MIME**.


C++ Interface: LAFmessage_POP3

POP3 stands for Post Office Protocol version 3, defined by RFCs 1939 and 2449. It provides a simple way for a mail server to expose a mailbox to a remote host. It does not support encryption, so sensitive content such as passwords and message content are potentially exposed to sniffers. It also does not support mailbox manipulation commands for mail folders, message flags, etc. POP3's primary advantage is its simplicity, making it very lightweight and very widespread. The protocol is supported by most mail servers because it is so easy to implement.

LAFmessage_POP3 provides all the functionality required to communicate with a POP3 server and manipulate mailboxes. The object is stateful, meaning that not all commands are available at all times, depending on what commands have been previously given.

Specifically, the POP3 object defines three operating states in the LAFmessage_enum::pop3_status_types enumeration, as follows:
Value Meaning
LAFmessage_enum::LAFMESSAGE_POP3_unconnected The object's cmd_connect() method has not been successfully called.
LAFmessage_enum::LAFMESSAGE_POP3_connected The object's cmd_connect() method has been successfully called, the object's cmd_disconnect() method has not been successfully called and the object has not been idle long enough to have automatically disconnected.
LAFmessage_enum::LAFMESSAGE_POP3_disconnected The object's cmd_connect() method has been successfully called and either the object's cmd_disconnect() method has been successfully called or the object has been idle long enough to have been automatically disconnected.

A POP3 object will automatically disconnect from a remote server after being idle for 5 minutes. An unreferenced POP3 object will not be removed from memory immediately -- it must remain idle for a specific amount of time before it will be removed. In this way, a POP3 object can be retrieved by its ID multiple times before it is discarded.

NOTE: It is very important to disconnect from a POP3 server as soon as possible. RFC 1939 states that the server must exclusively lock the mailbox while a POP3 connection is active. This means that no other POP3 connections can be established and no new mail can be added while a POP3 connection is open.

LAFmessage_POP3 provides the following member functions:

LAFmessage_enum::return_codes get_max_idle_mins(long *return_value)
If successful, *return_value is set to the maximum number of minutes the LAFmessage_POP3 object will persist in memory without being used.

By default, an LAFmessage_POP3 object will persist for 20 minutes. This can be changed with set_max_idle_mins().

LAFmessage_enum::return_codes get_message(long message_number, LAFmessage_Message **return_value)
If successful, *return_value is set equal to an LAFmessage_Message object that represents message number message_number on the POP3 server. The message numbers are assigned by the server and are required to remain constant throughout a POP3 session but not between sessions.

get_message() may return LAFmessage_enum::LAFMESSAGE_error_not_found if the requested message has not been retrieved from the server using cmd_retrieve() or cmd_retrieve_all().

LAFmessage_enum::return_codes get_message(long message_number, char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to the text of message number message_number on the POP3 server, exactly as the server sent it. The message numbers are assigned by the server and are required to remain constant throughout a POP3 session but not between sessions.

get_message() may return LAFmessage_enum::LAFMESSAGE_error_not_found if the requested message has not been retrieved from the server using cmd_retrieve() or cmd_retrieve_all().

LAFmessage_enum::return_codes get_all_messages(LAFmessage_Message ***return_value)
If successful, *return_value is set equal to a single-dimensional array of LAFmessage_Message objects that represent the messages in their sequence on the POP3 server. The message order is assigned by the server and is required to remain constant throughout a POP3 session but not between sessions.

get_all_messages() may leave one or more of the entries in the returned array NULL if some of the messages have not been retrieved from the POP3 server using cmd_retrieve() or cmd_retrieve_all().

LAFmessage_enum::return_codes get_all_messages(char ***return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to a single-dimensional array of strings contain the message text, exactly as the server sent it, in their sequence on the POP3 server. The message order is assigned by the server and is required to remain constant throughout a POP3 session but not between sessions.

get_all_messages() may leave one or more of the entries in the returned array NULL if some of the messages have not been retrieved from the POP3 server using cmd_retrieve() or cmd_retrieve_all().

LAFmessage_enum::return_codes get_num_messages(long *return_value)
If successful, *return_value is set equal to the number of messages available on the POP3 server.

LAFmessage_enum::return_codes get_sizes(long **return_value)
If successful, *return_value is set equal to a single-dimensional array of long integer values, each representing the size of the corresponding message on the server, in bytes. Note that this size measurement is determined by the server and should only be used as a rough guide to judge whether the message should be downloaded if the link to the POP3 server is slow or expensive. The actual message, once decoded, have a different size than the size given by this method.

LAFmessage_enum::return_codes get_id(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to the LAFmessage_POP3 object's unique ID. By default, all IDs are short GUIDs unless they have been set using set_id().

LAFmessage_enum::return_codes get_pop3_port(long *return_value)
If successful, *return_value is set equal to the port number of the POP3 server that will be used to establish the connection. By default, LAFmessage_POP3 uses port 110 unless it is changed by a call to set_pop3_port().

LAFmessage_enum::return_codes get_pop3_server(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to the string passed to set_pop3_server() or NULL if set_pop3_server() has not been called.

LAFmessage_enum::return_codes get_status(LAFmessage_enum::pop3_status_types *return_value)
If successful, *return_value is set equal to the POP3 object's status flag, indicating whether it is connected.

LAFmessage_enum::return_codes set_max_idle_mins(long new_max_idle_mins)
Sets the maximum number of minutes the POP3 object will persist in memory unused before it is automatically destroyed.

LAFmessage_enum::return_codes set_id(char *new_id)
Sets the POP3 object's ID field to new_id. If the given ID is already in use, set_id() will return LAFmessage_enum::LAFMESSAGE_error_id_in_use.

LAFmessage_enum::return_codes set_pop3_port(long new_pop3_port)
Sets the port number the POP3 object will use to connect to the POP3 server.

LAFmessage_enum::return_codes set_pop3_server(char *new_pop3_server, LAFmessage_enum::internet_address_types type = LAFMESSAGE_DEFAULT_POP3_SERVER_ADDRESS_TYPE)
Sets the internet address of the POP3 server to connect to. The type parameter can be used to specify new_pop3_server's meaning with the following values:
Value Meaning
LAFmessage_enum::LAFMESSAGE_INTERNET_ADDRESS_autodetect The object should attempt to determine the value's meaning on its own. Autodetecting is fairly safe: values are assumed to be fully qualified domain names unless they fit the format "X.X.X.X" where the Xs are all integers between 0 and 255, inclusive.

This option is the default.

LAFmessage_enum::LAFMESSAGE_INTERNET_ADDRESS_fqdn The value is a fully qualified domain name (e.g. mail.foo.com).
LAFmessage_enum::LAFMESSAGE_INTERNET_ADDRESS_ipv4_dotted_quad The value is an IP version 4 dotted quad IP address (e.g. 111.222.111.222).

LAFmessage_enum::return_codes cmd_connect(char *userid, char *password)
Establishes a connection to the POP3 server set by set_pop3_server() on the port set by set_pop3_port(). The object attempts to authenticate with the username and password given and retrieve the number of messages and their sizes.

LAFmessage_enum::return_codes cmd_disconnect()
Closes the connection to the POP3 server. Any deleted messages are physically removed from the server and the mailbox is unlocked.

LAFmessage_enum::return_codes cmd_retrieve(long message_number)
Downloads the message number message_number from the POP3 server.

NOTE: Downloading the message does not remove it from the server. That action must be done through a separate call to cmd_delete().

LAFmessage_enum::return_codes cmd_retrieve_all()
Downloads all the messages from the POP3 server.

NOTE: Downloading messages does not remove them from the server. That action must be done through a separate call to cmd_delete_all().

LAFmessage_enum::return_codes cmd_delete(long message_number)
Deletes the message message_number from the server, even if the message has not been retrieved.

NOTE: Deleting a message does not renumber the remaining messages. The message is not physically removed from the server until the connection is broken.

LAFmessage_enum::return_codes cmd_delete_all()
Deletes all messages from the server, even if the messages have not been retrieved.

NOTE: Deleting the messages does not renumber or change any of the retrieved messages. The messages are not physically removed from the server until the connection is broken.

LAFmessage_enum::return_codes update_ref_count(long update_amount, long *return_value = NULL)
If successful, update_ref_count() will update the object's internal reference counter by adding update_amount to it. To decrement the reference counter, update_amount may be negative. If return_value is not equal to NULL, *return_value will be set to the value of the reference counter after the update.

Generally speaking, update_ref_count() should only be called to increment an object's reference counter, not to decrement it; use put_object() for that. Decrementing an object's reference counter using update_ref_count() should only be done under the condition that the reference counter will be greater than zero after the update is complete. In some rare situations, this condition is guaranteed and update_ref_count() is appropriate. When in doubt, use put_object().

LAFmessage_enum::return_codes init()
init() initializes the object's internal data members to their default states. It does not free any allocated memory of properly dereference any objects. init() is normally only called by the object's constructor.

LAFmessage_enum::return_codes empty()
empty() frees any allocated memory contained by the object and properly dereferences any pointers it contains. empty() is normally only called by the object's destructor.

static LAFmessage_enum::return_codes get_object(LAFmessage_POP3 **return_value, char *target_id = NULL)
If successful, get_object() sets *return_value to point to a POP3 object. If target_id is not equal to NULL, the object cache is searched for a POP3 object with an ID equal to target_id. If the object cannot be found, LAFmessage_enum::LAFMESSAGE_error_not_found is returned and *return_value is not set. If target_id is equal to NULL, a new POP3 object is created, inserted into the object cache and *return_value is set to its address.

static LAFmessage_enum::return_codes put_object(LAFmessage_POP3 **object)
If successful, put_object() decrements the object's internal reference counter. Whether successful or not, put_object() always sets *object equal to NULL. This makes debugging easier if the passed variable is accidentally used after put_object() is called.

static LAFmessage_enum::return_codes pool_run_stop(int destroy_flag = 0)
pool_run_stop() will signal the running worker thread to stop running. The worker thread is responsible for removing idle POP3 objects after 20 minutes of inactivity. If destroy_flag is not equal to 0, pool_run_stop() will also mark the pool ready for destruction, which will prevent a new worker thread from spawning and any new POP3 objects from being created.

When LAFmessage's C++ interface is used by directly including its source files in a project, calling pool_run_stop() is a necessary step in the program's shutdown sequence. The LAFmessage project, when compiled as a DLL, includes calls to pool_run_stop() in the appropriate places.


C++ Interface: LAFmessage_SMTP

SMTP stands for Simple Message Transport Protocol, defined by RFC 821. SMTP the protocol used by internet mail servers to transfer email that an end user might download using POP3. In its original specification SMTP does not support encryption or authentication, though those features have been added through later revisions. LAFmessage_SMTP supports only the SMTP protocol as defined in RFC 821; some of the extensions may be added at a later date if they are needed.

LAFmessage_SMTP provides all the functionality required to send email via SMTP to an SMTP server. NOTE: this does NOT mean that using LAFmessage_SMTP will allow other computers on the internet to deliver email to your server. LAFmessage_SMTP is an SMTP client, not a server.

LAFmessage_SMTP has been built with a number of features to facilitate sending large volumes of mail and track the progress and success/failure of each message/recipient. The code was written with the single-use, one-at-a-time message generator in mind as well. Sending a single message is just as easy as sending thousands.

LAFmessage_SMTP provides the following member functions:

LAFmessage_enum::return_codes get_delivery_mode(LAFmessage_enum::smtp_delivery_modes *return_value)
If successful, *return_value is set equal to the SMTP object's current delivery mode. See set_delivery_mode() for details on the valid flags and their meanings.

LAFmessage_enum::return_codes get_failure_retries(long *return_value)
If successful, *return_value is set equal to the number of times the component will attempt to deliver the message to a recipient before marking that recipient "rejected".

By default, this value is 0 -- message delivery is not retried.

LAFmessage_enum::return_codes get_failure_retry_delay_secs(long *return_value)
If successful, *return_value is set equal to the minimum number of seconds between retrying a failed delivery. NOTE: This is the minimum number of seconds, not a precise measurement. If the queue is very full and/or there are very few threads working on delivering messages, a retry attempt may take much long to begin.

By default, this value is 60 (1 minute). This value has no meaning if set_failure_retries() is not called to set the number of failure retries to a number greater than 0.

LAFmessage_enum::return_codes get_max_idle_mins(long *return_value)
If successful, *return_value is set equal to the maximum number of minutes the SMTP object may remain idle before it is automatically destroyed.

By default, this value is 20.

LAFmessage_enum::return_codes get_max_recipients_per_message(long *return_value)
If successful, *return_value is set equal to the maximum number of recipients that can be sent per SMTP transaction. See set_max_recipients_per_message() for a detailed explanation of what this means.

By default, this value is 1.

LAFmessage_enum::return_codes get_max_worker_threads(long *return_value)
If successful, *return_value is set equal to the maximum number of worker threads that can be created to deliver messages from this queue in asynchronous mode. See set_max_worker_threads() for a more detailed explanation of what this means.

By default, this value is 5.

LAFmessage_enum::return_codes get_id(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to the SMTP object's unique ID.

By default, all IDs are short GUIDs unless they have been set using set_id().

LAFmessage_enum::return_codes get_processed_messages(LAFmessage_Message ***return_value)
If successful, *return_value is set equal to a single-dimensional array of the LAFmessage_Message objects completely processed by the SMTP object. That means that there are no retries to be attempted or recipients currently being processed for any of the returned messages.

NOTE: Repeatedly calling get_processed_messages() and comparing the number of returned objects is not a good way to see if the queue is empty. Use cmd_wait_until_empty() instead.

LAFmessage_enum::return_codes get_queued_messages(LAFmessage_Message ***return_value, long delivery_attempts = LAFMESSAGE_DEFAULT_SMTP_DELIVERY_ATTEMPTS)
If successful, *return_value is set equal to a single-dimensional array of the LAFmessage_Message objects currently queued to be processed by the SMTP object. If delivery_attempts is greater than -1, the returned array will only contain those objects whose number of retry attempts are equal to delivery_attempts.

NOTE: If the queue is not paused, the data returned from this function is not particularly useful except as a general indication of the rate of delivery. This is because SMTP delivery happens so quickly that the queue's shape may change drastically while the caller is still trying to iterate through the returned objects.

NOTE: Messages currently being processed by worker threads will not appear in the returned array. This is because the worker threads detach the messages from the queue before they begin processing and reattach them when they are finished.

LAFmessage_enum::return_codes get_queued_recipient_addresses(char ***return_value, LAFmessage_Message *processed_message, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to a single-dimensional array of strings containing the addresses of the recipients still queued for the given message. If the queue is not paused, this array will very quickly fall out of date as processing continues.

LAFmessage_enum::return_codes get_recipient_addresses(char ***return_value, LAFmessage_Message *target_message, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to a single-dimensional array of strings containing the addresses of the recipients for the given message, including all recipients that are still queued, have been rejected or have been accepted. This array's value will remain the same regardless of the message's status in the queue.

LAFmessage_enum::return_codes get_rejected_recipient_addresses(char ***return_value, LAFmessage_Message *processed_message, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to a single-dimensional array of strings containing the addresses of the recipients that have been rejected for the given message. If the message is still being delivered, this array may fall out of date as more addresses are added to it.

NOTE: In this case, "rejected" means that the SMTP server refused to accept the recipient address for delivery. Typically, this indicates that the format of the address was bad. Even if an address was not rejected by the SMTP server, there is still a chance that the message could bounce because the destination mailbox does not exist, is full, etc.

LAFmessage_enum::return_codes get_sender_address(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to the address used by the SMTP object to specify the sender in the SMTP envelope. See set_sender_address() for a more detailed explanation of this value's meaning.

LAFmessage_enum::return_codes get_sent_recipient_addresses(char ***return_value, LAFmessage_Message *processed_message, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to a single-dimensional array of strings containing the addresses of the recipients that have been accepted by the SMTP server for delivery. If the message is still being delivered, this array may fall out of date as more addresses are added to it.

NOTE: In this case, "accepted" means that the SMTP server accepted the recipient address for delivery. Typically, this indicates that the format of the address was good. Even if an address was not rejected by the SMTP server, there is still a chance that the message could bounce because the destination mailbox does not exist, is full, etc.

LAFmessage_enum::return_codes get_smtp_port(long *return_value)
If successful, *return_value is set equal to the port number on the SMTP server the SMTP object will connect to.

LAFmessage_enum::return_codes get_smtp_server(char **return_value, LAFmessage_enum::display_types *display = NULL)
If successful, *return_value is set equal to the address of the SMTP server the SMTP object will connect to.

LAFmessage_enum::return_codes get_min_delivery_secs(long *return_value)
If successful, *return_value is set equal to the minimum number of seconds between message delivery. See set_min_delivery_secs() for a more detailed explanation of this value's meaning.

LAFmessage_enum::return_codes set_delivery_mode(LAFmessage_enum::smtp_delivery_modes new_delivery_mode)
Directs LAFmessage_SMTP to deliver messages when they are added to the queue or to save them and deliver them in the background. See cmd_add() and set_max_worker_threads() for details of other behavioral changes caused by this property. The possible values are as follows:
Value Meaning
LAFmessage_enum::LAFMESSAGE_SMTP_DELIVERY_asynchronous Messages added to the queue are delivered in the background by worker threads.
LAFmessage_enum::LAFMESSAGE_SMTP_DELIVERY_synchronous Messages added to the queue are delivered immediately by the thread that added them.

This option is the default.

LAFmessage_enum::return_codes set_failure_retries(long new_failure_retries)
Sets the number of times LAFmessage_SMTP will attempt to deliver the message to a recipient before marking that recipient "rejected".

LAFmessage_enum::return_codes set_failure_retry_delay_secs(long new_failure_retry_delay_secs)
The minimum number of seconds between retrying a failed delivery. NOTE: This is the minimum number of seconds, not a precise measurement. If the queue is very full and/or there are very few threads working on delivering messages, a retry attempt may take much long to begin than is set with set_failure_retry_delay_secs().

LAFmessage_enum::return_codes set_max_idle_mins(long new_max_idle_mins)
Sets the number of minutes a queue must remain idle before it is removed from the object cache. NOTE: In this context, "idle" means "unreferenced". If a caller loads the queue from the object cache the queue is not considered "idle", even if the caller did not call any functions on it.

LAFmessage_enum::return_codes set_max_recipients_per_message(long new_max_recipients_per_message)
Sets how many recipients can be specified per message transaction. Increasing this value can give a significant reduction in the delivery times of messages with many recipients, so long as it does not exceed what the SMTP server is capable of.

NOTE: This property works by taking advantage of a feature in the specification used to describe the recipients of a message to the SMTP server. This feature has absolutely no impact and is not impacted by the contents of the "To", "Cc" or "Bcc" lines in the message's headers. The headers do not affect message delivery and need not contain any information about the true recipients of a message.

LAFmessage_enum::return_codes set_max_worker_threads(long new_max_worker_threads)
Sets the maximum number of worker threads created to deliver mail from the queue. This property is only used when set_delivery_mode() is called with LAFmessage_enum::LAFMESSAGE_SMTP_DELIVERY_asynchronous.

NOTE: At first glance, this may seem like a situation where "more is better". That is not necessarily (and most often is not) the case. The only time it is appropriate to increase this value is when delivering a single message to the SMTP server takes more than approximately 0.5 seconds. The most frequent cause of slow SMTP server response times are DNS queries. If the server is extremely paranoid about DNS resolution, delivery times can go up dramatically. To test this, telnet to your SMTP server's SMTP port (usually port 25) from the console of the server running LAFmessage. If you get a greeting banner immediately, do not increase this value. If it takes several seconds for a greeting banner to appear, increasing this value may have a positive effect.

The reason for this admonition is simple: Mail delivery can proceed only as fast as the slowest step in the process. When you increase this value, you are assuming that the slowest step is the transfer of data from LAFmessage to the SMTP server. If you are incorrect, you will be creating a bottleneck inside LAFmessage as multiple worker threads fight for control of the queue in order to remove messages from it and record their status. This may actually slow down mail delivery significantly. If the component becomes so busy that individual threads' attempts to lock the queue return timeout errors, your mail may get thrown on the floor.

ALSO NOTE: A high number of worker threads delivering mail to the SMTP server may put undue stress on the server, potentially keeping other users from using the server and/or upsetting the sysmadman.

LAFmessage_enum::return_codes set_id(char *new_id)
Sets the POP3 object's ID field to new_id. If the given ID is already in use, set_id() will return LAFmessage_enum::LAFMESSAGE_error_id_in_use.

LAFmessage_enum::return_codes set_queued_messages(LAFmessage_Message **new_queued_messages, char ***recipient_addresses)
set_queued_messages() removes all unprocessed messages from the queue and fills the queue with the value given. Passing NULL for both parameters will empty the queue. When a NULL-terminated array of LAFmessage_Message objects is given, a two-dimensional array strings must be given for the recipient_addresses parameter so that each index in the first dimension matches a message object in the new_queued_messages parameter. If this sounds confusing, it's only because this is hard to explain in English. See the examples.

When new_queued_messages is NULL and a two-dimensional array of email addresses is given for recipient_addresses, the recipient addresses will be checked by the SMTP server for validity. See cmd_add() for more details on this feature.

LAFmessage_enum::return_codes set_sender_address(char *new_sender_address)

LAFmessage_enum::return_codes set_smtp_port(long new_smtp_port)

LAFmessage_enum::return_codes set_smtp_server(char *new_smtp_server, LAFmessage_enum::internet_address_types type = LAFMESSAGE_DEFAULT_SMTP_SERVER_ADDRESS_TYPE)

LAFmessage_enum::return_codes set_min_delivery_secs(long new_min_delivery_secs)

LAFmessage_enum::return_codes cmd_add(LAFmessage_Message *new_message, char **recipient_addresses)

LAFmessage_enum::return_codes cmd_add(LAFmessage_Message *new_message, char *recipient_address)

LAFmessage_enum::return_codes cmd_pause(long pause_secs = LAFMESSAGE_DEFAULT_SMTP_PAUSE_SECS, long pause_delay_secs = LAFMESSAGE_DEFAULT_SMTP_PAUSE_DELAY_SECS)

LAFmessage_enum::return_codes cmd_remove(LAFmessage_Message *target_message)

LAFmessage_enum::return_codes cmd_remove_all()

LAFmessage_enum::return_codes cmd_unpause(long unpause_delay_secs = LAFMESSAGE_DEFAULT_SMTP_UNPAUSE_DELAY_SECS)

LAFmessage_enum::return_codes cmd_wait_until_empty(long max_wait_secs = LAFMESSAGE_DEFAULT_SMTP_WAIT_SECS)

LAFmessage_enum::return_codes update_ref_count(long update_amount, long *return_value = NULL)

LAFmessage_enum::return_codes init()

LAFmessage_enum::return_codes empty()

static LAFmessage_enum::return_codes get_object(LAFmessage_SMTP **return_value, char *target_id = NULL)

static LAFmessage_enum::return_codes put_object(LAFmessage_SMTP **object)

static LAFmessage_enum::return_codes pool_run_stop(int destroy_flag = 0)


COM Overview

LAFmessage's COM interface is simply a wrapper around its C++ interface. The property and method names are slightly different (following the persistant model of using StudlyCaps instead of underscores) and the argument lists are different (because COM does not allow function overloading like C++ does) but the COM properties and methods map directly to the C++ functions.

The biggest conceptual difference is the object sharing that occurs at the C++ level. Each COM object has an internal pointer to a C++ object. Each COM object routes its activities to that internal pointer. When the COM object is destroyed, its internal pointer is released and its reference count is decremented. In other words, a single C++ object may be shared across many COM objects -- any change made to the C++ object by calling its functions (either directly or through the COM interface) will immediately be visible to all the other callers who share it.

The practical implication is this: two LAFmessage COM objects may be "the same" but may also be distinct. For example, in VB, if two LAFmessage.SMTP objects are created by using CreateObject() and both of their ID properties are set to the same value, the two objects can be said to be "the same". This is true because they both will appear to contain the same data and updates to one of the objects will immediately be reflected by the other. However, they are still two distinct COM objects, so comparing their pointers (e.g. If (smtp_obj1 Is smtp_obj2) Then) will not work. Instead, their ID properties must be compared (e.g. If (StrComp(smtp_obj1.ID, smtp_obj2.ID, 1) = 0) Then).

If the above doesn't make sense, don't panic. Think of it this way -- don't use VBScript's Is operator to compare two LAFmessage objects. Only use the Is operator to compare LAFmessage objects to Nothing.


All of LAFmessage's COM objects are completely thread-safe. They can be safely stored in a persistant object cache outside of LAFmessage (e.g. LAFcommon.COMCollection, ASP's Session).


All of LAFmessage's COM objects have the following properties (using VB terminology):

ThrowErrors As Boolean
When ThrowErrors is True, any non-success return code will cause LAFmessage to generate an ATL COM exception that contains the relevant error message. This type of exception will cause a VB or VBScript program to immediately stop executing and display the message and line number that generated it. This is extremely handy for debugging but should not be used in production. The value of ThrowErrors is passed to any objects returned by any methods or properties. For example, if message_obj is an LAFmessage.Message object and its ThrowErrors property is set to True, any LAFmessage.Attachment objects returned by message_obj.Attachments will have their ThrowErrors property set to True automatically.

ThrowErrors defaults to False.

ErrorMessage As String (read-only)
ErrorMessage provides a human-readable explanation of the result of the last operation. If the last operation was successful, ErrorMessage will return Empty.

ErrorMessage defaults to Empty.

Note: Do not check ErrorMessage to determine if the last operation was successful, only use it to get the explanation of the return code. Use LastResult to retrieve the last return code.

NamedConstant(name As String) As Long (read-only)
NamedConstant will provide the numeric value of an enumerated value from LAFmessage's type library. These constants and their values are also defined in the text file lafmessage_vbs.txt for use from ASP pages, but some VBScript environments (notably Windows Scripting Host) cannot import text files. The value of the constant will be returned by NamedConstant when its name is passed as a parameter.

Note: NamedConstant is SLOW SLOW SLOW! It performs a linear search of the constant namespace to find the passed string. It should only be used as a last resort if the constants from the type library cannot be accessed and lafmessage_vbs.txt cannot be imported.

LastResult As EnumReturnCodes (read-only)
Every action against every LAFmessage COM object generates a return code, even property accesses. Unfortunately, in VB and VBScript it is not possible to determine if a property access was successful or not. LastResult returns the last return code that was generated.

LastResult defaults to LAFMESSAGE_success.

All of LAFmessage's COM objects have the following function (using VB terminology):

Sub VersionInfo(ByRef file_major As Variant, ByRef file_minor As Variant, ByRef file_revision As Variant, ByRef file_build As Variant, ByRef product_major As Variant, ByRef product_minor As Variant, ByRef product_revision As Variant, ByRef product_build As Variant, ByRef additional_info As Variant)
VersionInfo returns information about the version of the LAFmessage DLL that is running on the server. This enables the API programmer to discover the version number without having access to the server's filesystem. The major, minor, revision and build numbers can be assembled in order (m.m.r.b) to give the version number of the DLL. Because the file and product versions are stored separately, they are returned separately. The additional_info parameter can be used by the author to provide additional comments or information about the object or DLL.


Most of the member functions on LAFmessage COM objects return a EnumReturnCodes value. The possible values and their meanings are:

Value Meaning
LAFMESSAGE_success The operation completed successfully.
LAFMESSAGE_fail The operation failed but did not generate an error.
LAFMESSAGE_fail_queue_paused The message cannot be synchronously processed at this time because the queue is currently paused. The message has been queued and will be processed when the queue is unpaused.
LAFMESSAGE_fail_invalid_state The requested method cannot operate while the object is in its current state. Please check the documentation for a description of this object's states.
LAFMESSAGE_fail_file_exists The operation was specified to proceed without overwriting an existing file. The given filename already exists, so the operation has failed.
LAFMESSAGE_error_not_implemented The called method, while part of the API definition, has not yet been implemented. Bug the author to complete the implementation.
LAFMESSAGE_error_internal An internal error occurred. This process may need to be restarted.
LAFMESSAGE_error_string_too_long The given string could not be used because it exceeds the maximum length. See the documentation for maximum lengths of properties and parameters.
LAFMESSAGE_error_invalid_value The given value was not valid for the property or parameter it was given for. See the documentation for a list of valid values for each property and parameter.
LAFMESSAGE_error_mutex_timeout The operation failed while attempting to obtain an exclusive lock over a resource. The system is probably just too busy -- try again later.
LAFMESSAGE_error_invalid_object The object pointer passed as a parameter was either NULL or of the wrong type. See the documentation for details on the correct object types for each property and parameter.
LAFMESSAGE_error_not_found The referenced object or value could not be found.
LAFMESSAGE_error_no_such_constant A constant with the given name does not exist. Please check spelling and versions.
LAFMESSAGE_error_bad_bstr A string could not be extracted from the given BSTR.
LAFMESSAGE_error_bad_array An array operation failed, most likely due to a passed array having the wrong shape/size or perhaps because a new array could not be created or filled. See the documentation for details on the correct array types for each property and parameter.
LAFMESSAGE_error_queryinterface_failed A COM object could not be instantiated internally. This process should probably be restarted.
LAFMESSAGE_error_bad_variant The given VARIANT did not contain the expected data type. See the documentation for details of the expected types of each property and parameter.
LAFMESSAGE_error_bad_object The given object could not be cast to the correct type. See the documentation for details of the expected types of each property and parameter.
LAFMESSAGE_error_bad_address A bad internet address was given.
LAFMESSAGE_error_connection_lost The connection to the remote server was unexpectedly lost after it was established.
LAFMESSAGE_error_connection_refused The attempt to establish a connection to the remote server was refused. This indicates that the machine is alive but is not accepting messages, possibly because it is too busy.
LAFMESSAGE_error_connection_failed The connection to the remote server failed.
LAFMESSAGE_error_network_down The networking subsystem is down on this host. Check to ensure this machine has a network route to the remote server.
LAFMESSAGE_error_out_of_space This host is out of memory -- kill some processes to free up enough memory to process mail.
LAFMESSAGE_error_host_not_found No DNS record for the given remote host could be found.
LAFMESSAGE_error_not_initialized A subsystem was not initialized correctly, most likely the networking library.
LAFMESSAGE_error_try_again_later The networking subsystem on this host is too busy to deliver mail right now. Try again later.
LAFMESSAGE_error_cant_happen You shouldn't ever see this message. Report it to the author immediately along with all the details you have about what caused it.
LAFMESSAGE_error_already_paused The queue is already paused and cannot be paused again without being restarted first. If the intent is to change the amount of time the queue stays paused, set the resume delay using the unpause property instead.
LAFMESSAGE_error_not_paused The queue is not currently paused; it cannot be unpaused at this time.
LAFMESSAGE_error_id_in_use The object's ID cannot be changed to the given ID; the given ID is already in use.
LAFMESSAGE_error_cannot_open_file The attempt to open the file for reading or writing failed. The possible reasons are too numerous to list but the most likely causes are insufficient permissions and malformed filenames (UNCs are not allowed).
LAFMESSAGE_error_delivery_failure The message could not be delivered -- the remote server returned an unexpected response.
LAFMESSAGE_error_remote_timeout The operation timed out because a response was not received from the remote server in time. This might indicate a network problem or excessive load on the server.
LAFMESSAGE_error_missing_sender_address Message delivery cannot proceed without a sender address to authenticate to the remote server.
LAFMESSAGE_error_bad_greeting The remote server did not send a valid greeting banner. The connection attempt has been aborted.
LAFMESSAGE_error_bad_response_format The remote server responded to the command with a success indicator but gave a response that does not match the format specified in the protocol.
LAFMESSAGE_error_bad_message_format The message cannot be parsed and split into its representative objects -- its format does not match the specifications closely enough to be decyphered.
LAFMESSAGE_error_bad_header_format The header cannot be parsed and split into its representative parts -- its format does not match the specifications closely enough to be decyphered.
LAFMESSAGE_error_bad_attachment_format The attachment cannot be parsed and split into its representative parts -- its format does not match the specifications closely enough to be decyphered.
LAFMESSAGE_error_missing_server_address Message delivery cannot proceed without a server address.


Most of the String properties accept an optional Variant parameter named display. This parameter can be used to force the called function to canonicalize the string before returning it. Four types of canonicalization are possible: HTML, TEXTAREA, SQL and Javascript.

The parameter must be a single value from the EnumDisplayTypes enumeration or a single-dimensional array of values from the EnumDisplayTypes enumeration. If the value is a single value, the string will be canonicalized once using the corresponding algorithm. If the value is an array, each value in the array will cause the string to be canonicalized using the corresponding algorithm in the order given in the array. For example, the array Array(LAFMESSAGE_DISPLAY_javascript, LAFMESSAGE_DISPLAY_javascript, LAFMESSAGE_DISPLAY_html) will cause the string to be first canonicalized for Javascript. The result will be canonicalized for Javascript. The last result will be canonicalized for Javascript and the final result will be returned.

Note: The display parameter only has meaning when retrieving the value; it is ignored when assigning a new value (it will not "uncanonicalize" a string).


COM interface: LAFmessage.Attachment

LAFmessage.Attachment encapsulates the functionality required for a MIME message attachment. Attachments can be used to transmit arbitrary data within messages that may not be suitable for direct display to the user.


The "inline flag" referenced below affects how the attachment is embedded in the message. "Inline" attachment content may be included directly within the displayed message and is embedded in multipart/related blocks instead of multipart/mixed blocks. All inline attachments must have names; these names can be referenced using a "cid:" tag in a text body. For example, if an inline attachment named "foo.gif" is part of a message, an HTML text section may contain the following tag: <IMG SRC="cid:foo.gif">. This will cause the image file to be loaded into the HTML document from the attachment content without requiring internet access.

Unfortunately, Microsoft Outlook is not compliant with RFC 2387 and does not implement this feature. It does not recognize multipart/related MIME blocks and interprets them as multipart/mixed (in compliance with RFC 2045). Because of this, the presence of an inline attachment will cause Outlook to display a completely blank message that appears to have a single attachment -- a message. Opening the "attached" message will display the message as it was originally intended; the referenced inline attachments are inserted into the document where appropriate.

That's correct -- Outlook will insert the attachment content referenced by the "cid:" tag and it will not correctly recognize multipart/related MIME blocks. In direct violation of RFC 2387, Outlook will include any attachment's content that is referenced by name with a "cid:" tag; it does not require that the attachment be marked inline.

On the other hand, Netscape Communicator does implement RFC 2387 correctly and will not recognize "cid:" tags that reference non-inline attachments. This means that if you want to use "cid:" tags, you must choose which mail application to target -- Outlook or EveryoneElseTM. One of them will not see the email as you intended.

NOTE: The above only applies to inline attachments. There is no problem with sending HTML email that links to files and images on remote servers.


In addition to the properties listed above, LAFmessage.Attachment has the following properties (using VB terminology):

Content As Variant
The attachment's content. Content may be set from a String or a single-dimensional array of Bytes but is always returned as a single-dimensional array of Bytes. This is because a binary attachment may contain embedded null characters and/or may suffer from being encoded in a unicode string.

Note that Content will also accept a value of type Byte(). This type cannot be generated by VB or VBScript (they can only generate arrays of Variants, each of which may be a Byte). However, some external C++-based utilities (such as SAFileUp) will generate this type. The automation value of this type is VT_ARRAY | VT_UI1. See SoftArtisans.SAFile.UploadContents for an example usage of this type.

Encoding As EnumMIMEEncodingTypes
The attachment's MIME encoding algorithm. Each MIME type has "minimum" and "maximum" encoding levels that cannot be overridden by setting Encoding. When an attachment's Type is changed, its Encoding is automatically updated to the minimum level (if the current level is below the minimum) or the maximum level (if the current level is above the maximum). Because of this, explicitly setting the encoding level is usually not necessary. The minimum and maximum levels are listed below:
LAFmessage_enum::mime_types value Minimum encoding level Maximum encoding level
LAFMESSAGE_MIME_text_plain 7bit base64
LAFMESSAGE_MIME_text_xaol quoted-printable base64
LAFMESSAGE_MIME_text_html quoted-printable base64
LAFMESSAGE_MIME_image_gif base64 base64
LAFMESSAGE_MIME_image_jpeg base64 base64
LAFMESSAGE_MIME_application_octetstream base64 base64

ID(Optional display As Variant) As String
The object's unique identifier, canonicalized according to the value of display when retrieved. A new object's ID is automatically generated by LAFmessage when the object is created. The identifier can only be changed to the ID of another valid attachment object, which will reassociate the COM object with the new attachment. The old attachment will be lost unless it is stored somewhere else -- saving its ID is not enough (for more details, see the discussion of reference counting in the C++ Overview (above)). New ID values may not be assigned arbitrarily.

Inline As Boolean
Controls if the attachment is an "inline" attachment or not (see above).

MIMESection(Optional display As Variant) As String
The complete MIME message block that the attachment will create when attached to a message, canonicalized according to display when retrieved. When set, MIMESection will parse the given MIME message block and retrieve the values of Name, Type, Encoding from the block. If successful, the old values of Name, Type, Encoding and Content are lost.

MIMESectionContent(Optional display As Variant) As String (read-only)
Returns the body of the MIME message block that the attachment will create when attached to a message, canonicalized according to display.

MIMESectionHeader(Optional display As Variant) As String (read-only)
Returns the header of the MIME message block that the attachment will create when attached to a message, canonicalized according to display.

Name(Optional display As Variant) As String
The name of the attachment, canonicalized according to the value of display when retrieved. This field is required if Inline is True. The name may be used by "cid:" tags within a message's text sections and will also be the attachment's suggested filename if the user wishes to save it to disk.

Type as EnumMIMETypes
The attachment's MIME type. The following members of EnumMIMETypes are valid attachment types:
Value Meaning
LAFMESSAGE_MIME_text_plain Plain text attachment.
LAFMESSAGE_MIME_text_xaol AOL rich media attachment (only required for AOL 5.0 or earlier).
LAFMESSAGE_MIME_text_html HTML text attachment.
LAFMESSAGE_MIME_image_gif GIF image file attachment.
LAFMESSAGE_MIME_image_jpeg JPEG image file attachment.
LAFMESSAGE_MIME_application_octetstream Any other binary file attachment.

In addition to the method listed above, LAFmessage.Attachment has the following methods (using VB terminology):

Function LoadContent(file_path As String) As EnumReturnCodes
Opens the file named file_path and loads the entire file into the attachment's content field. It is the caller's responsibility to ensure the process owner has sufficient permission to read the file and that the path is correct. Windows UNC paths are not supported.

Function SaveContent(file_path As String, Optional save_option As EnumSaveOptions = LAFMESSAGE_DEFAULT_ATTACHMENT_SAVE_OPTION_IDL) As EnumReturnCodes
Opens the file named file_path and saves the attachment's content field to it. It is the caller's responsibility to ensure the process owner has sufficient permission to create or save the file and that the path is correct. Windows UNC paths are not supported.

If save_option is given, it must be one of the following two values:

  • LAFMESSAGE_SAVE_create_or_truncate -- the file will be created if it does not exist or will be truncated if it does exist.
  • LAFMESSAGE_SAVE_create_only -- the file will be created if it does not exist. If the file already exists, no action will be taken and an error will be returned.

Function Clone(ByRef clone_object As Variant) As EnumReturnCodes
Creates a new LAFmessage.Attachment COM object that is a clone of this COM object -- all of the values are copied but the ID is different and the object structures are discrete.


COM interface: LAFmessage.Header

LAFmessage.Header encapsulates all of the functionality necessary to represent a header of a MIME message or a MIME message block. Headers typically consist of a type and a value, though two types of header also have names.


The following members of EnumMIMETypes are valid headers and may be used as Type values:
Value Meaning
LAFMESSAGE_MIME_HEADER_return_path A "Return-Path" field. From RFC 822: The field is intended to contain definitive information about the address and route back to the message's originator. While the syntax indicates that a route specification is optional, every attempt should be made to provide that information in this field.
LAFMESSAGE_MIME_HEADER_received A "Recieved" field. From RFC 822: A copy of this field is added by each transport service that relays the message. The information in the field can be quite useful for tracing transport problems.
LAFMESSAGE_MIME_HEADER_date A "Date" field. The date the message was generated.
LAFMESSAGE_MIME_HEADER_from A "From" field. From RFC 822: This field contains the identity of the person(s) who wished this message to be sent. The message-creation process should default this field to be a single, authenticated machine address, indicating the AGENT (person, system or process) entering the message.
LAFMESSAGE_MIME_HEADER_subject A "Subject" field. From RFC 822: This is intended to provide a summary, or indicate the nature, of the message.
LAFMESSAGE_MIME_HEADER_sender A "Sender" field. From RFC 822: This field contains the authenticated identity of the AGENT (person, system or process) that sends the message. It is intended for use when the sender is not the author of the message, or to indicate who among a group of authors actually sent the message. If the contents of the "Sender" field would be completely redundant with the "From" field, then the "Sender" field need not be present and its use is discouraged (though still legal). In particular, the "Sender" field MUST be present if it is NOT the same as the "From" Field.
LAFMESSAGE_MIME_HEADER_to A "To" field. From RFC 822: This field contains the identity of the primary recipients of the message.
LAFMESSAGE_MIME_HEADER_cc A "Cc" field. From RFC 822: This field contains the identity of the secondary (informational) recipients of the message.
LAFMESSAGE_MIME_HEADER_x A user-defined field. User-defined fields must have names and are automatically prefixed with "X-".
LAFMESSAGE_MIME_HEADER_bcc A "Bcc" field. From RFC 822: This field contains the identity of additional recipients of the message.
LAFMESSAGE_MIME_HEADER_comments A "Comments" field. From RFC 822: Permits adding text comments onto the message without disturbing the contents of the message's body.
LAFMESSAGE_MIME_HEADER_encrypted An "Encrypted" field. From RFC 822: If the body of a message has been encrypted, to keep its contents private, the "Encrypted" field can be used to note the fact and to indicate the nature of the encryption.
LAFMESSAGE_MIME_HEADER_in_reply_to An "In-Reply-To" field. From RFC 822: The contents of this field identify previous correspon- dence which this message answers. Note that if message identifiers are used in this field, they must use the msg-id specification format.
LAFMESSAGE_MIME_HEADER_keywords A "Keywords" field. From RFC 822: This field contains keywords or phrases, separated by commas.
LAFMESSAGE_MIME_HEADER_message_id A "Message-ID" field. From RFC 822: This field contains a unique identifier (the local-part address unit) which refers to THIS version of THIS message. The uniqueness of the message identifier is guaranteed by the host which generates it.
LAFMESSAGE_MIME_HEADER_references A "References:" field. From RFC 822: The contents of this field identify other correspondence which this message references. Note that if message identifiers are used, they must use the msg-id specification format.
LAFMESSAGE_MIME_HEADER_reply_to A "Reply-To" field: From RFC 822: This field provides a general mechanism for indicating any mailbox(es) to which responses are to be sent.
LAFMESSAGE_MIME_HEADER_resent_bcc A "Resent-Bcc:" field. From RFC 822: This field contains the identity of additional recipients of the message.
LAFMESSAGE_MIME_HEADER_resent_cc A "Resent-Cc" field. From RFC 822: This field contains the identity of the secondary (informational) recipients of the message.
LAFMESSAGE_MIME_HEADER_resent_date A "Resent-Date" field. The date the resent message was generated.
LAFMESSAGE_MIME_HEADER_resent_from A "Resent-From" field. From RFC 822: This field contains the identity of the person(s) who wished this message to be sent. The message-creation process should default this field to be a single, authenticated machine address, indicating the AGENT (person, system or process) entering the message.
LAFMESSAGE_MIME_HEADER_resent_message_id A "Resent-Message-ID" field. From RFC 822: This field contains a unique identifier (the local-part address unit) which refers to THIS version of THIS message. The uniqueness of the message identifier is guaranteed by the host which generates it.
LAFMESSAGE_MIME_HEADER_resent_reply_to A "Resent-Reply-To" field: From RFC 822: This field provides a general mechanism for indicating any mailbox(es) to which responses are to be sent.
LAFMESSAGE_MIME_HEADER_resent_sender A "Resent-Sender" field. From RFC 822: This field contains the authenticated identity of the AGENT (person, system or process) that sends the message. It is intended for use when the sender is not the author of the message, or to indicate who among a group of authors actually sent the message. If the contents of the "Sender" field would be completely redundant with the "From" field, then the "Sender" field need not be present and its use is discouraged (though still legal). In particular, the "Sender" field MUST be present if it is NOT the same as the "From" Field.
LAFMESSAGE_MIME_HEADER_resent_to A "Resent-To" field. From RFC 822: This field contains the identity of the primary recipients of the message.

From RFC 822: Whenever the string "Resent-" begins a field name, the field has the same semantics as a field whose name does not have the prefix. However, the message is assumed to have been forwarded by an original recipient who attached the "Resent-" field. This new field is treated as being more recent than the equivalent, original field. For example, the "Resent-From", indicates the person that forwarded the message, whereas the "From" field indicates the original author.

The following members of EnumMIMETypes are valid headers but may not be used as Type values:
Value Meaning
LAFMESSAGE_MIME_HEADER_content_disposition A "Content-Disposition" field. This field conveys presentational information about a MIME message block, as defined in RFC 2183.
LAFMESSAGE_MIME_HEADER_content_type A "Content-Type" field. From RFC 2045: The purpose of the Content-Type field is to describe the data contained in the body fully enough that the receiving user agent can pick an appropriate agent or mechanism to present the data to the user, or otherwise deal with the data in an appropriate manner.
LAFMESSAGE_MIME_HEADER_content_transfer_encoding A "Content-Transfer-Encoding" field. From RFC 2045: Many media types which could be usefully transported via email are represented, in their "natural" format, as 8bit character or binary data. Such data cannot be transmitted over some transfer protocols. It is necessary, therefore, to define a standard mechanism for encoding such data into a 7bit short line format. This document specifies that such encodings will be indicated by a new "Content-Transfer-Encoding" header field.
LAFMESSAGE_MIME_HEADER_content_id A "Content-ID" field. From RFC 2045: In constructing a high-level user agent, it may be desirable to allow one body to make reference to another. Accordingly, bodies may be labelled using the "Content-ID" header field, which is syntactically identical to the "Message-ID" header field. Like the Message-ID values, Content-ID values must be generated to be world-unique.
LAFMESSAGE_MIME_HEADER_mime_version A "MIME-Version" field. From RFC 2045: "MIME-Version" is to be used to declare the version of the Internet message body format standard in use.
LAFMESSAGE_MIME_HEADER_content_description A "Content-Description" field. From RFC 2045: The ability to associate some descriptive information with a given body is often desirable. Such text may be placed in the Content-Description header field.
LAFMESSAGE_MIME_HEADER_unknown A header field that does not match any of the above fields. Its name field will be parsed into the LAFmessage_Header object's name field.


In addition to the properties listed above, LAFmessage.Header has the following properties (using VB terminology):

Content(Optional display As Variant) As String
The header's content. Header content is much more complex than it should be; casual users should beware. For full details, please refer to RFC 822 section 3. In a nutshell, the standard allows headers to contain comments, quoted strings, span multiple lines and defines strict limits on allowed characters inside and outside comments and quoted strings.

For now, LAFmessage.Header "parses" and "formats" headers by obeying RFC 822 "folding" rules (word-wrapping long lines). The more advanced format specifications are up to the caller to enforce. Specifically, illegal character sequences and requirements that some fields only contain email addresses are not enforced. Expect this to change in a future version.

Encoding As EnumMIMEEncodingTypes
The header's MIME encoding algorithm. Headers cannot be encoded with any algorithm other than LAFMESSAGE_MIME_ENCODING_7bit, so this property is only included for completeness.

Encoding defaults to LAFMESSAGE_MIME_ENCODING_7bit.

ID(Optional display As Variant) As String
The object's unique identifier, canonicalized according to the value of display when retrieved. A new object's ID is automatically generated by LAFmessage when the object is created. The identifier can only be changed to the ID of another valid header object, which will reassociate the COM object with the new header. The old header will be lost unless it is stored somewhere else -- saving its ID is not enough (for more details, see the discussion of reference counting in the C++ Overview (above)). New ID values may not be assigned arbitrarily.

MIMESection(Optional display As Variant) As String
The complete header, suitable for inclusion in a MIME message or MIME message block, canonicalized according to display when retrieved. When set, MIMESection will parse the given header and retrieve the values of Name, Type and Content from the header. If successful, the old values of Name, Type and Content are lost.

Name(Optional display As Variant) As String
The header's name. The name is only used for headers of type LAFMESSAGE_MIME_HEADER_x (where it is prepended with "X-" when the header is constructed) and LAFMESSAGE_MIME_HEADER_unknown.

Type As EnumMIMETypes
The header's type. Valid type values are listed above.

In addition to the method listed above, LAFmessage.Header has the following function (using VB terminology):

Function Clone(ByRef clone_object As Variant) As EnumReturnCodes
Creates a new LAFmessage.Header COM object that is a clone of this COM object -- all of the values are copied but the ID is different and the object structures are discrete.


COM interface: LAFmessage.Message

LAFmessage.Message is primarily a container of LAFmessage.Attachment, LAFmessage.Header and LAFmessage.Text objects to be assembled as an RFC 2045-compliant message.

When LAFmessage.Message.MIMEMessage assembles its text, it takes several actions that cannot be overridden.


In addition to the properties listed above, LAFmessage.Message has the following properties (using VB terminology):

Attachments(Optional type As EnumMIMETypes = LAFMESSAGE_DEFAULT_MIME_TYPE_IDL) As Variant
The LAFmessage.Attachment objects associated with the message, in a single-dimensional array. If type is given, only attachment objects whose Type property matches the given type will be returned. When successful, Attachments always returns an array, even when no attachment objects are associated with the message.

When assigning a value to Attachments, a single LAFmessage.Attachment object may be given or a single-dimensional array of LAFmessage.Attachment objects may be given. If the assignment is successful, the previously associated attachment objects are discarded and will be lost unless they are stored elsewhere (saving their IDs is not sufficient).

Note: A single attachment object may be associated with a message multiple times. In that case, it exists in the array as many times as it is associated, in the order it was added.

Encoding As EnumMIMEEncodingTypes
The message's MIME encoding type. This property is ignored during message generation and is only included for completeness.

HeaderContents(target_header_type As EnumMIMETypes, Optional display As Variant) As Variant (read-only)
HeaderContents is provided for the convenience of callers who need the content of a specific header field without needing to manipulate a LAFmessage.Header object. Since any header may appear more than once in a message, HeaderContents always returns a single-dimensional array, even if no headers of the requested type exist in the message. Each content string in the returned array is canonicalized according to display.

Headers(Optional type As EnumMIMETypes = LAFMESSAGE_DEFAULT_MIME_TYPE_IDL) As Variant
The LAFmessage.Header objects associated with the message, in a single-dimensional array. If type is given, only header objects whose Type property matches the given type will be returned. When successful, Headers always returns an array, even when no header objects are associated with the message.

When assigning a value to Headers, a single LAFmessage.Header object may be given or a single-dimensional array of LAFmessage.Header objects may be given. If the assignment is successful, the previously associated header objects are discarded and will be lost unless they are stored elsewhere (saving their IDs is not sufficient).

Note: A single header object may be associated with a message multiple times. In that case, it exists in the array as many times as it is associated, in the order it was added.

ID(Optional display As Variant) As String
The object's unique identifier, canonicalized according to the value of display when retrieved. A new object's ID is automatically generated by LAFmessage when the object is created. The identifier can only be changed to the ID of another valid message object, which will reassociate the COM object with the new message. The old message will be lost unless it is stored somewhere else -- saving its ID is not enough (for more details, see the discussion of reference counting in the C++ Overview (above)). New ID values may not be assigned arbitrarily.

MIMEMessage(Optional display As Variant) As String
The complete MIME message, suitable for delivery through a message transport system or storage within a message storage system. The message text is canonicalized according to display when retrieved. When set, MIMEMessage parses the given message and retrieves the headers, attachments and text sections from it. If successful, the old message contents are lost.

Texts(Optional type As EnumMIMETypes = LAFMESSAGE_DEFAULT_MIME_TYPE_IDL) As Variant
The LAFmessage.Text objects associated with the message, in a single-dimensional array. If type is given, only text objects whose Type property matches the given type will be returned. When successful, Texts always returns an array, even when no text objects are associated with the message.

When assigning a value to Texts, a single LAFmessage.Text object may be given or a single-dimensional array of LAFmessage.Text objects may be given. If the assignment is successful, the previously associated text objects are discarded and will be lost unless they are stored elsewhere (saving their IDs is not sufficient).

Note: A single text object may be associated with a message multiple times. In that case, it exists in the array as many times as it is associated, in the order it was added.

Type As EnumMIMETypes
The message's MIME type. This property is ignored during message generation and is only included for completeness.

In addition to the method listed above, LAFmessage.Message has the following methods:

Function Add(new_object As Object) As EnumReturnCodes
Associates a new LAFmessage.Attachment, LAFmessage.Header or LAFmessage.Text object with the message. The new_object object is appended the message's internal array of that object type. Calling Add() repeatedly with a set of objects of the same type is less efficient than constructing a single-dimensional array containing the objects and passing it to Attachment, Headers or Texts.

Function AddAttachment(type As EnumMIMETypes, content As Variant, Optional encoding As EnumMIMEEncodingTypes = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE_IDL, Optional name As Variant, Optional inline_flag As Boolean = LAFMESSAGE_DEFAULT_ATTACHMENT_INLINE_FLAG_IDL) As EnumReturnCodes
AddAttachment() is a convenience function for callers who wish to associate a new attachment object with a message but do not need to manipulate a LAFmessage.Attachment object. Internally, AddAttachment() constructs a LAFmessage.Attachment object and sets its Type property to type, its Content property to content, its Encoding property to encoding (if given), its Name property to name (if given) and its Inline property to inline_flag (if given). A failure of any property assignment will cause the resulting error to be returned by AddAttachment() and the new attachment object to be discarded.

Function AddHeader(type As EnumMIMETypes, content As String, Optional encoding As EnumMIMEEncodingTypes = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE_IDL, Optional name As Variant) As EnumReturnCodes
AddHeader() is a convenience function for callers who wish to associate a new header object with a message but do not need to manipulate a LAFmessage.Header object. Internally, AddHeader() constructs a LAFmessage.Header object and sets its Type property to type, its Content property to content, its Encoding property to encoding (if given) and its Name property to name (if given). A failure of any property assignment will cause the resulting error to be returned by AddHeader() and the new header object to be discarded.

Function AddText(type As EnumMIMETypes, content As String, Optional encoding As EnumMIMEEncodingTypes = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE_IDL) As EnumReturnCodes
AddText() is a convenience function for callers who wish to associate a new text object with a message but do not need to manipulate a LAFmessage.Text object. Internally, AddText() constructs a LAFmessage.Text object and sets its Type property to type, its Content property to content and its Encoding property to encoding (if given). A failure of any property assignment will cause the resulting error to be returned by AddText() and the new text object to be discarded.

Function Clone(ByRef clone_object As Variant) As EnumReturnCodes
Creates a new LAFmessage.Message COM object that is a clone of this COM objects -- of the values are copied but the ID is difference and the object structures are discrete.

Note: A cloned message object contains references to the same attachment, header and text objects as the original -- Clone() does not perform a deep copy. Modifying a property on an object associated with the clone will change the object associated with the original.

Function Load(file_path As String) As EnumReturnCodes
Opens the file named file_path and loads the entire file into the message's MIMEMessage property. It is the caller's responsibility to ensure the process owner has sufficient permission to read the file and that the path is correct. Windows UNC paths are not supported.

Function Save(file_path As String, Optional save_option As EnumSaveOptions = LAFMESSAGE_DEFAULT_MESSAGE_SAVE_OPTION_IDL) As EnumReturnCodes
Opens the file named file_path and saves the message's MIMEMessage property to it. It is the caller's responsibility to ensure the process owner has sufficient permission to create or save the file and that the path is correct. Windows UNC paths are not supported.

If save_option is given, it must be one of the following two values:

  • LAFMESSAGE_SAVE_create_or_truncate -- the file will be created if it does not exist or will be truncated if it does exist.
  • LAFMESSAGE_SAVE_create_only -- the file will be created if it does not exist. If the file already exists, no action will be taken and an error will be returned.


COM interface: LAFmessage.POP3

POP3 stands for Post Office Protocol version 3, defined by RFCs 1939 and 2449. It provides a simple way for a mail server to expose a mailbox to a remote host. It does not support encryption, so sensitive content such as passwords and message content are potentially exposed to sniffers. It also does not support mailbox manipulation commands for mail folders, message flags, etc. POP3's primary advantage is its simplicity, making it very lightweight and very widespread. The protocol is supported by most mail servers because it is so easy to implement.

LAFmessage.POP3 provides all the functionality required to communicate with a POP3 server and manipulate mailboxes. The object is stateful, meaning that not all commands are available at all times, depending on what commands have been previously given.

Specifically, the POP3 object defines three operating states in the EnumPOP3StatusTypes enumeration, as follows:
Value Meaning
LAFMESSAGE_POP3_unconnected The object's Connect() method has not been successfully called.
LAFMESSAGE_POP3_connected The object's Connect() method has been successfully called, the object's Disconnect() method has not been successfully called and the object has not been idle long enough to have automatically disconnected.
LAFMESSAGE_POP3_disconnected The object's Connect() method has been successfully called and either the object's Disconnect() method has been successfully called or the object has been idle long enough to have been automatically disconnected.

A POP3 object will automatically disconnect from a remote server after being idle for 5 minutes. An unreferenced POP3 object will not be removed from memory immediately -- it must remain idle for a specific amount of time before it will be removed. In this way, a POP3 object can be retrieved by its ID multiple times before it is discarded.

NOTE: It is very important to disconnect from a POP3 server as soon as possible. RFC 1939 states that the server must exclusively lock the mailbox while a POP3 connection is active. This means that no other POP3 connections can be established and no new mail can be added while a POP3 connection is open.

POP3 objects are COM collections, meaning that they are valid targets for VB's and VBScript's For Each loop construct. When enumerated in a For Each loop, a POP3 object will return LAFmessage.Message objects or Strings, depending on the value of ReturnObjects (below). The enumerated values correspond to the messages retrieved from the server, in the order they exist in the mailbox.

In addition to the properties listed above, LAFmessage.POP3 has the following properties:

Count As Long (read-only)
The number of messages in the mailbox. This number is not affected by selectively downloading only a few messages. In other words, if the mailbox on the server contains 10 messages but only the first 5 are downloaded, Count will have the value 10.

Count defaults to 0 until Connect() is called successfully.

ID(Optional display As Variant) As String
The unique identifier for this POP3 object. Setting this property to a value saved from a previous POP3 object will change the current POP3 object into the previously used one. This feature allows a request/response environment where persistantly saving objects is problematic (e.g. ASP) to retreive and reuse the same POP3 object again and again, even across multiple user "sessions".

ID is system-assigned to a short GUID.

Item(message_number As Long, Optional display As Variant) As Variant (read-only)
Returns the message number message_number from the mailbox as a LAFmessage.Message object or as a String depending on the value of ReturnObjects (below). If the requested message number is less than zero or greater than the value of Count, Item returns LAFMESSAGE_error_not_found. If the requested message has not been retrieved from the server, Item returns LAFMESSAGE_error_not_found.

Note: Item is the default property of LAFmessage.POP3. This means that, in VB and VBScript, pop3_object.Item(0) is the same as pop3_object(0).

MaxIdleMins As Long
The maximum number of minutes the POP3 object will persist in memory without being used.

MaxIdleMins defaults to 20.

POP3Port As Long
The port on the POP3 server to connect to. POP3Port must be a positive integer less than 65536.

POP3Port defaults to 110.

POP3Server(Optional type As EnumInternetAddressType = LAFMESSAGE_DEFAULT_POP3_SERVER_ADDRESS_TYPE_IDL, Optional display As Variant) As String
The POP3 server to connect to. The type parameter can be used to specify the value's meaning with the following values:
Value Meaning
LAFMESSAGE_INTERNET_ADDRESS_autodetect The object should attempt to determine the value's meaning on its own. Autodetecting is fairly safe: values are assumed to be fully qualified domain names unless they fit the format "X.X.X.X" where the Xs are all integers between 0 and 255, inclusive.

This option is the default.

LAFMESSAGE_INTERNET_ADDRESS_fqdn The value is a fully qualified domain name (e.g. mail.foo.com).
LAFMESSAGE_INTERNET_ADDRESS_ipv4_dotted_quad The value is an IP version 4 dotted quad IP address (e.g. 111.222.111.222).

POP3Server defaults to an empty string.

ReturnObjects As Boolean
ReturnObjects controls whether the POP3 object returns LAFmessage.Message objects or Strings from its Item property and when used in a VB(Script) For Each loop. If ReturnObjects is True, LAFmessage.Message objects are returned. If ReturnObjects is False, Strings are returned.

ReturnObjects defaults to True.

Sizes As Variant (read-only)
The sizes of each of the messages in the mailbox in bytes, as reported by the POP3 server. Sizes returns an array of Longs with as many elements as there are messages in the mailbox. Each array element corresponds with the message of the same index (e.g. Sizes(0) is the size of the message Item(0)).

Sizes is filled with data once the object's Connect() method has been successfully called -- all message sizes are available even if all of the messages have not been retreived.

NOTE: The POP3 server reports the message sizes as it sees them, which usually means that it is reporting the number of bytes that must be transferred from the server in order to retreive the entire message. In reality, these numbers can be inaccurate after line terminator translations have been made and attachments have been decoded. Sizes should really only be used as a guideline to judge if the message should be retreived from the server. The sizes should not be reported to the user -- they will be misleading at best.

Status As EnumPOP3StatusTypes (read-only)
The status of the object, as detailed above.

In addition to the function listed above, LAFmessage.POP3 has the following functions:

Function Connect(username As String, password As String) As EnumReturnCodes
Establishes a connection to the POP3 server named by POP3Server on the port given by POP3Port. The object attempts to authenticate with the username and password given and retrieve the number of messages and their sizes.

Function Delete(message_number As Long) As EnumReturnCodes
Deletes the message number message_number from the server, even if the message has not been retrieved. Delete() may only be called while the object's status is LAFMESSAGE_POP3_connected.

NOTE: Deleting a message does not renumber the remaining messages. The message is not physically removed from the server until the connection is broken.

Function DeleteAll() As EnumReturnCodes
Deletes all messages from the server, even if the messages have not been retrieved. DeleteAll() may only be called while the object's status is LAFMESSAGE_POP3_connected.

NOTE: Deleting the messages does not renumber or change any of the retrieved messages. The messages are not physically removed from the server until the connection is broken.

Function Disconnect() As EnumReturnCodes
Closes the connection to the POP3 server. Any deleted messages are physically removed from the server and the mailbox is unlocked.

Function Retrieve(message_number As Long) As EnumReturnCodes
Downloads the message number message_number from the POP3 server and parses it into an LAFmessage.Message object.

NOTE: Downloading the message does not remove it from the server. That action must be done through a separate call to Delete().

Function RetrieveAll() As EnumReturnCodes
Downloads all the messages from the POP3 server and parses them into LAFmessage.Message objects.

NOTE: Downloading messages does not remove them from the server. That action must be done through a separate call to DeleteAll().


COM interface: LAFmessage.SMTP

SMTP stands for Simple Message Transport Protocol, defined by RFC 821. SMTP the protocol used by internet mail servers to transfer email that an end user might download using POP3. In its original specification SMTP does not support encryption or authentication, though those features have been added through later revisions. LAFmessage.SMTP supports only the SMTP protocol as defined in RFC 821; some of the extensions may be added at a later date if they are needed.

LAFmessage.SMTP provides all the functionality required to send email via SMTP to an SMTP server. NOTE: this does NOT mean that using LAFmessage.SMTP will allow other computers on the internet to deliver email to your server. LAFmessage.SMTP is an SMTP client, not a server.

LAFmessage.SMTP has been built with a number of features to facilitate sending large volumes of mail and track the progress and success/failure of each message/recipient. The code was written with the single-use, one-at-a-time message generator in mind as well. Sending a single message is just as easy as sending thousands.

In addition to the properties listed above, LAFmessage.SMTP has the following properties:

DeliveryMode As EnumSMTPDeliveryModes
Directs LAFmessage.SMTP to deliver messages when they are added to the queue or to save them and deliver them in the background. See LAFmessage.Add() and LAFmessage.MaxWorkerThreads for details of other behavioral changes caused by this property. The possible values are as follows:
Value Meaning
LAFMESSAGE_SMTP_DELIVERY_asynchronous Messages added to the queue are delivered in the background by worker threads.
LAFMESSAGE_SMTP_DELIVERY_synchronous Messages added to the queue are delivered immediately by the thread that added them.

This option is the default.

FailureRetries As Long
The number of times the component will attempt to deliver the message to a recipient before marking that recipient "rejected".

FailureRetries defaults to 0.

FailureRetryDelaySecs As Long
The minimum number of seconds between retrying a failed delivery. NOTE: This is the minimum number of seconds, not a precise measurement. If the queue is very full and/or there are very few threads working on delivering messages, a retry attempt may take much long to begin than is set in FailureRetryDelaySecs.

FailureRetryDelaySecs defaults to 60 (1 minute).

ID(Optional display As Variant) As String
The unique identifier for this SMTP queue. Every LAFmessage.SMTP object has a unique ID (as does every other LAFmessage object), but LAFmessage.SMTP's use of the concept allows for some interesting possibilities.

Here's how it works internally: When a LAFmessage_SMTP (C++) object is created by a caller who did not specify an ID, it is assigned an ID automatically -- a short GUID. If the caller specifies an ID, the object cache is searched for an object with a matching ID. If a match is found, the object is returned. If not, an object is created with the requested ID and is returned.

When a LAFmessage.SMTP (COM) object is created, its ID field is a short GUID because the caller did not specify a desired ID (not possible in COM). When its ID field is set, the original internal (C++) object is discarded and another is fetched with the desired ID.

The practical application is this: Suppose two ASP pages are created. The first page creates an SMTP queue, sets its ID to a known value and loads it up with some messages to be delivered. The second page creates an SMTP object and sets its ID to the same known value used on the first page. When it does that, the second page has access to the same queue the first page used. It can then perform some additional actions -- it can pause the queue, check its delivery status, look for rejected addresses, etc.

NOTE: LAFmessage.SMTP objects will be removed from the object cache when they both (a) have no more messages to be delivered AND (b) have not been referenced for a long period of time (see MaxIdleMins). If the queue with the known ID has been removed from the cache, an attempt to retrieve it will return a new queue with the known ID. If this could be a problem, be sure to set the MaxIdleMins property to a suitably high value.

ID defaults to a short GUID.

MaxIdleMins As Long
The number of minutes a queue must remain idle before it is removed from the object cache. NOTE: In this context, "idle" means "unreferenced". If a caller loads the queue from the object cache the queue is not considered "idle", even if the caller did not call any functions on it.

MaxIdleMins defaults to 20.

MaxRecipientsPerMessage As Long
Controls how many recipients can be specified per message transaction. Increasing this value can give a significant reduction in the delivery times of messages with many recipients, so long as it does not exceed what the SMTP server is capable of.

NOTE: This property works by taking advantage of a feature in the specification used to describe the recipients of a message to the SMTP server. This feature has absolutely no impact and is not impacted by the contents of the "To", "Cc" or "Bcc" lines in the message's headers. The headers do not affect message delivery and need not contain any information about the true recipients of a message.

MaxRecipientsPerMessage defaults to 1.

MaxWorkerThreads As Long
Sets the maximum number of worker threads created to deliver mail from the queue. This property is only used when DeliveryMode is set to LAFMESSAGE_SMTP_DELIVERY_asynchronous.

NOTE: At first glance, MaxWorkerThreads may seem like a property where "more is better". That is not necessarily (and most often is not) the case. The only time it is appropriate to increase the value of MaxWorkerThreads is when delivering a single message to the SMTP server takes more than approximately 0.5 seconds. The most frequent cause of slow SMTP server response times are DNS queries. If the server is extremely paranoid about DNS resolution, delivery times can go up dramatically. To test this, telnet to your SMTP server's SMTP port (usually port 25) from the console of the server running LAFmessage. If you get a greeting banner immediately, do not increase the value of MaxWorkerThreads. If it takes several seconds for a greeting banner to appear, increasing this value may have a positive effect.

The reason for this admonition is simple: Mail delivery can proceed only as fast as the slowest step in the process. When you increase the value of MaxWorkerThreads, you are assuming that the slowest step is the transfer of data from LAFmessage to the SMTP server. If you are incorrect, you will be creating a bottleneck inside LAFmessage as multiple worker threads fight for control of the queue in order to remove messages from it and record their status. This may actually slow down mail delivery significantly. If the component becomes so busy that individual threads' attempts to lock the queue return timeout errors, your mail may get thrown on the floor.

ALSO NOTE: A high number of worker threads delivering mail to the SMTP server may put undue stress on the server, potentially keeping other users from using the server and/or upsetting the sysmadman.

MaxWorkerThreads defaults to 5.

MinDeliverySecs As Long
The minimum number of seconds between message deliveries from this queue. This is useful for slowing down message processing, especially during large message batches, so the SMTP server is not overloaded. Use of this feature is considered polite if you are delivering more than 100 messages.

MinDeliverySecs defaults to 0.

ProcessedMessages As Message() (read-only)
ProcessedMessages returns an array of all the LAFmessage.Message objects that were added to the queue and have been completely processed. This means that all of the intended recipients were either successfully delivered to or were rejected a number of times equal to the value of MaxFailureRetries.

NOTE: Checking ProcessedMessages repeatedly and comparing the number of messages to your initial number of messages is not a good way to see if the queue is empty. Use WaitUntilEmpty() instead.

ProcessedMessages defaults to an array with an upper bound of -1.

QueuedMessages(Optional parameter As Variant) As Message()
When read from, QueuedMessages provides an array of all the LAFmessage.Message objects that are still waiting in the queue for additional processing. Some of them may be partially processed, especially if MaxFailureRetries is greater than 0. If the parameter parameter is supplied, it must be a number and only those messages whose number of delivery attempts equal the value given will be returned.

NOTE: When reading from QueuedMessages, the returned array may be incomplete. Messages being processed when QueuedMessages is accessed will not appear in the returned array.

When set, QueuedMessages will remove all unprocessed messages from the queue and fill the queue with the value given. Setting QueuedMessages to Empty will empty the queue. When an array of LAFmessage.Message objects is given, a single-dimensional array must be given for the parameter parameter. Each element of that array must be a single-dimensional array of Strings that is the list of recipients for the message in the corresponding element of the message object array. If this sounds confusing, it's only because this is hard to explain in English. See the examples.

If QueuedMessages is set to Nothing and a single-dimensional array within a single-dimensional array is given (as above), the recipient addresses will be checked by the SMTP server for validity. See Add() for more details on this feature.

QueuedMessages defaults to an array with an upper bound of -1.

QueuedRecipientAddresses(processed_message As Message, Optional display As Variant) As String() (read-only)
Returns a single-dimensional array of Strings containing the addresses of the recipients still queued for the given message. If the queue is not paused, this array will very quickly fall out of date as processing continues.

RecipientAddresses(process_message As Message, Optional display As Variant) As String() (read-only)
Returns a single-dimensional array of Strings containing the addresses of the recipients for the given message, including all recipients that are still queued, have been rejected or have been accepted. This array's value will remain the same regardless of the message's status in the queue.

RejectedRecipientAddresses(processed_message As Message, Optional display As Variant) As String() (read-only)
Returns a single-dimensional array of Strings containing the addresses of the recipients that have been rejected for the given message. If the message is still being delivered, this array may fall out of date as more addresses are added to it.

NOTE: In this case, "rejected" means that the SMTP server refused to accept the recipient address for delivery. Typically, this indicates that the format of the address was bad. Even if an address was not rejected by the SMTP server, there is still a chance that the message could bounce because the destination mailbox does not exist, is full, etc.

SenderAddress(Optional display As String) As String
The address the SMTP queue uses to authenticate to the SMTP server. This must be set to a value before messages can be added to the queue.

In order to understand the significance of this property, a little explanation of SMTP is necessary. When an SMTP client (in this case, LAFmessage) begins delivering a message to an SMTP server, it must identify the sender of the message. It is this sender address that can receive some of the return messages if the message bounces. However, nothing in the SMTP specification requires that the sender address appear in the message headers. For this reason, LAFmessage allows the sender address to be set using this property while the message headers can be set independently.

Because most SMTP servers filter mail based on the sender address and not the "From:" line of the message headers, choosing a sender address that will not be rejected by the SMTP server is important. The sender address should be a valid address that can receive bounced messages.

SenderAddress defaults to an empty string.

SentRecipientAddresses(processed_message As Message, Optional display As Variant) As String() (read-only)
Returns a single-dimensional array of Strings containing the addresses of the recipients that have been accepted by the SMTP server for delivery. If the message is still being delivered, this array may fall out of date as more addresses are added to it.

NOTE: In this case, "accepted" means that the SMTP server accepted the recipient address for delivery. Typically, this indicates that the format of the address was good. Even if an address was not rejected by the SMTP server, there is still a chance that the message could bounce because the destination mailbox does not exist, is full, etc.

SMTPPort As Long
The port on the SMTP server to connect to. This value must be a positive integer between 1 and 65535, inclusive.

SMTPPort defaults to 25.

SMTPServer(Optional type = LAFMESSAGE_DEFAULT_SMTP_SERVER_ADDRESS_TYPE_IDL As EnumInternetAddressTypes, Optional display As Variant) As String
The SMTP server to connect to. The type parameter indicates what type of address is being provided. The possible values are as follows:
Value Meaning
LAFMESSAGE_INTERNET_ADDRESS_fqdn The address is a fully qualified domain name (e.g. smtp.foocorp.com).
LAFMESSAGE_INTERNET_ADDRESS_ipv4_dotted_quad The address is an IP address in the form WWW.XXX.YYY.ZZZ.
LAFMESSAGE_INTERNET_ADDRESS_autodetect The address' type should be automatically determined from its format. If the address is in the format WWW.XXX.YYY.ZZZ Where the four letter groups are each an integer between 0 and 255, the address is assumed to be an IP address. Otherwise, the address is assumed to be a fully qualified domain name.

This option is the default.

In addition to the function listed above, LAFmessage.SMTP has the following functions:

Function Add(new_message As Message, recipient_addresses As String()) As EnumReturnCodes
Adds the message new_message to the queue for delivery to the addresses in the single-dimensional array of strings recipient_addresses. If DeliveryMode is set to LAFMESSAGE_SMTP_DELIVERY_synchronous, the call blocks while the message delivery proceeds and Add() returns the result of the delivery attempt. If DeliveryMode is set to LAFMESSAGE_SMTP_DELIVERY_asynchronous, Add() returns the result of adding the message to the queue and delivery takes place in the background. Calls to Add() will fail if the queue is paused.

Function Pause(Optional pause_secs = LAFMESSAGE_DEFAULT_SMTP_PAUSE_SECS As Long, Optional pause_delay_secs = LAFMESSAGE_DEFAULT_SMTP_PAUSE_DELAY_SECS As Long) As EnumReturnCodes
Pauses all message delivery for the number of seconds given in the pause_secs parameter or pauses it indefinitely if pause_secs is equal to -1. If the pause_delay_secs parameter is given, the pause will not begin for the number of seconds given in that parameter. Pause() does not block the caller in any case.

For example, if the call Pause(120, 300) were made at exactly 2:00:00 PM, the queue would run normally until 2:05:00, when it would pause. The queue would then resume at 2:07:00.

NOTE: The process of pausing the queue is an inexact science. Internally, a paused queue has a flag set that prohibits any worker thread from removing messages from the queue for processing. However, if a worker thread is in the middle of processing a message when the queue becomes paused, that processing is completed. The worker thread is guaranteed to not begin processing any other messages from the queue while it is paused. If preventing immediate delivery is a critical issue, pause the queue before adding messages to it.

Function Remove(target_message As Message) As EnumReturnCodes
Removes the message target_message from the queue, if it is found there. Removing a message also removes its sent and rejected recipient lists.

NOTE: If a worker thread is processing the message, Remove() will not find the message and, thus, will not remove it. If message removal is a critical issue, pause the queue or wait until the number of queued recipients drops to zero before removing a message.

Function RemoveAll() As EnumReturnCodes
Removes all messages from the queue, along with their associated sent and rejected recipient lists.

NOTE: If a worker thread is processing a message, RemoveAll() will not remove it from the queue. If mass message removal is a critical issue, pause the queue before removing all the messages from it.

Function Unpause(Optional unpause_delay_secs = LAFMESSAGE_DEFAULT_SMTP_UNPAUSE_DELAY_SECS As Long) As EnumReturnCodes
Resumes message processing on a paused queue. If the parameter unpause_delay_secs is given, the queue will not be unpaused for the number of seconds given in the parameter. Unpause() does not block the caller in any case.

Function WaitUntilEmpty(Optional max_wait_secs = LAFMESSAGE_DEFAULT_SMTP_WAIT_SECS As Long) As EnumReturnCodes
Blocks the caller until the queue is empty or until max_wait_secs (if given) has expired, which ever comes first. If the timeout expires, WaitUntilEmpty() returns a non-success value. WaitUntilEmpty() waits until all message processing has stopped and is not affected by worker threads processing messages as Remove() and RemoveAll() are.

WaitUntilEmpty() is most useful in situations where the program is going to exit and needs to ensure that all messages have been delivered before doing so. Otherwise, messages that have not been processed will be lost.


COM interface: LAFmessage.Text

LAFmessage.Text encapsulates the functionality required for a MIME message body. Text sections are used for the message content, as opposed to attachments, which are used to transmit extra data. LAFmessage allows a single message to carry multiple text sections. In order to understand why this is possible, it is necessary to understand how MIME-compliant mail readers display messages.

MIME-compliant readers rely on the content type of each message section to determine the semantic meaning of that section's content. When multiple text sections are found within a multipart/alternative block, the reader knows that the sections within the block are all different versions of the same content, provided in different formats in case the reader cannot display the preferred format.

The reader starts with the last text section and displays it if it can. If not, it skips to the next-to-last section and tries that one. Eventually, the reader comes across a section it can display or it runs out of sections and displays an error message instead.

This means that if a rich media message is being sent to many users with different mail clients, a plain text section should be added first followed by an HTML section. If a recipient's mail reader can display HTML, it will choose that section (since it was added last). If not, it will display the plain text.

If AOL users are among the recipients and some of them use AOL 5 or earlier, the AOL content type can be used. In that case, sections should be added in the following order:

The AOL users will be unable to see the HTML content but they will see the AOL content. Users of HTML readers will see the HTML content. Text-based MIME-compliant readers will see the text content.


In addition to the properties listed above, LAFmessage.Text has the following properties (using VB terminology):

Content(Optional display As Variant) As String
The text section's content.

NOTE: LAFmessage.Text will not perform any content type translations. For example, if plain text content is assigned to Content and Type is later set to LAFMESSAGE_MIME_text_html, LAFmessage.Text will not magically transform the plain text into HTML text by inserting line break tags and so forth.

Encoding As EnumMIMEEncodingTypes
The text's MIME encoding algorithm. Each MIME type has "minimum" and "maximum" encoding levels that cannot be overridden by setting Encoding. When an attachment's Type is changed, its Encoding is automatically updated to the minimum level (if the current level is below the minimum) or the maximum level (if the current level is above the maximum). Because of this, explicitly setting the encoding level is usually not necessary. The minimum and maximum levels are listed below:
LAFmessage_enum::mime_types value Minimum encoding level Maximum encoding level
LAFMESSAGE_MIME_text_plain 7bit base64
LAFMESSAGE_MIME_text_xaol quoted-printable base64
LAFMESSAGE_MIME_text_html quoted-printable base64
LAFMESSAGE_MIME_image_gif base64 base64
LAFMESSAGE_MIME_image_jpeg base64 base64
LAFMESSAGE_MIME_application_octetstream base64 base64

HeaderContents(target_header_type As EnumMIMETypes, Optional display As Variant) As Variant (read-only)
HeaderContents is provided for the convenience of callers who need the content of a specific header field without needing to manipulate a LAFmessage.Header object. Since any header may appear more than once in a section, HeaderContents always returns a single-dimensional array, even if no headers of the requested type exist in the section. Each content string in the returned array is canonicalized according to display.

Headers(Optional type As EnumMIMETypes = LAFMESSAGE_DEFAULT_MIME_TYPE_IDL) As Variant
The LAFmessage.Header objects associated with the section, in a single-dimensional array. If type is given, only header objects whose Type property matches the given type will be returned. When successful, Headers always returns an array, even when no header objects are associated with the section.

When assigning a value to Headers, a single LAFmessage.Header object may be given or a single-dimensional array of LAFmessage.Header objects may be given. If the assignment is successful, the previously associated header objects are discarded and will be lost unless they are stored elsewhere (saving their IDs is not sufficient).

Note: A single header object may be associated with a section multiple times. In that case, it exists in the array as many times as it is associated, in the order it was added.

ID(Optional display As Variant) As String
The object's unique identifier, canonicalized according to the value of display when retrieved. A new object's ID is automatically generated by LAFmessage when the object is created. The identifier can only be changed to the ID of another valid message object, which will reassociate the COM object with the new text section. The old text section will be lost unless it is stored somewhere else -- saving its ID is not enough (for more details, see the discussion of reference counting in the C++ Overview (above)). New ID values may not be assigned arbitrarily.

MIMESection(Optional display As Variant) As String
The complete MIME message block that the text section will create when attached to a message, canonicalized according to display when retrieved. When set, MIMESection will parse the given MIME message block and retrieve the values of Name, Type, Encoding from the block. If successful, the old values of Name, Type, Encoding and Content are lost.

Type As EnumMIMETypes
The text section's MIME type. The following members of EnumMIMETypes are valid text types:
Value Meaning
LAFMESSAGE_MIME_text_plain Plain text section.
LAFMESSAGE_MIME_text_xaol AOL rich media section (only required for AOL 5.0 or earlier).
LAFMESSAGE_MIME_text_html HTML text seciton.

In addition to the function listed above, LAFmessage.Text has the following functions:

Function AddHeader(type As EnumMIMETypes, content As String, Optional encoding As EnumMIMEEncodingTypes = LAFMESSAGE_DEFAULT_MIME_ENCODING_TYPE_IDL, Optional name As Variant) As EnumReturnCodes
AddHeader() is a convenience function for callers who wish to associate a new header object with a section but do not need to manipulate a LAFmessage.Header object. Internally, AddHeader() constructs a LAFmessage.Header object and sets its Type property to type, its Content property to content, its Encoding property to encoding (if given) and its Name property to name (if given). A failure of any property assignment will cause the resulting error to be returned by AddHeader() and the new header object to be discarded.

Function Clone(ByRef clone_object As Variant) As EnumReturnCodes
Creates a new LAFmessage.Text COM object that is a clone of this COM object -- all of the values are copied but the ID is different and the object structures are discrete.

Function LoadContent(file_path As String) As EnumReturnCodes
Opens the file named file_path and loads the entire file into the text section's MIMESection property. It is the caller's responsibility to ensure the process owner has sufficient permission to read the file and that the path is correct. Windows UNC paths are not supported.

Function SaveContent(file_path As String, Optional save_option As EnumSaveOption = LAFMESSAGE_DEFAULT_TEXT_SAVE_OPTION_IDL) As EnumReturnCodes
Opens the file named file_path and saves the text section's MIMESection property to it. It is the caller's responsibility to ensure the process owner has sufficient permission to create or save the file and that the path is correct. Windows UNC paths are not supported.

If save_option is given, it must be one of the following two values:

  • LAFMESSAGE_SAVE_create_or_truncate -- the file will be created if it does not exist or will be truncated if it does exist.
  • LAFMESSAGE_SAVE_create_only -- the file will be created if it does not exist. If the file already exists, no action will be taken and an error will be returned.


COM Examples

These examples are provided in the context of ASP scripts written in VBScript. They all rely on an external file, lafmessage_vbs.txt that is expected to be mapped into the website's namespace through a top-level virtual directory named LAFmessage.

This is a simple example to send a plain text message to one address based on a submitted HTML form's content.

<%@ LANGUAGE="VBScript" %>
<!-- #INCLUDE VIRTUAL="/LAFmessage/lafmessage_vbs.txt" -->
<%
  Response.Expires = 0

  ' Presumably this script is running because a form was submitted and an email
  ' should be sent.  The following variables are usually submitted in these
  ' situations.
  sender_email = Request("sender_email")
  sender_name = Request("sender_name")
  recipient_email = Request("recipient_email")
  recipient_name = Request("recipient_name")
  subject = Request("subject")
  message_text = Request("message")

  ' Create the message object.
  Set message_obj = Server.CreateObject("LAFmessage.Message")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  message_obj.ThrowErrors = True

  ' Add the message's "From:" line.  Because ThrowErrors is True, we don't have
  ' to check this function's return value -- processing will stop if the
  ' function doesn't return LAFMESSAGE_success.  In production this should be
  ' wrapped in an if statement to gracefully handle errors.
  message_obj.AddHeader LAFMESSAGE_MIME_HEADER_from, sender_name & " <" & sender_email & ">"
  ' Add the message's "To:" line.  See above about ThrowErrors.
  message_obj.AddHeader LAFMESSAGE_MIME_HEADER_to, recipient_name & " <" & recipient_email & ">"
  ' Add the message's "Subject:" line.  See above about ThrowErrors.
  message_obj.AddHeader LAFMESSAGE_MIME_HEADER_subject, subject
  ' Add the message's text content as a plain text block.  See above about
  ' ThrowErrors.
  message_obj.AddText LAFMESSAGE_MIME_text_plain, message_text

  ' Create the SMTP object.
  Set smtp_obj = Server.CreateObject("LAFmessage.SMTP")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  smtp_obj.ThrowErrors = True
  ' Set the SMTP object's ID before we begin.  This is always a good idea,
  ' especially when we're sending email from multiple pages on the site and/or
  ' sending email in the background.  In any case it doesn't hurt anything and
  ' allows us to come back and check delivery status later.  See above about
  ' ThrowErrors.
  smtp_obj.ID = "my.websites.own SMTP queue"
  ' Set the SMTP server.  See above about ThrowErrors.
  smtp_obj.SMTPServer = "my.own.email.server"
  ' Set the sender address -- this must be an address that won't get rejected by
  ' the mail server's relay filter.  See above about ThrowErrors.
  smtp_obj.SenderAddress = "lafmessage_programmer@my.own.mail.server"

  ' Add the email message to the SMTP queue for delivery.  See above about
  ' ThrowErrors.
  smtp_obj.Add message_obj, recipient_email
%>
Done!

This is a more complex example to send an HTML/plain text email to several addresses and include two attachments, one dynamically generated, the other loaded from a file on disk.

<%@ LANGUAGE="VBScript" %>
<!-- #INCLUDE VIRTUAL="/LAFmessage/lafmessage_vbs.txt" -->
<%
  Response.Expires = 0

  ' Presumably this script is running because a form was submitted and an email
  ' should be sent.  The following variables are usually submitted in these
  ' situations.
  sender_email = Request("sender_email")
  sender_name = Request("sender_name")
  apparent_recipient_email = Request("apparent_recipient_email")
  apparent_recipient_name = Request("apparent_recipient_name")
  recipient_emails = Request("recipient_emails")
  subject = Request("subject")
  plain_message = Request("plain_message")
  html_message = Request("html_message")
  text_attachment_content = Request("text_attachment_content")
  binary_attachment_filename = Request("binary_attachment_filename")
  binary_attachment_name = Request("binary_attachment_name")

  ' Create the message object.
  Set message_obj = Server.CreateObject("LAFmessage.Message")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  message_obj.ThrowErrors = True

  ' Add the message's "From:" line.  Because ThrowErrors is True, we don't have
  ' to check this function's return value -- processing will stop if the
  ' function doesn't return LAFMESSAGE_success.  In production this should be
  ' wrapped in an if statement to gracefully handle errors.
  message_obj.AddHeader LAFMESSAGE_MIME_HEADER_from, sender_name & " <" & sender_email & ">"
  ' Add the message's "To:" line.  Note that in this example, the "apparent
  ' recipient" may be different from the real recipient.  This is perfectly
  ' legal -- mailing list software does it every day.  See above about
  ' ThrowErrors.
  message_obj.AddHeader LAFMESSAGE_MIME_HEADER_to, apparent_recipient_name & " <" & apparent_recipient_email & ">"
  ' Add the message's "Subject:" line.  See above about ThrowErrors.
  message_obj.AddHeader LAFMESSAGE_MIME_HEADER_subject, subject
  ' Add the message's text content as a plain text block.  See above about
  ' ThrowErrors.
  message_obj.AddText LAFMESSAGE_MIME_text_plain, plain_message
  ' Add the message's HTML content as an HTML text block.  Because of the way
  ' MIME messages are encoded, recipients capable of reading HTML email will see
  ' the HTML version (because it was added last, it is the preferred displayable
  ' content).  See above about ThrowErrors.
  message_obj.AddText LAFMESSAGE_MIME_text_html, html_message

  ' Create an attachment object.
  Set attachment_obj = Server.CreateObject("LAFmessage.Attachment")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  attachment_obj.ThrowErrors = True

  ' This first attachment is a plain text file whose content was passed in as a
  ' parameter.  See above about ThrowErrors.
  attachment_obj.Type = LAFMESSAGE_MIME_text_plain
  attachment_obj.Content = text_attachment_content
  ' The attachment is associated with the message.  See above about ThrowErrors.
  message_obj.Add attachment_obj

  ' Create another attachment object.  We're done with the first one, so we'll
  ' reuse the variable name.  That's OK because it's already been added to the
  ' message object.
  Set attachment_obj = Server.CreateObject("LAFmessage.Attachment")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  attachment_obj.ThrowErrors = True

  ' This second attachment should be loaded from disk, using the filename passed
  ' as a parameter.  See above about ThrowErrors.
  attachment_obj.LoadContent binary_attachment_filename
  ' We have no idea what type of file this is, so we'll mark it application/
  ' octet-stream, which is MIME's way of saying "I have no idea".  See above
  ' about ThrowErrors.
  attachment_obj.Type = LAFMESSAGE_MIME_application_octetstream
  ' A name for this attachment was passed as a parameter.  By setting it here,
  ' the recipient will have a "suggested" filename if they attempt to save the
  ' file.  Note that Outlook tends to trust this name more than it trusts the
  ' content type.  This means that even "unknown" types will still be handled
  ' if their filenames indicate a usable file type.  Go figure.  See above
  ' about ThrowErrors.
  attachment_obj.Name = binary_attachment_name
  ' The attachment is associated with the message.  See above about ThrowErrors.
  message_obj.Add attachment_obj

  ' Create the SMTP object.
  Set smtp_obj = Server.CreateObject("LAFmessage.SMTP")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  smtp_obj.ThrowErrors = True
  ' Set the SMTP object's ID before we begin.  This is always a good idea,
  ' especially when we're sending email from multiple pages on the site and/or
  ' sending email in the background.  In any case it doesn't hurt anything and
  ' allows us to come back and check delivery status later.  See above about
  ' ThrowErrors.
  smtp_obj.ID = "my.websites.own SMTP queue"
  ' Set the SMTP server.  See above about ThrowErrors.
  smtp_obj.SMTPServer = "my.own.email.server"
  ' Set the sender address -- this must be an address that won't get rejected by
  ' the mail server's relay filter.  See above about ThrowErrors.
  smtp_obj.SenderAddress = "lafmessage_programmer@my.own.mail.server"
  ' Because there are multiple messages to be delivered, LAFmessage make take a
  ' while to process them all.  Because of that, this script may take longer to
  ' execute than the user is willing to wait.  To counteract that, we'll set the
  ' SMTP object to deliver the messages in the background so the user can go
  ' about their business.  If we're interested, we can check back with the SMTP
  ' object later to see what happened.  See above about ThrowErrors.
  smtp_obj.DeliveryMode = LAFMESSAGE_SMTP_DELIVERY_asynchronous

  ' In this example, we expect to have multiple recipients passed in through the
  ' recipient_emails parameter.  We expect them to be separated by newlines, as
  ' though they were typed into an HTML TEXTAREA, one address per line.  We'll
  ' create a single-dimensional array of addresses, one address per entry.
  recipient_email_array = Split(recipient_emails, vbNewLine)
  For i = 0 to UBound(recipient_email_array) Step 1
    recipient_email_array(i) = Trim(recipient_email_array(i))
  Next

  ' Add the email message to the SMTP queue for delivery.  Because these
  ' messages will be delivered in the background, Add() will only return an
  ' error if there is a problem adding the message to the queue.  Delivery
  ' errors will be retained but not reported unless we check back later to see
  ' what happened.  See above about ThrowErrors.
  smtp_obj.Add message_obj, recipient_email_array
%>

This is an example of checking delivery status for an existing queue. This can be very useful when using LAFmessage to send a very large number of email messages where delivery may take many minutes or hours.

<%@ LANGUAGE="VBScript" %>
<!-- #INCLUDE VIRTUAL="/LAFmessage/lafmessage_vbs.txt" -->
<%
  Response.Expires = 0

  ' Create an SMTP object.
  Set smtp_obj = Server.CreateObject("LAFmessage.SMTP")
  ' In this example, we want to see error messages while we debug.  This line
  ' should be commented out in production.
  smtp_obj.ThrowErrors = True
  ' Presumably, the scripts that have been sending email have all set their
  ' SMTP objects' ID properties so we know what queue we should be checking
  ' here.  If we were sufficiently clever, we could have allowed LAFmessage to
  ' generate the ID fields in those other scripts and saved them somehow.  That
  ' would allow us to reattach to them here.  In any case, without a known ID,
  ' there's no way to check queue status.  Because ThrowErrors is True, we don't
  ' have to check this function's return value -- processing will stop if the
  ' function doesn't return LAFMESSAGE_success.
  smtp_obj.ID = "my.websites.own SMTP queue"

  ' Queued and processed messages are returned as single-dimensional arrays of
  ' LAFmessage.Message objects.  Note that each returned object has its
  ' ThrowErrors property set to match the object that returned it (in this case,
  ' smtp_obj).  Since we set ThrowErrors to True above, each Message object in
  ' these arrays also has its ThrowErrors property set to True.  See above about
  ' ThrowErrors if these calls fail.
  queued_messages = smtp_obj.QueuedMessages
  processed_messages = smtp_obj.ProcessedMessages

  ' First we print out some summary information.
%>
<% =(UBound(queued_messages) + 1) %> queued messages.
<BR><% =(UBound(processed_messages) + 1) %> processed messages.
<P>
Queued messages:
<DL>
<%
  ' In this loop, we're going to display some information about each queued
  ' message.  Some of these messages may be partially delivered -- they may
  ' have addresses that have already been delivered to and addresses for
  ' which delivery was refused by the SMTP server.
  For i = 0 to UBound(queued_messages) Step 1
    ' The SMTP object maintains a four lists of addresses -- queued for
    ' delivery, successful delivery, rejected and in progress.  The "in
    ' progress" list is not available because while the message is being
    ' delivered the addresses are detached from the SMTP queue.  Regardless,
    ' any "in progress" addresses on it are likely to be moved to the sent or
    ' rejected lists within some milliseconds.
    sent_addresses = smtp_obj.SentRecipientAddresses(queued_messages(i))
    queued_addresses = smtp_obj.QueuedRecipientAddresses(queued_messages(i))
    rejected_addresses = smtp_obj.RejectedRecipientAddresses(queued_messages(i))

    ' Every LAFmessage.Message object has a unique ID.  If 1000 nearly identical
    ' messages are in the queue, the ID may be the only realistic way to tell
    ' them apart.
%>
<DT><% =queued_messages(i).ID %>
<DD><B><% =(UBound(sent_addresses) + 1) %> sent:</B><BR>
<%
    ' If any addresses were on the "sent" list, they are listed here.
    For j = 0 to UBound(sent_addresses) Step 1
%>
<% =sent_addresses(j) %><BR>
<%
    Next
%>
<DD><B><% =(UBound(queued_addresses) + 1) %> queued:</B><BR>
<%
    ' If any addresses were on the "queued for delivery" list, they are listed
    ' here.
    For j = 0 to UBound(queued_addresses) Step 1
%>
<% =queued_addresses(j) %><BR>
<%
    Next
%>
<DD><B><% =(UBound(rejected_addresses) + 1) %> rejected:</B><BR>
<%
    ' If any addresses were on the "rejected" list, they are listed here.
    For j = 0 to UBound(rejected_addresses) Step 1
%>
<% =rejected_addresses(j) %><BR>
<%
    Next
%>
<DD><PRE><% =queued_messages(i).MIMESection(LAFMESSAGE_DISPLAY_HTML) %></PRE>
<%
  Next
%>
</DL>
<P>
Processed messages:
<DL>
<%
  ' In this loop, we're going to display some information about each processed
  ' message.  All of these messages are completely delivered -- every one of
  ' their recipient addresses was either accepted or rejected by the SMTP
  ' server.
  For i = 0 to UBound(processed_messages) Step 1
    ' Again, we pull the available lists of addresses for the message.  Note
    ' that the queued addresses are still being retrieved, even though that
    ' list will be empty.  It never hurts to check.
    sent_addresses = smtp_obj.SentRecipientAddresses(processed_messages(i))
    queued_addresses = smtp_obj.QueuedRecipientAddresses(processed_messages(i))
    rejected_addresses = smtp_obj.RejectedRecipientAddresses(processed_messages(i))
%>
<DT><% =processed_messages(i).ID %>
<DD><B><% =(UBound(sent_addresses) + 1) %> sent:</B><BR>
<%
    ' If any addresses were on the "sent" list, they are listed here.
    For j = 0 to UBound(sent_addresses) Step 1
%>
<% =sent_addresses(j) %><BR>
<%
    Next
%>
<DD><B><% =(UBound(queued_addresses) + 1) %> queued:</B><BR>
<%
    ' If any addresses were on the "queued for delivery" list, they are listed
    ' here.  This list should be empty.
    For j = 0 to UBound(queued_addresses) Step 1
%>
<% =queued_addresses(j) %><BR>
<%
    Next
%>
<DD><B><% =(UBound(rejected_addresses) + 1) %> rejected:</B><BR>
<%
    ' If any addresses were on the "rejected" list, they are listed here.
    For j = 0 to UBound(rejected_addresses) Step 1
%>
<% =rejected_addresses(j) %><BR>
<%
    Next

  ' Each message's MIME rendering is displayed here, canonicalized for HTML
  ' display.
%>
<DD><PRE><% =processed_messages(i).MIMESection(LAFMESSAGE_DISPLAY_HTML) %></PRE>
<%
  Next
%>
</DL>



LAFmessage API documentation
©2001-2003 lookandfeel new media