The Postal Manager exposes a set of APIs implemented by all plug-ins. For advanced, specific features, each service also exposes its own set of APIs. This section describes the generic API set.
All services implement certain generic concepts and procedures. Individual services also implement their own procedures, which are described in the following chapters:
Generic Usage Concepts for All Services
There are certain usage concepts that apply to each service - IMPS, Email, SMS, and MMS.
The Properties Concept
Most of the objects used by the Postal Manager are defined as a set of properties. The benefit is that each object is very flexible and can be composed of generic and specific properties.
On each object, you can set (add) a property and associate a value, and get a property value. Objects that are a set of properties are shown below:
Figure 2.1 Example of an Account object
Object Hierarchy
A service can be mono- or multi-session.
Most of the features of a service rely on a session. A session is a temporary object created by the Service object, which requires an Account.
If the creation of the session succeeds (some security could be introduced in this process), the session can be connected.
Using the session, you can send and receive envelopes, enumerate folders, and store envelopes.
Types of Properties
A property has one of the following types:
- A <STRING> (Size is not required, but must be null terminated.)
- A <BINARY> (You must pass
dataSizeas an argument.) - An <INTEGER> (Size is not required.)
- A <DATE> (Size is not required.)
- A <BOOLEAN> (Size is not required.)
For each object, one API is defined to set a property, and one is defined to get the value of a property. The value of the property (iValue) must be cast into alp_postal_property_value_t. iDataSize is 0, except for binaryData.
Set a Property on an Object
- iObject
- The object to which the property is added.
- iPropertyId
- The ID of the property. Each object defines its own set of properties. A property can be generic or specific to a service.
- iValue
- The value of the property. The value must be cast into
alp_postal_property_value_t. - iDataSize
- dataSize of data if
iValueis binary; otherwise 0.
alp_postal_xxx_set_property (AlpPostalObject* iObject, alp_postal_property_id_t iPropertyId, alp_postal_property_value_t iValue, uint16_t iDataSize);
Get a Property
When you retrieve a property, you call a helper to retrieve the value of the property. It is your responsibility to call the appropriate API according to the property's value type. (Getting a string from property that is defined to store an integer may be very risky.)
- When you get a property, you are only getting a pointer; you must not free that property.
- When you get a string or binary value, you are only getting a pointer; you must not free the value.
- A property can only be retrieved for an object that is in memory. If the object is stored and you have only the object ID, you must retrieve the object from the database.
Properties include the following:
- iObject
- The object to get the property from
- iPropertyId
- The ID of the property
- oProperty
- A pointer to the property that handles the value.
The following are examples of property functions:
alp_postal_xxx_get_property (AlpPostalObject* iObject,alp_postal_property_id_t iPropertyId,AlpPostalProperty** oProperty); // Next, retrieve the value using one of the following: alp_postal_property_get_int32 (AlpPostalProperty* iProperty, int32_t* oIntValue); alp_postal_property_get_string (AlpPostalProperty* iProperty, char** oStringValue); //(Other helpers are defined in postal_property.h)
Enumerators
Enumerators enable the retrieval of a large number of objects without overloading the memory. You can get an enumerator and invoke the next API on it while it contains items. After using the enumerator, you must release it.
NOTE: When you get an object from an enumerator (returned by the next API), it is duplicated. Consequently, you must free it.
Initializing Messaging Services
The postal event model relies on the gMainLoop to process events such as notifications or asynchronous responses.
To become a postal client and use the postal services, initialize communication with the Postal Manager by calling this function:
// Initialize contextP with g_main_context_default(); alp_postal_glib_init(GmainContext* contextP);
Once this is done, the client has to run gMainLoop.
NOTE: If your application doesn't rely on GTK, you can create and run your own gMainLoop by using Glib APIs.
If the client wants to use specific APIs or receive a specific notification, it must also call the related service's initialize function (alp_postal_xxx_service_initialize).
To finalize the notification, call the finalize function (alp_postal_xxx_service_finalize). For example, an IMPS the client must call alp_postal_imps_service_initialize and alp_postal_imps_service_finalize.
Account Examples
This section provides examples of how to create/save a new account and how to enumerate accounts that belong to the SMS service.
The first example, Listing 2.1, illustrates the life cycle of an object you are creating/saving:
- Declare the object
- Initialize the object
- Set properties on the object
- Create or Save the object (i.e., save the properties into the database)
- Free the object
Listing 2.1 Creating (Saving) a New Account
// Object ID of the account returned when saved alp_postal_account_id_t accountObjectId; // Declare the account AlpPostalAccount sms _account; // Initialize the account Alp_postal_account_init(&sms _account); // Set the properties // A generic property is used first alp_postal_account_set_property (&sms _account, ALP_POSTAL_PROPERTY_ACCOUNT_NAME, (alp_postal_property_value_t)"My_Account", 0); // A specific property relative to the SMS service (set the SMS center) // is then used alp_postal_account_set_property (&sms _account, ALP_POSTAL_SMS_PROPERTY_ACCOUNT_SMS_CENTER, (alp_postal_property_value_t)" 336534985", 0); // Save the account: ask the Postal Service to create (store) the account alp_postal_service_create_account (ALP_POSTAL_SERVICE_ID_SMS, &sms_account, & accountObjectId); // Free the object from memory; it is now in permanent storage
The second example, "Enumerating Accounts Belonging to SMS Service", illustrates the life cycle of an object you are enumerating:
- Declare the enumerator
- Initialize the enumerator
- Retrieve the enumerator
- Perform the enumeration
- Free the object retrieved
- Release the enumerator
Listing 2.2 Enumerating Accounts Belonging to SMS Service
AlpPostalAccount sms_account; // Declare the enumerator AlpPostalEnumerator enumerator; AlpPostalProperty* propertyP; alp_status_t err=POSTAL_STATUS_OK; // Initialize the enumerator alp_postal_service_account_init_enumerator (&enumerator); // Retrieve the enumerator alp_postal_service_get_account_enumerator (ALP_POSTAL_SERVICE_ID_SMS, &enumerator); // Perform the enumeration while (err==POSTAL_STATUS_OK) { alp_postal_account_init (&account); err=alp_postal_service_get_next_account (&enumerator, &account); // Free the object retrieved alp_postal_account_free (&account); } // Release the enumerator alp_postal_service_account_release_enumerator(&enumerator);
Connection Example
This section provides an example of how to create and connect an SMS session. In this example, you can assume the SMS account exists and has a known account_id.
Listing 2.3 Creating and Connecting an SMS Session
// Create a session using the existing account_id alp_postal_session_id_t sessionId; alp_postal_service_create_session (ALP_POSTAL_SERVICE_ID_SMS, account_id, &sessionId); // Connect the session alp_postal_session_connect (ALP_POSTAL_SERVICE_ID_SMS, sessionId, NULL, NULL, NULL);
The operation, alp_postal_session_connect(), has different meanings according to the service used. For SMS and MMS, the operation may switch the phone module ON. For IMPS, the operation attempts to log into the IMPS server.
Note that the second and third parameters of alp_postal_session_connect() can be used to force the use of a password for sending and receiving, but this feature is not used in the example.
The fourth parameter is the callback that will be called when the asynchronous session_connect call ends. The fourth parameter is set to NULL in the example.
The Envelope Object
An envelope contains the properties relative to a message that the service will need to send it. Like other property-based objects, an envelope can be composed of generic and specific properties.
An envelope is an aggregation of envelope properties, "To" recipients, "Cc" recipients, "Bcc" recipients, a "From" list, a "ReplyTo" list, and a bodypart list. After declaring and initializing an envelope object, you use envelope helper API to build the envelope.
The Bodypart Object
Text in a Single Bodypart
In the simplest case, we just want to set text to the envelope. If the text is small (less than ALP_POSTAL_BODYPART_SMALL_TEXT_MAX_SIZE), we can use the ALP_POSTAL_PROPERTY_BODYPART_SMALL_CONTENT property.
In this case we just have to declare a bodypart, set the text property, and add it to the envelope.
Listing 2.4 Attaching Text to an Envelope
AlpPostalEnvelope env; alp_postal_bodypart_part_no_t partNo; alp_postal_envelope_init(&env); AlpPostalBodypart bp; alp_postal_bodypart_init(&bp); // Set properties for bodypart alp_postal_bodypart_set_property(&bp, ALP_POSTAL_PROPERTY_BODYPART_SMALL_CONTENT, (alp_postal_property_value_t)"hello world", 0); // Add the bodypart to the envelope. // Since this is a simple bodypart, the parent number is // NO_PARENT. alp_postal_envelope_append_bodypart(&env, &bp, ALP_POSTAL_BODYPART_NO_PARENT, &partNo);
Multipart Bodyparts
The bodyparts have been designed to handle multipart bodypart structure. A complex bodypart is a tree; each part has a part number (first is 1). Example of multipart bodyparts:
Listing 2.5 Attaching Bodypart Structure to an Envelope
AlpPostalEnvelope env; alp_postal_bodypart_part_no_t partNo, partNo2; alp_postal_envelope_init(&env); AlpPostalBodypart bp1, bp3, bp4, bp5; AlpPostalBodypart multipart; alp_postal_bodypart_init(&bp1); alp_postal_bodypart_init(&bp3); alp_postal_bodypart_init(&bp4); alp_postal_bodypart_init(&bp5); alp_postal_bodypart_init(&multipart); // Set properties for bodypart 1 alp_postal_bodypart_set_property(&bp1, ALP_POSTAL_PROPERTY_BODYPART_NAME, (alp_postal_property_value_t)"bodypart 1", 0); // Set properties for bodypart 2, which is a multipart alp_postal_bodypart_set_property(&multipart, ALP_POSTAL_PROPERTY_BODYPART_CONTENT_DESCRIPTION, (alp_postal_property_value_t)"MULTIPART", 0); alp_postal_bodypart_set_property(&multipart, ALP_POSTAL_PROPERTY_BODYPART_NAME, (alp_postal_property_value_t)"bodypart 2", 0); // Set properties for bodypart 3 alp_postal_bodypart_set_property(&bp3, ALP_POSTAL_PROPERTY_BODYPART_NAME, (alp_postal_property_value_t)"bodypart 2-1", 0); // Set properties for bodypart 4 alp_postal_bodypart_set_property(&bp4, ALP_POSTAL_PROPERTY_BODYPART_NAME, (alp_postal_property_value_t)"bodypart 2-2", 0); // Set properties for bodypart 5 alp_postal_bodypart_set_property(&bp5, ALP_POSTAL_PROPERTY_BODYPART_NAME, (alp_postal_property_value_t)"bodypart 3", 0); // Append the bodyparts. Structure of the bodyparts tree is // defined by the order in which "append" APIs are called. alp_postal_envelope_append_bodypart(&env, &bp1, ALP_POSTAL_BODYPART_NO_PARENT, &partNo); alp_postal_envelope_append_bodypart(&env, &multipart, Â Â Â Â ALP_POSTAL_BODYPART_NO_PARENT, &partNo); alp_postal_envelope_append_bodypart(&env, &bp3, partNo, Â Â Â Â &partNo2); alp_postal_envelope_append_bodypart(&env, &bp4, partNo, Â Â Â Â &partNo2); alp_postal_envelope_append_bodypart(&env, &bp5, Â Â Â Â ALP_POSTAL_BODYPART_NO_PARENT, &partNo);
The following example shows how to build a simple envelope. In this example, the recipient is "+33689541258" and the message text is "Hello World".
Listing 2.6 Building a Simple Envelope
void build_sample_envelope(AlpPostalEnvelope* env) { // Declare the recipient AlpPostalAddress recipient; // Declare the bodypart AlpPostalBodypart bp; alp_postal_bodypart_part_no_t partNo; // Initialize the envelope alp_postal_envelope_init(env); // Initialize the recipient address alp_postal_address_init(&recipient); // Initialize the bodypart alp_postal_bodypart_init(&bp); // Set properties for recipient alp_postal_address_set_property(&recipient, ALP_POSTAL_PROPERTY_ADDRESS_STRING, (alp_postal_property_value_t)"+33689541258", 0); // Add the recipient as "TO" on the envelope alp_postal_envelope_add_address(env, &recipient, ALP_POSTAL_ADDRESS_CLASS_RECIPIENT_TO); // Set properties for bodypart alp_postal_bodypart_set_property(&bp, ALP_POSTAL_PROPERTY_BODYPART_SMALL_CONTENT, (alp_postal_property_value_t)"Hello World", 0); // Add the bodypart to the envelope // Since this is a simple bodypart, the parent number is NO_PARENT. alp_postal_envelope_append_bodypart(env, &bp, ALP_POSTAL_BODYPART_NO_PARENT, &partNo); }
Asynchronous Functions
When a client calls the asynchronous API, it has to give a function callback pointer, as shown in this example.
Listing 2.7 Asynchronous Request Callback
// Asynchronous request callback void send_envelope_async_reply_CB(alp_postal_service_id_t iServiceId, alp_postal_session_id_t iSessiontId, alp_postal_envelope_id_t iSentEnvelopeId, AlpPostalProgressInfo* iProgressInfoP, alp_status_t iRequestResult) { printf("Send envelope CB !!!!!"); printf("iServiceId = %d", iServiceId); printf("iSessiontId = %d", iSessiontId); printf("iSentEnvelopeId = %d", iSentEnvelopeId); printf("iRequestResult = %d", iRequestResult); } // Call the asynchronous request err = alp_postal_session_send_envelope(serviceId, session_id, &envelope, send_envelope_async_reply_CB);
Notifications
Postal Services implement two notification mechanisms to allow client applications to be notified when an unsolicited events occurs. For example, a notification occurs when a new incoming envelope arrives. The first notification method is "internal" notification, a mechanism that can only be used when with clients of Postal Services. The second method is the Notification Manager mechanism, which relies on the ACCESS Linux Platform Notify Manager. This mechanism allows an application to be notified even when the application is not active.
In terms of performance, the "internal" notification mechanism is more efficient.
Internal Notifications
Each service defines some notifications. The client must associate a callback function with the notifications it wants to receive.
For example, to receive a NEW_ENVELOPE notification, the client must call the following function:
alp_postal_notification_add_observer (ALP_POSTAL_NOTIFICATION_ENVELOPE_NEW, void* callbackFuncP);
When the client ends the process, it must call:
alp_postal_notification_remove_observer(ALP_POSTAL_NOTIFICATION_xxx);
Listing 2.8 Handling Internal Account Status Change Notification
// Account status change notification callback static void MyAccountStatusChangeNotificationCB (alp_postal_service_id_t iServiceId, alp_postal_account_id_t iAccountId, alp_postal_account_status_t iAccountStatus) { printf("ALP_POSTAL_NOTICATION_ACCOUNT_STATUS_CHANGE (ServiceId = %lu, AccountId = %lu, AccountStatus = %lu)", iServiceId, iAccountId, iAccountStatus); } // Register against the internal Account status change notification err = alp_postal_notification_add_observer (ALP_POSTAL_NOTIFICATION_ACCOUNT_STATUS_CHANGE, MyAccountStatusChangeNotificationCB);
Notification Manager Notifications
The postal Notification Manager notifications are defined in the file alp/postal_notify_mgr.h. The following sample code illustrates how to handle an account status change notification.
Listing 2.9 Handling an Account Status Change Notification
// Notification callback static void MyAccountStatusChangeNotificationCB (alp_postal_service_id_t iServiceId, alp_postal_account_id_t iAccountId, alp_postal_account_status_t iAccountStatus) { printf("ALP_NOTIFY_EVENT_POSTAL_ACCOUNT_STATUS_CHANGE (ServiceId = %lu, AccountId = %lu, AccountStatus = %lu)", oServiceId, oAccountId, oAccountStatus); } // Relaunch handler void my_relaunch_handler(int argc, char* argv[], gpointer cbData) { AlpNotifyLaunch notifyLaunchParamDetails; int detailsSize; // Launched for a notification if (strcmp(argv[1], ALP_APP_NOTIFY) == 0) { detailsSize = alp_notify_details(argv[2], ¬ifyLaunchParamDetails); // Check the notification if (strcmp(argv[3], ALP_NOTIFY_LAUNCH_TYPE ALP_NOTIFY_EVENT_POSTAL_ACCOUNT_STATUS_CHANGE) == 0) { AlpNotifyEventAccountStatusChange* accountStatusChangeEventP; accountStatusChangeEventP = (AlpNotifyEventAccountStatusChange*)&(notifyLaunchParamDetail s.details); // Invoke the callback MyAccountStatusChangeNotificationCB(accountStatusChangeEventP ->serviceId, accountStatusChangeEventP->accountId, accountStatusChangeEventP->accountStatus); } alp_notify_done(argv[0], 0 ,0); } int alp_main(int argc, char *argv[]) { ... // Set the relaunch handler err = alp_app_set_relaunch_handler(my_relaunch_handler, NULL); err = alp_notify_register_launch(argv[0], ALP_NOTIFY_EVENT_POSTAL_ACCOUNT_STATUS_CHANGE, ALP_NOTIFY_PRIORITY_NORMAL, 0); ... }










