SMS Guide: 3.2 Sending an SMS Message: Using the SMS Mailbox API

Up: GEOS SDK TechDocs | Up | Prev: 3.1 Sending and the Mailbox Library | Next: 4 Receiving an SMS Message
mailbox.goh

To send an SMS message to the outside world, an application will need to perform the following steps:

  1. Set up the text message. In many cases, the text will reside in a simple text buffer. If you wish to address the SMS message to a particular application, you will need to prepend a twelve-character prefix to the text message. The message itself will then be, at most, 148 characters in length. Without a prefix, you may send a 160-character SMS message. (See Setting Up the Text Message .)
  2. Copy the text into a VM file. The Mailbox library may store an SMS text message in a variety of formats (indicated by type GeoworksMailboxDataFormatID ), but a VM file is the easiest to manipulate. This VM file will be accessed by a Mailbox data driver. (See Storing the Text Within a VM File .)
  3. Set up the SMSendingOptions parameters in a DB item. These will be used by the SMS transport driver when the message is delivered. (See Setting Up the SMSending Options .)
  4. Set up the MailboxRegisterMessageArgs structure. (See Setting Up the MailboxRegisterMessageArgs .)
  5. Call MailboxRegisterMessage() . (See Registering the Message .)
  6. Upon success, notify the Mailbox library that we are done with the VM file.

These steps are detailed in the sections following.

Setting Up the Text Message

foam.goh

The Short Message Service protocol is able to handle a "short message" of up to 160 characters. The Foam library defines this character limit:

#define FOAM_MAX_SMS_TEXT_SIZE 160

The Nokia 9000i Communicator also allows you to further specify a particular application within the Nokia 9000i Communicator by adding a prefix to the SMS message (thus reducing the amount of useful text).

This prefix consists of two backslashes ("//"), a 4-byte GEOS application token ( TokenChars), a 5-byte GEOS ManufacturerID, and a carriage return ("/r", ASCII 13, or 0x0D hex). The Mailbox library will correctly read all characters up to the carriage return.

For example:

//SKAA11\r

is a valid prefix.

The application TokenChars and ManufacturerID should match those of the intended receiving application. If you define your own application, and only wish to communicate with its counterpart running on another device, you will define this string in the application's .gp file. The string is constructed from the tokenchars and tokenid lines of the .gp file.

You may also send a message to a different application, as long as you know its TokenChars and ManufacturerID .

This prefix should leave at least 148 characters to store the alpha-numeric text message. When concatenating two strings, be sure that you do not overflow the bounds of the array.

Code Display 3-1 Setting Up an SMS Text Message

/*
 * Declare the buffer to store the text message and write the prefix to the buffer. 
 */
TCHAR smsMessage[FOAM_MAX_SMS_TEXT_SIZE + 1] = "//SKAA11\r";
TCHAR smsText[] = "Here's my test message. I better make sure it fits.";
/*
 * Add the body of the text to the buffer but first check the bounds.
 */
if ( strlen( smsMessage ) + strlen( smsText ) <= FOAM_MAX_SMS_TEXT_SIZE )
{
    strcat( smsMessage, smsText );
}

Storing the Text Within a VM File

MailboxGetVMFile(), Mailbox\vmtree.goh

Text within an SMS message needs to be stored by the Mailbox until it connects with a cellular service provider. When you register a message with the Mailbox library, you indicate what storage format you have selected. The Mailbox uses a data driver to store and retrieve messages. There are a number of data drivers available, but we only need to know a few.

SMS messages may either be stored as VM (Virtual Memory) trees or as GEOS files, within the Mailbox itself. VM trees offer a lower overhead, and are simpler to use, so that is the format we have documented. (It is possible that future data drivers will become available as well.) If you are unfamiliar with GEOS VM trees, consult the Virtual Memory chapter of the Concepts book.

The mailbox data driver stores its data in what is called a VM tree. This tree is referenced by a VMTreeAppRef structure.

typedef struct {
    VMChain			VMTAR_vmChain;
    VMFileHandle			VMTAR_vmFile;
} VMTreeAppRef;
VMTAR_vmChain stores the VMChain to pass to the data driver. For SMS messages, simply use the VMCHAIN_MAKE_FROM_VM_BLOCK macro to construct a VMChain from a single VMBlockHandle .
VMTAR_ vmFile stores the VMFileHandle which contains the VM block.

The Mailbox stores these VM trees locally within an administation file that it maintains. To store the text within a Mailbox-compatible VM file, perform the following steps:

  1. Call MailboxGetVMFile() to retrieve a VM file handle within the Mailbox. This routine expects an argument indicating the expected number of blocks to be added to the file; because the SMS message can be contained in a single block, pass 1. Also pass a buffer to store the returned VMStatus.

    This routine returns a VMFileHandle.
  2. Allocate a VM block within this file, using VMAlloc().
  3. Lock the block down using VMLock(). This routine returns the segment address of the locked block. You will also receive a temporary memory handle that you can use to manipulate the block.
  4. Copy the text from your text buffer into the locked block starting at its segment address. (You will need to skip two bytes for the VMChainLink that begins the block.)
  5. Dirty the block using VMDirty().
  6. Unlock the block using VMUnlock().
  7. Store the VM file and VM block within a VMTreeAppRef structure. This format is understood by the mailbox data driver.

Code Display 3-2 Storing the Text In a VM File

VMFileHandle		vmFile;
VMBlockHandle		vmBlock;
TCHAR		*textPtr;
MemHandle		mh;
word		vmStatus;
VMTreeAppRef		vmRef;
/*
 * Obtain the handle of a writable VM file from the Mailbox library. We pass one
 * since we do not plan to add additional blocks to this file (because SMS messages 
 * are so short).
 */
vmFile = MailboxGetVMFile(1, &vmStatus);
/*
 * Allocate a VM block and store the text within this block. The block will need 
 * to fit a header (VMChainLink), the SMS message itself, and a null terminator.
 */
vmBlock = VMAlloc(vmFile, (sizeof(VMChainLink) + strlen(smsMessage) +1), 0);
charPtr = VMLock(vmFile, vmBlock, &mh);
/*
 * We advance past the VMChainLink contained at the beginning of the VM
 * block and copy the text to that location.
 */
strcpy(textPtr + sizeof(VMChainLink), smsMessage);
/*
 * Dirty the block and unlock it.
 */
VMDirty(mh);
VMUnlock(mh);
/*
 * Store the VM file and VM block within a VMTreeAppRef structure. This is the 
 * format that the mailbox library understands. Essentially, it ia a VM "chain"
 * of a single VM file.
 */
vmRef.VMTAR_vmChain = VMCHAIN_MAKE_FROM_VM_BLOCK(vmBlock);
vmRef.VMTAR_vmFile = vmFile;

Setting Up the SMSending Options

MailboxGetAdminFile(), smdefine.goh, SMSendingOptions, ReadCSNumberFromIniFile()

When registering a mailbox message, the application must specify a data driver to use when storing the message. It will also need to specify which transport driver to use when the message is to be delivered. The GEOS SMS transport driver expects a number of parameters indicating how the SMS message should be sent. These parameters are passed as a SMSendingOptions structure within a GEOS DBItem.

To store these parameters within a compatible DB item, perform the following steps:

  1. Call MailboxGetAdminFile() to retrieve the VMFileHandle of the Mailbox administration file. This is where you will copy your SMS parameters.
  2. Allocate an ungrouped DB item within this file, using DBAllocUngrouped().
  3. Lock the DB item using DBLockUngrouped().
  4. Copy your basic SMSendingOptions parameters into this DB item.
  5. Read the service center number from the .INI file using InitFileReadStringBuffer(). Copy that number into the DB item.
  6. Dirty the block using DBDirty().
  7. Unlock the block using DBUnlock().

The SMSendingOptions structure

The SMSendingOptions structure is defined within smdefine.goh and is shown below:

typedef struct {
	SMReplyPath			SMSO_replyPath;
	SMValidityPeriod			SMSO_validityPeriod;
	SMMessageConversion			SMSO_messageConversion;
	TCHAR		SMSO_scAddress[MAX_ADDRESS_SIZE];
} SMSendingOptions;

#define MAX_ADDRESS_SIZE 22
SMSO_ replyPath contains an SMReplyPath value which indicates whether the recipient may reply to the message through the sender's service provider. Possible values are:
SMRP_NO
This flag is set by default and indicates that the recipient must reply through the recipient's service provider.
SMRP_YES
Setting this flag allows the recipient to reply through the sender's service provider.
SMSO _validityPeriod contains an SMValidityPeriod value which indicates the length of time that the message is considered valid for delivery. The cellular provider should interpret this value and if it is unable to connect with the recipient, drop the message and inform the sender that the message was not delivered. Possible values are:
SMVP_1_HOUR
SMVP_6_HOURS
SMVP_24_HOURS
SMVP_1_WEEK
SMVP_MAX (The actual value of SMVP_MAX will vary by service provider.)
SMSO_ messageConversion contains an SMMessageConversion value, indicating that you wish to have the SMS message delivered to the recipient in a different format than that of an SMS message (provided that the cellular provider offers this service). Possible values are:
SMMC_NORMAL
Deliver the message as a normal SMS message. This is the default value.
SMMC_FAX_GROUP_3
Deliver the message as a fax according to the fax group 3 standard.
SMMC_FAX_GROUP_4
Deliver the message as a fax according to the fax group 4 standard.
SMMC_VOICE
Deliver the message as a voice mail message.
SMMC_ERMES
Deliver the message according to the European Radio Messaging System standard.
SMMC_PAGING
Deliver the message as an alpha-numeric page.
SMMC_EMAIL
Deliver the message as an Internet e-mail message.
SMMC_X400
Deliver the mesage according to the X400 e-mail standard.
SMSO_ scAddress stores the address of the service center (cellular provider). This number is stored as an ASCII text string and is limited to 22 characters. This number should be retrieved from the Nokia 9000i Communicator's .INI file; the number is stored under the scNumber field within the SMS category. This "address" in this case is really just a numerical phone number.

Reading a String From the .INI File

initfile.h, InitFileReadStringBuffer()

To read a string from the .INI file, call InitFileReadStringBuffer() . Pass "SMS" as the category and "scNumber" as the key. The routine will read the entry into a buffer passed to the routine. It will also read the size of the string into another buffer.

Code Display 3-3 Setting the SMSendingOptions Parameters

SMSendingOptions			*ptrSendingOptions
VMFileHandle			adminFile;
DBGroupAndItem			smsParams;
word			sizeOfBuffer;
adminFile = MailboxGetAdminFile();
smsParams = DBAllocUngrouped(adminFile, sizeof(SMSendingOptions));
ptrSendingOptions = (SMSendingOptions *) DBLockUngrouped(adminFile, smsParams);
/* set desired sending options */
ptrSendingOptions->SMSO_replyPath = SMRP_NO;
ptrSendingOptions->SMSO_validityPeriod = SMVP_24_HOURS;
ptrSendingOptions->SMSO_messageConversion = SMMC_NORMAL;
/* be sure to initialize these fields as well, otherwise the app will crash! */
ptrSendingOptions->SMSO_dataCodingScheme = 0;
ptrSendingOptions->SMSO_userDataLength = 0;
ptrSendingOptions->SMSO_userDataLength = 0;
InitFileReadStringBuffer("SMS",
			"scNumber",
			ptrSendingOptions->SMSO_scAddress,
			0,
			&sizeOfBuffer);
DBDirty(ptrSendingOptions);
DBNUnlock(ptrSendingOptions);

Setting Up the MailboxRegisterMessageArgs

MailboxRegisterMessageArgs, MailboxTransAddr

By now, you have created a reference to the SMS message itself (within a VMTreeAppRef structure); you have also placed the SMS sending parameters within the mailbox administration file. You are now ready to load the MailboxRegisterMessageArgs structure with parameters. (Two of these parameters will be the location of the SMS message and its sending parameters.)

  1. Indicate the format in which you wish the SMS message stored while it resides in the outbox. (The mailbox will call the appropriate mailbox data driver when the message is registered.)
  2. Indicate the format of the actual message. This is where you tell the mailbox that this message is an SMS message.
  3. Indicate the VM file in which the SMS message is stored.
  4. Tell the mailbox to use the SMS transport driver when the mailbox sends the message.
  5. Give the mailbox the address of the recipient.
  6. Indicate the DB item where the SMSendingOptions are located.
  7. Tell the mailbox when to begin delivery of the message and when to give up delivery attempts if unsuccessful.

All of these steps are accomplished by simply filling in a MailboxRegisterMessageArgs structure. This structure is large, but it is not altogether complicated. Each field is described in the following sections, though only SMS relevant information is shown. It is shown below:

typedef struct {
	MailboxStorage				MRA_bodyStorage;
	MailboxDataFormat				MRA_bodyFormat;
	const void 				*MRA_bodyRef;
	word				MRA_bodyRefLen;
	MailboxTransport				MRA_transport;
	MailboxTransportOption				MRA_transOption;
	MailboxTransAddr				*MRA_transAddrs;
	word				MRA_numTransAddrs;
	dword				MRA_transData;
	MailboxMessageFlags				MRA_flags;
	const char				*MRA_summary;
	GeodeToken				MRA_destApp;
	FileDateAndTime				MRA_startBound;
	FileDateAndTime				MRA_endBound;
} MailboxRegisterMessageArgs;

Data Format for Storage

As we mentioned, the Mailbox will need to store a message in a format that it can understand. Rather than have the Mailbox understand every data format that is (or will ever be) developed, it is easier to "package" the message in a small number of standard formats. You specify in what format the message will be wrapped in the MRA _bodyStorage field.

MRA_ bodyStorage indicates under what format (of type MailboxStorage ) the message will be stored in the internal Mailbox administration file. MailboxStorage is defined as follows:
typedef struct {
	word			MS_id;
	ManufacturerID			MS_manuf;
} MailboxStorage;
MS_id
...stores a word of data indicating the type of data driver that will be used to store the message. For SMS messages, we will use a Geoworks data driver referenced by a GeoworksMailboxStorageID. The following types are valid:
	GMSID_FILE
	GMSID_VM_TREE
As we indicated earlier, SMS messages should be stored in VM trees. You do have the option of storing them as GEOS files as well, but VM trees are easier to manipulate.
MS_manuf
...stores the ManufacturerID of the data driver being used. Since we will be using Geoworks VM trees, we will store MANUFACTURER_ID_GEOWORKS in this location.

Data Format of the Message

MRA_bodyFormat
...indicates the format of the actual data being stored (in its original source). This field takes an argument of type MailboxDataFormat . This is defined below:
typedef struct {
	word			MDF_id;
	ManufacturerID			MDF_manuf;
} MailboxDataFormat;
MDF_id
...stores a word of data indicating the data stored in the message. We will use a Geoworks data format referenced by a GeoworksMailboxDataFormatID. Pass GMDFID_SHORT_MESSAGE in this field to indicate that the message is a Geoworks SMS message.
MDF_manuf
...stores the ManufacturerID of the format. Since we will be using Geoworks SMS , we will store MANUFACTURER_ID_GEOWORKS in this location.

References to the VM Tree Structure

MRA _bodyRef stores a pointer to the reference of the SMS message. Since we will be storing the message in a VM tree, we will reference that message with a VMAppTreeRef .
MRA_ bodyRefLen stores the length of the reference indicated in MRA_ bodyRef . In this case, we will store the size of the VMAppTreeRef structure.

The Transport Driver

The Mailbox uses an applicable transport driver when it wants to deliver a message to the outside world. You specify this transport driver in the MRA_ transport field.
MRA _transport takes an argument of type MailboxTransport . This is defined below:
typedef struct {
	word			MT_id;
	ManufacturerID			MT_manuf;
} MailboxTransport;
MT_id
...stores a word of data indicating the type of transport driver that will be used to deliver the message. For SMS messages, we will use a Geoworks transport driver referenced by a GeoworksMailboxTransportID.
To send an SMS message, we will use the SM transport driver referenced by GMTID_SM.
MT_manuf
...stores the ManufacturerID of the transport driver being used. Since we will be using Geoworks SM transport driver, we will store MANUFACTURER_ID_GEOWORKS in this location.
MRA_transOption
...stores an unsigned value that the the transport driver uses. For SMS, set this to zero.
MRA_transAddrs
stores the address of the eventual recipient. This address is stored as a pointer to a MailboxTransAddr structure; it is only important that the transport driver understand the format for this address.

If there are multiple recipients, this pointer references multiple MailboxTransAddr structures. This structure is defined below:
typedef struct {
	const void			*MTA_transAddr;
	unsigned			MTA_transAddrLen;
	const char			*MTA_userTransAddr;
} MailboxTransAddr;
MTA_transAddr
...stores a pointer to the recipient address, understood by the transport driver. (For SMS this is a simple ASCII text string of the recipient's phone number.)
MTA_transAddrLen
...stores the length of the address stored in MTA_transAddr.
MTA_userTransAddr
...stores a user-readable version of the address, as a null-terminated text string. Because the SMS address is already stored as an ASCII text value, this field will be the same as MTA_transAddr.
MRA_numTransAddrs
...stores the number of recipient addresses stored in MRA_transAddrs.
MRA_transData
...stores data understood by the transport driver. As mentioned in Setting Up the SMSending Options , the SMS transport driver expects a DBItem containing a structure of type SMSendingOptions .
MRA_flags
...stores a field of MailboxMessageFlags.
For SMS messages, the only flag you may care about is MMF_DELETE_BODY_AFTER_TRANSMISSION, which will delete the VM file containing the SMS message after it is delivered. Do not set any other flags.
MRA_summary
stores a text string that the Outbox displays to indicate the type of message being delivered. For example, if you are sending a chess move for a game in progress, you might set this field to display "Chess Move."
MRA_destApp
...stores the GeodeToken of the destination application.
MRA_startBound
...indicates a time and date (of type FileDateAndTime ) to begin transmission of the message. You may also pass the value MAILBOX_NOW (0) for immediate delivery.
MRA_endBound
...indicates a time and date (of type FileDateAndTime ) to end attempts to transmit the message to the service provider. You may also pass the value MAILBOX_ETERNITY (-1) to indicate attempt until successful.

Code Display 3-4 Setting Up the Mailbox Registration Arguments

MailboxRegisterMessageArgs				registerArgs;
MailboxTransAddr				addressList;
TCHAR				number[MAX_ADDRESS_SIZE]
/*
 * We tell the mailbox that the data is stored within a VMTreeAppRef.
 */
registerArgs.MRA_bodyStorage.MS_id = GMSID_VM_TREE;
registerArgs.MRA_bodyStorage.MS_manuf = MANUFACTURER_ID_GEOWORKS;
/*
 * We tell the mailbox that this is an SMS message. 
 */
registerArgs.MRA_bodyFormat.MDF_id = GMDFID_SHORT_MESSAGE;
registerArgs.MRA_bodyFormat.MDF_manuf = MANUFACTURER_ID_GEOWORKS;
/*
 * We tell the mailbox where the body text is stored.
 */
MRA_bodyRef = &vmRef;
registerArgs.MRA_bodyRefLen = sizeof(VMTreeAppRef);
/*
 * We tell the mailbox to use the SM transport driver when sending the message. 
 */
registerArgs.MRA_transport.MT_id = GMTID_SM;
registerArgs.MRA_transport.MT_manuf = MANUFACTURER_ID_GEOWORKS;
registerArgs.MRA_transOption = 0;
/*
 * Normally, we would retrieve the phone number from an outside source (such as 
 * the contact database). Here, we just set it to a specific value.
 */
number = "13125551212";
/*
 * We need to stuff each address (in this case, a phone number) into a
 * MailboxTransAddr structure.
 */
addressList.MTA_transAddr = &number;
addressList.MTA_transAddrLen = strlen(number);
addressList.MTA_userTransAddr = &number;
registerArgs.MRA_transAddrs = &addressList;
registerArgs.MRA_numTransAddr = 1;
/*
 * We pass the previously set up SMSendingOptions in the DBGroupItem to the 
 * transport driver.
 */
registerArgs.MRA_transData = smsParams;
registerArgs.MRA_flags = MMF_DELETE_BODY_AFTER_TRANSMISSION;
registerArgs.MRA_summary = "Chess Move";
strcpy(registerArgs.MRA_destApp.GT_chars, "SKAA");
registerArgs.MRA_destApp.GT_manufID = MANUFACTURER_ID_NOKIA;
registerArgs.MRA_startBound = MAILBOX_NOW;
registerArgs.MRA_endBound = MAILBOX_ETERNITY;

Registering the Message

MailboxRegisterMessage(), MailboxDoneWithVMFile()

Finally, you have everything set up. To send the message, all you need to do is register the message with the mailbox library.

MailboxRegisterMessage() takes an address (of type MailboxMessage ) as a second parameter. The routine will write a token value into this address so that future mailbox routines can refer to this message by its token.

Do not confuse this token with the actual message (which is sent through the MailboxRegisterMessageArgs ).

After you have successfully registered the message, call MailboxDoneWithVMFile() to tell the mailbox to get rid of the temporary VM file it had created.

Code Display 3-5 Using MailboxRegisterMessage

MailboxRegisterMessageArgs				registerArgs;
MailboxMessage				msgToken;
MailboxError				mailboxError;
VMFileHandle				vmFile;
mailboxError = MailboxRegisterMessage(&registerArgs, &msgToken);
if (mailboxError == ME_SUCCESS) {
    /* If we want to do anything special upon success, here it would reside ... */
};
MailboxDoneWithVMFile(vmFile);

There is a possibility that MailboxRegisterMessage() may return an error (of type MailboxError ) if something is not working correctly (or if the message was not set up correctly). The routine returns ME_SUCCESS if the registration was successful.

The following are valid mailbox error codes:

ME_SUCCESS = 0
ME_NOT_ENOUGH_MEMORY
ME_CANNOT_LOAD_DATA_DRIVER
ME_CANNOT_LOAD_TRANSPORT_DRIVER
ME_UNABLE_TO_CREATE_TRANSMIT_THREAD
ME_CANNOT_ENQUEUE_MESSAGE
ME_USER_CANCELED
ME_LOST_CONNECTION
ME_CANNOT_CREATE_MESSAGE_FILE
ME_CANNOT_RESIZE_MBOX_REF
ME_CANNOT_SAVE_MESSAGE_FILE
ME_CANCELLED_BY_RECEIVER
ME_MESSAGE_BLOCKS_ARE_MISATCHED
ME_DATA_DRIVER_CANNOT_STORE_MESSAGE_BODY
ME_INSUFFICIENT_DISK_SPACE
ME_CANNOT_CONNECT

The following are unrecoverable MailboxError error codes. (The high bit is set if this is the case.)

#define ME_UNRECOVERABLE 0x8000
ME_ADDRESS_INVALID
ME_DATA_DRIVER_CANNOT_ACCESS_MESSAGE_BODY
ME_UNSUPPORTED_BODY_FORMAT
ME_MESSAGE_BODY_INVALID
ME_INVALID_MESSAGE
ME_REPLY_ADDRESS_NOT_AVAILABLE
ME_DESTINATION_APPLICATION_UNKNOWN
ME_UNKNOWN_DISK_ERROR
ME_APP_REF_BUF_TOO_SMALL
ME_CANNOT_OPEN_MESSAGE_FILE
ME_CANNOT_READ_MESSAGE_FILE
ME_CAN_NEVER_CONNECT

Up: GEOS SDK TechDocs | Up | Prev: 3.1 Sending and the Mailbox Library | Next: 4 Receiving an SMS Message