WINDS Remote Transport SUMMARY ======= The Remote.Xp sample is a remote transport provider (XPWDSR) implemented in C++. This transport demonstrates the following extended MAPI features: - Support for the REMOTE interface for slow-link connection to a remote host. This includes implementation of the IMAPIFolder methods needed to interact with a client remote viewer application. - Support for deferred delivery mechanism for scheduled message submission to a remote host at a user-settable upload time. - Support for forced immediate connection and submission (using the FlushQueues method implemented in the IMAPIStatus object) in response to Deliver Mail Now and Transfer Mail in the Microsoft Exchange Client. - How to implement server-based messaging. - Real-world working mail transport which sends messages to a Windows NT workstation or server running the Windows Developer Support (WINDS) Sample Server Messaging Host included with this and other MAPI samples. - Use of the Transport Neutral Encapsulation Format (TNEF) interface to encapsulate ALL the message properties. - How to implement the IMAPIStatus interface for client access to transport status. - Multithread safe. - Configuration dialogs. - Generation of delivery and non-delivery reports. - Support for the profile Wizard for interactive profile configuration. - Support for the WINDS address type. - Support for background download and FlushQueues spooler interaction for spooler-marshalled remote connections. - Single binary file for Windows NT and Windows 95. MORE INFORMATION ================ This program requires: Windows NT 3.51 (or later) or Windows 95, The MAPI 1.0 SDK included in the Win32 SDK, Microsoft Visual C++ version 2.1 (or later.) Optionally, the Win32 SDK tools with the MIDL compiler. This sample uses RPCs over named pipes to establish connections with the remote server. For convenience to customers without the full Win32 SDK, we have provided the MIDL compiler-generated C and header files (WINDS.H, WINDS_S.C, and WINDS_C.C) along with the IDL and ACF files. If you make changes to the IDL or ACF, you'll NEED to recompile them using the MIDL compiler available only in the Win32 SDK and Visual C++ 4.0 and later. Building the Sample Code ------------------------ The sample remote transport provider has only been compiled and tested under Windows NT 3.51 and Windows 95, there are no plans for a Windows 3.1x (16-bit) release of this sample. Users should update the project file dependencies as soon as the files are copied to your machine. Use the PROJECTS.UPDATE DEPENDENCIES options in the Visual C++ Developer Studio. This sample was developed using Microsoft Visual C++ 2.x. Its associated makefile is XPWDSR32.MAK. Traces can be enabled for Release builds by defining ENABLE_DEBUG_OUTPUT in the preprocessor symbols define in the project settings menu. A command-line compiler-independent MAKE file has been provided for users of command-line tools. Configuration ------------- Use the MERGEINI.EXE utility from the MAPI PDK to merge this transport's message service INF file, XPWDSR.INF, with the MAPISVC.INF file: MERGEINI {full-path}\XPWDSR.INF -m -q for example: MERGEINI C:\SAMPLES\MAPI\XPWDSR\XPWDSR.INF -m -q You will then need to configure a profile that includes this transport. When you create a new profile, the Profile Wizard will detect this transport and set the initial properties through the wizard pages. XPWDSR has two property sheets for interactive configuration which are accessible through the service provider logon, through the ServiceEntry call and through the IMAPIStatus::SettingsDialog method. The sample also defines custom properties for the configuration options so they may be set and retrieved programmatically. User Account Configuration Page ------------------------------- Server Name: This is the network UNC (Universal Naming Convention) name of the machine where the user account is. The server machine MUST be running the WINDS Sample Server Messaging Host application prior to the provider setup. Mailbox Name: The mailbox name is the name of the account on the remote server. To select a mailbox or change the current one, click on Browse and select a name from the list that will appear. Once selected, you will be asked to enter the password for the mailbox. For new mailboxes, the default password is PASSWORD. If the list of mailboxes is empty or you don't find the one for you, then a new account must be created on the server program. This field cannot be edited manually by the user. Full Name: This is the complete name of the owner of the mailbox selected. This field is updated automatically when a new mailbox is selected. This information cannot be edited manually. Change Mailbox Password: Click on this button to change the password for this mailbox. You will be asked to type the old password, a new password, and then confirm the new password. Using Local Network (LAN): This is the option to use if the machine is directly hooked to on the network there is a connection to the server machine at all times. If this option is selected, when running, the provider will install notification links with the remote system allowing the provider to update internal data dynamically as the data changes on the server, and to react to changes or commands from the server. Remote - With Local Address Book Directory: This setting instructs the provider to adjust itself to use local data and to defer communication with the server until the user changes the connection mode. This mode is useful if the server or the clientmachine are not on a persistent connection link with each other. If this option is selected the provider will not know of changes that occur on the server to cached data. Message Transport Configuration Page ------------------------------------ Automatic Remote Uploads At: The daily time when the transport will submit all the messages it has deferred. At this time it will connect to the server and submit all the messages in a batch. The time must be entered in the 24-hour format, i.e. 12:00 AM is 0:00. 10:30 PM is 22:30, etc. When Connected: If this option is selected, after the messages have been uploaded at the scheduled time, the transport will request to update the message headers. Note: Prior to installing this transport into a profile, the WINDS Sample Server Messaging Host should be running in the remote (or local) computer. See the readme file in the above sample for instructions on compiling, installing, and using it. Address Format -------------- This transport supports the address type WINDS. When creating new one-off entries in an address book (i.e. the Personal Address Book), this is the value for the Address Type field. Email addresses for this transport look like this: \\server-name\mailbox-name The backslashes are literals, while the server-name and mailbox-name should be replaced with the server and mailbox name you wish to send to. Note: When typing one-off addresses in the Exchange client, you must double the backslashes, like this: [WINDS:\\\\myserver\\mymailbox] This is because the client treats the backslash as an escape character. Remote Access ------------- The main difference between this transport and a non-remote transport is the ability to schedule mail transmission and to preview the header information of messages residing in a user's remote mailbox. Outgoing messages aren't transmitted at time of submission, but are queued by the spooler until the scheduled delivery time occurs. This is known as deferred submission. As long as the message is deferred, it stays in the user's local message store and the spooler retains responsibility. The spooler maintains an internal queue of deferred messages which it empties at the scheduled upload time, passing each message in sequence to the transport for delivery. Inbound messages stay in the user's mailbox on the remote server until explicitly downloaded. This transport supports the Remote Viewer semantics, where the user can request the transport to connect to the server and retrieve a table of mail headers of messages in the user's remote mailbox. The remote viewer lets the user mark those rows of the header table he wishes to download. Rows are marked for move, delete, or copy operations according to which operation the user wants to perform on the corresponding message. Downloads are started explicitly, not scheduled, since the user has to explicitly mark the rows of the table that are to be downloaded. The download table is a contents table of a MAPI folder that is returned by the IMAPIStatus object. This folder has no objects in it, but only exists to provide the contents table. When the user marks messages for download in the Remote Viewer, the corresponding row in the table has the PR_MSG_STATUS property set to a bitmask indicating the requested operation. To obtain the data for the header table, the transport makes an RPC to the server requesting the name of a named pipe. The server creates the pipe and returns the name. The transport then opens its end of the pipe and streams the actual data over it. This data is used to populate the contents table. Download Semantics ------------------ A user request to start downloading marked messages causes the transport's IMAPIStatus::ValidateState method to be called with the PROCESS_XP_HEADER_CACHE flag passed. At this point the download table is walked and each marked entry is added to a list of messages to be downloaded. When the list has been constructed, an RPC is made to the server to obtain the name of a named pipe over which to stream the data. The transport opens the pipe and requests the first message in the to-download list. The transport uses a simple form of handshaking: control messages are passed first to tell the server what the operation is, ACK the request, and get the size of the following stream. Transmission errors can be reported in these messages also. An error causes the message-in-transit to be dropped and the next one started. When a message is downloaded, its storage is backed by a temporary file which is created on the fly to hold the incoming stream. When the message stream arrives successfully on the client side, its to-download list node is moved to a just-downloaded list. If the user logs off the session before the just-dowloaded list is processed, the downloaded file is kept in a to-do directory which will be processed in the next session logon. Processing the just-downloaded list occurs in the same manner as for a non-deferred message; each message is pumped through the spooler's StartMessage loop. The spooler is notified that new mail has arrived by setting the STATUS_INBOUND_FLUSH in the PR_STATUS_CODE bit in the status table row. This causes the spooler to start requesting messages from the XP (by calling StartMessage). We stream the message in from the temp file, decode the TNEF stream into the passed MAPI message object, remove the non-transmittable properties, set the transport computed properties, delete the temporary file, and remove that message's node from the just-downloaded list. When the list is empty, we signal the spooler to stop requesting incoming messages by clearing the STATUS_INBOUND_FLUSH bit and passing back the untouched message object in StartMessage. Upload Semantics ---------------- Uploading messages works in a similar fashion except that we initiate the process based on the time of day. The user configuration specifies a daily upload time which is saved in the profile section. The XP can be in several states with respect to the upload operation. When the upload time arrives, the state changes from WAITING to PROCESSING_TIMER_EVENT. When a message is submitted by the user, SubmitMessage is called and the message is passed to us by the spooler. If our state is WAITING, we return without doing anything. In the subsequent call to EndMessage, we check the state and if we're WAITING, pass back END_DONT_RESEND. This signals the spooler to queue this message until later requested. A timer event is initialized on logon with the interval between the current time and the upload time. When the timer expires, the spooler is notified to start submitting the earlier deferred messages from its internal queue. We get called again at SubmitMessage and are passed each message that's dequeued. In SubmitMessage, we check our state, recognize that we're being called this time to send the message, take responsibility (PR_RESPONSIBILITY), and go ahead and deliver it. We also change our status to STATUS_OUTBOUND_FLUSH to signal the spooler to keep calling our SubmitMessage method as long as it's queue is not empty. In the subsequent EndMessage, we check our state, realize that a message has just been dequeued and sent, decrement our counter and pass back 0. When the spooler has dequeued the last message, it callls TranportNotify, passing NOTIFY_END_OUTBOUND_FLUSH and we change state back to WAITING. After the transport has finished uploading the messages at the scheduled time, we also request to get a new table of message headers on the server. This table is downloaded to the local file and if the remote viewer is displayed, the table of contents is refreshed. Aborting Message Delivery ------------------------- Once deferred, a message can be aborted any time before the actual delivery. This happens if the user opens or deletes the message before it's sent. The XP gets called at TransportNotify with NOTIFY_ABORT_DEFERRED. The spooler will remove this message from its internal queue and we won't be called at SubmitMessage for it. Debug Traces and Asserts ------------------------ This sample uses several output debug string functions in the Win32 environment to avoid attaching the DLL to any debugger. By default, the trace messages are output to a debug terminal attached to COM1 with settings at 9600, N, 8, 1. The debug messages can also be written to a log file whose default location is C:\MAPILOG.TXT. The file TRACES.CPP defines some macros that can be easily modified for different communications settings, output port, and log file name. It also implements a macro (ASSERT) and function that test the validity of a given statement. If the assertion fails, the user/developer has the opportunity to break into the debugger at the exact point where the assertion occurred. The debug routines are found in the TRACES.CPP and TRACES.H files. To enable the TRACExx functions in the TRACES.CPP files, define the ENABLE_DEBUG_OUTPUT macro during compilation. These functions work in DEBUG or RELEASE builds. You can only enable/disable traces at compile time through ENABLE_DEBUG_OUTPUT. There is no run time switch to enable or disable the traces. TRACES.CPP implements an ASSERT macro which test a evaluates an expression expecting a TRUE result, i.e. ASSERT (expression) will interrupt execution if the expression is NOT TRUE (non-zero). The ASSERT macro informs the line and source code file name where the assertion failed and writes the result to a debug trace. The assert information is also written to the log file. Also the this file implements a variation of assert: ASSERTMSG (expression, user-message) which behaves identical to ASSERT except that is the assertion fails, the message string is printed along with the assertion line and file information. By default, if an assertion fails, a dialog will come up and interrupt execution until a selection is made to break into the debugger or ignore the assertion.