One of the most important functions of a portable computing device is to exchange information with other devices. A portable computer might use a number of different physical means to transmit and receive information; it might also use a variety of communication protocols (some of which may not be invented yet!).
GEOS helps to insulate an application from the physical details of transmitting and receiving information. It does this with its Mailbox library, and associated drivers. The library lets each application transmit and receive information in more or less the same way, no matter how it uses the information, and no matter what it does with the information once it receives it (or how it assembles the information before transmitting it).
The Mailbox API relies heavily on a basic understanding of VMChain structures and routines. Please consult the Virtual Memory chapter of the Concepts book for more information.
NOTE: This is a preliminary document. Please consult the Mailbox definition files ( mailbox.def or mailbox.goh ) for current and complete information.
1 Design Philosophy
1.1 Mailbox Overview
1.2 Transport Driver Overview
2 Inbox/Outbox Structure
3 Mailbox Structures & Utilities
4 Data Formats
4.1 GMDFID_STREAM_GSTRING
4.2 GMDFID_VM_GSTRING
4.3 GMDFID_TRANSFER_ITEM
4.4 GMDFID_TEXT_CHAIN
4.5 GMDFID_TEXT_FILE
4.6 GMDFID_DOCUMENT
4.7 GMDFID_INK
4.8 GMDFID_VM_COMPOSITE
5 Data Drivers
6 Application Responsibilities
6.1 Providing a User-Interface
6.2 Sending a Message
6.3 Receiving and Processing Messages
7 Examples of Mailbox Usage
7.1 An Incoming Pager Email Message
7.2 Sending and Receiving an Address Book Page
The purpose of the Mailbox library is to let applications send and receive information to and from other computers, through a variety of communications media. It insulates the application as much as possible from the mechanics of transmitting and receiving the information.
An application might need to transmit information over several different kinds of connection. For example, it might send information over a modem or through a direct connection. It might need to use the same device in very different ways; for example, it might fax a document through a modem, or copy a file to a server through that same modem.
The Mailbox library helps to insulate applications from the details of the hardware and communication protocols. It does this by dividing up the communications tasks.
The Mailbox library implements an Inbox/Outbox system. This lets the user select an appropriate device through which to transport the information; it also lets the user select a delivery time (though most applications will always designate messages for immediate delivery, so it is possible that the first release of the Inbox/Outbox system will not support applications setting an early-bound for delivery time). (Most applications will do this by including the Mailbox Library's MailboxSendControl device, which the user can use to create an outgoing message.)
The application must, when requested, package up information in a format acceptable to a transport driver being used. (Most applications will only need to support one or a few different data formats, such as GStrings or raw text in a VM Chain.) It then registers this message with the Mailbox library.
The Mailbox library stores messages that have not yet been transmitted in its outbox . Messages are held in the outbox until the hardware connection necessary for transmission is available. For example, if the transport driver needed a direct serial link, the message would stay in the outbox until that link was available. An application can also specify that a message not be delivered until a specific time. In this case, the message will automatically remain in the outbox until that time. While a message is in the outbox, the device's user can examine information about the message, and (if he or she chooses) delay or cancel its delivery.
When the time comes to send the message, the Mailbox library activates the appropriate data and transport drivers. This activation is transparent to an application developer. It is helpful to understand how a transport driver operates, however.
The transport driver handles actually sending a message of the device. It interacts with the actual hardware used for sending the message. This document does not attempt to discuss creating transport drivers in detail.
Messages are received much the way they are delivered. The transport driver receives a message, registers it with the Mailbox library, and passes its data, block by block, to the appropriate data driver. The data driver translates this message into its own data format. The Mailbox library stores the message in its inbox until it can deliver the message. When the appropriate time for delivery is reached, the Mailbox library starts up an appropriate application (if it is not already running) and passes the message to the application. The application must notify the Mailbox library when it has finished with the message.
A user or application can address a message to the local machine. In this case, the message will not be sent to the transport driver; the Mailbox library will deliver the message simply by moving it from the machine's outbox to its inbox.
The Mailbox library interacts directly with applications. It also manages drivers, which provide information about data formats, and also interact with the hardware links to the outside world. Thus, to support a new hardware device or data format, you just need to write a new driver.
The responsibilities of the Inbox/Outbox system are divided among four different agents:
MailboxGetVMFile(), MailboxMessageFlags, MailboxMessagePriority, MailboxDeliveryVerb
There are a few data types that are used at various parts of the message-transmission and reception process. There are also a few concepts related to these types that need to be discussed first.
First of all, we need to distinguish between two basic data types: data format types , and data storage types .
Every message has two data tokens associated with it. One is the data format type, and the other is the data storage type. The data storage type describes the gross structure of the message data. Every data driver can accept one data storage type. For example, the GEOS system includes data drivers for messages whose bodies are stored in VM trees, and messages whose bodies are stored in byte-files. The data driver is not concerned with the message on a lower level. If you send a message in a VM tree, the data drivers at both machines will make sure that the message arives in a valid VM tree (which may mean fixing up the VM handles that make up the links in the tree), but they won't care about the data inside the blocks of the tree.
On a higherlevel, every message has its own data format type. This type describes how the data is actually stored in the underlying structure. For example, a message might be stored as a GString in a VM chain. In this case, the message's data storage type would be "VM tree" (a VM chain is a special kind of VM tree), and the data format type would be "VM Gstring".
Every data format type implies a particular data storage type; all messages with data format type a will necessarily use data storage type x . The converse, however, is not necessarily true. For example, all messages with data format type "VM GString" have data storage type "VM tree"; as do all messages with data format "VM composite".
When an application passes a message to a data driver, it may designate the message body as either "volatile" or "non-volatile". If the data is marked "volatile", the application may alter or destroy the data as soon as it has registered the message. That is, when the application registers the message, the appropriate Data Driver copies all the message's information out of the passed message before execution returns to the application. You should pass "volatile" if, for example, you are passing a pointer to a VM file used by the application.
If you designate the data as non-volatile , you are not allowed to alter or delete the data in the message. If you ask it to, the data driver will take care of freeing the storage used for the message when it is done with it. Every data driver has its own conventions about what applications should do with non-volatile messages after sending them.
For example, if you send a message through the VM tree data driver, you should write non-volatile messages to a special VM file created by the routine
MailboxGetVMFile()
. This routine is passed a single argument, the number of blocks you expect to allocate in the VM block. The routine returns the handle of a VM file. You can write the message in this file, and register the message (designated "non-volatile"). The data driver will then take care of freeing the file's storage when the message has been safely delivered.
Each message is associated with a set of
MailboxMessageFlags
. These are a byte-length record. This record has the following flags:
MailboxMessagePriority
enumerated type, discussed below.
MailboxDeliveryVerb
enumerated type, discussed below.
Every message can be assigned a priority. This priority is represented by a member of the
MailboxMessagePriority
enumerated type. The priority affects how the Mailbox library will handle the message at both the sending and the receiving end. This type has the following values:
Every message has a "delivery verb" associated with it. This is useful in notifying the user of the message's arrival. Each delivery verb is represented by a member of the
MailboxDeliveryVerb
enumerated type. This type has the following members:
An application is largely insulated from the details of transmitting a message. It needs merely to write the message's data in a format which the chosen data driver can understand; the data driver will then translate this into an appropriate format for transmission.
GEOS provides two data drivers. These drivers use the data transport formats "File Data" and "VM tree data". Each data-format type provided by GEOS is built on top of one of these formats.
There are a few concerns in common to each data driver.
Every data driver will have its own format for accepting the data. In each case, the data driver is passed a pointer to a "data block" which tells it where the rest of the message is. Thus, every data driver is called in the same way: it is passed a single far pointer to some data block. How that data block stores the location of the message will vary from one data storage format to another.
GEOS will almost certainly define the following different data format types. An application should, if possible, support as many of these data types as is feasible. Each format is uniquely identified by a member of the
GeoworksMailboxDataFormatID
enumerated type. (The following sections will be expanded when the Inbox/Outbox suite is completed.)
In this data type, the message is stored as a GString written to a byte file. This is useful for printing and faxing messages. Applications should make every effort to support this data type. These messages are handled by the GEOS file data driver.
This is much like STREAM_GSTRING, except that the message is stored in a GString written to a VM Chain. These messages are handled by the GEOS VM-tree data driver.
This format is used when the user wishes to send the contents of the clipboard as a message. These messages are handled by the GEOS VM-tree data driver.
The message is plain text, written to a VM chain. These messages are handled by the GEOS VM-tree data driver.
The message is plain text, written to a byte file. These messages are handled by the GEOS file data driver.
The message is written to some kind of GEOS document with extended attributes and a full GEOS filename. The document's internal format is opaque to the data and transport drivers. These messages are handled by the GEOS file data driver.
The message is stored as GEOS ink. These messages are handled by the GEOS VM-tree data driver.
The message is passed as a VM tree. The first block of the tree holds an array of pointers to blocks that head up other VM-based data formats, followed by an array of
MailboxDataFormat
tokens which identifies the format of each chain. These messages are handled by the GEOS VM-tree data driver.
A data driver specifies how the data in a transmitted message is arranged. An application that sends a message must provide it in the format required by the data driver. The data driver then translates the message into an appropriate format for the transport driver. When the transport driver receives a message, it determines which data driver can interpret the message, and then calls theMailbox library.
There are a number of default GEOS data types, and developers may write other data drivers of their own to support new data types.
A data driver has to perform a few different tasks:
The application is insulated from most of the mundane responsibilities of transmitting and receiving data. Its behavior is event-driven. It has to know how to respond to a variety of requests from the Mailbox library; however, it generally does not have to initiate action on its own.
There are a number of specific tasks an application must perform:
Receiving and Processing Messages
To use the Mailbox library, an application will usually want to include a MailboxSendControl object in its UI. (Of course, you can create your own UI, but the MailboxSendControl is a convenient package.) Although most applications will use the UI provided by this controller other may want to use the
The settings of the MailboxSendControl will determine what formats the application can use when it sends data. There are a number of standard data types specified by the MailboxSendControl. If you wish to support a new format for transporting data, you must subclass the MailboxSendControl object and add any tools necessary for that new data type. If you wish to provide extra, application specific functionality, you must add tools to the controller yourself. For example, if you are writing a form-collection application, you might wish to add an icon that selects the certain fields of each form, assembles them into a message, and transmits them.
A user will generally send a message by using the application's MailboxSendControl object. This object lets the user send the selected material in the application. It lets the user select such things as the message's destination, transmission time, subject line, etc.
Once the user has selected the message, the MailboxSendControl sends the message MSG_META_MAILBOX_CREATE_MESSAGE to its output object (generally the application object). This message instructs the object to create an appropriate message.
When the application has been notified that it should create a message, it should then determine what data type the message will need. It does this by sending MSG_MAILBOX_SEND_CONTROL_CHOOSE_FORMAT to its MailboxSendControl. It can find out other information about the message to be sent by sending other messages to the MailboxSendControl.
The MailboxSendControl informs the application what data type it should use. The application must determine what data to send (generally the current selection); it then assembles this data into the format requested by the send control object. Finally, it registers the message by sending MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE to its MailboxSendControl. This message instructs the MailboxSendControl to register the message with the Mailbox library.
MetaMailboxMessage void MSG_META_MAILBOX_CREATE_MESSAGE(
optr sendControl,
word transHandle);
This message informs the output of a MailboxSendControl that it should create the body of the message. It is the obligation of that object to notify the controller that it has accomplished this task with MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE.
MailboxMessage MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE(
word TransID,
MailboxStorage bodyStorage,
MailboxDataFormat bodyFormat,
const void * bodyRef,
unsigned word bodyRefLen,
MailboxMessageFlags flags,
const char * subject,
const GeodeToken * destApp)
When an application wishes to send a message, it does this by registering a message with the Mailbox library. The Mailbox library then loads the appropriate data and transport drivers to actually deliver the message. The application registers a message by sending MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE to its MailboxSendControl object.
Source: The recipient of MSG_META_MAILBOX_CREATE_MESSAGE. Generally sent by the Application object.
Destination: The application's MailboxSendControl.
Parameters:
TransID
This is the token the MailboxSendControl passed with its
MSG_META_MAILBOX_CREATE_MESSAGE
.
bodyStorage
MailboxStorage
) identifying the data driver which should be used. (The message will be passed in a format appropriate to that data driver.)
bodyFormat
MailboxDataFormat
) identifying the kind of data in the message body. Each format token implies a particular storage type.
bodyRef
bodyRefLen
*bodyRef
.
flags
MailboxMessageFlags
.
subject
destApp
GeodeToken
of the application which will receive the message (on whichever machine receives the message). If this is a generic token, the recipient machine's Mailbox library will automatically convert it to the token of whichever application will actually receive the message.Interception: If you define a subclass of the MailboxSendControl, you may wish to intercept this message to add functionality.
MetaMailboxMessage void MSG_META_MAILBOX_MESSAGE_REGISTERED(
MemHamdle args);
This message is sent by the MailboxSendControl once it has attempted (via
MailboxRegisterMessage()
) to register the message. The controller may call
MailboxDoneWithVMFile()
(or any other cleanup procedures) in response to this message. <
MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE, MailboxAcknowledgeMessageReceipt(), MailboxGetSubjectBlock(), MailboxGetSubjectLMem(), MailboxGetMessageFlags(), MailboxGetBodyFormat(), MailboxGetBodyRef(), MailboxDoneWithBody(), MailboxStealBody(), MailboxDeleteMessage()
An application that uses the Inbox/Outbox technology must be able to handle incoming messages, as well as generate outgoing ones.
The Mailbox holds incoming messages in its Inbox until an appropriate time to deliver them. For example, it may choose to wait until the user starts up the application, or until the delivery deadline is reached. In any event, when the Inbox library needs to deliver the message it sends MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE to the application object of an appropriate application. This message has a single argument, a 32-bit
MailboxMessage
token which uniquely identifies the message.
If the application is going to handle the message, it must call the routine
MailboxAcknowledgeMessageReceipt()
. The routine is passed the
MailboxMessage
token for the message. (If you do not acknowledge the receipt of the message, your application will be bothered by repeated notifications.)
Once the application has been notified of an incoming message, it can call a number of routines to find out about the message. To find out the message's subject line, it should call either
MailboxGetSubjectLMem()
or
MailboxGetSubjectBlock()
. Both of these routines return the subject text for the message.
MailboxGetSubjectBlock()
is passed a single parameter, namely the
MailboxMessage
token for the message. The routine allocates a global memory block, writes the subject line to that block (as a null-terminated string), and returns the handle of the block. If it fails, it returns zero and sets the thread's error value. (See
ThreadGetError()
.)
MailboxGetSubjectLMem()
is very similar. It is passed two arguments: the
MailboxMessage
token for the message, and the global memory handle of an LMem heap. The routine allocates a chunk in that LMem heap, writes the subject line to that chunk (again, as a null-terminated string), and returns the chunk handle of that chunk. Again, if the routine is unable to comply, it returns zero and sets the thread's error value.
MailboxGetMessageFlags()
is passed the
MailboxMessage
token for the message. It returns the
MailboxMessageFlags
for that message. It is often used to find the message's priority.
MailboxGetBodyFormat()
is passed the
MailboxMessage
token for the message. It returns the
MailboxDataFormat
value for the message's body. The application can use this to decide if the message is written in a format the application can understand.
MailboxGetBodyRef()
is used to actually retrieve the data for the message. The routine is passed the following arguments:
MailboxMessage
token for the message.
If the routine is successful, it returns
true
and writes a reference to the message body in the passed buffer. This reference is in the same format that would be passed to
MailboxRegisterMessage()
, i.e. it varies according to the data driver. If the routine is unsuccessful, it returns
false
and sets the thread's error value to an appropriate
MailboxError
value.
For every time the application successfully calls
MailboxGetBodyRef()
, it must call
MailboxDoneWithBody()
. This routine is passed three arguments:
MailboxMessage
token for the message.
MailboxGetBodyRef()
.
The routine notifies the Mailbox library that the application is done with the message body for the time being. The mailbox library is thus free to close the file holding the data, or take some similar action. (The message body will
not
be destroyed; it will still be available for future calls to
MailboxGetBodyRef()
.)
An application can also get the message body by calling
MailboxStealBody()
. This is passed the same arguments, and returns the same values, as
MailboxGetBodyRef()
. However, if the routine is successful, the storage area containing the message body will be transferred to the ownership of the application, and the data driver will free any other storage it used to hold the message body. After a successful call to
MailboxStealBody()
, any future calls to
MailboxGetBodyRef()
or
MailboxStealBody()
will fail; as far as the Mailbox library is concerned, the message has no body any more. When the application is finished with the message, it must free the storage used for the message. In particular, if the data reside in a VM file, the application
must
call
MailboxDoneWithVMFile()
to notify the Mailbox library to free that file.
When an application is done processing a message, it must call
MailboxDeleteMessage()
. This routine is passed the
MailboxMessage
token for the message. It instructs the mailbox library to delete the message and any associated data. The
MailboxMessage
token becomes invalid after this routine returns.
It may be helpful to go through a step-by-step example of how a message might be sent, illustrating the function of the transport driver, and how much is done for you. The Mailbox library can be used for a great many different applications, so it's hard to describe a "typical" mailbox transaction; but there are certain elements most mailbox transactions have in common.
We present several different examples of how the Mailbox library might be used. Note that in the following discussion, the behavior of the transport driver (which is opaque to an application developer) is discussed. Unless you are writing a transport driver yourself, you will not need to worry about this.
An Incoming Pager Email Message
Sending and Receiving an Address Book Page
In this example, we consider a device that's receiving an Email message. The message was generated by some central server, which need not have been running any variety of GEOS, as long as the message conformed to the transport driver's expectations for the message format.
The message is received via a wireless pager card that's plugged into the device. The transport driver for the card, which was loaded when the card was inserted, periodically polls the card to see if new messages have arrived. On the next poll, it finds that there is indeed a new message. It extracts enough of the message to determine it's something for the Email application to see.
Armed with that, it calls the Mailbox library to register a message, specifying:
If the card will delete the message after a certain time, the pager driver will want to set an ending time-bound well before that deletion is scheduled to occur. If the card may delete the message after a while to free up space, as happens in the EMBARC card, the driver may well want to set an ending time-bound of, say, one day from when the message was received. The driver could also set the ending bound to match the starting bound, thus forcing the Email application to be loaded. The final choice, of course, is to set no ending bound.
For its part, the Mailbox library will load the data driver and call it, passing the data address the library was given and allowing the driver to copy the data (if required) and return its own version of the "opaque" data. (The message body is "opaque" to the Mailbox library, in that it never looks into this data for information; instead, it passes the data to the various drivers and applications, which may interpret and manipulate the data.) For example, the file data driver might well copy the file, whose name was passed, into a spool directory for safekeeping, returning the shorter name back to the Mailbox library as the opaque data to store with the message. The rest of the data will be copied into the message descriptor, after checking to make sure it's reasonable, and the descriptor will be placed at the head of the inbox queue.
Assuming the message is given standard First-Class priority and that the Email application isn't currently running in the foreground, the Mailbox library will catch the existence of the new message at its next (user-settable) check interval and will notify the user of its arrival. If the message has a higher priority, the user may be notified immediately, and may be forced to deal with the message immediately, as well. If the Email application is the foreground application, and has told the system it will take care of notifications itself, the message is immediately given to the application, which will presumably notify the user in its own way.
When the delivery box is presented to the user, the user can decide how to proceed with the message.
If the user says to send it, the Mailbox library will establish an IACP connection to the application that is to receive the message and send it an appropriate notification. When the application acknowledges receipt of the message, the message is removed from the Mailbox library's "inbox" and any list displaying the message is updated appropriately.
In essence, the Mailbox library will use IACP to send a MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE to the application object, passing the token for the message.
Using the message token, the application calls the Mailbox library to acknowledge receipt of the message. This acknowledgment will remove the message from the inbox, but will not delete the message descriptor.
The application will then call the Mailbox library back to fetch the body of the message, its subject, time stamp, etc. When the user deletes the message, the Email application will notify the library that it is done with the message. This call will cause the message data and the message descriptor to both be deleted.
This transmission is about as complex as it gets. In it, a user on one GEOS machine transmits an address book page to another user, on another GEOS machine. The transaction is broken into four parts, involving both machines.
First, the user selects the page he or she wishes to send. The user selects the means of transport from a Send menu or set of send tools (depending on the system and how the application is set up). The act of going to the page causes the application to notify the message-send controller of the context in which the application finds itself. It is this context that determines what type of data will be placed, by default, in the message body.
The message controller will bring up a dialog displaying the UI for setting transport options, including the address to which the message is to be sent. This UI is obtained by calling the transport driver.
When the user has chosen the address, the controller will send off a MSG_META_MAILBOX_CREATE_MESSAGE to its output, including the 16-bit token bound to the "Current Page" string in the message controller's table.
Having received the message, it becomes the application's responsibility to determine in what formats it can provide the data to be sent. It calls the message controller back, passing an array of data format descriptors that the controller then passes to the transport driver, to see which format it prefers. There will be transport drivers, like the one that uses the Socket library, where any data format will do, so long as the data driver supports the READ_NEXT_BLOCK and WRITE_NEXT_BLOCK calls. Other transport drivers will be more closely coupled with a data driver. For example, the Fax transport driver will require additional information about the fax being sent and will insist on a particular data driver being used.
The transport driver will select one (or none) of the formats and return it to the controller, which will return it to the application. If the transport driver indicates none of the formats is acceptable, the message controller will inform the user that the selected object (page, document, whatever) cannot be sent via the selected medium. The transaction will then be at an end.
Each data format descriptor consists of two 32-bit tokens (composed of the driver's manufacturer ID combined with a 16-bit constant). One token indicates the underlying storage format and refers to a single well-defined data driver. The other token defines the way data are stored in that underlying storage format. From an application's point of view, each data format token can only be used with one particular storage type token. Passing both allows things like the pager driver, described earlier, to be more flexible in how a message is stored. Such flexibility is not needed for the application; as far as it is concerned, a particular data driver is always associated with a particular data format.
If the Mailbox library is asked to load a data driver using a storage type descriptor with which it is not familiar, it will search the appropriate directory, loading each driver it's not seen before and asking it for its storage type token. This allows the work for a non-standard driver to be delayed as long as possible.
For argument's sake, let's say the transport driver indicated that its preferred format is that of a HugeArray with variable-sized elements. The application will ask the Mailbox library for a VM file to use, then convert the page into such an array, presumably placing each field of the address page into its own array element. Any references between the pieces are stored as indices into the array. For example, if the page is made up of the individual entries within the address card, plus a header that contains the names for each entry and a pointer to the entry data, the header must store the array index of the entry data, not a VM block + offset or even a DB group & item. Avoiding reference to individual VM blocks allows the data to be transmitted and stored in arbitrary VM blocks of a data file on the remote side.
The application would then pass a data format descriptor for an address book page stored in a HugeArray, and a storage type descriptor of a VM tree (HugeArrays are based on VM trees).
(If the data are just a stream, without internal pointers to different parts of it, the data can be put into a straight VM chain, rather than a HugeArray. For example, an application that sends just text across would have no need of a HugeArray, but could simply use a VM chain of the text blocks. It would then pass a data format descriptor indicating the thing is an address book page in a VM chain, but the storage type descriptor would still be for a VM tree.)
The matter of which VM file should be used bears a little bit of discussion, as well. If the data that make up the body of a message are already present in the application's document, by all means pass the address to them and mark the body data as volatile (you may not be about to free them, but you also can't guarantee the Mailbox library that they'll be around when the message needs to be sent). If you need to perform any conversion, however, you should use the Mailbox library's utility routines to obtain an appropriate VM file. Then you don't need to mark the body as volatile and the Mailbox library is saved the trouble and time of copying them into its own VM file for safekeeping.
Having created the appropriate HugeArray, the application then calls the message controller back to finally register the message with the Mailbox library.
The controller will determine the time bounds, and the transport driver. It will call the address controller to fetch the block of data to be stored in the transport descriptor. The application provides the following information in its call to the message controller:
As for any registered message, the Mailbox library will call the data driver, passing it the HugeArray's address and the data-is-volatile flag. The VM tree data driver will copy the array to a private VM file and return that file's name & block handle for storing in the message descriptor.
After the controller method returns, the application must remember to call the Mailbox library to tell it the application is done using the VM file in which it stored the message body. It must wait until after the message is registered to do this.
Once the message is in the Mailbox library's Outbox, it still needs to be sent to the destination computer. When the message is safely in the outbox, the Mailbox library will put up a confirmation box that allows the user to send the message now or later. Assuming the user chooses to delay the message, the user will (at some point in the future) bring up the outbox control panel and choose to see the messages destined for InfraRed (if InfraRed is the only transport with messages stored for it, InfraRed will be brought up automatically). The user will then click the Beam All trigger in the main control panel. This will prompt the Mailbox library to load the InfraRed transport driver and tell it to create a connection to the address stored in the message.
If the connection is successful, the Mailbox library will instruct the transport driver to send each message in turn, passing in the message descriptor. The transport driver, having connected to the well-known mailbox port on the remote machine, will send across the message descriptor, indicating the data format and the destination application, followed by the blocks of the message. The data blocks are obtained by calling the data driver for the message.
On the receiving side, the receipt of the first bytes of the connection request will cause the socket driver for the InfraRed medium to be loaded. When the link is established and an attempt is made to connect to the mailbox socket, the InfraRed transport driver will be loaded to handle the connection. The transport driver will receive each block from the Socket library. The first block will allow it to load the appropriate data driver, which it will use to store the subsequent blocks. If it is unable to load the requisite data driver, it will reject the message and the sending user will be told why.
When all the data blocks have been received, the data driver will be told the message is complete, at which point it will attempt to write the data to disk (it may also have been writing it to disk block by block, or in batches of blocks if the message is particularly large; the driver is allowed to return an error from any block write call and the transport driver will call the data driver to delete what it had received so far and will discard any remaining blocks in this message as they are received, eventually returning an error to the sending transport driver). If this write fails (due to lack of disk space, for example), the receiving transport driver will return an error, which will be returned back to the Mailbox library on the sending machine. The user will be notified and the message will remain in the outbox.
Following the commitment of the message data, the destination machine's transport driver will store the message in the inbox. Again, if the descriptor cannot be committed to disk, an error will propagate back to the sending Mailbox library. Only when the message descriptor is safely stored will the remote transport driver indicate the message has been successfully transmitted.
When the receiving machine has accepted the message, the sending machine will delete the message from its outbox, freeing the message data, etc.
Once the message is stored in the inbox, it progresses exactly as for the Email message, except it's the Address Book application that will receive the data.
Depending on the priority of the message, the Mailbox library will, sooner or later, notify the user that the message has arrived. If the user chooses not to store the card right then, he will be asked again when the Address Book is next launched or another message arrives for it.
When the user gives the go-ahead for delivering the message, the application is notified, via IACP. When the application has acknowledged receipt of the message, the descriptor is removed from the inbox (though not from the file).
The application will call the Mailbox library to obtain the data format of the message body. If it can accept that format, the application will call the library again, telling it to ask the data driver for the appropriate reference to the data.
Armed with this reference, the application can then act on the message. The message body should be treated as read-only and not be modified by the application in any way.
When it's done with the message, it calls the library again to say it's done with the message (this call is passed on to the data driver to do with as it sees fit).
If the application has no further use for the message descriptor, it will call the Mailbox library to say the message should be deleted.
If the application is unable to understand the data format of the message body, it is responsible for informing the user and deleting the message.
In this case, the Address Book will learn the data are a HugeArray-based address book page, which it understands. It will ask for and receive the VM file and block handle of the HugeArray, extract the relevant pieces and merge them into the address book. In all likelihood, the application will want to confirm with the user that she really wants to do this (especially if it will be overwriting an existing entry) and to do it in the current document. If the user indicates she would rather place the record in another document, the application is free to open the other document; the Mailbox library sets no time limits on how long an application may keep a delivered message active.
Having applied the change (or not), the Address Book will tell the library it's done with the data and to delete the message.