Message Sending
Message sending is accomplished by a series of calls from the Spooler to the transport provider. The calls are sequenced as follows.
1. BeginMessage() to start the ball rolling. The transport provider assigns the message a number.
2. SelectOrder() allows the transport provider to tell the Spooler the order it wants things passed.
3. Message properties, attachments, and recipients are passed in multiple calls in the order requested by the transport provider on the SelectOrder() call.
4. SendMessage() indicates to the transport that all the data has been passed and the message may be sent.
5. FailureRecips() allows the transport provider to indicate if it knows of any recipients that can't be delivered to. It is a convenience for transports that have determined already that some of the recipients can not be delivered to.
6. EndMessage() is the final responsibility handoff for the message from the Spooler to the transport provider.
BeginMessage()
This call indicates to the transport that the spooler has a message ready for sending. If the transport is ready to accept the message it returns a SUCCESS code and a reference value for the Spooler to use on future calls.
ulResult = BeginMessage([in] ulSession, [in] ulFlags, [out] lpulMsgRef, [out] lpulReturnParm)
ULONG ulSession 32-bit opaque handle obtained on the TransportLogin() call.
ULONG ulFlags Flags. For future use. May be ignored for now.
ULONG * lpulMsgRef Address of where the transport should return a reference value for this message. The spooler will pass this value in on subsequent calls dealing with this message.
ULONG * lpulReturnParm return parameter, variable usage, see below.
If the transport is not prepared to handle the transfer it should return an error code, and optionally an additional value in returnParm.
Some known error codes:
MAPI_E_NETWORK The transport can not currently handle the message. The returnParm value indicates how long (in seconds) the spooler should wait before retrying the call.
MAPI_E_WAIT A temporary problem prevents the transport from handling the message. The returnParm value indicates how long (in seconds) the spooler should wait before trying again.
MAPI_E_BUSY This transport can only handle one message at a time and is currently working on one. The current message should be completed or aborted.
After the BeginMessage() call the Spooler will issue a SelectOrder() call to find out in what order the transport would like to receive the data of the message.
SelectOrder()
This call allows the transport provider to specify the order in which it wants the properties of a message or an attachment passed in to it. The spooler will pass a ulRef obtained from either the BeginMessage() or the BeginAttachment() call.
ulResult = SelectOrder([in] ulRef, [in] ulSizeGuess, [in] lpPropertyIDs, [in] ulFlags,
[out] lpaOrdering, [out] lpulBuffSize)
ULONG ulRef 32-bit opaque value obtained on the BeginMessage() or the BeginAttachment() call.
ULONG ulSizeGuess Best guess the Spooler has on the total number of bytes of the message and all its attachments.
LPSPropIDArray lpPropertyIDs List of the properties (defined in ISysProp document)
ULONG ulFlags Flags. For future use. May be ignored for now.
LPULONG lpaOrdering Array in which the transport provider indicates the order it wants to receive the properties. This array is allocated by the Message Spooler and filled in by the transport provider. It is *pPropertyIDs->cPropIDs entries long.
ULONG * lpulBuffSize The size of data (in bytes) the transport would like transferred on each call when passing large properties.
The ulSizeGuess value is the best guess the Spooler can make about the total size of the message, all the recipient information, and the attachments. It may be used by transports to abort the whole process right now if they know they can't handle a message of this size.
The number of properties in the message is stored in the pPropertyIDs->cPropIDs value and the property names are in the rest of the structure. Property names are specified in UNICODE.
The transport provider should return, in the aOrdering array, values indicating the order in which the properties should be passed. For example, a 5 returned in the first element of the array indicates that the property (perhaps "Message-Class") listed in the SPropIDArray structure at offset 5 should be passed to the transport first. A value of 0xFFFFFFFF in an element of the returned array indicates that no more properties should passed. This allows the transport provider to ignore properties it doesn't need.
Two special property names will be included in the SPropIDArray structure. These are "MAPIRecipients" and "MAPIAttachments". These are marked to indicate the relative order that recipients and attachments should be passed to the transport provider.
Some known error codes:
MAPI_E_EXCEEDS_USER_LIMIT This message would exceed the user's limit on outbound message size.
MAPI_E_EXCEEDS_DISK_SPACE This message would exceed the available space on the disk.
MAPI_E_EXCEEDS_IMPL_LIMIT This message would exceed an implementation limit on the size of messages that may be sent.
In the above three cases, the message will be returned by the Spooler to the user as undeliverable.
WriteRecips()
ulResult = WriteRecips([in] ulMsgRef, [in] pRecipList)
ULONG ulMsgRef 32-bit opaque value obtained on the BeginMessage() call.
LPADRLIST lpRecipList a list of recipients (structure defined in MAPI document)
Each call transfers data about one or more recipients of the message. The ADRLIST is an array of structures. Each structure holds information about one recipient, including pointers to various properties of the recipient. Per-recipient features are indicated in the values of the properties, along with such things as Email-Address, EntryID, Display-Name, and Recipient-Type. The Spooler will perform this call multiple times till all the recipients of the message have are transferred.
The transport provider has two options on how to handle recipients that can not be delivered to. One or both can be used. For problem recipients that are learned of quickly, the transport provider can pass back information about them as part of the data exchange with the Spooler that occurs at the end of the message (see FailureRecips() below). For problem recipients that are learned of later (minutes, hours, or days later) the transport can send back an undeliverable message report. The advantage of those that are learned about quickly is that the Spooler will take care of generating the undeliverable message report.
Some recipients may be marked to indicate that this transport is not responsible for delivering to them. A different transport will handle those recipients. If possible, each transport should attempt to make all recipients visible on the To: / Cc: lists the final recipient of the message sees, and should attempt to handle such recipient addresses during transport that Reply-All functionality is available to recipients of the message. Often this requires that the transport provider talk to the underlying messaging system with a gateway-type or transport-type of API. Transports providers that do not have this luxury should ignore these extra recipients, or transfer information about them to a separate visible part of the message (perhaps an "Also-To:" field or similar mechanism).
When doing distribution list expansions, some transports may have the capability of discarding any recipients which have already been handled by other means. This is quite valuable in reducing duplicate deliveries. Transport providers that pass on even the "BCC" recipients that have been marked as already being handled can assist the underlying transports in this operation.
A "Responsibility" property (boolean) for each recipient indicates to the transport provider those recipients that provider is responsible for handling.
WriteProps()
This call transfers one or more of the properties from the Spooler to the transport provider. These properties are either for the message or for an attachmen, depending on the ulRef value passed in.
ulResult = WriteProps([in] ulRef, [in] lpPropSet)
ULONG ulRef 32-bit opaque value returned on the BeginMessage() or BeginAttachment() call.
LPSPropSet lpPropSet a set of properties (see Property Spec for definition)
The properties of a message or an attachment will be transferred in the order indicated by the transport in its response to the SelectOrder() call.
The property set will include the Sender-Time the message was marked for sending. Transports can override this if they need to use the date-time the message was given to the transport instead.
The property set will normally not include the Email-Address of the originator, her Display-Name, or her EntryID. (Sender-Address, Sender-EntryID, and Sender-Name properties.) Transports typically don't actually use this information when sending mail, and in any event with multiple transport providers installed, these may be different for each provider, and are already known to the provider. (If a provider does not have a notion of a Display-Name for the originator, such as a FAX transport, it is acceptable to use the user's local identity from the TransportLogin() call as the Display-Name.)
If the property set includes Sender-Address, Sender-EntryID, and Sender-Name, this indicates that the sender of the message would like to transport to give the appearance of this message being sent by this person (i.e. impersonation). This is a transport specific option that may or may not be supported. It is commonly desired when one person is authorized to send mail on behalf of another. (For example, Brian might be allowed to send mail as his boss, Paige. A recipient of such a message could not tell that Paige did not actually originate the mail.) Transports which do not support this option may ignore these properties.
BeginStream()
This call initiates the transfer of a property too large to conveniently be transferred with WriteProps().
ulResult = BeginStream([in] ulRef, [in] lpszPropName, [in] ulPropType, [in] ulTotalSize,
[out] lpulStreamRef)
ULONG ulRef 32-bit opaque value returned on the BeginMessage() or BeginAttachment() call.
WCString lpszPropName UNICODE property name
ULONG ulPropType property type (PT_BINARY or PT_STRING8 or PT_WCSTRING)
ULONG ulTotalSize total size of property (bytes for binary, characters for string)
ULONG * lpulStreamRef Address of variable in which the transport provider should return a 32-bit opaque reference to the property.
The TotalSize parameter allows the transport provider to pre-allocate the space for large properties only if it uses a fixed-size character set.
The transport provider assigns a 32-bit opaque handle to this attachment, which will be transferred in on subsequent calls. Subsequent calls will be to WriteStream() to transfer the data of the large property. The Spooler will transfer all the data of one stream, and call EndStream(), before going on to other data to be transferred.
WriteStream()
This call transfers a portion of one large property. The amount of data transferred is no larger than the limit returned on the SelectOrder() call.
ulResult = WriteStream([in] ulStreamRef, [in] ulOffset, [in] ulCount, [in] ulFlags, [in] lpData)
ULONG ulStreamRef 32-bit opaque value returned on the BeginStream() call.
ULONG ulOffset Number of bytes of this property that have been written already.
ULONG ulCount Count (in bytes) of the data transferred on this call.
ULONG ulFlags Flags. For future use. May be ignored for now.
LPBYTE lpData Pointer to the data transferred on this call.
EndStream()
This call indicates to the transport provider that the Spooler has finished transferring all the data of a large property. The transport provider may release any storage it might have allocated for handling the transfer.
ulResult = EndStream([in] ulStreamRef, [in] ulFlags)
ULONG ulStreamRef 32-bit opaque value returned on the BeginStream() call.
ULONG ulFlags Flags. For future use. May be ignored for now.
The next call will based on what the transport provider indicated, on the SelectOrder() call, should be transferred after this property.
BeginAttachment()
This call initiates the transfer of an attachment to the message.
ulResult = BeginAttachment([in] ulMsgRef, [out] lpulAttachRef)
ULONG ulMsgRef 32-bit opaque value obtained on the BeginMessage() call.
ULONG * lpulAttachRef Address of variable in which the Reference Value for this attachment should be returned by the transport
The transport provider returns a reference value for this attachment which is used on subsequent calls to BeginStream() or WriteProps(). The Spooler will completely transfer the properties of one attachment (all with the same ulAttachRef) before doing other calls on the original ulMsgRef.
This call will be followed by a call to SelectOrder() to allow the transport provider to indicate the order in which it wants the properties of the attachment to be sent.
Issue: There is insufficient information in attachment handling to allow a reference to an attachment that might somehow still be in the underlying messaging system. Mac Mail, for example, allows the user to forward an attachment to others on his own post office without transferring the attachment to the client and back again. Is this a feature we have to lose completely? Maybe we just have to wait for Server-based message stores with tightly-coupled transports.
EndAttachment()
This call finishes the sending of the data of one attachment. The ulAttachRef assigned on BeginAttachment() may be released by the transport provider.
ulResult = EndAttachment([in] ulAttachRef, [in] ulFlags)
ULONG ulAttachRef 32-bit opaque value obtained on the BeginAttachment() call.
ULONG ulFlags Flags. For future use. May be ignored for now.
This call indicates to the transport provider that the Spooler has transferred all the data of an attachment. If there are more attachments to be done this call will be followed by a call to BeginAttachment(). Otherwise it will be followed by calls to write the subsequent parts of the message as indicated by the data returned on the original call to SelectOrder().
EndMessage('>SendMessage()
The Spooler calls SendMessage() after transferring all the data of the entire message.
ulResult = EndMessage([in] ulMsgRef, [in] ulFlags)
ULONG ulMsgRef 32-bit opaque value obtained on the BeginMessage() call.
ULONG ulFlags Flags. For future use. Will be zero for now.
Some transport providers will form a copy of the outgoing message on the local disk and actually transfer it over the network during the processing of this call. Transport providers should of course call the SpoolerYield() callback routine from time to time during long processing.
FailureRecips()
The transport provider and its underlying messaging system are expected to generate a report when unable to deliver a message to a recipient. Such reports will often be generated hours later and on systems hundreds of miles away. Some transport providers, however, may know right away during the processing of a message of some recipients can not be handled. The FailureRecips() call is provided as a convenience to these transport providers, as gets the Spooler to generate the undeliverable messages.
After the SendMessage() call returns, the Spooler will call FailureRecips() repeatedly to learn of recipients that the Transport provider. The transport provider can return one or more recipients on each call. It should return a NIL list of recipients, or a valid list with zero entries, when there are no more.
ulResult = FailureRecips([in] ulMsgRef, [out] lppRecipList)
ULONG ulMsgRef 32-bit opaque value obtained on the BeginMessage() call.
LPADRLIST * lppRecipList Address of a variable in which the transport provider will store a pointer to a ADRLIST structure holding a set of recipients who could not be delivered to.
With this call the transport provider can pass back a set of one or more recipients who could not be delivered to. The structure passed back is the same used in the Address() function defined in the MAPI document. The ulRecipFlags field of each entry returned contains an error code indicating why that recipient could not be delivered to.
When allocating memory for the complex ADRLIST structure the transport provider should use SpoolerAllocate() to allocate one buffer to hold the entire structure. The Spooler will release the memory by calling SpoolerFree() with *lppRecipList.
The Spooler will make repeated calls to the transport provider to learn of all the failed recipients. The Spooler will generate the appropriate undeliverable messages for unreachable recipients reported by this call.
When no more failed recipients are known the transport provider should return a NIL in *lppRecipList, or may return a valid list with zero entries.
The failure codes will include: unknown recipient, recipient's disk space limit reached, improperly formed address.
Issue: Do we need the ability for the transport to give a STRING error message? We could just say that any transport that wants can generate its own undeliverable error message.
EndMessage()
The Spooler calls EndMessage() after learning of failure recipients. This call is the formal handoff of responsibility for the message from the Spooler to the Transport Provider.
The Spooler may also call EndMessage() before calling SendMessage() under certain error conditions. (See below.)
ulResult = EndMessage([in] ulMsgRef, [in] ulFlags)
ULONG ulMsgRef 32-bit opaque value obtained on the BeginMessage() call.
ULONG ulFlags Flags. See below.
Once this call returns, the ulMsgRef value is no longer valid. The transport provider may reuse the same value on a future message if it wishes.
The ulFlags value indicates:
END_CANCEL_MESSAGE The user wants to cancel the sending of the message regardless of who might have received it. This call may be made without the Spooler ever having made the SendMessage() call..
|