Client MTM

Client MTMs provide a client-side API to functionality provided by the Server MTM, mainly for message manipulation and transport. It acts as the interface between the internal representation of a message’s data and the User Interface (UI) MTM. This architecture provides certain advantages; for example, a UI MTM can offer a user interface to enter message addresses without knowing the address storage format. Message client applications that do not require a user interface can use these client-side functions directly for automated message handling.

The Message Server entry with which Client MTMs currently work is referred to as the context. UI MTMs always operate on the contexts set through Client MTMs.

Client MTMs offer some or all of the following capabilities:

  • Address list manipulation

  • Creation of forward and response messages

  • Message subject access

  • Message validation

  • Internalising and externalising MTM-specific data to the Message Store

Client MTM base class

The base class for Client MTMs is CBaseMtm which provides a high-level interface for accessing and manipulating a Message Server entry

The following are some significant functions in CBaseMtm. Some functions are implemented by the base class, and others must be implemented in a derived class:

  • Context functions

    The CBaseMtm::SetCurrentEntryL (CMsvEntry *) and CBaseMtm::SwitchCurrentEntryL (TMsvId) functions change the Message Server entry (the context) on which later actions are performed. After creating a new Client MTM object, a message client application must set an initial context before using other functions.

  • Store and restore entry data functions

    The changes that a message client application makes to a message context through Client MTM functions, such as altering the body text obtained through CBaseMtm::Body(), are, for efficiency, cached in memory by a Client MTM. The message store (SaveMessageL())and restore (LoadMessageL()), functions are concerned with transferring data between that cache and committed storage.

    Note: Unlike message contexts, message client applications are not expected to manipulate directly contexts for message services. Instead, the corresponding User Interface MTM provides a dialog to allow the user to alter service settings, and calls Client MTM functions to handle their retrieval and storage.

  • Store and restore body text functions

    The base class maintains a private CRichText object cache to store the body text for the current context. This can be accessed for reading and writing by message client applications through CBaseMtm::Body(). The CBaseMtm::StoreBodyL() (CMsvStore &) and CBaseMtm::RestoreBodyL() (CMsvStore &) functions encapsulate the retrieval and storage of this CRichText object to a CMsvStore.

  • Address list functions

    The format and storage of message addresses is MTM-specific. The CBaseMtm::AddresseeList() const, CBaseMtm::AddAddresseeL (const TDesC &), and CBaseMtm::RemoveAddressee (TInt) allow clients to access address information in a generic way without having MTM-specific knowledge.

  • MTM-specific client functions

    MTM components can offer protocol-specific functionality not provided by base class interface functions. MTM components define IDs that correspond to each protocol-specific operation offered, and implement the CBaseMtm::InvokeSyncFunctionL() and CBaseMtm::InvokeAsyncFunctionL() functions to allow clients to access these operations by passing in the appropriate ID. These functions are provided to allow the MTM component to offer both synchronous and asynchronous functionality. Message client applications can dynamically add user-interface features for these operations using CBaseMtmUiData::MtmSpecificFunctions().

  • Query function

    The Client MTM QueryCapability() function is used to inquire whether an MTM supports a given feature. The first argument is a UID defined to correspond to a particular capability. The function returns a system error code and the response (whether the MTM supports the queried capability) is passed back through the second (by-reference)aResponse parameter. This value can be an integer or a bit set, depending on the capability.

    The UIDs for the different types of query are as follows:

    UID Capability

    KUidMsvMtmQueryMaxBodySize

    Maximum message body size

    KUidMtmQueryMaxTotalMsgSize

    Maximum total size of message.

    KUidMtmQuerySupportedBody

    Character widths supported by message type. The returned value is the sum of the appropriate values. The return type is either of KMtm7BitBody, KMtm8BitBody, KMtm16BitBody, and KMtmBinaryBody)

    KUidMtmQuerySupportAttachments

    To check if the attachments are supported

    KUidMtmQuerySupportSubject

    Does the MTM message type have a subject field

    KUidMtmQuerySupportsFolder

    Does the MTM support folders

    KUidMtmQueryOffLineAllowed

    Off-line operation allowed

    KUidMtmQueryCanSendMsg

    Can send the message

    KUidMtmQueryCanReceiveMsg

    Can receive the message

    KUidMtmQueryMaxRecipientCount

    Maximum number of recipients (-1 indicates unlimited numbers).

    KUidMtmQuerySendAsRequiresRenderedImage

    When using the MTM in Send As, does a rendered image have to be prepared.

    KUidMtmQuerySendAsRenderingUid

    Printer driver UID for rendering the fax image.

    KUidMsvMtmQueryEditorUid

    UID of default message editor.

    KUidMsvQuerySupportsBioMsg

    Does the MTM support BIO messages

    KUidMsvQuerySupportsScheduling

    Does the MTM support scheduled sending.

    KUidMtmQuerySupportsRecipientType

    Does the MTM support the use of recipient type.

    KUidMtmQuerySendAsMessageSendSupport

    Support for Sending messages using SendAs.

    If this is supported, then the MTM supports sending messages created through the SendAs API.

    TInt CTextMtmClient::QueryCapability(TUid aCapability, TInt& aResponse)
        {
        TInt error = KErrNone;
        switch (aCapability.iUid)
            {
            case KUidMtmQueryMaxBodySizeValue:
            case KUidMtmQueryMaxTotalMsgSizeValue:
                aResponse = KMaxTextMessageSize;
                break;
            case KUidMtmQuerySupportedBodyValue:
                aResponse = KMtm8BitBody + KMtm7BitBody;
                break;
            case KUidMtmQueryOffLineAllowedValue:
            case KUidMtmQueryCanReceiveMsgValue:
                aResponse = ETrue;
                break;
            case KUidMtmQuerySupportAttachmentsValue:
            default:
                return KErrNotSupported;
            }
        return error;
        }
    

The CBaseMtm class provides the virtual functions for overriding in derived classes.

Client-side operations

Many Messaging tasks can take a long time to complete. For example, sending all items from the Outbox or downloading a large number of items from a POP3 server. In these situations, the use of active objects to wait for the completion of the task is inadequate, since applications also require to obtain progress information while the task is running (for example, to update a progress dialog). To solve this problem, most asynchronous functions in Messaging create a CMsvOperation object and return a pointer to it, as well as taking a TRequestStatus& parameter. The CMsvOperation class is derived from CActive and is used to obtain progress information about a long-running task.

The base class for all client-side Messaging operations is CMsvOperation. The asynchronous methods in the CBaseMtm interface all return a pointer to a CMsvOperation object. When you implement an MTM, you will have to provide your own custom operations, derived from CMsvOperation, and return those polymorphically from your implementations of the asynchronous APIs.

Example

The forward operation of the Text MTM is explained in the following example.

The forward operation is an active object state machine that runs a series of asynchronous tasks one after another and waits for each to complete before moving on to the next. After the operation has been completed, an active object is notified through its TRequestStatus passed into the operation at construction.

The forward operation is constructed using standard two-phase construction. The NewL() function takes two arguments of a TMsvId (source message and destination folder) type, a CMsvSession reference, and the observer’s TRequestStatus, which is notified on completion. The active object is constructed and added to the scheduler, and then ConstructL() is called:

void CTxtMtmForwardOperation::ConstructL()
    {
    iObserverRequestStatus = KRequestPending;
    SetActive();
    TRequestStatus* stat = &iStatus;
    User::RequestComplete(stat, KErrNone);
    }

This has the effect of marking this stage of the operation as complete and means that RunL() is called at the next opportunity. This simplifies the state machine because it means that all states and tasks are started in RunL() and not in ConstructL().

The RunL() function starts each task and receives a notification when it completes. The current task being executed is stored in iProgress and iState, as are the errors and the ID of the new forwarded message, once complete. The default state for iState on construction is EInit.

void CTxtMtmForwardOperation::RunL()
    {
    User::LeaveIfError(iStatus.Int());
    switch(iProgress.iState)
        {        
        case TTxtMtmForwardOpProgress::EInit:
            iProgress.iState =
               TTxtMtmForwardOpProgress::ECreateMessage;
            CreateMessageL();
            break;

        case TTxtMtmForwardOpProgress::ECreateMessage:
            iProgress.iState =
               TTxtMtmForwardOpProgress::EFormatBodyText;
            FormatBodyTextL();
            break;

        case TTxtMtmForwardOpProgress::EFormatBodyText:
            iProgress.iState =
               TTxtMtmForwardOpProgress::EFinished;
        
        default:
            break;
        }
    if(!IsActive())
        {
        TRequestStatus* stat = &iObserverRequestStatus;
        User::RequestComplete(stat, KErrNone);
        }
    }

If the state is EInit, the RunL() changes state to ECreateMessage and then calls CreateMessageL() to start the process of creating a message asynchronously.

void CTxtMtmForwardOperation::CreateMessageL()
    {

The function constructs a CMsvEntry for the source entry and then retrieves its index entry.

delete iMsvEntry;
    iMsvEntry = NULL;
    iMsvEntry = iMsvSession.GetEntryL(iSourceMessage);
        
    // Get the entry to be forwarded    
    TMsvEntry forwardEntry = iMsvEntry->Entry();

The context of iMsvEntry is changed to the destination folder and an entry is created.

// Set the context to the destination folder
    iMsvEntry->SetEntryL(iDestinationFolder);

The index entry’s date is updated to reflect the current date.

// Create the forwarded index entry
    forwardEntry.iDate.HomeTime();
    iMsvEntry->CreateL(forwardEntry);

The iFinalMsgId member of the progress is updated to reflect the newly created entry.

iProgress.iFinalMsgId = forwardEntry.Id();

    // schedules the active object to complete so that this 
    // class's RunL method
    // will be called by the active scheduler
    SetActive();

The operation’s TRequestStatus is then flagged to cause RunL().

TRequestStatus* stat = &iStatus;
    User::RequestComplete(stat, KErrNone);
     }

When this happens, iProgress.iState is set to ECreateMessage, so the following code is executed:

case TTxtMtmForwardOpProgress::ECreateMessage:
    iProgress.iState =        TTxtMtmForwardOpProgress::EFormatBodyText;
    FormatBodyTextL();
    break;

The state is set to EFormatBodyText and the FormatBodyTextL() function is called.

void CTxtMtmForwardOperation::FormatBodyTextL()
   { 

The function constructs a CParaFormatLayer, a CCharFormatLayer, and a CRichText object. These objects are required to store the body text of the forwarded message.

CParaFormatLayer* paraLayer = CParaFormatLayer::NewL();
   CleanupStack::PushL(paraLayer);
   CCharFormatLayer* charLayer = CCharFormatLayer::NewL();
   CleanupStack::PushL(charLayer);
   CRichText* body = CRichText::NewL(paraLayer, charLayer);
   CleanupStack::PushL(body);

Then the context of iMsvEntry is changed to point at the source message and a read-only CMsvStore is opened for that entry.

// Get the message store from the source entry
   iMsvEntry->SetEntryL(iSourceMessage);
   CMsvStore* readStore = iMsvEntry->ReadStoreL();
   CleanupStack::PushL(readStore);

The context of the iMsvEntry object is changed to point at the newly created entry and a CMsvStore is opened for that entry.

// Get the message store from the newly created destination 
   // entry
   iMsvEntry->SetEntryL(iProgress.iFinalMsgId);
   CMsvStore* writeStore = iMsvEntry->EditStoreL();
   CleanupStack::PushL(writeStore);

   // Get the body text from the source entry and write it
   // to the destination entry, prepending the forward 
   // prefix
   readStore->RestoreBodyTextL(*body);
   body->InsertL(0, KTxtMtmFwdPrefix);

     // this method performs a commit for us
   writeStore->StoreBodyTextL(*body);
 
   CleanupStack::PopAndDestroy(5, paraLayer); 

   // schedules the active object to complete so that this 
   // class's RunL method
   // will be called by the active scheduler
   SetActive();
   TRequestStatus* stat = &iStatus;
   User::RequestComplete(stat, KErrNone);
   }

The iProgress.iState is set to EFinished, and because the object has not been activated the following conditional statement evaluates true:

if(!IsActive())
    {
    TRequestStatus* stat = &iObserverRequestStatus;
    User::RequestComplete(stat, KErrNone);
    }

The observer active object’s TRequestStatus is flagged to let it know that the operation is complete.

To return progress from the operation, implement the ProgressL() and FinalProgress() functions. ProgressL() must be used to return progress information on the operation during processing, and FinalProgress() is guaranteed to return the status of a completed operation. ProgressL() must be implemented to leave with KErrNotReady if the operation is not in progress, and FinalProgress() must panic if the operation is not complete.

const TDesC8& CTxtMtmForwardOperation::ProgressL()
            {
            if (!IsActive())
                        {
                        User::Leave(KErrNotReady);
                        }
   iProgressBuf() = iProgress;
   return iProgressBuf;
   }

const TDesC8& CTxtMtmForwardOperation::FinalProgress()
            {
    __ASSERT_ALWAYS(!IsActive(), User::Panic(KTxtCPanic,
                            ETxtcOperationIsActive));
            iProgressBuf() = iProgress;
   return iProgressBuf;
   }

The RunError() function must also be implemented to get any leaves in RunL().

TInt CTxtMtmForwardOperation::RunError(TInt aError)
    {
    iProgress.iError = aError;    
    TRequestStatus* stat = &iObserverRequestStatus;
    User::RequestComplete(stat, aError);
    return KErrNone;
    }

Related concepts

Related tasks