Table of contents
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:
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.
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.
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.
LAFmessage_Message *foo = new LAFmessage_Message;).
Instead, use each class' get_object() function (i.e. LAFmessage_Message *foo; LAFmessage_Message::get_object(&foo);).
get_object() allows the caller to specify the ID of the target object so an existing object can be
returned. get_object() also gives a separate return code to indicate success or failure.
delete (LAFmessage_Message *)foo;) or allow
a pointer to an LAFmessage C++ object to be lost by falling out of scope. Instead, use each class'
put_object() function (i.e. LAFmessage_Message::put_object((LAFmessage_Message **)&foo)).
put_object() always sets the passed pointer equal to NULL and gives a separate return code to indicate
success or failure.
((LAFmessage_Message *)foo)->update_ref_count(1)). update_ref_count()
will increment the object's internal reference counter to guarantee the object will not be destroyed until
all of the pointers to it have been passed to put_object().
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:
|
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)
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)
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)
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:
|
virtual LAFmessage_enum::return_codes cmd_clone(LAFmessage_MIME **return_value) = 0
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);
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)
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)
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)
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():
|
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)
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)
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)
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)
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)
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)
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)
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)
set_inline() sets the attachment's inline flag equal to new_inline_flag.
LAFmessage_enum::return_codes set_mime_section(char *new_section)
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)
set_name() copies the text from new_name into the attachment's name field.
LAFmessage_enum::return_codes cmd_load_content(char *file_path)
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)
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::LAFMESSAGE_SAVE_create_or_truncate -- the file will be created if it does not
exist or will be truncated if it does exist.
LAFmessage_enum::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.
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)
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)
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)
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():
|
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():
|
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)
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.
LAFmessage_Header object has been added of type LAFmessage_enum::LAFMESSAGE_MIME_HEADER_date,
get_mime_message() adds a "Date" line to the message header using the current system date and time. A
LAFmessage_Header object is not added to the message.
get_mime_message() completely ignores the LAFmessage_Message object's type that the caller had
previously set. It constructs the message as follows:
get_mime_message() does not change the message's type field, nor does it add (a) LAFmessage_Header
object(s) to hold the values it discovers.
get_mime_message() always adds a header of type LAFmessage_enum::LAFMESSAGE_MIME_HEADER_x with
the name "Mailer" and the content copied from the preprocessor directive PROJECT_HELPSTRING defined in
version.h.
get_mime_message() always adds a header of type LAFmessage_enum::LAFMESSAGE_MIME_HEADER_mime_version
with the content "1.0".
get_mime_message() does not add to the message header any stored headers of types LAFmessage_enum::LAFMESSAGE_MIME_HEADER_content_disposition,
LAFmessage_enum::LAFMESSAGE_MIME_HEADER_content_type,
LAFmessage_enum::LAFMESSAGE_MIME_HEADER_content_transfer_encoding,
LAFmessage_enum::LAFMESSAGE_MIME_HEADER_content_id,
LAFmessage_enum::LAFMESSAGE_MIME_HEADER_mime_version or
LAFmessage_enum::LAFMESSAGE_MIME_HEADER_content_description. The unused header objects are not removed
from the message -- they are just ignored during message construction as their values are automatically generated by
examining the stored objects within the message.
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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::LAFMESSAGE_SAVE_create_or_truncate -- the file will be created if it does not
exist or will be truncated if it does exist.
LAFmessage_enum::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.
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)
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)
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)
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)
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)
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)
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:
|
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)
*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)
*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)
*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)
*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)
*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)
*return_value is set equal to the number of messages available on the POP3 server.
LAFmessage_enum::return_codes get_sizes(long **return_value)
*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)
*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)
*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)
*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)
*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)
LAFmessage_enum::return_codes set_id(char *new_id)
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)
LAFmessage_enum::return_codes set_pop3_server(char *new_pop3_server, LAFmessage_enum::internet_address_types type = LAFMESSAGE_DEFAULT_POP3_SERVER_ADDRESS_TYPE)
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)
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()
LAFmessage_enum::return_codes cmd_retrieve(long 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()
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)
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()
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)
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)
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)
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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
*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)
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)
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)
set_failure_retry_delay_secs().
LAFmessage_enum::return_codes set_max_idle_mins(long new_max_idle_mins)
LAFmessage_enum::return_codes set_max_recipients_per_message(long new_max_recipients_per_message)
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)
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)
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)
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
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)
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:
|
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
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
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:
|
ID(Optional display As Variant) As String
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
MIMESection(Optional display As Variant) As String
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)
display.
MIMESectionHeader(Optional display As Variant) As String (read-only)
display.
Name(Optional display As Variant) As String
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
EnumMIMETypes are valid attachment types:
|
In addition to the method listed above, LAFmessage.Attachment has the following methods (using VB
terminology):
Function LoadContent(file_path As String) As EnumReturnCodes
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
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
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:
|
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:
|
In addition to the properties listed above, LAFmessage.Header has the following properties (using VB terminology):
Content(Optional display As Variant) As String
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
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
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
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
LAFMESSAGE_MIME_HEADER_x (where it is
prepended with "X-" when the header is constructed) and LAFMESSAGE_MIME_HEADER_unknown.
Type As EnumMIMETypes
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
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.
LAFmessage.Header object has been added of type LAFMESSAGE_MIME_HEADER_date,
MIMEMessage adds a "Date" line to the message header using the current system date and time. A
LAFmessage.Header object is not added to the message.
MIMEMessage completely ignores the LAFmessage.Message object's type that the caller had
previously set. It constructs the message as follows:
MIMEMessage does not change the message's type field, nor does it add (a) LAFmessage.Header
object(s) to hold the values it discovers.
MIMEMessage always adds a header of type LAFMESSAGE_MIME_HEADER_x with the name and
version of the LAFmessage library. For example, a message generated by LAFmessage version 1.2.3.4 will contain a header
line that reads: X-Mailer: LAFmessage version 1.2.3.4.
MIMEMessage always adds a header of type LAFMESSAGE_MIME_HEADER_mime_version
with the content "1.0".
MIMEMessage does not add to the message header any stored headers of types LAFMESSAGE_MIME_HEADER_content_disposition,
LAFMESSAGE_MIME_HEADER_content_type,
LAFMESSAGE_MIME_HEADER_content_transfer_encoding,
LAFMESSAGE_MIME_HEADER_content_id,
LAFMESSAGE_MIME_HEADER_mime_version or
LAFMESSAGE_MIME_HEADER_content_description. The unused header objects are not removed
from the message -- they are just ignored during message construction as their values are automatically generated by
examining the stored objects within the message.
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
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
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
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
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
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
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
In addition to the method listed above, LAFmessage.Message has the following methods:
Function Add(new_object As Object) As EnumReturnCodes
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
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
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
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:
|
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)
Count
will have the value 10.
Count defaults to 0 until Connect() is called successfully.
ID(Optional display As Variant) As String
ID is system-assigned to a short GUID.
Item(message_number As Long, Optional display As Variant) As Variant (read-only)
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
MaxIdleMins defaults to 20.
POP3Port As Long
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
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)
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)
In addition to the function listed above, LAFmessage.POP3 has the following functions:
Function Connect(username As String, password As String) As EnumReturnCodes
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
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
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
Function Retrieve(message_number As Long) As EnumReturnCodes
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
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
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
FailureRetries defaults to 0.
FailureRetryDelaySecs As Long
FailureRetryDelaySecs.
FailureRetryDelaySecs defaults to 60 (1 minute).
ID(Optional display As Variant) As String
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
MaxIdleMins defaults to 20.
MaxRecipientsPerMessage As Long
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
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
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()
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)
RecipientAddresses(process_message As Message, Optional display As Variant) As String() (read-only)
RejectedRecipientAddresses(processed_message As Message, Optional display As Variant) As String() (read-only)
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
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)
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
SMTPPort defaults to 25.
SMTPServer(Optional type = LAFMESSAGE_DEFAULT_SMTP_SERVER_ADDRESS_TYPE_IDL As EnumInternetAddressTypes, Optional display As Variant) As String
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. |
LAFmessage.SMTP has the following functions:
Function Add(new_message As Message, recipient_addresses As String()) As EnumReturnCodes
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
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
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
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
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
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:
In addition to the properties listed above, LAFmessage.Text has the following properties (using VB terminology):
Content(Optional display As Variant) As String
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
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:
|
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
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
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
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
EnumMIMETypes are valid text types:
|
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
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
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
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.
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.
<%@ 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!
<%@ 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
%>
<%@ 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>