/*======================================================
FILE:  AEESMS.c

SERVICES:  AEESMS member functions

GENERAL DESCRIPTION:

   Implements ISMSMsg, ISMS, ISMSNotifier, ISMSStorage, ISMSBCSrvOpts, 
   ISMSBCConfig interfaces and related functionality

        Copyright © 1999-2004 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=====================================================*/
#include "AEEWebOptMgr.h"
#include "AEE_OEM.h"
#include "AEESMS.h"
#include "AEESMS_priv.h"
#include "AEEModTable.h"
#include "AEETAPI.h"
#include "OEMConfig.h" 
#include "OEMHeap.h"
#include "bridle_sms.h"
#include "AEE_OEMDispatch.h"

#define MAX_MSG_LENGTH           256

#define RELEASEIF(x) if (x) { IBASE_Release((IBase *)(x)); (x) = 0; }
#define AEEKCB_DELETEIF(pkcb) if (*pkcb) { AEEkCB_Delete(pkcb); (*pkcb) = 0; }

/*-------------------------------------------------------------------
            Type Declarations
-------------------------------------------------------------------*/

struct ISMSMsg {
   const AEEVTBL(ISMSMsg)        *pvt;
   uint32                        uRef;
   IShell                        *pIShell;                 
   WebOptMgr                     *pWom;          // Our collection of WebOpts
};

struct ISMS {
   const AEEVTBL(ISMS)           *pvt;
   uint32                        uRef;
   IShell                        *pIShell; 
   OEMSMSMgr                     *pOEMSms;
};

AEEINTERFACE(ISMSNotifier)
{
   INHERIT_INotifier(ISMSNotifier);
};

struct ISMSNotifier{
   const AEEVTBL(ISMSNotifier)   *pvt;
   uint32                        uRef;
   IShell                        *pIShell;
   uint32                        dwNotify;
   OEMSMSMgr                     *pOEMSms;
   hAEEkCB                       kcbOnMtSMS;
};

struct ISMSStorage {
   const AEEVTBL(ISMSStorage)    *pvt;
   uint32                        uRef;
   IShell                        *pIShell;
   AEESMSTag                     mt;            // Enum message tag
   AEESMSStorageType             st;            // Enum storage type
   uint32                        dwCount;       // Messages IDs in pdwIndex
   uint32                        *pdwIndex;     // Array of message IDs                      
   uint32                        dwOffset;      // Offset in Array of message IDs
};

struct ISMSBCSrvOpts {
   const AEEVTBL(ISMSBCSrvOpts)  *pvt;
   uint32                        uRef;
   IShell                        *pIShell;                 
   WebOptMgr                     *pWom;          // Our collection of WebOpts 
};

struct ISMSBCConfig {
   const AEEVTBL(ISMSBCConfig)   *pvt;
   uint32                        uRef;
   IShell                        *pIShell; 
}; 

typedef struct {
   hAEEkCB        kcb;
   AEECallback *  pcb;
   ACONTEXT *     pac;
} SMSAppCbData;

typedef struct {
   hAEEkCB        kcb;
   AEECallback *  pcb;
   ACONTEXT *     pac;
   uint32 *       pnRv;
   ISMSMsg **     ppISMSMsg;
   OEMMsgData     sMsgData;
   OEMMsgPayload  sMsgPayload;
} SMSAppReadMsgCbData;

typedef struct {
   hAEEkCB        kcb;
   AEECallback *  pcb;
   ACONTEXT *     pac;
   uint32 *       pnRv;
   ISMSMsg **     ppISMSMsg;
   uint32 *       pnIndex;
   OEMMsgData     sMsgData;
   OEMMsgPayload  sMsgPayload;
   ISMSStorage *  pISMSStorage;
} SMSAppEnumMsgCbData;

typedef struct {
   hAEEkCB         kcb;
   AEECallback *   pcb;
   ACONTEXT *      pac;
   uint32 *        pnCount;
   AEESMSBCSrvID **ppsid;
   uint32 *        pnErr;
} SMSAppGetSrvIDsCbData;

typedef struct {
   hAEEkCB         kcb;
   AEECallback *   pcb;
   ACONTEXT *      pac;
   ISMSBCSrvOpts **ppi;
   uint32 *        pnErr;
   OEMSMSBCSrvInfo sSMSBcSrvInfo;
} SMSAppGetSrvOptsCbData;

typedef struct {
   hAEEkCB         kcb;
   AEECallback *   pcb;
   ACONTEXT *      pac;
   uint32 *        pnCount;
   ISMSBCSrvOpts ***pppi;
   uint32 *        pnErr;
} SMSAppGetAllSrvOptsCbData;

// Mapping of SMS teleservice ID to message type
typedef struct
{
   int ts;
   int mt;
} MsgTypeMap;

static const MsgTypeMap   gsMsgTypeMap[] = 
                  {{SMS_TELESERVICE_CPT_95,              AEESMS_TYPE_PAGE },
                  {SMS_TELESERVICE_IS91_PAGE,            AEESMS_TYPE_PAGE },
                  {SMS_TELESERVICE_MWI,                  AEESMS_TYPE_VOICE_MAIL_NOTIFICATION },
                  {SMS_TELESERVICE_VMN_95,               AEESMS_TYPE_VOICE_MAIL_NOTIFICATION },
                  {SMS_TELESERVICE_IS91_VOICE_MAIL,      AEESMS_TYPE_VOICE_MAIL_NOTIFICATION },
                  {SMS_TELESERVICE_CMT_95,               AEESMS_TYPE_TEXT },
                  {SMS_TELESERVICE_IS91_SHORT_MESSAGE,   AEESMS_TYPE_TEXT },
                  {SMS_TELESERVICE_WAP,                  AEESMS_TYPE_WAP },
                  {SMS_TELESERVICE_WEMT,                 AEESMS_TYPE_EMS }};

// Mapping of SMS encoding to AEE encoding
typedef struct
{
   int se;
   int ae;
} AEESMSEncMap;

static const AEESMSEncMap   gsAEESMSEncMap[] =                   
                  {{AEESMS_ENC_OCTET,            AEE_ENC_OCTET},
                  {AEESMS_ENC_ASCII,             AEE_ENC_ISOLATIN1},
                  {AEESMS_ENC_IS91EP,            AEE_ENC_ISOLATIN1},
                  {AEESMS_ENC_LATIN,             AEE_ENC_ISOLATIN1},
                  {AEESMS_ENC_IA5,               AEE_ENC_ISOLATIN1},
                  {AEESMS_ENC_UNICODE,           AEE_ENC_UNICODE},
                  {AEESMS_ENC_SHIFT_JIS,         AEE_ENC_S_JIS},
                  {AEESMS_ENC_KOREAN,            AEE_ENC_KSC5601},  
                  {AEESMS_ENC_LATIN_HEBREW,      AEE_ENC_LATIN_HEBREW},
                  {AEESMS_ENC_GSM_7_BIT_DEFAULT, AEE_ENC_GSM_7_BIT_DEFAULT}};

// Mapping of UDH message options to AEE UDH IDs
typedef struct
{
   int mo;
   int id;
} AEEMsgOptUDHIDMap;

static const AEEMsgOptUDHIDMap   gsAEEMsgOptUDHIDMap[] =                   
                  {{MSGOPT_UDH_CONCAT,       AEESMS_UDH_CONCAT_8},
                  {MSGOPT_UDH_CONCAT,        AEESMS_UDH_CONCAT_16},
                  {MSGOPT_UDH_SPECIAL_SM,    AEESMS_UDH_SPECIAL_SM},
                  {MSGOPT_UDH_PORT,          AEESMS_UDH_PORT_8},
                  {MSGOPT_UDH_PORT,          AEESMS_UDH_PORT_16},
                  {MSGOPT_UDH_TEXT_FORMAT,   AEESMS_UDH_TEXT_FORMATING},
                  {MSGOPT_UDH_PREDEFINED,    AEESMS_UDH_PRE_DEF_SOUND},
                  {MSGOPT_UDH_PREDEFINED,    AEESMS_UDH_PRE_DEF_ANIM},
                  {MSGOPT_UDH_USERDEFINED,   AEESMS_UDH_USER_DEF_SOUND},
                  {MSGOPT_UDH_USERDEFINED,   AEESMS_UDH_LARGE_ANIM},
                  {MSGOPT_UDH_USERDEFINED,   AEESMS_UDH_SMALL_ANIM},
                  {MSGOPT_UDH_USERDEFINED,   AEESMS_UDH_LARGE_PICTURE},
                  {MSGOPT_UDH_USERDEFINED,   AEESMS_UDH_SMALL_PICTURE},
                  {MSGOPT_UDH_VAR_PICTURE,   AEESMS_UDH_VAR_PICTURE},
                  {MSGOPT_UDH_RFC822,        AEESMS_UDH_RFC822},  
                  {MSGOPT_UDH_OTHER,         AEESMS_UDH_SMSC_CONTROL},  
                  {MSGOPT_UDH_OTHER,         AEESMS_UDH_SOURCE},  
                  {MSGOPT_UDH_OTHER,         AEESMS_UDH_WCMP}};

/*-------------------------------------------------------------------
            Function Prototypes
-------------------------------------------------------------------*/


static uint32      CSMSMsg_AddRef(ISMSMsg *pme);
static uint32      CSMSMsg_Release(ISMSMsg *pme);
static int         CSMSMsg_QueryInterface(ISMSMsg *pme, AEECLSID id, void **ppo);
static int         CSMSMsg_AddOpt(ISMSMsg *pme, SMSMsgOpt *awozList);
static int         CSMSMsg_RemoveOpt(ISMSMsg *pme, int32 nOptId, int32 nIndex);
static int         CSMSMsg_GetOpt(ISMSMsg *pme, int32 nOptId, int32 nIndex, SMSMsgOpt *pwo) ;

static const ISMSMsgVtbl gvtISMSMsg = {
   CSMSMsg_AddRef,
   CSMSMsg_Release,
   CSMSMsg_QueryInterface,
   CSMSMsg_AddOpt,
   CSMSMsg_RemoveOpt,
   CSMSMsg_GetOpt
};

static uint32      CSMS_AddRef(ISMS *pme);
static uint32      CSMS_Release(ISMS *pme);
static int         CSMS_QueryInterface(ISMS *pme, AEECLSID id, void **ppo);
static void        CSMS_SendMsg(ISMS *pme, ISMSMsg * pISMSMsg, AEECallback * pcb, uint32 *pnRv);
static int         CSMS_ReceiveMsg(ISMS *pme, uint32 uMsgId, ISMSMsg **ppMsg);
static int         CSMS_SetClientStatus(ISMS *pme, AEESMSClientStatus cs, AEESMSType mt);
static int         CSMS_GetBytesAvailableForPayload(ISMS * po, ISMSMsg * pISMSMsg, int * pnBytes, int * pnPadding);
static int         CSMS_GetEncodingsAvailableForMOSMS(ISMS * po, uint32 * pBuffer, uint32 * pnSize);

static const ISMSVtbl gvtISMS = {
   CSMS_AddRef,
   CSMS_Release,
   CSMS_QueryInterface,
   CSMS_SendMsg,
   CSMS_ReceiveMsg,
   CSMS_SetClientStatus,
   CSMS_GetBytesAvailableForPayload,
   CSMS_GetEncodingsAvailableForMOSMS
};

static uint32      CSMSNotifier_AddRef(ISMSNotifier *pme);
static uint32      CSMSNotifier_Release(ISMSNotifier *pme);
static void        CSMSNotifier_SetMask(ISMSNotifier *pme, const uint32 *pdwMask);

static const ISMSNotifierVtbl gvtISMSNotifier = {
   CSMSNotifier_AddRef,
   CSMSNotifier_Release,
   CSMSNotifier_SetMask
};

// Used by ISMS for notifications
static ISMSNotifier * gpSMSNotifier = NULL;

static uint32              CSMSStorage_AddRef(ISMSStorage *pme);
static uint32              CSMSStorage_Release(ISMSStorage *pme);
static int                 CSMSStorage_QueryInterface(ISMSStorage *pme, AEECLSID id, void **ppo);
static void                CSMSStorage_GetStorageStatus(ISMSStorage * po, AEESMSStorageType st, AEESMSTag mt, AEECallback *pcb, AEESMSStorageStatus * pss, uint32 *pnRv);
static void                CSMSStorage_StoreMsg(ISMSStorage * po, AEESMSStorageType st, ISMSMsg * pi, AEECallback *pcb, uint32 * pnIndex, uint32 *pnRv);
static void                CSMSStorage_ReadMsg(ISMSStorage * pme, AEESMSStorageType st, uint32 nIndex, AEECallback *pcb, ISMSMsg ** ppi, uint32 * pnErr);
static void                CSMSStorage_Delete(ISMSStorage * pme, AEESMSStorageType st, AEESMSTag mt, uint32 nIndex, AEECallback *pcb, uint32 * pnErr);
static void                CSMSStorage_EnumMsgInit(ISMSStorage * pme, AEESMSStorageType st, AEESMSTag mt, AEECallback *pcb, uint32 * pnErr);
static void                CSMSStorage_EnumNextMsg(ISMSStorage * pme, AEESMSStorageType st, AEECallback *pcb, uint32 * pnIndex, ISMSMsg ** ppi, uint32 * pnErr);

static const ISMSStorageVtbl gvtISMSStorage = {
   CSMSStorage_AddRef,
   CSMSStorage_Release,
   CSMSStorage_QueryInterface,
   CSMSStorage_GetStorageStatus,
   CSMSStorage_StoreMsg,
   CSMSStorage_ReadMsg,
   CSMSStorage_Delete,
   CSMSStorage_EnumMsgInit,
   CSMSStorage_EnumNextMsg
};

static uint32      CSMSBCSrvOpts_AddRef(ISMSBCSrvOpts *pme);
static uint32      CSMSBCSrvOpts_Release(ISMSBCSrvOpts *pme);
static int         CSMSBCSrvOpts_QueryInterface(ISMSBCSrvOpts *pme, AEECLSID id, void **ppo);
static int         CSMSBCSrvOpts_AddOpt(ISMSBCSrvOpts *pme, SMSBCSrvOpts *awozList);
static int         CSMSBCSrvOpts_RemoveOpt(ISMSBCSrvOpts *pme, int32 nOptId, int32 nIndex);
static int         CSMSBCSrvOpts_GetOpt(ISMSBCSrvOpts *pme, int32 nOptId, int32 nIndex, SMSBCSrvOpts *pwo) ;

static const ISMSBCSrvOptsVtbl gvtISMSBCSrvOpts = {
   CSMSBCSrvOpts_AddRef,
   CSMSBCSrvOpts_Release,
   CSMSBCSrvOpts_QueryInterface,
   CSMSBCSrvOpts_AddOpt,
   CSMSBCSrvOpts_RemoveOpt,
   CSMSBCSrvOpts_GetOpt
};

static uint32      CSMSBCConfig_AddRef(ISMSBCConfig *pme);
static uint32      CSMSBCConfig_Release(ISMSBCConfig *pme);
static int         CSMSBCConfig_QueryInterface(ISMSBCConfig *pme, AEECLSID id, void **ppo);
static void        CSMSBCConfig_GetBCConfig(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, AEESMSBCConfig * pConfig, uint32 * pnErr);
static void        CSMSBCConfig_GetBCPref(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, AEESMSBCPref * pPref, uint32 * pnErr);
static void        CSMSBCConfig_SetBCPref(ISMSBCConfig * pme, AEESMSMode dwMode, AEESMSBCPref nPref, AEECallback *pcb, uint32 * pnErr);
static void        CSMSBCConfig_GetServiceIDs(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, uint32 * pnCount, AEESMSBCSrvID ** ppsid, uint32 * pnErr);
static void        CSMSBCConfig_GetServiceOpts(ISMSBCConfig * pme, AEESMSMode dwMode, AEESMSBCSrvID sSrvID, AEECallback *pcb, ISMSBCSrvOpts ** ppi, uint32 * pnErr);
static void        CSMSBCConfig_SetServiceOpts(ISMSBCConfig * pme, AEESMSMode dwMode, ISMSBCSrvOpts * pi, AEECallback *pcb, uint32 * pnErr);
static void        CSMSBCConfig_GetAllServiceOpts(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, uint32 * pnCount, ISMSBCSrvOpts *** pppi, uint32 * pnErr);
static void        CSMSBCConfig_DeleteService(ISMSBCConfig * pme, AEESMSMode dwMode, AEESMSBCSrvID sSrvID, AEECallback *pcb, uint32 * pnErr);

static const ISMSBCConfigVtbl gvtISMSBCConfig = {
   CSMSBCConfig_AddRef,
   CSMSBCConfig_Release,
   CSMSBCConfig_QueryInterface,
   CSMSBCConfig_GetBCConfig,
   CSMSBCConfig_GetBCPref,
   CSMSBCConfig_SetBCPref,
   CSMSBCConfig_GetServiceIDs,
   CSMSBCConfig_GetServiceOpts,
   CSMSBCConfig_SetServiceOpts,
   CSMSBCConfig_GetAllServiceOpts,
   CSMSBCConfig_DeleteService
};

/*======================================================================= 
Method Prototypes
=======================================================================*/
static int GetEncodedMsgPayload(char* pszBrewPrefix, byte * pMsgPayload, int nBytes, int nEncoding, AEESMSEncType nSMSEncoding, byte * pEncodedPayload, int * pnBytes, int * pnPadding);

/*======================================================================= 
Local Method Prototypes
=======================================================================*/

static void DecodePackedString(byte *pSrc, int i, char *pszDst);
static void DecodeNetworkOrder(byte *pbSrc, int i, byte *pszDst);
static void DecodeString(byte *pSrc, int i, char *pszDst);
static void EncodeNetworkOrder(AECHAR * pSrc, int i, uint16 * pszDst);
static void PackString(char * pText, int nText, byte * pBuffer);

static void InvokeAppCb(void * pData);
static void ReleaseAppCb(AEECallback * pcb);
static int LinkAppCb(AEECallback * pcb, SMSAppCbData ** ppAppCbData);
static int LinkAppReadMsgCb(AEECallback * pcb, SMSAppReadMsgCbData ** ppAppReadMsgCbData);
static int LinkAppEnumMsgCb(AEECallback * pcb, SMSAppEnumMsgCbData ** ppAppEnumMsgCbData);
static int LinkAppGetSrvIDsCb(AEECallback * pcb, SMSAppGetSrvIDsCbData ** ppAppGetSrvIDsCbData);
static int LinkAppGetSrvOptsCb(AEECallback * pcb, SMSAppGetSrvOptsCbData ** ppAppGetSrvOptsCbData);
static int LinkAppGetAllSrvOptsCb(AEECallback * pcb, SMSAppGetAllSrvOptsCbData ** ppAppGetAllSrvOptsCbData);
static int ISMSMsgToOEMMsg(ISMSMsg * pISMSMsg, OEMMsgData ** ppMsgData, OEMMsgPayload ** ppMsgPayload);
static int OEMMsgToISMSMsg(OEMMsgData * pMsgData, OEMMsgPayload * pMsgPayload, ISMSMsg ** ppISMSMsg);
static int Xlate_TStoMT(int ts);
static int Xlate_MTtoTS(int mt);
static int Xlate_SMSEncToAEEEnc(int se);
static int Xlate_AEEEncToSMSEnc(int ae);
static int Xlate_UDHIDToMsgOpt(int id);
static void ParseBrewMsg( char *pszText, AEECLSID *pCls, char ** ppszBrewHandler, byte **ppParsedText );
static boolean IsMsgPackable(byte * pData, int nBytes, int en, boolean b7bit);
static void PackUDHMsgOpts(byte * pBytes, int nBytes, ISMSMsg * pISMSMsg);
static void UnpackUDHMsgOpts(ISMSMsg *pISMSMsg, int * pnBytes, byte * pBytes);
static int ComputeUDHSizeOnSMSPayload(ISMSMsg * pISMSMsg);
static boolean UnpackSMSBCSrvOpts(ISMSBCSrvOpts *pISMSBCSrvOpts, OEMSMSBCSrvInfo * pSrvInfo);
static boolean PackSMSBCSrvOpts(OEMSMSBCSrvInfo * pSrvInfo, ISMSBCSrvOpts *pISMSBCSrvOpts);

/*======================================================================= 
Extern Method Prototypes
=======================================================================*/
extern void AEETAPI_NotifyOnMtSMS( uint32 dwTransactionId, int ts );

/*===========================================================================

Function: ISMSMsg_New

Description:
   ISMSMsg constructor
 
===========================================================================*/
int ISMSMsg_New(IShell *pIShell, AEECLSID uCls, void **ppif)
{
   // uCls is intentionally unused
   int nReturn = ENOMEMORY;
   SMSMsgOpt awoz[1];
   ISMSMsg *pme = NULL;

   FARF(SMS, ("==>  ISMSMsg_New"));

   // Make empty option list
   awoz[0].nId  = WEBOPT_END;

   // Get our memory including a WebOpt manager
   pme = MALLOCREC_EX(ISMSMsg, WebOptMgr_GetSize(awoz));

   *ppif = pme;

   if (pme) {
      // Our vtable
      pme->pvt = &gvtISMSMsg;

      pme->pWom = (WebOptMgr *)(pme+1);
      WebOptMgr_CtorZ(pme->pWom,awoz);

      // The shell 
      pme->uRef = 1;
      pme->pIShell = pIShell;
      ISHELL_AddRef(pIShell);

      nReturn = SUCCESS;
   }
   return nReturn;
}

/*===========================================================================

Function: CSMSMsg_AddRef

Description:
   Increases reference count
 
===========================================================================*/
static uint32 CSMSMsg_AddRef(ISMSMsg *pme)
{   
   FARF(SMS, ("==>  CSMSMsg_AddRef"));
   return ++pme->uRef;
}

/*===========================================================================

Function: CSMSMsg_Release

Description:
   Decrements reference count. Frees object when reference count reaches 0. 
 
===========================================================================*/
static uint32 CSMSMsg_Release(ISMSMsg *pme)
{   
   FARF(SMS, ("==>  CSMSMsg_Release"));

   if (--pme->uRef) {
      return pme->uRef;
   }

   WebOptMgr_Dtor(pme->pWom);
   RELEASEIF(pme->pIShell);
   FREEIF(pme);

   return 0;
}

/*===========================================================================

Function: CSMSMsg_QueryInterface

Description:
   Query Interface
 
===========================================================================*/
static int CSMSMsg_QueryInterface(ISMSMsg *pme, AEECLSID id, void **ppo)
{  
   FARF(SMS, ("==>  CSMSMsg_QueryInterface"));

   if ((id == AEECLSID_QUERYINTERFACE) || 
       (id == AEECLSID_SMSMSG)         ||
       (id == AEECLSID_WEBOPTS)) 
   {
      *ppo = pme;
      IQI_AddRef(*ppo);
      return SUCCESS;
   } 
   else 
   {
      return ECLASSNOTSUPPORT;
   }
}

/*===========================================================================

Function: CSMSMsg_AddOpt

Description:
   Uses base class (IWebOpts) implementation
 
===========================================================================*/
static int CSMSMsg_AddOpt(ISMSMsg *pme, SMSMsgOpt *awozList)
{  
   FARF(SMS, ("==>  CSMSMsg_AddOpt"));
   return WebOptMgr_AddOptV(pme->pWom, awozList);
}


/*===========================================================================

Function: CSMSMsg_RemoveOpt

Description:
   Uses base class (IWebOpts) implementation
 
===========================================================================*/
static int CSMSMsg_RemoveOpt(ISMSMsg *pme, int32 nOptId, int32 nIndex)
{  
   FARF(SMS, ("==>  CSMSMsg_RemoveOpt:Id=%d", nOptId));
   return WebOptMgr_RemoveOpt(pme->pWom,nOptId, nIndex);
}

/*===========================================================================

Function: CSMSMsg_GetOpt

Description:
   Uses base class (IWebOpts) implementation
 
===========================================================================*/
static int CSMSMsg_GetOpt(ISMSMsg *pme, int32 nOptId, int32 nIndex, SMSMsgOpt *pwo)
{  
   FARF(SMS, ("==>  CSMSMsg_GetOpt:Id=%d", nOptId));
   return WebOptMgr_GetOpt(pme->pWom,nOptId, nIndex, pwo);
}

/*===========================================================================

Function: ISMS_New

Description:
   ISMS constructor
 
===========================================================================*/
int ISMS_New(IShell *pIShell, AEECLSID uCls, void **ppif)
{   
   int nErr = SUCCESS;
   OEMSMSMgr * pif = NULL;
   ISMS * pme = NULL;

   FARF(SMS, ("==>  ISMS_New"));

   if (SUCCESS == (nErr = OEMSMS_New(&pif))) 
   {
      pme = (ISMS *)MALLOC(sizeof(ISMS));

      *ppif = pme;

      if (pme) 
      {
         pme->pvt      = &gvtISMS;
         pme->pOEMSms  = pif;

         pme->uRef     = 1;
         pme->pIShell  = pIShell;
         ISHELL_AddRef(pIShell);
      }
      else 
      {                       
         OEMSMS_Release(pif);
         nErr = ENOMEMORY;
      }
   }

   return nErr;
}

/*===========================================================================

Function: CSMS_AddRef

Description:
   Increases reference count
 
===========================================================================*/
static uint32 CSMS_AddRef(ISMS *pme)
{
   FARF(SMS, ("==>  CSMS_AddRef"));
   return ++pme->uRef;
}

/*===========================================================================

Function: CSMS_Release

Description: 
   Decrements reference count. Frees object when reference count reaches 0. 
 
===========================================================================*/
static uint32 CSMS_Release(ISMS *pme)
{
   FARF(SMS, ("==>  CSMS_Release"));

   if (--pme->uRef) {
      return pme->uRef;
   }

   RELEASEIF(pme->pIShell);
   OEMSMS_Release(pme->pOEMSms);
   FREEIF(pme);

   return 0;
}

/*===========================================================================

Function: CSMS_QueryInterface

Description:
   Query Interface
 
===========================================================================*/
static int CSMS_QueryInterface(ISMS *pme, AEECLSID id, void **ppo)
{
   FARF(SMS, ("==>  CSMS_QueryInterface"));

   if ((id == AEECLSID_QUERYINTERFACE) || 
       (id == AEECLSID_SMS)) 
   {
      *ppo = pme;
      IQI_AddRef(*ppo);
      return SUCCESS;
   } 
   else 
   {
      return ECLASSNOTSUPPORT;
   }
}

/*===========================================================================

Function: CSMS_SendMsg

Description:
   Sends specified message
 
===========================================================================*/
static void CSMS_SendMsg(ISMS *pme, ISMSMsg * pISMSMsg, AEECallback * pcb, uint32 *pnRv)
{
   OEMMsgData * pMsgData = NULL;
   OEMMsgPayload * pMsgPayload = NULL;
   SMSAppCbData * pAppCbData = NULL;
   int nErr = SUCCESS;

   FARF(SMS, ("==>  CSMS_SendMsg"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) != SUCCESS)
      goto Done;

   if ((nErr = ISMSMsgToOEMMsg(pISMSMsg, &pMsgData, &pMsgPayload)) != SUCCESS)
      goto Done;

   OEMSMS_SendMessage(pme->pOEMSms, pMsgData, pMsgPayload, pnRv, pAppCbData->kcb);

Done:
   FREEIF(pMsgData);
   FREEIF(pMsgPayload);

   // Handle Error Here. No Error means that we are able to call OEMSMS_SendMessage
   if (nErr)
   {  
      if (nErr == ENOMEMORY)
         AEESMS_SETERROR(*pnRv, AEESMS_ERRORTYPE_TEMP, AEESMS_ERROR_NO_MEMORY);
      else
         AEESMS_SETERROR(*pnRv, AEESMS_ERRORTYPE_TEMP, AEESMS_ERROR_GENERAL_PROBLEM);

      if (pAppCbData)
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      else
         ISHELL_Resume(pme->pIShell, pcb);
   }
}

/*===========================================================================

Function: CSMS_ReceiveMsg

Description:
   Receives specified message
 
===========================================================================*/
static int CSMS_ReceiveMsg(ISMS *pme, uint32 uMsgId, ISMSMsg **ppMsg)
{
   int nErr = EFAILED;
   ISMSMsg * pISMSMsg = NULL;
   OEMMsgData * pMsgData = NULL;
   OEMMsgPayload * pMsgPayload = NULL;

   FARF(SMS, ("==>  CSMS_ReceiveMsg"));
   
   if ((pMsgData = MALLOC(sizeof(OEMMsgData))) == NULL)
   {
      nErr = ENOMEMORY;
      goto Done;
   }

   if ((nErr = OEMSMS_GetMsgData(pme->pOEMSms, uMsgId, pMsgData)) != SUCCESS)
   {
      FARF(SMS, ("Failed to get message data"));
      goto Done;
   }

   if (pMsgData->nBytes)
   {
      // Coming here means message data has UDH. Reallocate size and get
      // message data with UDH
      OEMMsgData * pTemp = NULL;

      if ((pTemp = REALLOC(pMsgData, sizeof(OEMMsgData) + pMsgData->nBytes)) == NULL)
      {
         nErr = ENOMEMORY;
         goto Done;
      }
      else
      {
         pMsgData = pTemp;
         pTemp = NULL;
      }

      if ((nErr = OEMSMS_GetMsgData(pme->pOEMSms, uMsgId, pMsgData)) != SUCCESS)
      {
         FARF(SMS, ("Failed to get message data"));
         goto Done;
      }
   }

   if ((Xlate_TStoMT(pMsgData->ts) == AEESMS_TYPE_TEXT) ||
       (Xlate_TStoMT(pMsgData->ts) == AEESMS_TYPE_WAP) ||
       (Xlate_TStoMT(pMsgData->ts) == AEESMS_TYPE_EMS))
   {
      byte * pszText = NULL;
      OEMMsgPayload sMsgPayload;
        
      MEMSET(&sMsgPayload, 0, sizeof(OEMMsgPayload));

      if (OEMSMS_GetMsgPayload(pme->pOEMSms, uMsgId, &sMsgPayload) != SUCCESS)
      {
         FARF(SMS, ("Failed to get message payload size"));
         nErr = EFAILED;
         goto Done;
      }

      if ((pMsgPayload = MALLOC(sizeof(OEMMsgPayload) + sMsgPayload.nBytes)) == NULL)
      {
         FARF(SMS, ("Memory Allocation Failed"));
         nErr = ENOMEMORY;
         goto Done;
      }

      pMsgPayload->nBytes = sMsgPayload.nBytes;

      if (OEMSMS_GetMsgPayload(pme->pOEMSms, uMsgId, pMsgPayload) != SUCCESS)
      {
         FARF(SMS, ("Failed to get message payload"));
         nErr = EFAILED;
         goto Done;
      }      
  
      if (Xlate_TStoMT(pMsgData->ts) == AEESMS_TYPE_TEXT)
      {
         pszText = AEE_GetDecodedTextString(pMsgPayload->data, pMsgPayload->nBytes, pMsgPayload->nEncoding, NULL, NULL);

         if (pszText)
         {
            if (AEE_IsBREWDirectedSMS((char*)pszText) == TRUE)
            {
               // BREW directed messages will be fetched by destination class only
               // so mark OEM layer entry as free
               FARF(SMS, ("Free Msg=%d", uMsgId));
               OEMSMS_FreeMsg(pme->pOEMSms, uMsgId);
            }
            FREE(pszText);
         }
      }
   }

   if ((nErr = OEMMsgToISMSMsg(pMsgData, pMsgPayload, &pISMSMsg)) != SUCCESS)
   {         
      FARF(SMS, ("Failed to create ISMSMsg"));
      nErr = EFAILED;
      goto Done;
   }

Done:
   FREEIF(pMsgData);         
   FREEIF(pMsgPayload);

   if (nErr == SUCCESS)
      *ppMsg = pISMSMsg;

   return nErr;
}

/*===========================================================================

Function: CSMS_SetClientStatus

Description:
   Sets main client status for the specified message type. Client needs 
   AEECLSID_SMSMAINCLIENT privilege for this.
 
===========================================================================*/
static int CSMS_SetClientStatus(ISMS *pme, AEESMSClientStatus cs, AEESMSType mt)
{
   FARF(SMS, ("==>  CSMS_SetClientStatus"));

   if (!pme->pOEMSms)
      return EFAILED;

   if (ISHELL_CheckPrivLevel(pme->pIShell, AEECLSID_SMSMAINCLIENT, TRUE)) 
   {
      if ((mt == AEESMS_TYPE_PAGE) ||
          (mt == AEESMS_TYPE_VOICE_MAIL_NOTIFICATION) ||   
          (mt == AEESMS_TYPE_TEXT))    
      {
         OEMSMS_SetClientStatus(pme->pOEMSms, cs, mt);
         return(SUCCESS);
      }
      else
      {
         FARF(SMS,("Invalid Message Type"));  
         return(EBADPARM);
      }
   }
   else
   {
      FARF(SMS,("FindClass: No ext class entry for priv class %x",AEECLSID_SMSMAINCLIENT));
      return EPRIVLEVEL;
   }
}

/*===========================================================================

Function: CSMS_GetBytesAvailableForPayload

Description:
   Checks various ISMSMsg parameters and finds that how many more bytes and
   are available for payload. Also specifies if bits are padded for last byte.

===========================================================================*/
static int CSMS_GetBytesAvailableForPayload(ISMS * po, ISMSMsg * pISMSMsg, int * pnBytes, int * pnPadding)
{
   SMSMsgOpt smo;
   int nEncoding, nBytes = 0;
   int nMaxPayloadLength, nPayloadBytes = 0, nPadding = 0, nErr = SUCCESS;
   byte * pMsgPayload = NULL;
   char * pszBrewPrefix = NULL;
   AEESMSEncType nSMSEncoding;

   FARF(SMS, ("==>  CSMS_GetBytesAvailableForPayload"));

   if (OEM_GetConfig(CFGI_SMS_MAX_PAYLOAD_LENGTH, &nMaxPayloadLength, sizeof(nMaxPayloadLength)) != SUCCESS)
   {   
      FARF(SMS, ("Max Payload Length Not Configured"));
      nErr = EFAILED;
      goto Done;
   }
   else
   {
      if (!pISMSMsg)
      {
         goto Done;
      }
   }
   
   // Get MO SMS Encoding
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_MOSMS_ENCODING, &smo) == SUCCESS)
      nSMSEncoding = (uint32)smo.pVal;
   else
   {
      // Get default MO SMS Encoding
      if (OEM_GetConfig(CFGI_SMS_DEFAULT_MO_ENC, &nSMSEncoding, sizeof(nSMSEncoding)) != SUCCESS)
      {   
         FARF(SMS, ("MO SMS Encoding Not Specified"));
         nErr = EFAILED;
         goto Done;
      }
   }

   // See if there is a destination class or handler
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_BREW_CLASS, &smo) == SUCCESS)
   {
      char szBuffer[32];
      nBytes = SNPRINTF(szBuffer, sizeof(szBuffer), "%s%.8x:", BREW_SMS_PREFIX, (AEECLSID)smo.pVal);
      if ((pszBrewPrefix = MALLOC(nBytes + 1)) != NULL)
         STRNCPY(pszBrewPrefix, szBuffer, nBytes);
      else
      {
         nErr = ENOMEMORY;
         goto Done;
      }
   }
   else if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_BREW_HANDLER_SZ, &smo) == SUCCESS)
   {
      boolean bSpace = FALSE;

      // Account for //%s:
      nBytes = STRLEN((char*)smo.pVal) + 3;

      if (nBytes & 0x1)
      {
         bSpace = TRUE;
         nBytes++;
      }
      
      if ((pszBrewPrefix = MALLOC(nBytes + 1)) != NULL)
      {
         if (bSpace)
            SNPRINTF(pszBrewPrefix, nBytes + 1, "// %s:", (char*)smo.pVal);
         else
            SNPRINTF(pszBrewPrefix, nBytes + 1, "//%s:", (char*)smo.pVal);
      }
      else
      {
         nErr = ENOMEMORY;
         goto Done;
      }
   }
      
   // Get Message Payload Encoding
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_ENCODING, &smo) == SUCCESS)
      nEncoding = (uint32)smo.pVal;
   else
   {
      // Assume Device Encoding by default
      AEEDeviceInfo di;
      di.wStructSize = sizeof(AEEDeviceInfo);
      AEE_GetDeviceInfo(&di);
      nEncoding = di.wEncoding; 
   }

   // Get Message Payload
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_SZ, &smo) == SUCCESS)
   {
      pMsgPayload = (byte*)smo.pVal;
      nPayloadBytes = STRLEN((char*)smo.pVal);
   }
   else if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_WSZ, &smo) == SUCCESS)
   {
      pMsgPayload = (byte*)smo.pVal;
      nPayloadBytes = WSTRLEN((AECHAR*)smo.pVal)*sizeof(AECHAR);
   }
   else if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_BINARY, &smo) == SUCCESS)
   {
      pMsgPayload = (byte*)WEBOPT_GETVARBUFFERDATA(smo.pVal);
      nPayloadBytes = WEBOPT_GETVARBUFFERLENGTH(smo.pVal);
   }

   if ((nErr = GetEncodedMsgPayload(pszBrewPrefix, pMsgPayload, nPayloadBytes, nEncoding, nSMSEncoding, NULL, &nBytes, &nPadding)) != SUCCESS)
      goto Done;

   // Account for UDH size on SMS payload
   nBytes += ComputeUDHSizeOnSMSPayload(pISMSMsg);

Done:
   FREEIF(pszBrewPrefix);

   if (nErr == SUCCESS)
   {
      if (pnBytes)
         *pnBytes = nMaxPayloadLength - nBytes;

      if (pnPadding)
         *pnPadding = nPadding;
   }

   return nErr;
}

/*===========================================================================

Function: CSMS_GetEncodingsAvailableForMOSMS

Description:
   Queries OEM layer to list encodings available for MO SMS.

===========================================================================*/
static int CSMS_GetEncodingsAvailableForMOSMS(ISMS * po, uint32 * pBuffer, uint32 * pnSize)
{
   int nErr;
   uint32 nSize;

   if (!pBuffer && !pnSize)
      return EBADPARM;

   if ((nErr = OEM_GetConfig(CFGI_SMS_MO_ENC_TYPES_COUNT, &nSize, sizeof(nSize))) == SUCCESS)
   {
      nSize *= sizeof(uint32); // Convert count to size

      if (pBuffer && (*pnSize >= nSize))
         nErr = OEM_GetConfig(CFGI_SMS_MO_ENC_TYPES, pBuffer, *pnSize);
      else
         *pnSize = nSize;
   }

   return nErr;
}

/*======================================================================= 
EXPORTED cb from OEM
Get any new messages from the system.
On every new message parse if needed for brew directed messages.
Start or notify the applets that are destined.
=======================================================================*/
static void CSMSNotifier_cbOnMtSMS( ISMSNotifier *pMe )
{
   uint32 dwTransactionId = 0;
   int ts = 0, mt = 0;
   boolean bNoClientWantsThisMsg;
   AEECLSID clsid;

   FARF(SMS, ("==>  CSMSNotifier_cbOnMtSMS"));
   
   while( 1 ) 
   {
      bNoClientWantsThisMsg = TRUE; // Reset bNoClientWantsThisMsg
      clsid = 0;                    // Reset destination class ID

      if( SUCCESS != OEMSMS_GetNewMsgInfo( pMe->pOEMSms, &dwTransactionId, &ts ) ) 
      {   
         FARF(SMS, ("No Msg Pending"));
         break;
      }
      
      // Translate Teleservice ID to Message Type
      mt = Xlate_TStoMT(ts);
   
      FARF(SMS, ("New message with ID = %d, type = %d", dwTransactionId, mt)); 

      if( mt == AEESMS_TYPE_TEXT) 
      {
         char * pszBrewHandler = NULL;
         byte * pText = NULL, * pParsedText = NULL;
         OEMMsgPayload * pMsgPayload = NULL;
         OEMMsgPayload   sMsgPayload;
        
         MEMSET(&sMsgPayload, 0, sizeof(OEMMsgPayload));

         if (OEMSMS_GetMsgPayload(pMe->pOEMSms, dwTransactionId, &sMsgPayload) != SUCCESS)
         {
            FARF(SMS, ("Failed to get message size"));
            break;
         }

         if ((pMsgPayload = sys_malloc(sizeof(OEMMsgPayload) + sMsgPayload.nBytes)) == NULL)
         {
            FARF(SMS, ("Memory Allocation Failed"));
            break;
         }

         pMsgPayload->nBytes = sMsgPayload.nBytes;

         if (OEMSMS_GetMsgPayload(pMe->pOEMSms, dwTransactionId, pMsgPayload) != SUCCESS)
         {
            FARF(SMS, ("Failed to get message data"));
            sys_free(pMsgPayload);
            break;
         }

         if ((pText = AEE_GetDecodedTextString(pMsgPayload->data, pMsgPayload->nBytes, pMsgPayload->nEncoding, NULL, NULL)) == NULL)
         {
            FARF(SMS, ("Memory Allocation Failed"));
            FREEIF(pMsgPayload);
            break;
         }
         
         ParseBrewMsg((char*)pText, &clsid, &pszBrewHandler, &pParsedText);

         if (clsid || pszBrewHandler) 
         {      
            // This is a brew directed message and needs special handling
            if (pszBrewHandler)
            {
               if (clsid)
               {
                  // Message if of //<SMSHandler>:<Payload> type
                  char * pData = (char*)pText;     // pData = //<SMSHandler>'\0'<Payload>

                  // While sending handler directed SMS, odd length handler are made even length by placing ' '
                  // after "//". This is done to ensure that //<SMSHandler>:<Payload> can pass transformation from
                  // payload encoding to MO SMS encoding. Following check ensures that this is not exposed to
                  // applications
                  if (!STRNCMP(pData, "// ", STRLEN("// ")))
                  {
                     STRNCPY(pData, " //", STRLEN(" //"));  // Replace "// XXXX'\0'<Payload>" with " //XXXX'\0'<Payload>"
                     pData++;                               // pData = "//XXXX'\0'<Payload>"
                  }
                  
                  // Make pData = "//XXXX:<Payload>"
                  *(pData + STRLEN("//") + STRLEN(pszBrewHandler)) = ':';
                  FARF(SMS, ("EVT_APP_BROWSE_URL to cls=0x%x, msg=%s", clsid, pData));
                  ISHELL_StartAppletArgs(pMe->pIShell, clsid, pData); 
                  
                  FARF(SMS, ("Post EVT_APP_MESSAGE_EX to cls=0x%x, tid=%d", clsid, dwTransactionId));
                  ISHELL_PostEvent( pMe->pIShell, clsid, EVT_APP_MESSAGE_EX, 0, dwTransactionId );
                  bNoClientWantsThisMsg = FALSE;
               }
               else
               {                 
                  FARF(SMS, ("No class found for %s", pszBrewHandler));
               }
            }
            else
            {
               AEEAppInfo ai;

               if (ISHELL_QueryClass(pMe->pIShell, clsid, &ai) == TRUE) 
               {
                  // Message is of //BREW:<ClsID>:<Payload> type. pParsedText points to 
                  // <Payload>
                  FARF(SMS, ("EVT_APP_MESSAGE to cls=0x%x, en=%d, msg=%s", clsid, pMsgPayload->nEncoding, pParsedText));
                  ISHELL_SendEvent(pMe->pIShell, clsid, EVT_APP_MESSAGE, (uint16)pMsgPayload->nEncoding, (uint32)pParsedText);
                  
                  FARF(SMS, ("Send EVT_APP_MESSAGE_EX to cls=0x%x, tid=%d", clsid, dwTransactionId));
                  ISHELL_SendEvent( pMe->pIShell, clsid, EVT_APP_MESSAGE_EX, 0, dwTransactionId );
                  bNoClientWantsThisMsg = FALSE;
               }
               else
               {                 
                  FARF(SMS, ("cls=0x%x not found", clsid));
               }
            }
         }
         else
         {
            if( pMe->dwNotify & NMASK_SMS_TYPE ) 
            {
               FARF(SMS, ("Text message Notification"));
               ISHELL_Notify( pMe->pIShell, AEECLSID_SMSNOTIFIER, (AEESMS_TYPE_TEXT << 16l)|NMASK_SMS_TYPE, (void*)dwTransactionId );
               bNoClientWantsThisMsg = FALSE;
            }
         }
         sys_free(pMsgPayload);
         FREEIF(pText);
      }
      else 
      {
         if( pMe->dwNotify & NMASK_SMS_TYPE ) 
         {   
            FARF(SMS, ("Message Type %d Notification", mt));
            ISHELL_Notify( pMe->pIShell, AEECLSID_SMSNOTIFIER, (mt << 16l)|NMASK_SMS_TYPE, (void*)dwTransactionId);
            bNoClientWantsThisMsg = FALSE;
         }
      }

      // For messages not directed to a particular BREW applet, 
      // invoke ITAPI notification to let ITAPI send notifications 
      // to its clients
      if (!clsid)
         AEETAPI_NotifyOnMtSMS(dwTransactionId, ts);

      if (bNoClientWantsThisMsg == TRUE)
      {
         // If no client is interested in this message, which means we haven't sent
         // transaction ID to any client, set message free
         OEMSMS_FreeMsg(pMe->pOEMSms, dwTransactionId);
      }
   }

   // Re-register callback
   OEMSMS_OnMTMessage( pMe->pOEMSms, gpSMSNotifier->kcbOnMtSMS );
}

/*===========================================================================

Function: ISMSNotifier_New

Description:
   ISMSNotifier constructor
 
===========================================================================*/
int ISMSNotifier_New(IShell *pIShell, AEECLSID uCls, void **ppif)
{   
   int nErr = SUCCESS;

   FARF(SMS, ("==>  ISMSNotifier_New"));

   if( !gpSMSNotifier ) 
   {
      OEMSMSMgr *pif;
      
      if( SUCCESS == (nErr = OEMSMS_New( &pif )) ) 
      {
         gpSMSNotifier = (ISMSNotifier *)sys_malloc( sizeof(ISMSNotifier) );
         
         if( gpSMSNotifier ) 
         {
            int nErr;

            gpSMSNotifier->pvt      = &gvtISMSNotifier;
            gpSMSNotifier->pOEMSms  = pif;
            
            gpSMSNotifier->uRef     = 1;
            
            gpSMSNotifier->pIShell  = pIShell;
            ISHELL_AddRef(pIShell);

            if ((nErr = AEEkCB_New( &gpSMSNotifier->kcbOnMtSMS, (PFNNOTIFY)CSMSNotifier_cbOnMtSMS, gpSMSNotifier )) != SUCCESS)
            {
               CSMSNotifier_Release(gpSMSNotifier);
               return nErr;
            }

            OEMSMS_OnMTMessage( pif, gpSMSNotifier->kcbOnMtSMS );
      
            *ppif = gpSMSNotifier;
         }
         else 
         {                       
            OEMSMS_Release( pif );
            nErr = ENOMEMORY;
         }
      }
   }
   else   
   {
      CSMSNotifier_AddRef( (ISMSNotifier *)gpSMSNotifier );
      *ppif = gpSMSNotifier;
   }

   return nErr;
}

/*===========================================================================

Function: CSMSNotifier_AddRef

Description:
   Increases reference count
 
===========================================================================*/
static uint32 CSMSNotifier_AddRef(ISMSNotifier *pme)
{
   FARF(SMS, ("==>  CSMSNotifier_AddRef"));
   return ++pme->uRef;
}

/*===========================================================================

Function: CSMSNotifier_Release

Description: 
   Decrements reference count. Frees object when reference count reaches 0. 
 
===========================================================================*/
static uint32 CSMSNotifier_Release(ISMSNotifier *pme)
{
   FARF(SMS, ("==>  CSMSNotifier_Release"));

   if (--pme->uRef) {
      return pme->uRef;
   }

   gpSMSNotifier = 0;
   RELEASEIF(pme->pIShell);
   OEMSMS_Release(pme->pOEMSms);
   AEEKCB_DELETEIF( &pme->kcbOnMtSMS );   
   sys_free(pme);

   return 0;
}

/*===========================================================================

Function: CSMSNotifier_SetMask

Description: 
   Sets notification mask. 
 
===========================================================================*/
static void CSMSNotifier_SetMask(ISMSNotifier *pme, const uint32 *pdwMasks)
{
   FARF(SMS, ("==>  CSMSNotifier_SetMask"));

   while (*pdwMasks)
   {
      pme->dwNotify |= GET_NOTIFIER_MASK(*pdwMasks);
      pdwMasks++;
   }
}

/*===========================================================================

Function: ISMSStorage_New

Description:
   ISMSStorage constructor
 
===========================================================================*/
int ISMSStorage_New(IShell *pIShell, AEECLSID uCls, void **ppif)
{
   int nErr = SUCCESS;
   ISMSStorage * pme = NULL;

   FARF(SMS, ("==>  ISMSStorage_New"));

   // Ensure that application has AEECLSID_SMSMAINCLIENT privilege
   if (ISHELL_CheckPrivLevel(pIShell, AEECLSID_SMSMAINCLIENT, TRUE) == FALSE)
   {
      return EPRIVLEVEL;
   }
   
   if (OEMSMS_IsStorageInitialized()) 
   {
      pme = (ISMSStorage *)MALLOC(sizeof(ISMSStorage));

      *ppif = pme;

      if (pme) 
      {
         pme->pvt      = &gvtISMSStorage;

         pme->uRef     = 1;
         pme->pIShell  = pIShell;
         ISHELL_AddRef(pIShell);
      }
      else 
      {
         nErr = ENOMEMORY;
      }
   }
   else
   {
      return EFAILED;
   }

   return nErr;
}

/*===========================================================================

Function: CSMSStorage_AddRef

Description:
   Increases reference count
 
===========================================================================*/
static uint32 CSMSStorage_AddRef(ISMSStorage *pme)
{
   FARF(SMS, ("==>  CSMSStorage_AddRef"));
   return ++pme->uRef;
}

/*===========================================================================

Function: CSMSStorage_Release

Description: 
   Decrements reference count. Frees object when reference count reaches 0. 
 
===========================================================================*/
static uint32 CSMSStorage_Release(ISMSStorage *pme)
{
   FARF(SMS, ("==>  CSMSStorage_Release"));

   if (--pme->uRef) 
   {
      return pme->uRef;
   }

   RELEASEIF(pme->pIShell);
   FREEIF(pme->pdwIndex);
   FREEIF(pme);

   return 0;
}

/*===========================================================================

Function: CSMSStorage_QueryInterface

Description:
   Query Interface
 
===========================================================================*/
static int CSMSStorage_QueryInterface(ISMSStorage *pme, AEECLSID id, void **ppo)
{
   FARF(SMS, ("==>  CSMSStorage_QueryInterface"));

   if ((id == AEECLSID_QUERYINTERFACE) || 
       (id == AEECLSID_SMSSTORAGE)) 
   {
      *ppo = pme;
      IQI_AddRef(*ppo);
      return SUCCESS;
   } 
   else 
   {
      return ECLASSNOTSUPPORT;
   }
}

/*===========================================================================

Function: CSMSStorage_GetStorageStatus

Description:
   Returns storage status of current storage type.
 
===========================================================================*/
static void CSMSStorage_GetStorageStatus(ISMSStorage * pme, AEESMSStorageType st, AEESMSTag mt, AEECallback *pcb, AEESMSStorageStatus * pss, uint32 *pnRv)
{
   int nErr;
   SMSAppCbData * pAppCbData = NULL;

   FARF(SMS, ("==>  CSMSStorage_GetStorageStatus"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      OEMSMS_GetStorageStatus(st, mt, pss, pnRv, pAppCbData->kcb);
   }
   else
   {
      *pnRv = nErr;

      if (pAppCbData)
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      else
         ISHELL_Resume(pme->pIShell, pcb);
   }
}

/*===========================================================================

Function: CSMSStorage_StoreMsg

Description:
   Stores message in current storage type.
 
===========================================================================*/
static void CSMSStorage_StoreMsg(ISMSStorage * pme, AEESMSStorageType st, ISMSMsg * pi, AEECallback *pcb, uint32 * pnIndex, uint32 *pnRv)
{   
   OEMMsgData * pMsgData = NULL;
   OEMMsgPayload * pMsgPayload = NULL;
   SMSAppCbData * pAppCbData = NULL;
   int nErr = SUCCESS;

   FARF(SMS, ("==>  CSMSStorage_StoreMsg"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) != SUCCESS)
      goto Done;

   if ((nErr = ISMSMsgToOEMMsg(pi, &pMsgData, &pMsgPayload)) != SUCCESS)
      goto Done;
      
   OEMSMS_StoreMsg(st, pMsgData, pMsgPayload, pnIndex, pnRv, pAppCbData->kcb);

Done:
   FREEIF(pMsgData);
   FREEIF(pMsgPayload);

   // Handle Error Here. No Error means that we are able to call OEMSMS_SendMessage
   if (nErr)
   {
      *pnRv = nErr;

      if (pAppCbData)
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      else
         ISHELL_Resume(pme->pIShell, pcb);
   }
}

/*===========================================================================

Function: CSMSStorage_ReadMsg

Description:
   Reads message from current storage type.
 
===========================================================================*/
static void CSMSStorage_ReadMsg(ISMSStorage * pme, AEESMSStorageType st, uint32 nIndex, AEECallback *pcb, ISMSMsg ** ppi, uint32 * pnErr)
{  
   int nErr;
   SMSAppReadMsgCbData * pAppReadMsgCbData = NULL;

   FARF(SMS, ("==>  CSMSStorage_ReadMsg")); 

   if ((nErr = LinkAppReadMsgCb(pcb, &pAppReadMsgCbData)) == SUCCESS)
   {
      pAppReadMsgCbData->pnRv = pnErr;
      pAppReadMsgCbData->ppISMSMsg = ppi;
      OEMSMS_ReadMsg(st, nIndex, &pAppReadMsgCbData->sMsgData, &pAppReadMsgCbData->sMsgPayload, pnErr, pAppReadMsgCbData->kcb);
   }
   else
   {
      *pnErr = nErr;

      if (pAppReadMsgCbData)
         ISHELL_Resume(pme->pIShell, pAppReadMsgCbData->pcb);
      else
         ISHELL_Resume(pme->pIShell, pcb);
   }
}

/*===========================================================================

Function: CSMSStorage_Delete

Description:
   Deletes specified message from current storage type.
 
===========================================================================*/
static void CSMSStorage_Delete(ISMSStorage * pme, AEESMSStorageType st, AEESMSTag mt, uint32 nIndex, AEECallback *pcb, uint32 * pnErr)
{
   int nErr;
   SMSAppCbData * pAppCbData = NULL;

   FARF(SMS, ("==>  CSMSStorage_Delete"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      OEMSMS_RemoveMsg(st, mt, nIndex, pnErr, pAppCbData->kcb);
   }
   else
   {
      *pnErr = nErr;

      if (pAppCbData)
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      else
         ISHELL_Resume(pme->pIShell, pcb);
   }
}

/*===========================================================================

Function: CSMSStorage_EnumMsgInit

Description:
   Initializes current storage type for enumeration according to the specified
   parameters.
 
===========================================================================*/
static void CSMSStorage_EnumMsgInit(ISMSStorage * pme, AEESMSStorageType st, AEESMSTag mt, AEECallback *pcb, uint32 * pnErr)
{   
   int nErr = SUCCESS;
   uint32 dwSize = 0;

   FARF(SMS, ("==>  CSMSStorage_EnumMsgInit"));

   if (!pcb || !pnErr)
      return;

   // Reset if its already processing an enumeration
   if (pme->pdwIndex)
   {
      FREEIF(pme->pdwIndex);
      pme->pdwIndex = NULL;
   }

   if ((SUCCESS != OEMSMS_GetEnumMsgIDs(st, mt, NULL, &dwSize)) ||
       (0 == dwSize) ||
       (NULL == (pme->pdwIndex = MALLOC(dwSize))) ||
       (SUCCESS != OEMSMS_GetEnumMsgIDs(st, mt, pme->pdwIndex, &dwSize)))
   {
      nErr = EFAILED;
      FREEIF(pme->pdwIndex);
      pme->pdwIndex = NULL;
   }
   else
   {
      pme->mt = mt;
      pme->st = st;
      pme->dwOffset = 0;
      pme->dwCount = dwSize/sizeof(uint32);
   }
   
   *pnErr = nErr;
   ISHELL_Resume(pme->pIShell, pcb);
}

/*===========================================================================

Function: CSMSStorage_EnumNextMsg

Description:
   Find next message in enumeration
 
===========================================================================*/
static void CSMSStorage_EnumNextMsg(ISMSStorage * pme, AEESMSStorageType st, AEECallback *pcb, uint32 * pnIndex, ISMSMsg ** ppi, uint32 * pnErr)
{
   SMSAppEnumMsgCbData * pAppEnumMsgCbData = NULL;
   int nErr = SUCCESS;
   
   FARF(SMS, ("==>  CSMSStorage_EnumNextMsg"));

   // Null Check
   if (!pcb || !pnErr)
      return;

   // We need atleast one of ppi and pnIndex. Also make sure that storage type
   // specified is same as the one specified in CSMSStorage_EnumMsgInit
   if ((!ppi && !pnIndex) || (pme->st != st))
   {
      nErr = EBADPARM;
      goto Done;
   }

   // Check if ISMSSTORAGE_EnumMsgInit() isn't called
   if (!pme->pdwIndex)
   {
      nErr = EFAILED;
      goto Done;
   }

   // Check if enumeration is complete
   if (pme->dwOffset >= pme->dwCount)
   {
      if (ppi)
         *ppi = NULL;

      if (pnIndex)
         *pnIndex = 0xffffffff;

      FREEIF(pme->pdwIndex);
      pme->pdwIndex = NULL;

      goto Done;
   }
   

   if ((nErr = LinkAppEnumMsgCb(pcb, &pAppEnumMsgCbData)) == SUCCESS)
   {
      pAppEnumMsgCbData->pnRv = pnErr;
      pAppEnumMsgCbData->ppISMSMsg = ppi;
      pAppEnumMsgCbData->pnIndex = pnIndex;
      pAppEnumMsgCbData->pISMSStorage = pme;
      OEMSMS_ReadMsg(st, pme->pdwIndex[pme->dwOffset], &pAppEnumMsgCbData->sMsgData, 
                     &pAppEnumMsgCbData->sMsgPayload, pnErr, pAppEnumMsgCbData->kcb);
      return;
   }
   else
   {
      goto Done;
   }

Done:
   *pnErr = nErr;

   if (pAppEnumMsgCbData)
      ISHELL_Resume(pme->pIShell, pAppEnumMsgCbData->pcb);
   else
      ISHELL_Resume(pme->pIShell, pcb);
}

/*===========================================================================

Function: ISMSBCSrvOpts_New

Description:
   ISMSBCSrvOpts constructor
 
===========================================================================*/
int ISMSBCSrvOpts_New(IShell *pIShell, AEECLSID uCls, void **ppif)
{
   // uCls is intentionally unused
   int nReturn = ENOMEMORY;
   SMSMsgOpt awoz[1];
   ISMSBCSrvOpts *pme;

   // Ensure that application has AEECLSID_SMSMAINCLIENT privilege
   if (ISHELL_CheckPrivLevel(pIShell, AEECLSID_SMSMAINCLIENT, TRUE) == FALSE)
   {
      return EPRIVLEVEL;
   }

   // Make empty option list
   awoz[0].nId  = WEBOPT_END;

   // Get our memory including a WebOpt manager
   pme = MALLOCREC_EX(ISMSBCSrvOpts, WebOptMgr_GetSize(awoz));

   *ppif = pme;

   if (pme) 
   {
      // Our vtable
      pme->pvt = &gvtISMSBCSrvOpts;

      pme->pWom = (WebOptMgr *)(pme+1);
      WebOptMgr_CtorZ(pme->pWom,awoz);

      // The shell 
      pme->uRef = 1;
      pme->pIShell = pIShell;
      ISHELL_AddRef(pIShell);

      nReturn = SUCCESS;
   }
   return nReturn;
}

/*===========================================================================

Function: CSMSBCSrvOpts_AddRef

Description:
   Increases reference count
 
===========================================================================*/
static uint32 CSMSBCSrvOpts_AddRef(ISMSBCSrvOpts *pme)
{
   return ++pme->uRef;
}

/*===========================================================================

Function: CSMSBCSrvOpts_Release

Description:
   Decrements reference count. Frees object when reference count reaches 0. 
 
===========================================================================*/
static uint32 CSMSBCSrvOpts_Release(ISMSBCSrvOpts *pme)
{
   if (--pme->uRef) 
   {
      return pme->uRef;
   }

   WebOptMgr_Dtor(pme->pWom);
   RELEASEIF(pme->pIShell);
   FREEIF(pme);

   return 0;
}

/*===========================================================================

Function: CSMSBCSrvOpts_QueryInterface

Description:
   Query Interface
 
===========================================================================*/
static int CSMSBCSrvOpts_QueryInterface(ISMSBCSrvOpts *pme, AEECLSID id, void **ppo)
{
   if ((id == AEECLSID_QUERYINTERFACE) || 
       (id == AEECLSID_SMSMSG)         ||
       (id == AEECLSID_WEBOPTS)) 
   {
      *ppo = pme;
      IQI_AddRef(*ppo);
      return SUCCESS;
   } 
   else 
   {
      return ECLASSNOTSUPPORT;
   }
}

/*===========================================================================

Function: CSMSBCSrvOpts_AddOpt

Description:
   Uses base class (IWebOpts) implementation
 
===========================================================================*/
static int CSMSBCSrvOpts_AddOpt(ISMSBCSrvOpts *pme, SMSBCSrvOpts *awozList)
{
   return WebOptMgr_AddOptV(pme->pWom, awozList);
}

/*===========================================================================

Function: CSMSBCSrvOpts_RemoveOpt

Description:
   Uses base class (IWebOpts) implementation
 
===========================================================================*/
static int CSMSBCSrvOpts_RemoveOpt(ISMSBCSrvOpts *pme, int32 nOptId, int32 nIndex)
{
   return WebOptMgr_RemoveOpt(pme->pWom,nOptId, nIndex);
}

/*===========================================================================

Function: CSMSBCSrvOpts_GetOpt

Description:
   Uses base class (IWebOpts) implementation
 
===========================================================================*/
static int CSMSBCSrvOpts_GetOpt(ISMSBCSrvOpts *pme, int32 nOptId, int32 nIndex, SMSBCSrvOpts *pwo)
{
   return WebOptMgr_GetOpt(pme->pWom,nOptId, nIndex, pwo);
}

/*===========================================================================

Function: ISMSBCConfig_New

Description:
   ISMSBCConfig constructor
 
===========================================================================*/
int ISMSBCConfig_New(IShell *pIShell, AEECLSID uCls, void **ppif)
{
   // uCls is intentionally unused
   int nReturn = ENOMEMORY;
   ISMSBCConfig *pme;

   // Ensure that application has AEECLSID_SMSMAINCLIENT privilege
   if (ISHELL_CheckPrivLevel(pIShell, AEECLSID_SMSMAINCLIENT, TRUE) == FALSE)
   {
      return EPRIVLEVEL;
   }

   // Get our memory
   pme = MALLOCREC(ISMSBCConfig);

   *ppif = pme;

   if (pme) 
   {
      // Our vtable
      pme->pvt = &gvtISMSBCConfig;

      // The shell 
      pme->uRef = 1;
      pme->pIShell = pIShell;
      ISHELL_AddRef(pIShell);

      nReturn = SUCCESS;
   }
   return nReturn;
}

/*===========================================================================

Function: CSMSBCConfig_AddRef

Description:
   Increases reference count
 
===========================================================================*/
static uint32 CSMSBCConfig_AddRef(ISMSBCConfig *pme)
{
   return ++pme->uRef;
}

/*===========================================================================

Function: CSMSBCConfig_Release

Description:
   Decrements reference count. Frees object when reference count reaches 0. 
 
===========================================================================*/
static uint32 CSMSBCConfig_Release(ISMSBCConfig *pme)
{
   if (--pme->uRef) 
   {
      return pme->uRef;
   }

   RELEASEIF(pme->pIShell);
   FREEIF(pme);

   return 0;
}

/*===========================================================================

Function: CSMSBCConfig_QueryInterface

Description:
   Query Interface
 
===========================================================================*/
static int CSMSBCConfig_QueryInterface(ISMSBCConfig *pme, AEECLSID id, void **ppo)
{
   if ((id == AEECLSID_QUERYINTERFACE) || 
       (id == AEECLSID_SMSBCCONFIG)) 
   {
      *ppo = pme;
      IQI_AddRef(*ppo);
      return SUCCESS;
   } 
   else 
   {
      return ECLASSNOTSUPPORT;
   }
}

/*===========================================================================

Function: CSMSBCConfig_GetBCConfig

Description:
   Returns carrier's broadcast configuration
 
===========================================================================*/
static void CSMSBCConfig_GetBCConfig(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, AEESMSBCConfig * pConfig, uint32 * pnErr)
{
   SMSAppCbData * pAppCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_GetBCConfig"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      // Pass through to OEM
      OEMSMSBCConfig_GetBCConfig(dwMode, pAppCbData->kcb, pConfig, pnErr);
   }
      
   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pAppCbData)
      {
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_GetBCPref

Description:
   Returns user's broadcast preference
 
===========================================================================*/
static void CSMSBCConfig_GetBCPref(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, AEESMSBCPref * pPref, uint32 * pnErr)
{
   SMSAppCbData * pAppCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_GetBCPref"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      // Pass through to OEM
      OEMSMSBCConfig_GetBCPref(dwMode, pAppCbData->kcb, pPref, pnErr);
   }

   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pAppCbData)
      {
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_SetBCPref

Description:
   Sets user's broadcast preference
 
===========================================================================*/
static void CSMSBCConfig_SetBCPref(ISMSBCConfig * pme, AEESMSMode dwMode, AEESMSBCPref nPref, AEECallback *pcb, uint32 * pnErr)
{
   SMSAppCbData * pAppCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_SetBCPref"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      // Pass through to OEM 
      OEMSMSBCConfig_SetBCPref(dwMode, nPref, pAppCbData->kcb, pnErr);
   }

   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pAppCbData)
      {
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_GetServiceIDs

Description:
   Returns service IDs currently configured in the table.
 
===========================================================================*/
static void CSMSBCConfig_GetServiceIDs(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, uint32 * pnCount, AEESMSBCSrvID ** ppsid, uint32 * pnErr)
{
   SMSAppGetSrvIDsCbData * pSMSAppGetSrvIDsCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_GetServiceIDs"));

   if ((nErr = LinkAppGetSrvIDsCb(pcb, &pSMSAppGetSrvIDsCbData)) == SUCCESS)
   {
      pSMSAppGetSrvIDsCbData->pnCount = pnCount;
      pSMSAppGetSrvIDsCbData->ppsid   = ppsid;
      pSMSAppGetSrvIDsCbData->pnErr   = pnErr;
      
      // Pass through to OEM 
      OEMSMSBCConfig_GetNumberOfServiceIDs(dwMode, pSMSAppGetSrvIDsCbData->kcb, pSMSAppGetSrvIDsCbData->pnCount, pSMSAppGetSrvIDsCbData->pnErr);
   }
   
   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pSMSAppGetSrvIDsCbData)
      {
         ISHELL_Resume(pme->pIShell, pSMSAppGetSrvIDsCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_GetServiceOpts

Description:
   Returns service options corresponding to the specified ID.
 
===========================================================================*/
static void CSMSBCConfig_GetServiceOpts(ISMSBCConfig * pme, AEESMSMode dwMode, AEESMSBCSrvID sSrvID, AEECallback *pcb, ISMSBCSrvOpts ** ppi, uint32 * pnErr)
{
   SMSAppGetSrvOptsCbData * pSMSAppGetSrvOptsCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_GetServiceOpts"));

   if ((nErr = LinkAppGetSrvOptsCb(pcb, &pSMSAppGetSrvOptsCbData)) == SUCCESS)
   {
      pSMSAppGetSrvOptsCbData->ppi     = ppi;
      pSMSAppGetSrvOptsCbData->pnErr   = pnErr;

      // Pass through to OEM
      OEMSMSBCConfig_GetServiceOpts(dwMode, &sSrvID, pSMSAppGetSrvOptsCbData->kcb, &pSMSAppGetSrvOptsCbData->sSMSBcSrvInfo, pSMSAppGetSrvOptsCbData->pnErr);
   }
   
   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pSMSAppGetSrvOptsCbData)
      {
         ISHELL_Resume(pme->pIShell, pSMSAppGetSrvOptsCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_SetServiceOpts

Description:
   Sets service options corresponding to the specified ID.
 
===========================================================================*/
static void CSMSBCConfig_SetServiceOpts(ISMSBCConfig * pme, AEESMSMode dwMode, ISMSBCSrvOpts * pi, AEECallback *pcb, uint32 * pnErr)
{
   SMSAppCbData * pAppCbData = NULL;
   OEMSMSBCSrvInfo sSrvInfo;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_SetServiceOpts"));

   MEMSET(&sSrvInfo, 0, sizeof(OEMSMSBCSrvInfo));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      if (UnpackSMSBCSrvOpts(pi, &sSrvInfo))
      {
         // Pass through to OEM 
         OEMSMSBCConfig_SetServiceOpts(dwMode, &sSrvInfo, pAppCbData->kcb, pnErr);
      }
      else
      {
         nErr = EBADPARM;
      }
   }

   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pAppCbData)
      {
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_GetAllServiceOpts

Description:
   Returns service options corresponding to all of the service IDs.
 
===========================================================================*/
static void CSMSBCConfig_GetAllServiceOpts(ISMSBCConfig * pme, AEESMSMode dwMode, AEECallback *pcb, uint32 * pnCount, ISMSBCSrvOpts *** pppi, uint32 * pnErr)
{
   SMSAppGetAllSrvOptsCbData * pSMSAppGetAllSrvOptsCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_GetAllServiceOpts"));

   if ((nErr = LinkAppGetAllSrvOptsCb(pcb, &pSMSAppGetAllSrvOptsCbData)) == SUCCESS)
   {
      pSMSAppGetAllSrvOptsCbData->pnCount = pnCount;
      pSMSAppGetAllSrvOptsCbData->pppi     = pppi;
      pSMSAppGetAllSrvOptsCbData->pnErr   = pnErr;

      // Pass through to OEM 
      OEMSMSBCConfig_GetNumberOfServiceOpts(dwMode, pSMSAppGetAllSrvOptsCbData->kcb, pSMSAppGetAllSrvOptsCbData->pnCount, pSMSAppGetAllSrvOptsCbData->pnErr);
   }
   
   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pSMSAppGetAllSrvOptsCbData)
      {
         ISHELL_Resume(pme->pIShell, pSMSAppGetAllSrvOptsCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*===========================================================================

Function: CSMSBCConfig_DeleteService

Description:
   Removes a service from the device service table.
 
===========================================================================*/
static void CSMSBCConfig_DeleteService(ISMSBCConfig * pme, AEESMSMode dwMode, AEESMSBCSrvID sSrvID, AEECallback *pcb, uint32 * pnErr)
{
   SMSAppCbData * pAppCbData = NULL;
   int nErr = EFAILED;
      
   FARF(SMS, ("==>  CSMSBCConfig_DeleteService"));

   if ((nErr = LinkAppCb(pcb, &pAppCbData)) == SUCCESS)
   {
      // Pass through to OEM 
      OEMSMSBCConfig_DeleteService(dwMode, &sSrvID, pAppCbData->kcb, pnErr);
   }

   if (nErr != SUCCESS)
   {
      *pnErr = nErr;
      if (pAppCbData)
      {
         ISHELL_Resume(pme->pIShell, pAppCbData->pcb);
      }
      else
      {
         ISHELL_Resume(pme->pIShell, pcb);
      }
   }
}

/*======================================================================= 
EXPORTED by external reference.
=======================================================================*/
boolean AEE_IsBREWDirectedSMS( char *pMsg )
{
   AEECLSID cls = 0;
   char * pszBREWHandler = NULL;

   if (!pMsg)
      return FALSE;

   ParseBrewMsg(pMsg, &cls, &pszBREWHandler, NULL);

   if (cls || pszBREWHandler)
   {
      //Restore pMsg as ParseBrewMsg will split pMsg
      *(pMsg + STRLEN(pMsg)) = ':';

      return TRUE;
   }

   return FALSE;
}

/*===========================================================================
Called to get decoded text string
===========================================================================*/
byte * AEE_GetDecodedTextString( byte * pText, uint32 nText, int se, int * pae, uint32 * pnBytes)
{
   uint32 nSize;
   byte * pBuffer = NULL;

   FARF(SMS, ("==>  AEE_GetDecodedTextString"));

   if (!pText || !nText)
      return NULL;

   switch (se) 
   {
      case AEESMS_ENC_IA5:
      case AEESMS_ENC_ASCII:
         // For IA5 and ASCII encoding, pText contains packed 7-bit characters.
         // Inflate size as we unpack characters here.
         nSize = ( nText * 8)/7;
         break;
           
      default:
         // For all other encoding types, set nSize same as nText         
         nSize = nText;
         break;
   }

   if ((pBuffer = MALLOC(nSize + sizeof(AECHAR))) == NULL)
      return NULL;

   switch (se) 
   {   
      case AEESMS_ENC_IA5:
      case AEESMS_ENC_ASCII:
         // Unpack the 7 bit data of pText in pBuffer
         DecodePackedString(pText, nSize, (char*)pBuffer);
         break;

      case AEESMS_ENC_UNICODE:        
         // Flip the bytes in each Unicode Character, convert from Network 
         // byte order to to Host Byte Order
         DecodeNetworkOrder(pText, nSize, pBuffer);
         break;

      case AEESMS_ENC_GSM_7_BIT_DEFAULT:
         // For GSM 7 bit encoding the User Data has already been unpacked by
         // the DMSS, we may need to do some translation to ASCII, but there
         // is a one-to-one mapping from the GSM default encoding and ASCII for
         // most commonly used characters
      case AEESMS_ENC_IS91EP:
      case AEESMS_ENC_LATIN:
         // User Data is already unpacked.
         // fall through
      default:        
         DecodeString(pText, nSize, (char*)pBuffer);
         break;
   }

   if (pae)
      *pae = Xlate_SMSEncToAEEEnc(se);

   if (pnBytes)
      *pnBytes = nSize;

   return pBuffer;
}

/*===========================================================================
Called to get encoded text string
===========================================================================*/
static int GetEncodedMsgPayload(char* pszBrewPrefix, byte * pMsgPayload, int nBytes, int nEncoding, AEESMSEncType nSMSEncoding, byte * pEncodedPayload, int * pnBytes, int * pnPadding)
{
   int nErr = SUCCESS;
   int nExtra = (pszBrewPrefix ? STRLEN(pszBrewPrefix) : 0);

   switch (nSMSEncoding) 
   {  
      case AEESMS_ENC_OCTET:
         nBytes += nExtra;
         *pnBytes = nBytes;
         *pnPadding = 0;

         if (pEncodedPayload)
         {
            if (pszBrewPrefix)
               MEMCPY(pEncodedPayload, pszBrewPrefix, nExtra);

            if (pMsgPayload)
               MEMCPY(pEncodedPayload + nExtra, pMsgPayload, nBytes - nExtra);
         }
         break;

      case AEESMS_ENC_ASCII:
         switch (nEncoding)
         {
            case AEE_ENC_ISOLATIN1:
               if (IsMsgPackable(pMsgPayload, nBytes, nEncoding, TRUE))
               {
                  nBytes += nExtra;
                  *pnBytes = (nBytes * 7)/8;
                  *pnBytes += ((nBytes * 7) % 8 ? 1 : 0);
                  *pnPadding = ((nBytes * 7) % 8 ? 8 - (nBytes * 7)%8 : 0);

                  if (pEncodedPayload)
                  {
                     byte * pBuffer = NULL;
                     if ((pBuffer = MALLOC(nBytes)) != NULL)
                     {
                        if (pszBrewPrefix)
                           MEMCPY(pBuffer, pszBrewPrefix, nExtra);

                        if (pMsgPayload)
                           MEMCPY(pBuffer + nExtra, pMsgPayload, nBytes - nExtra);

                        PackString((char*)pBuffer, nBytes, (byte*)pEncodedPayload);

                        FREE(pBuffer);
                     }
                     else
                        nErr = ENOMEMORY;
                  }
               }
               else
                  nErr = EFAILED;
               break;

            case AEE_ENC_UNICODE:
            case AEE_ENC_EUC_CN:
               if (IsMsgPackable(pMsgPayload, nBytes, nEncoding, TRUE))
               {
                  nBytes /= sizeof(AECHAR);
                  nBytes += nExtra;
                  *pnBytes = (nBytes * 7)/8;
                  *pnBytes += ((nBytes * 7) % 8 ? 1 : 0);
                  *pnPadding = ((nBytes * 7) % 8 ? 8 - (nBytes * 7)%8 : 0);
                  if (pEncodedPayload)
                  {                  
                     byte * pBuffer = NULL;
                     if ((pBuffer = MALLOC(nBytes + 1)) != NULL)
                     {
                        if (pszBrewPrefix)
                           MEMCPY(pBuffer, pszBrewPrefix, nExtra);

                        if (pMsgPayload)
                           WSTRTOSTR((AECHAR*)pMsgPayload, (char*)(pBuffer + nExtra), nBytes - nExtra + 1);

                        PackString((char*)pBuffer, nBytes, (byte*)pEncodedPayload);

                        FREE(pBuffer);
                     }
                     else
                        nErr = ENOMEMORY;
                  }
               }
               else
                  nErr = EFAILED;
               break;

            default:
               nErr = EFAILED;
         }
         break;

      case AEESMS_ENC_LATIN:
         switch (nEncoding)
         {
            case AEE_ENC_ISOLATIN1:
               nBytes += nExtra;
               *pnBytes = nBytes;
               *pnPadding = 0;
               if (pEncodedPayload)
               {
                  if (pszBrewPrefix)
                     MEMCPY(pEncodedPayload, pszBrewPrefix, nExtra);

                  if (pMsgPayload)
                     MEMCPY(pEncodedPayload + nExtra, pMsgPayload, nBytes - nExtra);
               }
               break;
      
            case AEE_ENC_UNICODE:      
            case AEE_ENC_EUC_CN: 
               if (IsMsgPackable(pMsgPayload, nBytes, nEncoding, FALSE))
               {
                  nBytes /= sizeof(AECHAR);
                  nBytes += nExtra;
                  *pnBytes = nBytes;               
                  *pnPadding = 0;
                  if (pEncodedPayload)
                  {                  
                     byte * pBuffer = NULL;
                     if ((pBuffer = MALLOC(nBytes + 1)) != NULL)
                     {
                        if (pszBrewPrefix)
                           MEMCPY(pBuffer, pszBrewPrefix, nExtra);

                        if (pMsgPayload)
                           WSTRTOSTR((AECHAR*)pMsgPayload, (char*)(pBuffer + nExtra), nBytes - nExtra + 1);

                        MEMCPY(pEncodedPayload, pBuffer, nBytes);

                        FREE(pBuffer);
                     }
                     else
                        nErr = ENOMEMORY;
                  }
               }
               else
                  nErr = EFAILED;
               break;

            default:
               nErr = EFAILED;
         }
         break;

      case AEESMS_ENC_UNICODE:
         switch (nEncoding)
         {
            case AEE_ENC_ISOLATIN1:
               nBytes *= sizeof(AECHAR);
               nBytes += nExtra;
               *pnBytes = nBytes;
               *pnPadding = 0;

               if (pEncodedPayload)
               {
                  byte * pBuffer = NULL;
                  if ((pBuffer = MALLOC(nBytes + sizeof(AECHAR))) != NULL)
                  {
                     if (pszBrewPrefix)
                        MEMCPY(pBuffer, pszBrewPrefix, nExtra);

                     if (pMsgPayload)
                        STRTOWSTR((char*)pMsgPayload, (AECHAR*)(pBuffer + nExtra), nBytes - nExtra + sizeof(AECHAR));

                     EncodeNetworkOrder((AECHAR*)pBuffer, nBytes, (AECHAR*)pEncodedPayload);

                     FREE(pBuffer);
                  }
                  else
                     nErr = ENOMEMORY;
               }
               break;
            
            case AEE_ENC_UNICODE:
            case AEE_ENC_EUC_CN:
               nBytes += nExtra;
               *pnBytes = nBytes;
               *pnPadding = 0;

               if (pEncodedPayload)
               {               
                  byte * pBuffer = NULL;
                  if ((pBuffer = MALLOC(nBytes)) != NULL)
                  {
                     if (pszBrewPrefix)
                        MEMCPY(pBuffer, pszBrewPrefix, nExtra);

                     if (pMsgPayload)
                        MEMCPY(pBuffer + nExtra, pMsgPayload, nBytes - nExtra);

                     EncodeNetworkOrder((AECHAR*)pBuffer, nBytes, (AECHAR*)pEncodedPayload);

                     FREE(pBuffer);
                  }
                  else
                     nErr = ENOMEMORY;
               }
               break;

            default:
               nErr = EFAILED;
         }
         break;

      default:
         if (Xlate_SMSEncToAEEEnc(nSMSEncoding) == nEncoding)
         {
            nBytes += nExtra;
            *pnBytes = nBytes;
            *pnPadding = 0;

            if (pEncodedPayload)
            {
               if (pszBrewPrefix)
                  MEMCPY(pEncodedPayload, pszBrewPrefix, nExtra);

               if (pMsgPayload)
                  MEMCPY(pEncodedPayload + nExtra, pMsgPayload, nBytes - nExtra);
            }
         }
         else
            nErr = EFAILED;
   }
   return nErr;
}

/*=================================================================================
Size in bits of data type
=================================================================================*/
#define bitsize(type) (sizeof(type) * 8)

/*=================================================================================
Create a mask of bits sized to the number of bits in the given type
=================================================================================*/
#define copymask(type) ((0xffffffff) >> (32 - bitsize(type)))

/*=================================================================================
Masks the bits in data at the given offset for given number of width bits
=================================================================================*/
#define MASK(width, offset, data) \
   (((width) == bitsize(data)) ? (data) :   \
   ((((copymask(data) << (bitsize(data) - ((width) % bitsize(data)))) & copymask(data)) >>  (offset)) & (data)))

/*=================================================================================
Same as the macro MASK except also shifts the data in the result by the given number 
of shift bits
=================================================================================*/
#define MASK_AND_SHIFT(width, offset, shift, data)  \
                  ((((signed) (shift)) < 0) ?       \
                    MASK((width), (offset), (data)) << -(shift) :  \
                    MASK((width), (offset), (data)) >>  (((unsigned) (shift))))

/*============================================================================

FUNCTION UNPACKBYTES

DESCRIPTION
  Given a buffer and an offset, unpacks the requested number of bits into
  a byte

DEPENDENCIES
  None
   
RETURN VALUE
  Unpacked item

SIDE EFFECTS
  None
   
============================================================================*/
static byte UNPACKBYTES(byte *src, word pos, word len)
{
   byte result = 0;
   int rshift = 0;

   src += pos/8;
   pos %= 8;
   
   rshift = MAX( 8 - (pos + len), 0);

   if ( rshift > 0 ) {

     result = (byte)MASK_AND_SHIFT(len, pos, rshift, *src);
  
   } else {

     result = (byte)MASK(8-pos, pos, *src);
     src++;
     len -= 8 - pos;

      if ( len > 0 ) result = ( result<<len ) | (*src >> (8-len));  // if any bits left
   }

   return result;
}

/*===========================================================================
Function: DecodePackedString

Description:
   Unpack an byte array containing 7-bit packed characters into a character
   array.

Parameter(s):
   byte *pSrc    [ in ]
   int i         [ in ]  Number of 7-bit characters in the array. 
   char *pszDst  [ out ] Pointer to destination character array.

Return Value:  None
Comments:      None
Side Effects:  None
See Also:      None
===========================================================================*/
static void DecodePackedString(byte *pSrc, int i, char *pszDst)
{
   byte     b;
   uint16   wPos;

   FARF(SMS, ("==>  DecodePackedString"));

   for (wPos = 0; i ; i--, wPos += 7) {
      b = UNPACKBYTES((byte *)pSrc,wPos,(word)7);
      *(pszDst++) = (char)b;
   }
}

/*===========================================================================
Function: DecodeString

Description:
   Copy an array of bytes into an array of characters.

Parameter(s):
   byte *pSrc    [ in ]
   int i         [ in ]  Length of byte array. 
   char *pszDst  [ out ] Pointer to destination character array.

Return Value:  None
Comments:      None
Side Effects:  None
See Also:      None
===========================================================================*/
static void DecodeString(byte *pSrc, int i, char *pszDst)
{
   byte  bchar;

   FARF(SMS, ("==>  DecodeString"));

   for (; i; i--, pSrc++) {
      bchar = *pSrc;
      *(pszDst++) = (char)bchar;
   }
}

/*===========================================================================
Function: DecodeNetworkOrder

Description: 
   Take a network order unicode character and converts to host order unicode character.

Prototype:
   void DecodeNetworkOrder(byte *pbSrc, int i, byte *pszDst)

Parameter(s):  

Return Value:  None
Comments:      None
Side Effects:  None
See Also:      None
===========================================================================*/
static void DecodeNetworkOrder(byte *pbSrc, int i, byte *pszDst)
{
   uint16  achar;

   FARF(SMS, ("==>  DecodeNetworkOrder"));

   // previous code had alignment issues when pbSrc or pszDst were not on
   // word boundries. thus, we have to do it a byte at a time.
   for (i = i/2; i; i--) {
      achar = (*(pbSrc++) << 8);
      achar |= *(pbSrc++);
      achar = NTOHS(achar);
      *(pszDst++) = (achar & 0xFF00) >> 8;
      *(pszDst++) = achar&0xFF;
   }
}

/* --------------------------------------------------
** Masks the number of bits give by length starting
** at the given offset.  Unlike MASK and MASK_AND_SHIFT,
** this macro only creates that mask, it does not
** operate on the data buffer
** -------------------------------------------------- */
#define MASK_B(offset, len) \
  ((0xff >> offset) & (0xff << (8 - (offset + len))))

/*============================================================================

FUNCTION PACKBYTES

DESCRIPTION
  Packs the given byte into the destination at the given offset for the
  given number of length bits

DEPENDENCIES
  None
   
RETURN VALUE
  None

SIDE EFFECTS
  None
   
============================================================================*/
static void PACKBYTES(
   byte src, 
   byte dst[], 
   word pos, 
   word len 
)
{
   word   t_pos = pos % 8;
   word   bits  = 8 - t_pos;

   dst += (pos+len-1)/8;

   if ( bits >= len )
   {
       *dst &= (byte) ~MASK_B(t_pos, len);
       *dst |= (byte) (MASK_B(t_pos, len) & (src << (bits - len)));
   }
   else /* len > bits */
   {
       dst--;
       *dst &= (byte) ~MASK_B(t_pos, bits);
       *dst |= (byte) (MASK_B(t_pos, bits) & (src >> (len - bits)));

       dst++;
       *dst &= (byte) ~MASK_B(0, (len - bits));
       *dst |= (byte) (MASK_B(0, (len - bits)) & (src << (8 - (len - bits))));
   }
}

/*===========================================================================
Function: PackString

Description:
   Packs a character array into a byte array containing 7-bit packed characters .

Parameter(s):
   char *pText    [ in ] Character array
   int nText      [ in ]  Number of characters in the character array. 
   byte *pBuffer  [ out ] Pointer to destination byte array.

Return Value:  None
Comments:      None
Side Effects:  None
See Also:      None
===========================================================================*/
void PackString(char * pText, int nText, byte * pBuffer)
{
   int i;
   word bit_pos = 0;

   if (pBuffer) 
   {
      for (i = 0; i < (int)nText; i++)
      {
         PACKBYTES((pText[i]), (byte *)pBuffer, bit_pos, 7);
         bit_pos += 7;
      }
                 
      if (bit_pos % 8 != 0)
      {  
         PACKBYTES(0, (byte *)pBuffer, bit_pos, (word)(8 - bit_pos % 8));               
         bit_pos += 8 - bit_pos % 8;            
      }
   }
}

/*===========================================================================
Function: EncodeNetworkOrder

Description: 
   Take a host order Unicode character and converts
   to network order Unicode character.

Prototype:
   void EncodeNetworkOrder(AECHAR * pSrc, int i, uint16 * pszDst);

Parameter(s):  

Return Value:  None
Comments:      None
Side Effects:  None
See Also:      None
===========================================================================*/
static void EncodeNetworkOrder(AECHAR * pSrc, int i, uint16 * pszDst)
{
   uint16  uchar;

   for (i = i/2; i; i--, pSrc++) {
      uchar = HTONS(*pSrc);
      *(pszDst++) = uchar;
   }
}

/*========================================================================== 
Local Methods
==========================================================================*/
/*===========================================================================

Function: InvokeAppCb

Description:
   Invokes app callback
 
===========================================================================*/
static void InvokeAppCb(void * pData)
{
   SMSAppCbData * pAppCbData = (SMSAppCbData *)pData;
  
   AEE_ResumeCallback(pAppCbData->pcb, pAppCbData->pac);
}

/*===========================================================================

Function: ReleaseAppCb

Description:
   Releases app callback
 
===========================================================================*/
static void ReleaseAppCb(AEECallback * pcb)
{  
   if(pcb->pfnCancel != ReleaseAppCb)
      return;

   AEEKCB_DELETEIF(&((SMSAppCbData *)pcb->pCancelData)->kcb);
}

/*===========================================================================

Function: LinkAppCb

Description:
   Links app callback to system
 
===========================================================================*/
static int LinkAppCb(AEECallback * pcb, SMSAppCbData ** ppAppCbData)
{
   int nErr = SUCCESS;
   AEECallback * pcbLinked = NULL;
   SMSAppCbData * pAppCbData = NULL;

   if ((pcbLinked = AEE_LinkSysCallbackEx(pcb, sizeof(SMSAppCbData))) == NULL)
   {
      nErr = ENOMEMORY;
      goto Done;
   }

   pAppCbData = (SMSAppCbData *)(pcbLinked + 1);
   pAppCbData->pac = AEE_GetAppContext();
   pAppCbData->pcb = pcbLinked;

   pcbLinked->pfnCancel = ReleaseAppCb;
   pcbLinked->pCancelData = pAppCbData;
   *ppAppCbData = pAppCbData;

   // Create kernel callback to be passed to OEM layer.
   if ((nErr = AEEkCB_New(&pAppCbData->kcb, InvokeAppCb, pAppCbData)) != SUCCESS)
      goto Done;

Done:
   return nErr;
}

/*===========================================================================

Function: InvokeAppReadMsgCb

Description:
   Invokes app callback for ISMSSTORAGE_ReadMsg
 
===========================================================================*/
static void InvokeAppReadMsgCb(void * pData)
{
   SMSAppReadMsgCbData * pAppReadMsgCbData = (SMSAppReadMsgCbData *)pData;
  
   if (*pAppReadMsgCbData->pnRv == SUCCESS)
   {
      OEMMsgToISMSMsg(&pAppReadMsgCbData->sMsgData, &pAppReadMsgCbData->sMsgPayload, pAppReadMsgCbData->ppISMSMsg);
   }

   AEE_ResumeCallback(pAppReadMsgCbData->pcb, pAppReadMsgCbData->pac);
}

/*===========================================================================

Function: ReleaseAppReadMsgCb

Description:
   Releases app callback for ISMSSTORAGE_ReadMsg
 
===========================================================================*/
static void ReleaseAppReadMsgCb(AEECallback * pcb)
{   
   if(pcb->pfnCancel != ReleaseAppReadMsgCb)
      return;

   AEEKCB_DELETEIF(&((SMSAppReadMsgCbData*)pcb->pCancelData)->kcb);
}

/*===========================================================================

Function: LinkAppReadMsgCb

Description:
   Links app callback for ISMSSTORAGE_ReadMsg to system
 
===========================================================================*/
static int LinkAppReadMsgCb(AEECallback * pcb, SMSAppReadMsgCbData ** ppAppReadMsgCbData)
{
   int nErr = SUCCESS;
   AEECallback * pcbLinked = NULL;
   SMSAppReadMsgCbData * pAppReadMsgCbData = NULL;

   if ((pcbLinked = AEE_LinkSysCallbackEx(pcb, sizeof(SMSAppReadMsgCbData) + MAX_MSG_LENGTH)) == NULL)
   {
      nErr = ENOMEMORY;
      goto Done;
   }

   pAppReadMsgCbData = (SMSAppReadMsgCbData *)(pcbLinked + 1);
   pAppReadMsgCbData->pac = AEE_GetAppContext();
   pAppReadMsgCbData->pcb = pcbLinked;

   pcbLinked->pfnCancel = ReleaseAppReadMsgCb;
   pcbLinked->pCancelData = pAppReadMsgCbData;

   *ppAppReadMsgCbData = pAppReadMsgCbData;

   // Create kernel callback to be passed to OEM layer.
   if ((nErr = AEEkCB_New(&pAppReadMsgCbData->kcb, InvokeAppReadMsgCb, pAppReadMsgCbData)) != SUCCESS)
      goto Done;

Done:
   return nErr;
}

/*===========================================================================

Function: InvokeAppEnumMsgCb

Description:
   Invokes app callback for ISMSSTORAGE_EnumNextMsg
 
===========================================================================*/
static void InvokeAppEnumMsgCb(void * pData)
{
   ISMSStorage * pme = NULL;
   SMSAppEnumMsgCbData * pAppEnumMsgCbData = (SMSAppEnumMsgCbData *)pData;
  
   // Get pme
   pme = pAppEnumMsgCbData->pISMSStorage;

   // Check if we could read message and message still has same tag as the one
   // being enumerated
   if ((SUCCESS == *pAppEnumMsgCbData->pnRv) &&
       ((AEESMS_TAG_NONE == pAppEnumMsgCbData->sMsgData.dwTag) || 
        (pAppEnumMsgCbData->sMsgData.dwTag == pme->mt)))
   {
      if (pAppEnumMsgCbData->ppISMSMsg)
      {
         OEMMsgToISMSMsg(&pAppEnumMsgCbData->sMsgData, 
                         &pAppEnumMsgCbData->sMsgPayload, 
                         pAppEnumMsgCbData->ppISMSMsg);
      }

      if (pAppEnumMsgCbData->pnIndex)
      {
         *pAppEnumMsgCbData->pnIndex = pme->pdwIndex[pme->dwOffset];
      }

      // Move offset by one
      pme->dwOffset++;

      AEE_ResumeCallback(pAppEnumMsgCbData->pcb, pAppEnumMsgCbData->pac);
   }
   else
   {
      // Guard against scenario where one client is enumerating where as
      // other has deleted some message

      // Move offset by one
      pme->dwOffset++;

      if (pme->dwOffset < pme->dwCount)
      {
         // Read Next Msg
         OEMSMS_ReadMsg(pme->st, pme->pdwIndex[pme->dwOffset], 
                        &pAppEnumMsgCbData->sMsgData, &pAppEnumMsgCbData->sMsgPayload, 
                        pAppEnumMsgCbData->pnRv, pAppEnumMsgCbData->kcb);
      }
      else
      {
         // End of Enumeration
         if (pAppEnumMsgCbData->ppISMSMsg)
         {
            *pAppEnumMsgCbData->ppISMSMsg = NULL;
         }

         if (pAppEnumMsgCbData->pnIndex)
         {
            *pAppEnumMsgCbData->pnIndex = 0xffffffff;
         }

         FREEIF(pme->pdwIndex);
         pme->pdwIndex = NULL;

         AEE_ResumeCallback(pAppEnumMsgCbData->pcb, pAppEnumMsgCbData->pac);
      }
   }
}

/*===========================================================================

Function: ReleaseAppEnumMsgCb

Description:
   Releases app callback for ISMSSTORAGE_EnumNextMsg
 
===========================================================================*/
static void ReleaseAppEnumMsgCb(AEECallback * pcb)
{   
   if(pcb->pfnCancel != ReleaseAppEnumMsgCb)
      return;

   AEEKCB_DELETEIF(&((SMSAppEnumMsgCbData*)pcb->pCancelData)->kcb);
}

/*===========================================================================

Function: LinkAppEnumMsgCb

Description:
   Links app callback for ISMSSTORAGE_EnumNextMsg to system
 
===========================================================================*/
static int LinkAppEnumMsgCb(AEECallback * pcb, SMSAppEnumMsgCbData ** ppAppEnumMsgCbData)
{
   int nErr = SUCCESS;
   AEECallback * pcbLinked = NULL;
   SMSAppEnumMsgCbData * pAppEnumMsgCbData = NULL;

   if ((pcbLinked = AEE_LinkSysCallbackEx(pcb, sizeof(SMSAppEnumMsgCbData) + MAX_MSG_LENGTH)) == NULL)
   {
      nErr = ENOMEMORY;
      goto Done;
   }

   pAppEnumMsgCbData = (SMSAppEnumMsgCbData *)(pcbLinked + 1);
   pAppEnumMsgCbData->pac = AEE_GetAppContext();
   pAppEnumMsgCbData->pcb = pcbLinked;

   pcbLinked->pfnCancel = ReleaseAppEnumMsgCb;
   pcbLinked->pCancelData = pAppEnumMsgCbData;

   *ppAppEnumMsgCbData = pAppEnumMsgCbData;

   // Create kernel callback to be passed to OEM layer.
   if ((nErr = AEEkCB_New(&pAppEnumMsgCbData->kcb, InvokeAppEnumMsgCb, pAppEnumMsgCbData)) != SUCCESS)
      goto Done;

Done:
   return nErr;
}

/*===========================================================================

Function: InvokeAppGetSrvIDsCb

Description:
   Invokes app callback for ISMSBCConfig_GetServiceIDs
 
===========================================================================*/
static void InvokeAppGetSrvIDsCb(void * pData)
{
   SMSAppGetSrvIDsCbData *pAppGetSrvIDsCbData = (SMSAppGetSrvIDsCbData *)pData;
   
   FARF(SMS, ("==>  InvokeAppGetSrvIDsCb"));

   if ((*pAppGetSrvIDsCbData->pnErr == SUCCESS) &&
       (*pAppGetSrvIDsCbData->pnCount != 0))
   {
      if ((*pAppGetSrvIDsCbData->ppsid = MALLOC(*pAppGetSrvIDsCbData->pnCount*sizeof(AEESMSBCSrvID))) != NULL)
      {
         // Pass through to OEM
         OEMSMSBCConfig_FillServiceIDs(*pAppGetSrvIDsCbData->ppsid, *pAppGetSrvIDsCbData->pnCount*sizeof(AEESMSBCSrvID));
      }
      else
      {
         *pAppGetSrvIDsCbData->pnErr = ENOMEMORY;
      }
   }

   // Finally Resume the Application 
   AEE_ResumeCallback(pAppGetSrvIDsCbData->pcb, pAppGetSrvIDsCbData->pac);
}

/*===========================================================================

Function: ReleaseAppGetSrvIDsCb

Description:
   Releases app callback for ISMSBCConfig_GetServiceIDs
 
===========================================================================*/
static void ReleaseAppGetSrvIDsCb(AEECallback * pcb)
{   
   if(pcb->pfnCancel != ReleaseAppGetSrvIDsCb)
      return;

   AEEKCB_DELETEIF(&((SMSAppGetSrvIDsCbData*)pcb->pCancelData)->kcb);
}

/*===========================================================================

Function: LinkAppGetSrvIDsCb

Description:
   Links app callback to system for ISMSBCConfig_GetServiceIDs
 
===========================================================================*/
static int LinkAppGetSrvIDsCb(AEECallback * pcb, SMSAppGetSrvIDsCbData ** ppAppGetSrvIDsCbData)
{
   int nErr = SUCCESS;
   AEECallback * pcbLinked = NULL;
   SMSAppGetSrvIDsCbData * pAppGetSrvIDsCbData = NULL;

   if ((pcbLinked = AEE_LinkSysCallbackEx(pcb, sizeof(SMSAppGetSrvIDsCbData))) == NULL)
   {
      nErr = ENOMEMORY;
   }
   else
   {
      pAppGetSrvIDsCbData = (SMSAppGetSrvIDsCbData *)(pcbLinked + 1);
      pAppGetSrvIDsCbData->pac = AEE_GetAppContext();
      pAppGetSrvIDsCbData->pcb = pcbLinked;

      pcbLinked->pfnCancel = (PFNCBCANCEL)ReleaseAppGetSrvIDsCb;
      pcbLinked->pCancelData = pAppGetSrvIDsCbData;
      *ppAppGetSrvIDsCbData = pAppGetSrvIDsCbData;

      // Create kernel callback to be passed to OEM layer.
      nErr = AEEkCB_New(&pAppGetSrvIDsCbData->kcb, InvokeAppGetSrvIDsCb, pAppGetSrvIDsCbData);
   }
   return nErr;
}

/*===========================================================================

Function: InvokeAppGetSrvOptsCb

Description:
   Invokes app callback for ISMSBCConfig_GetServiceOpts

===========================================================================*/
static void InvokeAppGetSrvOptsCb(void * pData)
{
   int nErr = SUCCESS;
   SMSAppGetSrvOptsCbData *pAppGetSrvOptsCbData = (SMSAppGetSrvOptsCbData *)pData;
   
   FARF(SMS, ("==>  InvokeAppGetSrvOptsCb"));

   if (*pAppGetSrvOptsCbData->pnErr == SUCCESS)
   {
      if ((nErr = ISHELL_CreateInstance(AEE_GetShell(), AEECLSID_SMSBCSRVOPTS, (void **)pAppGetSrvOptsCbData->ppi)) == SUCCESS)
      {
         if (PackSMSBCSrvOpts(&pAppGetSrvOptsCbData->sSMSBcSrvInfo, *pAppGetSrvOptsCbData->ppi) != TRUE)
         {
            *pAppGetSrvOptsCbData->pnErr = EBADPARM;
         }
      }
      else
      {
         *pAppGetSrvOptsCbData->pnErr = nErr;
      }
   }

   // Finally Resume the Application 
   AEE_ResumeCallback(pAppGetSrvOptsCbData->pcb, pAppGetSrvOptsCbData->pac);
}

/*===========================================================================

Function: ReleaseAppGetSrvOptsCb

Description:
   Releases app callback for ISMSBCConfig_GetServiceOpts
 
===========================================================================*/
static void ReleaseAppGetSrvOptsCb(AEECallback * pcb)
{   
   if(pcb->pfnCancel != ReleaseAppGetSrvOptsCb)
      return;

   AEEKCB_DELETEIF(&((SMSAppGetSrvOptsCbData*)pcb->pCancelData)->kcb);
}

/*===========================================================================

Function: LinkAppGetSrvOptsCb

Description:
   Links app callback to system for ISMSBCConfig_GetServiceOpts
 
===========================================================================*/
static int LinkAppGetSrvOptsCb(AEECallback * pcb, SMSAppGetSrvOptsCbData ** ppAppGetSrvOptsCbData)
{
   int nErr = SUCCESS;
   AEECallback * pcbLinked = NULL;
   SMSAppGetSrvOptsCbData * pAppGetSrvOptsCbData = NULL;

   if ((pcbLinked = AEE_LinkSysCallbackEx(pcb, sizeof(SMSAppGetSrvOptsCbData))) == NULL)
   {
      nErr = ENOMEMORY;
   }
   else
   {
      pAppGetSrvOptsCbData = (SMSAppGetSrvOptsCbData *)(pcbLinked + 1);
      pAppGetSrvOptsCbData->pac = AEE_GetAppContext();
      pAppGetSrvOptsCbData->pcb = pcbLinked;

      pcbLinked->pfnCancel = (PFNCBCANCEL)ReleaseAppGetSrvOptsCb;
      pcbLinked->pCancelData = pAppGetSrvOptsCbData;
      *ppAppGetSrvOptsCbData = pAppGetSrvOptsCbData;

      // Create kernel callback to be passed to OEM layer.
      nErr = AEEkCB_New(&pAppGetSrvOptsCbData->kcb, InvokeAppGetSrvOptsCb, pAppGetSrvOptsCbData);
   }
   return nErr;
}

/*===========================================================================

Function: InvokeAppGetAllSrvOptsCb

Description:
   Invokes app callback

===========================================================================*/
static void InvokeAppGetAllSrvOptsCb(void * pData)
{
   SMSAppGetAllSrvOptsCbData *pAppGetAllSrvOptsCbData = (SMSAppGetAllSrvOptsCbData *)pData;
   
   FARF(SMS, ("==>  CSMSBCConfig_SrvInfoCb"));

   if ((*pAppGetAllSrvOptsCbData->pnErr == SUCCESS) &&
       (*pAppGetAllSrvOptsCbData->pnCount != 0))
   {   
      OEMSMSBCSrvInfo *pSrvInfo = NULL;

      if (((pSrvInfo = MALLOC(*pAppGetAllSrvOptsCbData->pnCount*sizeof(OEMSMSBCSrvInfo))) != NULL) &&
          ((*pAppGetAllSrvOptsCbData->pppi = MALLOC(*pAppGetAllSrvOptsCbData->pnCount*sizeof(ISMSBCSrvOpts*))) != NULL))
      {
         int nErr = SUCCESS;
         uint32 i = 0;

         // Pass through to OEM
         OEMSMSBCConfig_FillServiceInfo(pSrvInfo, *pAppGetAllSrvOptsCbData->pnCount*sizeof(OEMSMSBCSrvInfo));

         for (i = 0; 
              ((i < *pAppGetAllSrvOptsCbData->pnCount) && (*pAppGetAllSrvOptsCbData->pnErr == SUCCESS)); 
              i++)
         {
            if ((nErr = ISHELL_CreateInstance(AEE_GetShell(), AEECLSID_SMSBCSRVOPTS, (void **)pAppGetAllSrvOptsCbData->pppi[i])) == SUCCESS)
            {
               if (PackSMSBCSrvOpts(&pSrvInfo[i], *pAppGetAllSrvOptsCbData->pppi[i]) != TRUE)
               {
                  *pAppGetAllSrvOptsCbData->pnErr = EBADPARM;
               }
            }
            else
            {
               *pAppGetAllSrvOptsCbData->pnErr = nErr;
            }
         }

         if (*pAppGetAllSrvOptsCbData->pnErr != SUCCESS)
         {
            for (i = 0; i < *pAppGetAllSrvOptsCbData->pnCount; i++)
            {
               if (*pAppGetAllSrvOptsCbData->pppi[i])
               {
                  ISMSBCSRVOPTS_Release(*pAppGetAllSrvOptsCbData->pppi[i]);
               }
            }
            if (*pAppGetAllSrvOptsCbData->pppi)
            {
               FREE(*pAppGetAllSrvOptsCbData->pppi);
               *pAppGetAllSrvOptsCbData->pppi = NULL;
            }
         }
      }
      else
      {
         *pAppGetAllSrvOptsCbData->pnErr = ENOMEMORY;
      }

      FREEIF(pSrvInfo);
   }

   // Finally Resume the Application 
   AEE_ResumeCallback(pAppGetAllSrvOptsCbData->pcb, pAppGetAllSrvOptsCbData->pac);
}

/*===========================================================================

Function: ReleaseAppGetAllSrvOptsCb

Description:
   Releases app callback for ISMSBCConfig_GetAllServiceOpts
 
===========================================================================*/
static void ReleaseAppGetAllSrvOptsCb(AEECallback * pcb)
{   
   if(pcb->pfnCancel != ReleaseAppGetAllSrvOptsCb)
      return;

   AEEKCB_DELETEIF(&((SMSAppGetAllSrvOptsCbData*)pcb->pCancelData)->kcb);
}

/*===========================================================================

Function: LinkAppGetAllSrvOptsCb

Description:
   Links app callback to system for ISMSBCConfig_GetAllServiceOpts
 
===========================================================================*/
static int LinkAppGetAllSrvOptsCb(AEECallback * pcb, SMSAppGetAllSrvOptsCbData ** ppAppGetAllServiceOptsCbData)
{
   int nErr = SUCCESS;
   AEECallback * pcbLinked = NULL;
   SMSAppGetAllSrvOptsCbData * pAppGetAllServiceOptsCbData = NULL;

   if ((pcbLinked = AEE_LinkSysCallbackEx(pcb, sizeof(SMSAppGetAllSrvOptsCbData))) == NULL)
   {
      nErr = ENOMEMORY;
   }
   else
   {
      pAppGetAllServiceOptsCbData = (SMSAppGetAllSrvOptsCbData *)(pcbLinked + 1);
      pAppGetAllServiceOptsCbData->pac = AEE_GetAppContext();
      pAppGetAllServiceOptsCbData->pcb = pcbLinked;

      pcbLinked->pfnCancel = (PFNCBCANCEL)ReleaseAppGetAllSrvOptsCb;
      pcbLinked->pCancelData = pAppGetAllServiceOptsCbData;
      *ppAppGetAllServiceOptsCbData = pAppGetAllServiceOptsCbData;

      // Create kernel callback to be passed to OEM layer.
      nErr = AEEkCB_New(&pAppGetAllServiceOptsCbData->kcb, InvokeAppGetAllSrvOptsCb, pAppGetAllServiceOptsCbData);
   }
   return nErr;
}

/*===========================================================================

Function: ISMSMsgToOEMMsg

Description:
   Converts ISMSMsg to OEMMsgData and OEMMsgPayload
 
===========================================================================*/
static int ISMSMsgToOEMMsg(ISMSMsg * pISMSMsg, OEMMsgData ** ppMsgData, OEMMsgPayload ** ppMsgPayload)
{
   SMSMsgOpt smo;
   AEEDeviceInfo di;
   OEMMsgData * pMsgData = NULL;
   OEMMsgPayload * pMsgPayload = NULL;
   int nMaxPayloadLength, nErr = EFAILED, nPayloadBytes = 0, nBytes = 0, nEncoding;
   byte * pBuffer = NULL;
   char * pszBrewPrefix = NULL;
   AEESMSEncType nSMSEncoding;

   if (OEM_GetConfig(CFGI_SMS_MAX_PAYLOAD_LENGTH, &nMaxPayloadLength, sizeof(nMaxPayloadLength)) != SUCCESS)
   {   
      FARF(SMS, ("Max Payload Length Not Configured"));
      nErr = EFAILED;
      goto Done;
   }
   
   // Get MO SMS Encoding
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_MOSMS_ENCODING, &smo) == SUCCESS)
      nSMSEncoding = (uint32)smo.pVal;
   else
   {
      // Get default MO SMS Encoding
      if (OEM_GetConfig(CFGI_SMS_DEFAULT_MO_ENC, &nSMSEncoding, sizeof(nSMSEncoding)) != SUCCESS)
      {   
         FARF(SMS, ("MO SMS Encoding Not Specified"));
         nErr = EFAILED;
         goto Done;
      }
   }

   // Calculate size required for UDH data
   UnpackUDHMsgOpts(pISMSMsg, &nBytes, NULL);

   if (((pMsgData = MALLOC(sizeof(OEMMsgData) + nBytes)) == NULL) ||
       ((pMsgPayload = MALLOC(sizeof(OEMMsgPayload) + nMaxPayloadLength)) == NULL))
   {
      nErr = ENOMEMORY;
      goto Done;
   }

   // Set bytes for UDH data
   pMsgData->nBytes = nBytes;
   nBytes = 0;

   MEMSET(&di, 0, sizeof(di));

   // Get Destination Device/Email Address
   if (((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_TO_DEVICE_SZ, &smo)) == SUCCESS) ||
       ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_TO_EMAIL_SZ, &smo)) == SUCCESS))
   {
      STRNCPY(pMsgData->szMOFromMTTo, (char*)smo.pVal, STRLEN((char*)smo.pVal));
      pMsgData->bIsMOSMS = TRUE;
   }
   else if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_FROM_DEVICE_SZ, &smo)) == SUCCESS)
   {
      STRNCPY(pMsgData->szMOFromMTTo, (char*)smo.pVal, STRLEN((char*)smo.pVal));
      pMsgData->bIsMOSMS = FALSE;
   }
   else
   {
      FARF(SMS, ("Message doesn't contain source/destination"));
   }

   // See if there is a destination class or handler
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_BREW_CLASS, &smo) == SUCCESS)
   {
      char szBuffer[32];
      nBytes = SNPRINTF(szBuffer, sizeof(szBuffer), "%s%.8x:", BREW_SMS_PREFIX, (AEECLSID)smo.pVal);
      if ((pszBrewPrefix = MALLOC(nBytes + 1)) != NULL)
         STRNCPY(pszBrewPrefix, szBuffer, nBytes);
      else
      {
         nErr = ENOMEMORY;
         goto Done;
      }
      pMsgData->bIsBREWDirectedSMS = TRUE;
   }
   else if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_BREW_HANDLER_SZ, &smo) == SUCCESS)
   {
      boolean bSpace = FALSE;

      // Account for //%s:
      nBytes = STRLEN((char*)smo.pVal) + 3;
      
      if (nBytes & 0x1)
      {
         bSpace = TRUE;
         nBytes++;
      }
      
      if ((pszBrewPrefix = MALLOC(nBytes + 1)) != NULL)
      {
         if (bSpace)
            SNPRINTF(pszBrewPrefix, nBytes + 1, "// %s:", (char*)smo.pVal);
         else
            SNPRINTF(pszBrewPrefix, nBytes + 1, "//%s:", (char*)smo.pVal);
      }
      else
      {
         nErr = ENOMEMORY;
         goto Done;
      }
      pMsgData->bIsBREWDirectedSMS = TRUE;
   }

   // Get Message Payload Encoding
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_ENCODING, &smo)) == SUCCESS)
      nEncoding = (uint32)smo.pVal;
   else
   {
      // Assume Device Encoding by default
      di.wStructSize = sizeof(di);
      AEE_GetDeviceInfo(&di);
      nEncoding = di.wEncoding; 
   }
   
   // Get Message Payload
   if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_SZ, &smo) == SUCCESS)
   {
      pBuffer = (byte*)smo.pVal;
      nPayloadBytes = STRLEN((char*)smo.pVal);
   }
   else if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_WSZ, &smo) == SUCCESS)
   {
      pBuffer = (byte*)smo.pVal;
      nPayloadBytes = WSTRLEN((AECHAR*)smo.pVal)*sizeof(AECHAR);
   }
   else if (ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PAYLOAD_BINARY, &smo) == SUCCESS)
   {
      pBuffer = (byte*)WEBOPT_GETVARBUFFERDATA(smo.pVal);
      nPayloadBytes = WEBOPT_GETVARBUFFERLENGTH(smo.pVal);
   }
   else
   {
      FARF(SMS, ("Message doesn't contain payload"));
   }

   if (((nErr = GetEncodedMsgPayload(pszBrewPrefix, pBuffer, nPayloadBytes, nEncoding, nSMSEncoding, NULL, &pMsgPayload->nBytes, &pMsgPayload->nPadding)) == SUCCESS) &&
       (nMaxPayloadLength >= pMsgPayload->nBytes) &&
       ((nErr = GetEncodedMsgPayload(pszBrewPrefix, pBuffer, nPayloadBytes, nEncoding, nSMSEncoding, pMsgPayload->data, &pMsgPayload->nBytes, &pMsgPayload->nPadding)) == SUCCESS))
   {
      pMsgPayload->nEncoding = nSMSEncoding;
   }
   else
      goto Done;
      
   // Get callback number or SC Address
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_CALLBACK_SZ, &smo)) == SUCCESS)
   {      
      pMsgData->bIsSCAddress = FALSE;
      STRNCPY(pMsgData->szCallback, (char*)smo.pVal, STRLEN((char*)smo.pVal));
   }
   else if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_SC_ADDRESS_SZ, &smo)) == SUCCESS)
   {
      pMsgData->bIsSCAddress = TRUE;
      STRNCPY(pMsgData->szCallback, (char*)smo.pVal, STRLEN((char*)smo.pVal));
   }

   // Get Message Language
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_LANG, &smo)) == SUCCESS)
      pMsgData->dwLang = (uint32)smo.pVal;
   else
   {
      // Assume Device Language by default
      if (!di.wStructSize)
      {
         di.wStructSize = sizeof(AEEDeviceInfo);
         AEE_GetDeviceInfo(&di);
      }

      pMsgData->dwLang = di.dwLang; 
   }

   // Get Message Priority
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PRIORITY, &smo)) == SUCCESS)
      pMsgData->nPriority = (uint32)smo.pVal;
   else
      pMsgData->nPriority = SMS_PRIORITY_NORMAL; // Assume Normal by default

   // Get Message Privacy
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_PRIVACY, &smo)) == SUCCESS)
      pMsgData->nPrivacy = (uint32)smo.pVal;
   else
      pMsgData->nPrivacy = SMS_PRIVACY_NORMAL; // Assume Normal by default

   // Get Message Delivery Ack Request Status
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_REQUEST_DL_ACK, &smo)) == SUCCESS)
      pMsgData->bRequestDLAck = ((uint32)smo.pVal ? TRUE : FALSE);
   else
      pMsgData->bRequestDLAck = FALSE; // Assume that DL ack isn't requested by default

   // Get Message User Ack Request Status
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_REQUEST_USER_ACK, &smo)) == SUCCESS)
      pMsgData->bRequestUserAck = ((uint32)smo.pVal ? TRUE : FALSE);
   else
      pMsgData->bRequestUserAck = FALSE; // Assume that User ack isn't requested by default

   // Get Message Read Ack Request Status
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_REQUEST_READ_ACK, &smo)) == SUCCESS)
      pMsgData->bRequestReadAck = ((uint32)smo.pVal ? TRUE : FALSE);
   else
      pMsgData->bRequestReadAck = FALSE; // Assume that Read ack isn't requested by default
   
   // Get Message DL Ack Status
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_DL_ACK, &smo)) == SUCCESS)
      pMsgData->bIsDLAck = ((uint32)smo.pVal ? TRUE : FALSE);
   else
      pMsgData->bIsDLAck = FALSE; // Assume that message isn't Delivery Ack by default

   // Get Message User Ack Status
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_USER_ACK, &smo)) == SUCCESS)
      pMsgData->bIsUserAck = ((uint32)smo.pVal ? TRUE : FALSE);
   else
      pMsgData->bIsUserAck = FALSE; // Assume that message isn't User Ack by default

   // Get Message Read Ack Status
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_READ_ACK, &smo)) == SUCCESS)
      pMsgData->bIsReadAck = ((uint32)smo.pVal ? TRUE : FALSE);
   else
      pMsgData->bIsReadAck = FALSE; // Assume that message isn't Read Ack by default

   // Get Message Tag
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_TAG, &smo)) == SUCCESS)
      pMsgData->dwTag = (uint32)smo.pVal;
   else
      pMsgData->dwTag = (pMsgData->bIsMOSMS ? AEESMS_TAG_MO_NOT_SENT : AEESMS_TAG_MT_NOT_READ);

   // Get message type
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_MSGTYPE, &smo)) == SUCCESS)
      pMsgData->ts = Xlate_MTtoTS((int)smo.pVal);
   else
      pMsgData->ts = (pMsgData->nBytes ? SMS_TELESERVICE_WEMT : SMS_TELESERVICE_CMT_95);

   // Get Message ID
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_MSG_ID, &smo)) == SUCCESS)
      pMsgData->dwMsgID = (uint32)smo.pVal;
   else
      pMsgData->dwMsgID = 0;

   // Get message time stamp
   if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_TIMESTAMP, &smo)) == SUCCESS)
      pMsgData->dwTimeStamp = JULIANTOSECONDS((JulianType*)smo.pVal);
   else
      pMsgData->dwTimeStamp = 0;

   if (!pMsgData->bIsMOSMS)
   {
      // Get message count for voice mail indication
      if ((nErr = ISMSMSG_GetOpt(pISMSMsg, MSGOPT_NUM, &smo)) == SUCCESS)
         pMsgData->nNumMsgs = (int)smo.pVal;
      else
         pMsgData->nNumMsgs = 0;
   }

   // Get UDH data
   UnpackUDHMsgOpts(pISMSMsg, &pMsgData->nBytes, pMsgData->data);

   nErr = SUCCESS;

Done:
   FREEIF(pszBrewPrefix);

   if (nErr == SUCCESS)
   {   
      *ppMsgData = pMsgData;
      *ppMsgPayload = pMsgPayload;
   }
   else
   {
      FREEIF(pMsgData);
      FREEIF(pMsgPayload);
   }
   return nErr;
}

/*===========================================================================

Function: OEMMsgToISMSMsg

Description:
   Converts OEMMsgData and OEMMsgPayload to ISMSMsg
 
===========================================================================*/
static int OEMMsgToISMSMsg(OEMMsgData * pMsgData, OEMMsgPayload * pMsgPayload, ISMSMsg ** ppISMSMsg)
{
   int nErr;
   SMSMsgOpt smo[2];
   ISMSMsg * pISMSMsg = NULL;

   if (!pMsgData && !pMsgPayload)
      return EBADPARM;

   if ((nErr = ISHELL_CreateInstance(AEE_GetShell(), AEECLSID_SMSMSG, (void**)&pISMSMsg)) != SUCCESS)
      return nErr;
      
   MEMSET(smo, 0, sizeof(smo));

   smo[1].nId = MSGOPT_END;

   // Set for copying of options
   smo[0].nId = MSGOPT_COPYOPTS;
   smo[0].pVal = (void *)TRUE;
      
   ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);

   if (pMsgData)
   {     
      JulianType jt;
      MEMSET(&jt, 0, sizeof(jt));

      // Set message language
      smo[0].nId = MSGOPT_LANG;
      smo[0].pVal = (void*)pMsgData->dwLang;
      
      ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);

      // Set message priority
      smo[0].nId = MSGOPT_PRIORITY;
      smo[0].pVal = (void*)pMsgData->nPriority;
      
      ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);

      // Set message privacy
      smo[0].nId = MSGOPT_PRIVACY;
      smo[0].pVal = (void*)pMsgData->nPrivacy;
      
      ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);

      // Set callback number/SC address
      if (*pMsgData->szCallback)
      {
         smo[0].nId = pMsgData->bIsSCAddress ? MSGOPT_SC_ADDRESS_SZ : MSGOPT_CALLBACK_SZ;
         smo[0].pVal = (void*)pMsgData->szCallback;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set sender address
      if (*pMsgData->szMOFromMTTo)
      {
         smo[0].nId = (pMsgData->bIsMOSMS ? MSGOPT_TO_DEVICE_SZ : MSGOPT_FROM_DEVICE_SZ);
         smo[0].pVal = (void*)pMsgData->szMOFromMTTo;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set delivery ack request
      if (pMsgData->bRequestDLAck)
      {
         smo[0].nId = MSGOPT_REQUEST_DL_ACK;
         smo[0].pVal = (void*)pMsgData->bRequestDLAck;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set user ack request
      if (pMsgData->bRequestUserAck)
      {
         smo[0].nId = MSGOPT_REQUEST_USER_ACK;
         smo[0].pVal = (void*)pMsgData->bRequestUserAck;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set read ack request
      if (pMsgData->bRequestReadAck)
      {
         smo[0].nId = MSGOPT_REQUEST_READ_ACK;
         smo[0].pVal = (void*)pMsgData->bRequestReadAck;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set Delivery ack
      if (pMsgData->bIsDLAck)
      {
         smo[0].nId = MSGOPT_DL_ACK;
         smo[0].pVal = (void*)pMsgData->bIsDLAck;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set user ack
      if (pMsgData->bIsUserAck)
      {
         smo[0].nId = MSGOPT_USER_ACK;
         smo[0].pVal = (void*)pMsgData->bIsUserAck;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set read ack
      if (pMsgData->bIsReadAck)
      {
         smo[0].nId = MSGOPT_READ_ACK;
         smo[0].pVal = (void*)pMsgData->bIsReadAck;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      // Set message tag
      smo[0].nId = MSGOPT_TAG;
      smo[0].pVal = (void*)pMsgData->dwTag;
      
      ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);

      // Set user data headers
      PackUDHMsgOpts(pMsgData->data, pMsgData->nBytes, pISMSMsg);

      // Set message type
      smo[0].nId = MSGOPT_MSGTYPE;
      smo[0].pVal = (void*)Xlate_TStoMT(pMsgData->ts);
      
      ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);

      // Set message ID
      if (0 != pMsgData->dwMsgID)
      {
         smo[0].nId = MSGOPT_MSG_ID;
         smo[0].pVal = (void*)pMsgData->dwMsgID;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }  
         
      // Set message time stamp
      if (pMsgData->dwTimeStamp)
      {
         GETJULIANDATE(pMsgData->dwTimeStamp, &jt);
         smo[0].nId = MSGOPT_TIMESTAMP;
         smo[0].pVal = (void*)&jt;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      }

      if (!pMsgData->bIsMOSMS)
      {
         // Set message count for voice mail indication
         if (AEESMS_TYPE_VOICE_MAIL_NOTIFICATION == Xlate_TStoMT(pMsgData->ts))
         {
            smo[0].nId = MSGOPT_NUM;
            smo[0].pVal = (void*)pMsgData->nNumMsgs;
      
            ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
         }
      }
   }
      
   if(pMsgPayload) 
   {
      uint32 nBytes;

      byte * pText = AEE_GetDecodedTextString(pMsgPayload->data, pMsgPayload->nBytes, pMsgPayload->nEncoding, &pMsgPayload->nEncoding, &nBytes);

      if (pText)
      {   
         AEECLSID clsid = 0;
         char * pszBrewHandler = NULL;
         byte * pParsedText = pText;      // Initialize to pText
         
         // Set message encoding to an AEE encoding
         smo[0].nId = MSGOPT_PAYLOAD_ENCODING;
         smo[0].pVal = (void*)pMsgPayload->nEncoding;
      
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
      
         ParseBrewMsg((char*)pText, &clsid, &pszBrewHandler, &pParsedText);

         // Set BREW class or BREW handler
         if (clsid || pszBrewHandler)
         {
            if (pszBrewHandler)
            {
               smo[0].nId = MSGOPT_BREW_HANDLER_SZ;
               smo[0].pVal = (void*)pszBrewHandler;
               ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
            }
            else
            {
               smo[0].nId = MSGOPT_BREW_CLASS;
               smo[0].pVal = (void*)clsid;
               ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
            }

            // For BREW directed message, reduce nBytes by BREW specific prefix
            // This will reduce length by STRLEN("//BREW:<ClsId>:") for applet
            // directed and by STRLEN("//BREWHANDLER:") for handler directed message
            nBytes -= (STRLEN((char*)pText) + 1);
         }

         // Set message payload
         if ((pMsgPayload->nEncoding == AEE_ENC_UNICODE) ||
             (pMsgPayload->nEncoding == AEE_ENC_ISOLATIN1))
         {
            smo[0].nId = ((pMsgPayload->nEncoding == AEE_ENC_UNICODE) ? MSGOPT_PAYLOAD_WSZ : MSGOPT_PAYLOAD_SZ);
            smo[0].pVal = (void*)pParsedText;
            ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
         }
         else
         {
            char * pBuffer = NULL;
            if ((pBuffer = MALLOC(nBytes + sizeof(AECHAR))) != NULL)
            {
               WEBOPT_SETVARBUFFER(pBuffer, pParsedText, nBytes);
               smo[0].nId = MSGOPT_PAYLOAD_BINARY;
               smo[0].pVal = (void*)pBuffer;
               ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
               FREE(pBuffer);
            }
         }
         FREE(pText);
      }
   }
   
   *ppISMSMsg = pISMSMsg;
   return SUCCESS;
}

/*===========================================================================

Function: ParseBrewMsg

Description:
   Parses pszText to check if message is directed to a BREW app.
   //BREW:<ClsID>:<Payload> is changed to //BREW:<ClsID>'\0'<Payload>.
   //BREWHANDLER:<Payload> is changed to //BREWHANDLER'\0'<Payload>.
===========================================================================*/
static void ParseBrewMsg( char *pszText, AEECLSID *pCls, char ** ppszBrewHandler, byte **ppParsedText )
{
   char * psz = NULL;

   FARF(SMS, ("==>  ParseBrewMsg: %s", pszText));

   // Advance to the first token "//" on test devices. On non test devices, we assume
   // that BREW directed message is of //BREW:<clsid>:<payload> or //BREWHANDLER:<payload>
   // type
   if (pszText && AEE_IsDebugBREWSMS()) 
   {
      if ((psz = STRSTR(pszText,"//")) != NULL)
         pszText = psz;
   }

   // Check if message is of "//BREW:<clsid>:<payload>" type
   if(0 == STRNICMP(pszText,BREW_SMS_PREFIX,BREW_SMS_PREFIX_LEN))
   {
      psz = pszText + BREW_SMS_PREFIX_LEN;   // psz == "<clsid>:<payload>"

      pszText = STRCHR(psz, ':');            // pszText == ":<payload>"
      
      if(pszText)
      {
         *pszText++ = 0;                     // pszText == "<payload>", psz == "<clsid>"

         if (pCls)
            *pCls = (AEECLSID)STRTOUL(psz, NULL, 16);

         if (ppParsedText)
            *ppParsedText = (byte*)pszText;  // *ppParsedText == "<payload>"
      }
   }
   else if (0 == STRNCMP(pszText, "//", STRLEN("//")))
   {
      // Check if message is of "//BREWHANDLER:<payload>" type

      psz = pszText;                         // psz == "//BREWHANDLER:<payload>"

      pszText = STRCHR(psz, ':');            // pszText == ":<payload>"

      if (pszText) 
      {
         char * pszBrewHandler = NULL;

         *pszText++ = 0;                     // pszText == "<payload>", psz == "//BREWHANDLER"

         if(0 == STRNCMP(psz, "// ", STRLEN("// ")))
            pszBrewHandler = psz + STRLEN("// ");     // pszBrewHandler == "BREWHANDLER"
         else
            pszBrewHandler = psz + STRLEN("//");      // pszBrewHandler == "BREWHANDLER"

         if (ppszBrewHandler)
            *ppszBrewHandler = pszBrewHandler;

         if (pCls)
            *pCls = ISHELL_GetHandler(AEE_GetShell(), AEECLSID_APP, pszBrewHandler);
         
         if (ppParsedText)
            *ppParsedText = (byte*)pszText;       // *ppParsedText == "<payload>"
      }
   }
}

/*===========================================================================

Function: Xlate_TStoMT

Description:
   Translates OEM teleservice to AEE message type
 
===========================================================================*/
static int Xlate_TStoMT(int ts)
{
   int i;
   for (i = 0; i < sizeof(gsMsgTypeMap)/sizeof(MsgTypeMap); i++)
   {
      if (ts == gsMsgTypeMap[i].ts)
      {
         return gsMsgTypeMap[i].mt;
      }
   }
   return AEESMS_TYPE_UNKNOWN;
}

/*===========================================================================

Function: Xlate_MTtoTS

Description:
   Translates AEE message type to OEM teleservice
 
===========================================================================*/
static int Xlate_MTtoTS(int mt)
{
   int i;
   for (i = 0; i < sizeof(gsMsgTypeMap)/sizeof(MsgTypeMap); i++)
   {
      if (mt == gsMsgTypeMap[i].mt)
      {
         return gsMsgTypeMap[i].ts;
      }
   }
   return SMS_TELESERVICE_UNKNOWN;
}

/*===========================================================================

Function: Xlate_SMSEncToAEEEnc

Description:
   Translates SMS encoding to AEE encoding
 
===========================================================================*/
static int Xlate_SMSEncToAEEEnc(int se)
{
   int i;
   for (i = 0; i < sizeof(gsAEESMSEncMap)/sizeof(AEESMSEncMap); i++)
   {
      if (gsAEESMSEncMap[i].se == se)
      {
         return gsAEESMSEncMap[i].ae;
      }
   }

   // Pass whatever encoding we have assuming that it makes to client
   return se;
}

/*===========================================================================

Function: Xlate_AEEEncToSMSEnc

Description:
   Translates AEE encoding to SMS encoding
 
===========================================================================*/
static int Xlate_AEEEncToSMSEnc(int ae)
{   
   int i;
   for (i = 0; i < sizeof(gsAEESMSEncMap)/sizeof(AEESMSEncMap); i++)
   {
      if (gsAEESMSEncMap[i].ae == ae)
      {
         return gsAEESMSEncMap[i].se;
      }
   }

   // Pass whatever encoding we have assuming that it makes to client
   return ae;
}

/*===========================================================================

Function: IsMsgPackable

Description:
   Tests if message can be sent as packed 7/8 bit
 
===========================================================================*/
static boolean IsMsgPackable(byte * pData, int nBytes, int en, boolean b7bit)
{
   int i;
   boolean bReturn = FALSE;

   if (en == AEE_ENC_ISOLATIN1 && b7bit)
   { 
      char * pText = (char*)pData;

      for (i = 0; i < nBytes; i++)
      {
         if ((unsigned char)pText[i] > 0x7f)
            goto Done;
      }
      bReturn = TRUE;
   }
   else if (en == AEE_ENC_UNICODE)
   {
      AECHAR * pText = (AECHAR*)pData;

      if (nBytes & 0x1) // Check for Odd number of bytes
         goto Done;

      for (i = 0; i < nBytes/2; i++)
      {
         if ((unsigned)pText[i] > (unsigned)(b7bit ? 0x7f : 0xff))
            goto Done;
      }
      bReturn = TRUE;
   }

Done:
   return bReturn;
}

/*===========================================================================

Function: Xlate_UDHIDToMsgOpt

Description:
   Translates UDH ID to corresponding message option
 
===========================================================================*/
static int Xlate_UDHIDToMsgOpt(int id)
{
   int i;
   for (i = 0; i < sizeof(gsAEEMsgOptUDHIDMap)/sizeof(AEEMsgOptUDHIDMap); i++)
   {
      if (gsAEEMsgOptUDHIDMap[i].id == id)
      {
         return gsAEEMsgOptUDHIDMap[i].mo;
      }
   }

   // Pass whatever UDH ID we have assuming that it makes to client
   return id;
}

/*===========================================================================

Function: UnpackUDHMsgOpts

Description:
   Unpack the UDH MSGOPTS Structure to byte pointer. Can be used to calculate
   size required by specifying pBytes as NULL.

===========================================================================*/
static void UnpackUDHMsgOpts(ISMSMsg *pISMSMsg, int * pnBytes, byte * pBytes)
{
   int nIndex = 0, nLength = 0, nBytes = 0;   
   SMSMsgOpt smo;

   if (!pnBytes)
      return;

   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_CONCAT, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }
   
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_SPECIAL_SM, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }
         
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_PORT, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_TEXT_FORMAT, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }
   
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_PREDEFINED, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }
   
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_USERDEFINED, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_VAR_PICTURE, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_RFC822, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_OTHER, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      nLength = MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) + 2; // 2 bytes for Length
      if (pBytes && ((nBytes + nLength) <= *pnBytes))
      {
         MEMCPY(pBytes + nBytes, (byte*)(smo.pVal), nLength);
      }
      nBytes += nLength;
   }
   *pnBytes = nBytes;
}

/*===========================================================================

Function: PackUDHMsgOpts

Description:
   Pack the UDH MSGOPTS Structure from byte pointer

===========================================================================*/
static void PackUDHMsgOpts(byte * pBytes, int nBytes, ISMSMsg * pISMSMsg)
{
   if (pBytes && pISMSMsg && nBytes)
   {
      int nProcessed = 0;
      SMSMsgOpt smo[2];
      
      MEMSET(smo, 0, sizeof(smo));

      smo[1].nId = MSGOPT_END;

      // Set UDH options
      while (nProcessed < nBytes)
      {
         smo[0].nId = Xlate_UDHIDToMsgOpt(*(MSGOPT_GETUDHBUFFERDATA((byte*)(pBytes + nProcessed))));
         smo[0].pVal = (void*)(pBytes + nProcessed);
   
         ISMSMSG_AddOpt(pISMSMsg, (SMSMsgOpt*)smo);
         
         // Increment nProcessed by (bytes containing length + bytes containing data)
         nProcessed += (2 + MSGOPT_GETUDHBUFFERLENGTH((byte*)(pBytes + nProcessed)));
      }
   }
}

/*===========================================================================

Function: ComputeUDHSizeOnSMSPayload

Description:
   Computes number of bytes UDH will take on SMS payload

===========================================================================*/
static int ComputeUDHSizeOnSMSPayload(ISMSMsg * pISMSMsg)
{
   int nIndex = 0, nBytes = 0;   
   SMSMsgOpt smo;

   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_CONCAT, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      if (*(MSGOPT_GETUDHBUFFERDATA(smo.pVal)) == AEESMS_UDH_CONCAT_8)
      {       
         // Header ID Octet + Header Length Octet + Header Length
         nBytes += 5; 
      }
      else if (*(MSGOPT_GETUDHBUFFERDATA(smo.pVal)) == AEESMS_UDH_CONCAT_16)
      {
         // Header ID Octet + Header Length Octet + Header Length
         nBytes += 6; 
      }
   }
   
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_SPECIAL_SM, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length
      nBytes += 4;
   }
         
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_PORT, nIndex, &smo) == SUCCESS)
   {
      nIndex++;
      if (*(MSGOPT_GETUDHBUFFERDATA(smo.pVal)) == AEESMS_UDH_PORT_8)
      {
         // Header ID Octet + Header Length Octet + Header Length
         nBytes += 4; 
      }
      else if (*(MSGOPT_GETUDHBUFFERDATA(smo.pVal)) == AEESMS_UDH_PORT_16)
      {
         // Header ID Octet + Header Length Octet + Header Length
         nBytes += 6; 
      }
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_TEXT_FORMAT, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length
      nBytes += 5;
   }
   
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_PREDEFINED, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length
      nBytes += 4;
   }
   
   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_USERDEFINED, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length + 1 (for position octet)
      nBytes += MSGOPT_GETUDHBUFFERLENGTH(smo.pVal);
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_VAR_PICTURE, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length + 3 (for height, width and position octets)
      nBytes += MSGOPT_GETUDHBUFFERLENGTH(smo.pVal);
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_RFC822, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length
      nBytes += 3;
   }

   nIndex = 0;
   while (ISMSMSG_GetOptWithIndex(pISMSMsg, MSGOPT_UDH_OTHER, nIndex, &smo) == SUCCESS)
   {
      nIndex++;

      // Header ID Octet + Header Length Octet + Header Length
      nBytes += (MSGOPT_GETUDHBUFFERLENGTH(smo.pVal) - 1);
   }
   return (nBytes);
}

/*===========================================================================

Function: UnpackSMSBCSrvOpts

Description:
   Unpack the Broadcast Service Options to the OEMSMSBCSrvInfo Strcuture

Return Value:
   Indicates success / failure

===========================================================================*/
static boolean UnpackSMSBCSrvOpts(ISMSBCSrvOpts *pISMSBCSrvOpts, OEMSMSBCSrvInfo * pSrvInfo)
{
   SMSBCSrvOpts sso;  
   
   // Service ID
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_SERVICEID, &sso) == SUCCESS)
   {
      pSrvInfo->sSrvID = *((AEESMSBCSrvID *)sso.pVal);
   }
   else
   {
      return FALSE; // Mandatory Parameter - Hence return False
   }

   // Service Selected
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_SELECTED, &sso) == SUCCESS)
   {
      pSrvInfo->bSelected = (boolean)((uint32)sso.pVal);
   }
   else
   {
      pSrvInfo->bSelected = FALSE; // Default Value
   }

   // Service Priority
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_PRIORITY, &sso) == SUCCESS)
   {
      pSrvInfo->nPriority = (AEEMSGPriority)sso.pVal;
   }
   else
   {
      pSrvInfo->nPriority = AEESMS_PRIORITY_NORMAL; // Default Value
   }

   // Service Label
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_LABEL_SZ, &sso) == SUCCESS)
   {
      STRLCPY(pSrvInfo->szLabel, (char*)sso.pVal, sizeof(pSrvInfo->szLabel));
   }
   else
   {
      MEMSET(pSrvInfo->szLabel, 0, sizeof(pSrvInfo->szLabel));
   }

   // Service Label Encoding
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_LABEL_ENCODING, &sso) == SUCCESS)
   {
      pSrvInfo->nLabelEncoding = Xlate_AEEEncToSMSEnc((int)sso.pVal);
   }
   else
   {
      pSrvInfo->nLabelEncoding = Xlate_AEEEncToSMSEnc(AEE_ENC_ISOLATIN1); // Default Value
   }

   // Service Alert
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_ALERT, &sso) == SUCCESS)
   {
      pSrvInfo->nAlert = (AEESMSBCAlertType)sso.pVal;
   }
   else
   {
      pSrvInfo->nAlert = AEESMS_BC_ALERT_DEFAULT;  // Default Value
   }

   // Service Max Messages
   if (ISMSBCSRVOPTS_GetOpt(pISMSBCSrvOpts, SMSBCSRVOPT_MAX_MSGS, &sso) == SUCCESS)
   {
      pSrvInfo->nMaxMessages = (byte)((uint32)sso.pVal);
   }
   else
   {
      pSrvInfo->nMaxMessages = 10; // Default Value 
   }

   return TRUE;
}

/*===========================================================================

Function: PackSMSBCSrvOpts

Description:
   Pack the OEMSMSBCSrvInfo Strcuture to Broadcast Service Options

Return Value:
   Indicates success / failure

===========================================================================*/
static boolean PackSMSBCSrvOpts(OEMSMSBCSrvInfo * pSrvInfo, ISMSBCSrvOpts *pISMSBCSrvOpts)
{
   SMSBCSrvOpts sso[2];

   MEMSET(sso, 0, sizeof(sso));

   sso[1].nId = SMSBCSRVOPT_END;

   // Set for copying of options
   sso[0].nId = SMSBCSRVOPT_COPYOPTS;
   sso[0].pVal = (void *)TRUE;
      
   ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso);

   // Service ID
   sso[0].nId = SMSBCSRVOPT_SERVICEID;
   sso[0].pVal = (void*)&pSrvInfo->sSrvID;
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }
   
   // Service Selected
   sso[0].nId = SMSBCSRVOPT_SELECTED;
   sso[0].pVal = (void*)pSrvInfo->bSelected;
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }

   // Service Priority
   sso[0].nId = SMSBCSRVOPT_PRIORITY;
   sso[0].pVal = (void*)pSrvInfo->nPriority;
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }
     
   // Service Label 
   sso[0].nId = SMSBCSRVOPT_LABEL_SZ;
   sso[0].pVal = (void*)pSrvInfo->szLabel;
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }
     
   // Service Label Encoding 
   sso[0].nId = SMSBCSRVOPT_LABEL_ENCODING;
   sso[0].pVal = (void*)Xlate_SMSEncToAEEEnc(pSrvInfo->nLabelEncoding);
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }
     
   // Service Alert
   sso[0].nId = SMSBCSRVOPT_ALERT;
   sso[0].pVal = (void*)pSrvInfo->nAlert;
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }
     
   // Service Max Messages
   sso[0].nId = SMSBCSRVOPT_MAX_MSGS;
   sso[0].pVal = (void*)pSrvInfo->nMaxMessages;
   if (ISMSBCSRVOPTS_AddOpt(pISMSBCSrvOpts, (SMSBCSrvOpts*)sso) != SUCCESS)
   {
      return FALSE;
   }
     
   return TRUE;
}
