/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/msg.cpp $
 * $Revision: /main/ms-d/67 $
 *     $Date: 2013-09-25 15:19:14 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/msg.cpp$ 
 * 
 * /main/ms-d/67   2013-09-25 15:19:14   pmackent 
 * Remove warning in Windows CE6 
 * 
 * /main/ms-d/66   2013-09-13 09:27:07   pmackent 
 * Cell Broadcast for CDMA (IPIS100091379)
 *
 * /main/ms-d/65   2013-06-25 13:52:11   pmackent
 * Handle new syntax of AT^SMGL command for CDMA (IPIS100089198)
 *
 * /main/ms-d/64   2013-04-24 11:44:41   pmackent
 * New CDMA/CNMI setting and CDMA/CLCC workaround
 *
 * /main/ms-d/63   2013-01-23 14:54:50   pmackent
 * Workaround for wring "SIM not inserted" error at AT+CSCA?
 *
 * /main/ms-d/62   2013-01-08 11:16:14   pmackent
 * No AT+CMGW for EHS5 1.220
 *
 * /main/ms-d/61   2013-01-07 10:14:52   pmackent
 * Workaround for exit in AT+CPMS in EHS5
 *
 * /main/ms-d/60   2012-10-30 14:36:20   pmackent
 * Fix call status problems (voice and data) in CDMA on WinCE6/7
 *
 * /main/ms-d/59   2012-10-01 09:58:27   pmackent
 * Handle class 2 SMS for PTCRB 8.2.1 (IPIS100082634)
 *
 * /main/ms-d/58   2012-07-18 15:57:07   pmackent
 * Handle UCS2 parameter of AT+CSCA
 *
 * /main/ms-d/57   2012-07-13 13:04:29   pmackent
 * Changes for compatibility with Windows CE6/6
 *
 * /main/ms-d/56   2012-07-10 11:55:46   pmackent
 * Workaround for lost +CMT: URC.
 *
 * /main/ms-d/55   2012-04-18 13:47:02   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/54   2012-02-22 13:26:49   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/53   2012-02-16 13:44:56   ctna0256
 * Comply to CE6 and CE7 compiler warnings
 *
 * /main/ms-d/52   2012-02-14 14:52:15   ctna0256
 * Improved comments
 *
 * /main/ms-d/51   2012-02-09 16:44:25   ctna0256
 * Check for missed SMS with AT+CRSM on EHS5, small bug fixes
 *
 * /main/ms-d/50   2012-01-31 16:38:38   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/49   2011-09-26 13:34:44   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/48   2010-09-21 09:27:28   ctna0256
 * Workaround for wrong alpha when reading SMS
 *
 * /main/ms-d/47   2010-08-10 09:17:27   ctna0256
 * Prevent changing CNMI settings to <mt>=2, if disabled by registry
 *
 * /main/ms-d/46   2010-07-22 14:44:44   ctna0256
 * Allow setting of default behaviour for incoming text messages
 *
 * /main/ms-d/45   2010-03-12 13:18:56   ctna0256
 * Missing check for numbers of languages added
 *
 * /main/ms-d/44   2009-03-10 10:31:55   ctna0256
 * Corrected wrong placed generator erro option
 *
 * /main/ms-d/43   2009-02-23 13:16:08   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/42   2009-02-18 09:46:41   ctna0256
 * Prevent sending SMS status query when module is not ready
 *
 * /main/ms-d/41   2008-10-22 15:29:26   ctna0256
 * Check length of UDH element before copying
 *
 * /main/ms-d/40   2008-10-06 16:04:23   ctna0256
 * New options: Disable SMS completely; Use SMS reply address
 *
 * /main/ms-d/39   2008-09-16 16:20:19   ctna0256
 * New registry keys for retries on multi-part SMS
 *
 * /main/ms-d/38   2008-08-28 15:12:24   ctna0256
 * Forgotten "=" added
 *
 * /main/ms-d/37   2008-08-27 11:39:07   ctna0256
 * Optional filter of Type 0 SMS
 *
 * /main/ms-d/36   2008-08-22 10:24:34   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/35   2008-08-21 09:34:49   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/34   2008-05-14 14:51:59   mp49210
 * Workaround for problems when deleting cell broadcast channels
 *
 * /main/ms-d/33   2008-04-03 14:41:41   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/32   2008-02-28 14:10:17   mp49210
 * Invalid index error message as assynchonous response
 *
 * /main/ms-d/31   2008-02-04 14:56:13   mp49210
 * Send SMS workaround also for command retries
 *
 * /main/ms-d/30   2008-01-29 09:07:05   mp49210
 * Fix of occasional failure of RIL_GetSetMsgServiceOptions
 *
 * /main/ms-d/29   2007-12-05 13:57:01   mp49210
 * Proper use of debug prints
 *
 * /main/ms-d/28   2007-12-05 13:54:17   mp49210
 * Introduction of SMS persist link
 *
 * /main/ms-d/27   2007-11-29 15:56:26   mp49210
 * added parsing of <ackpdu> parameter
 *
 * /main/ms-d/26   2007-11-27 11:44:11   mp49210
 * Avoid wrong character set settings after failed sending of stored SMS
 *
 * /main/ms-d/25   2007-11-21 13:35:01   mp49210
 * Lint compatibility
 *
 * /main/ms-d/24   2007-09-20 10:00:26   mp49210
 * No AT+CMGC in HC28
 *
 * /main/ms-d/23   2007-09-07 11:43:09   mp49210
 * Workaround for occasionally missing parameter im WM6
 *
 * /main/ms-d/22   2007-08-09 09:56:40   mp49210
 * Check ME storage for accidently wrong routed SMS
 *
 * /main/ms-d/21   2007-07-03 10:36:39   mp49210
 * Workaround for WM6 sending wrong SMS storage location
 *
 * /main/ms-d/20   2007-06-28 09:58:48   mp49210
 * Close handle to threads to prevent handle leaks in memory
 *
 * /main/ms-d/19   2007-06-01 16:20:59   mp49210
 * IPIS100036445: workaround for sending and receiving SMS per registry key
 *
 * /main/ms-d/18   2007-04-27 08:33:54   mp49210
 * Removed Lint warnings
 *
 * /main/ms-d/17   2007-04-10 16:46:04   mp49210
 * Use SMSC workaround for send SMS, too
 *
 * /main/ms-d/16   2007-04-10 15:26:18   mp49210
 * Change SMS index range
 * Query SMSC before wiriting SMS to SIM
 *
 * /main/ms-d/15   2007-03-28 13:48:01   mp49210
 * Remove 'Classic RIL' specific code and comments
 *
 * /main/ms-d/14   2007-03-24 12:35:20   mp49210
 * Workaround for AT+CSCA problems
 *
 * /main/ms-d/13   2007-03-22 16:14:18   mp49210
 * Workaround for AT+CMSS in UCS2 mode
 *
 * /main/ms-d/12   2007-02-26 16:15:27   mp49210
 * New behaviour of CBS, SIM storage and SMS SR
 *
 * /main/ms-d/11   2007-02-19 10:56:28   mp49210
 * AT+CMGS/+CMGW now use data port
 *
 * /main/ms-d/10   2007-02-09 15:15:13   mp49210
 * Lint compatibility
 *
 * /main/ms-d/9   2007-01-31 17:00:07   mp49210
 * Changed handling of cell broadcast messages to pass Crossbow radiometrics.exe
 *
 * /main/ms-d/8   2006-12-22 15:05:40   mp49210
 * Workaround for missing '+CMGR: 0,,0' response
 *
 * /main/ms-d/7   2006-12-21 17:20:30   mp49210
 * Use "SM" memory as long "MT" is not completly usable
 *
 * /main/ms-d/6   2006-12-20 17:00:59   mp49210
 * Changes for AT+CNMI, AT+CAOC.
 * Modified URC configuration commands.
 *
 * /main/ms-d/5   2006-12-14 16:40:10   mp49210
 * AT+CMGR response is now correct
 *
 * /main/ms-d/4   2006-12-06 13:34:47   mp49210
 * Changed behaviour of AT+CSCB command
 *
 * /main/ms-d/3   2006-11-22 11:44:48   mp49210
 * Workaround for HC25 SMS problems
 *
 * /main/ms-d/2   2006-11-02 14:56:50   mp49210
 * Modifications for HC25 limitations
 *
 * /main/ms-d/1   2006-10-25 16:54:00   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"

DWORD g_dwLastSMSCmd = 0;

// Determine, which is the default CNMI behaviour
BOOL g_bDefaultCNMIstoreOnSIM = FALSE;
// Change CNMI settings to "store on SIM" while sending SMS
BOOL g_bChangeCNMIwhileSMSsend = TRUE;

// Modes for g_dwSMSPersistLink: see msg.h
DWORD g_dwSMSPersistLink = SMSPERSISTLINK_NONE;

//
// Broadcast message languages
//
static const BYTE g_rgbBroadcastLangs[] =
{
    15,         // RIL_DCSLANG_UNKNOWN
    0,          // RIL_DCSLANG_GERMAN
    1,          // RIL_DCSLANG_ENGLISH
    2,          // RIL_DCSLANG_ITALIAN
    3,          // RIL_DCSLANG_FRENSH
    4,          // RIL_DCSLANG_SPANISH
    5,          // RIL_DCSLANG_DUTCH
    6,          // RIL_DCSLANG_SWEDISH
    7,          // RIL_DCSLANG_DANISH
    8,          // RIL_DCSLANG_PORTUGUESE
    9,          // RIL_DCSLANG_FINNISH
    10,         // RIL_DCSLANG_NORWEGIAN
    11,         // RIL_DCSLANG_GREEK
    12,         // RIL_DCSLANG_TURKISH
    13,         // RIL_DCSLANG_HUNGARIAN
    14,         // RIL_DCSLANG_POLISH
    32,         // RIL_DCSLANG_CZECH
};
#define NUM_LANGUAGES   (sizeof(g_rgbBroadcastLangs) / sizeof(BYTE))


//
// Message storage locations
//
//
static const LPCSTR g_rgszMsgLocations[] =
{
    "",             // RIL_MSGLOC_UNKNOWN
    "BM",           // RIL_MSGLOC_BROADCAST
    "SM",           // RIL_MSGLOC_SIM
    "SR"            // RIL_MSGLOC_STATUSREPORT
};
#define NUM_MSGLOCS     (sizeof(g_rgszMsgLocations) / sizeof(LPCSTR))


//
// Message status values
//
static const DWORD g_rgdwMsgStats[] =
{
    RIL_MSGSTATUS_RECUNREAD,    // 0
    RIL_MSGSTATUS_RECREAD,      // 1
    RIL_MSGSTATUS_STOUNSENT,    // 2
    RIL_MSGSTATUS_STOSENT,      // 3
};
#define NUM_MSGSTATS    (sizeof(g_rgdwMsgStats) / sizeof(DWORD))

// Buffer size to hold EF_SMS SIM file record.
#define MAX_SMS_SIM_FILE_SIZE  200

// Temporary storage for AT+CSCB? output
static char g_szLanguageRange[MAX_ATCMD_LEN];
static char g_szBroadcastMsgIDs[MAXLENGTH_MSGIDS];

// Critical section needed to synchronize AT+CSCB requests
CRITICAL_SECTION    g_csSCSBInProgress;

// Set tor TRUE, if the thread to check the CNMI status is running.
static DWORD s_fCNMIThreadRunning = FALSE;

// Static variables and definitions for CDMA Cell Broadcast.
#define MAX_CDMA_CSCB_SETTINGS    32
#define MAX_CDMA_CSCB_SETTING_STRING   64
static int s_nCdmaConfig = 0;
static char s_szCdmaConfig[MAX_CDMA_CSCB_SETTINGS][MAX_CDMA_CSCB_SETTING_STRING];
static char s_szCdmaConfigTemp[MAX_CDMA_CSCB_SETTINGS][MAX_CDMA_CSCB_SETTING_STRING];

const char *GetCdmaCnmiSetting(void)
{
    if (FULL_CDMA_CNMI_SUPPORT) return "AT"SMS_CNMI_CDMA_FULL"\r";
    else return "AT"SMS_CNMI_CDMA_BASIC"\r";
}

//
// Appends specified GSM broadcast DCS value value to the generated DCS range
//
static void AppendLangString(const LPCSTR szAppend, LPSTR& szWalk, const UINT cbOut, UINT& rcbUsed)
{
    UINT cbAppend = strlen(szAppend);

    if ((1 < rcbUsed) && (rcbUsed + 1 <= cbOut))  // Need for and room for ','
    {
        *szWalk = ',';  // Overwrite '\0'
        szWalk++;
        *szWalk = '\0';  // Add '\0' back
        rcbUsed++;  // Caller has already counted the '\0', so just count the ',' here
    }

    if (rcbUsed + cbAppend <= cbOut)
    {
        (void)strncpyz(szWalk, szAppend, cbOut - rcbUsed);
        szWalk += cbAppend;
        rcbUsed += cbAppend;
    }
}


//
// Generates a GSM broadcast DCSs range string corresponding to the specified language mask
//
static void MakeBroadcastLangRange(const DWORD dwLanguages, const LPSTR szOut, const UINT cbOut)
{
    UINT i;
    LPSTR szWalk = szOut;
    UINT cbUsed = 0;
    char szNumber[3];

    *szWalk = '\0';

    // The empty string is sufficient for RIL_DCSLANG_ALL.

    if (RIL_DCSLANG_ALL != dwLanguages)
    {
        cbUsed = 1;  // Account for the '\0'

        // Iterate through all bits of the mask to see which languages to add
        for (i = 0; (i < sizeof(DWORD)*8) && (i < NUM_LANGUAGES); i++)
        {
            if ((dwLanguages >> i) & 0x01)
            {
                (void)_itoa(g_rgbBroadcastLangs[i], szNumber, 10);
                AppendLangString(szNumber, szWalk, cbOut, cbUsed);
            }
        }
    }
}


static void MakeBroadcastMsgIDs(const RILRANGE rgrrRange[], DWORD dwNumRanges, const LPSTR szOut, const UINT cbOut)
{
    LPSTR szWalk = szOut;
    LPSTR szEnd = szOut + cbOut;
    DWORD cbRange = 0;
    char szRange[64];
    char c_szRangeFmt[] = ",%u-%u";
    char c_szSingleFmt[] = ",%u";
    char * szRangeFmt = c_szRangeFmt + 1;
    char * szSingleFmt = c_szSingleFmt + 1;

    for (DWORD dwCurrentRange = 0; dwCurrentRange < dwNumRanges; dwCurrentRange++)
    {
        if (rgrrRange[dwCurrentRange].dwMinValue == rgrrRange[dwCurrentRange].dwMaxValue)
        {
            cbRange = _snprintfz(szRange, 64, szSingleFmt, rgrrRange[dwCurrentRange].dwMinValue);
        }
        else
        {
            cbRange = _snprintfz(szRange, 64, szRangeFmt, rgrrRange[dwCurrentRange].dwMinValue, rgrrRange[dwCurrentRange].dwMaxValue);
        }

        if (szWalk + cbRange < szEnd)
        {
            (void)strncpyz(szWalk, szRange, cbOut - (szWalk - szOut));
            szWalk += cbRange;
        }
        else
        {
            break;
        }

        szRangeFmt = c_szRangeFmt;
        szSingleFmt = c_szSingleFmt;
    }

    *szWalk = '\0';
}

//
// Set a flag in the language mask correponding to the
//    specified GSM broadcast DCS value
//
static BOOL AppendLanguage(const UINT nValue, DWORD& rdwLanguages)
{
    UINT i;
    BOOL fRet = FALSE;

    for (i = 0 ; i < NUM_LANGUAGES; i++)
    {
        if (nValue == g_rgbBroadcastLangs[i])
        {
            rdwLanguages |= (0x01 << i);
            break;
        }
    }
    return fRet;
}


//
// Parses a range of GSM broadcast DCSs into a language mask
//
static BOOL ParseBroadcastLangRange(LPCSTR szRange, DWORD& rdwLanguages)
{
    UINT nValue1;
    UINT nValue2;
    UINT i;
    BOOL fRet = FALSE;

    if (*szRange)
    {
        // Start off with none...
        rdwLanguages = 0;

        for (;;)
        {
            if (!ParseUInt(szRange, TRUE, nValue1, szRange))
            {
                goto Error;
            }
            if (MatchStringBeginning(szRange, "-", szRange) &&
                ParseUInt(szRange, TRUE, nValue2, szRange))
            {
                // This is a mini-range
                for (i = nValue1; i <= nValue2; i++)
                {
                    (void)AppendLanguage(i, rdwLanguages);
                }
            }
            else
            {
                (void)AppendLanguage(nValue1, rdwLanguages);
            }

            // If there is no trailing comma, we're done
            if (!MatchStringBeginning(szRange, ",", szRange))
            {
                break;
            }
        }
    }
    else
    {
        // An empty string implies all languages.
        rdwLanguages = RIL_DCSLANG_ALL;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

BOOL ParseGetLocation(const LPCSTR szRspOrig, UINT& rnLocation, LPCSTR& rszPointer)
{
    char szLocation[MAX_ATCMD_LEN];
    UINT nLocation;

    LPCSTR szRsp = szRspOrig;

    // Parse "<xxx_location>" (somtime quoted, sometimes not)
    if (!ParseString(szRsp, szLocation, MAX_ATCMD_LEN, szRsp))
    {
        // If there is no quoted string, there might be an unquoted one
        if (!ParseUnquotedString(szRsp, ',', szLocation, MAX_ATCMD_LEN, szRsp))
            return FALSE;
    }

    for (nLocation = NUM_MSGLOCS-1; nLocation > 0; nLocation--)
    {
        if (!strcmp(szLocation, g_rgszMsgLocations[nLocation]))
        {
            break;
        }
    }

    rnLocation = nLocation;
    rszPointer = szRsp;
    return TRUE;
}

static BOOL ParseGetLocationAmount(LPCSTR szRsp, LPCSTR& rszPointer, PUINT pLocation, PUINT pUsed, PUINT pTotal)
{
    UINT nLocation;
    UINT nUsed;
    UINT nTotal;

    // Go past initial ',' if it's there
    if (*szRsp==',')
    {
        szRsp++;
    }

    // Parse "<xxx_location>"
    if (!ParseGetLocation(szRsp, nLocation, szRsp))
    {
        return FALSE;
    }

    // Parse ",<xxx_used>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nUsed, szRsp))
    {
        return FALSE;
    }

    // Parse ",<xxx_total>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nTotal, szRsp))
    {
        return FALSE;
    }

    *pLocation = nLocation;
    *pUsed = nUsed;
    *pTotal = nTotal;
    rszPointer = szRsp;
    return TRUE;
}

//
//
//
static HRESULT ParseGetMsgServiceOptionsRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = E_FAIL;
    RILMSGSERVICEINFO* prmsi = NULL;
    UINT nValue;
    UINT nLocation;
    UINT nUsed;
    UINT nTotal;

    pBlob = NULL;
    cbBlob = 0;

    prmsi = (RILMSGSERVICEINFO*)AllocBlob(sizeof(RILMSGSERVICEINFO));
    if (!prmsi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmsi, 0x00, sizeof(RILMSGSERVICEINFO));
    prmsi->cbSize = sizeof(RILMSGSERVICEINFO);

    // Parse "<prefix>+CSMS: <svc_type>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSMS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    if (!nValue)
    {
        prmsi->dwService = RIL_MSGSVCTYPE_PHASE2;
    }
    else if (1 == nValue)
    {
        prmsi->dwService = RIL_MSGSVCTYPE_PHASE2PLUS;
    }
    else
    {
        prmsi->dwService = RIL_MSGSVCTYPE_UNKNOWN;
    }
    prmsi->dwParams |= RIL_PARAM_MSI_SERVICE;

    // Parse ",<incoming>"
    prmsi->dwMsgClasses = RIL_MSGCLASS_NONE;
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp))
    {
        goto Error;
    }

    if (1 == nValue)
    {
        prmsi->dwMsgClasses |= RIL_MSGCLASS_INCOMING;
    }

    // Parse ",<outgoing>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp))
    {
        goto Error;
    }

    if (1 == nValue)
    {
        prmsi->dwMsgClasses |= RIL_MSGCLASS_OUTGOING;
    }

    // Parse ",<broadcast><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        goto Error;
    }

    if (1 == nValue)
    {
        prmsi->dwMsgClasses |= RIL_MSGCLASS_BROADCAST;
    }
    prmsi->dwParams |= RIL_PARAM_MSI_MSGCLASSES;

    // Parse "<prefix>+CPMS: "
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPMS: ", szRsp))
    {
        goto Error;
    }

    // Parse Read fields
    if (!ParseGetLocationAmount(szRsp, szRsp, &nLocation, &nUsed, &nTotal))
    {
        goto Error;
    }

    prmsi->dwReadLocation = nLocation;
    prmsi->dwReadUsed = min(nUsed, nTotal);
    prmsi->dwReadTotal = nTotal;
    prmsi->dwParams |= RIL_PARAM_MSI_READLOCATION | RIL_PARAM_MSI_READUSED | RIL_PARAM_MSI_READTOTAL;

    // Note: If write or store fields are missing, we use the values from the last set of fields.
    // Therefore, we don't check the return value from the following two ParseGetLocationAmount calls.
    // This works around Wavecom issues with both versions of Wavecom modules where they don't report
    // all three sets of fields.

    // Parse Write fields
    (void)ParseGetLocationAmount(szRsp, szRsp, &nLocation, &nUsed, &nTotal);
    prmsi->dwWriteLocation = nLocation;
    prmsi->dwWriteUsed= min(nUsed, nTotal);
    prmsi->dwWriteTotal = nTotal;
    prmsi->dwParams |= RIL_PARAM_MSI_WRITELOCATION | RIL_PARAM_MSI_WRITEUSED | RIL_PARAM_MSI_WRITETOTAL;

    // Parse Store fields
    (void)ParseGetLocationAmount(szRsp, szRsp, &nLocation, &nUsed, &nTotal);
    prmsi->dwStoreLocation = nLocation;
    prmsi->dwStoreUsed= min(nUsed, nTotal);
    prmsi->dwStoreTotal = nTotal;
    prmsi->dwParams |= RIL_PARAM_MSI_STORELOCATION | RIL_PARAM_MSI_STOREUSED | RIL_PARAM_MSI_STORETOTAL;

    (void)ParseRspPostfix(szRsp, szRsp);
    pBlob = (void*)prmsi;
    cbBlob = sizeof(RILMSGSERVICEINFO);

    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(prmsi);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetMsgServiceOptionsCdma(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = E_FAIL;
    RILMSGSERVICEINFO* prmsi = NULL;

    pBlob = NULL;
    cbBlob = 0;

    prmsi = (RILMSGSERVICEINFO*)AllocBlob(sizeof(RILMSGSERVICEINFO));
    if (!prmsi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmsi, 0x00, sizeof(RILMSGSERVICEINFO));
    prmsi->cbSize = sizeof(RILMSGSERVICEINFO);

    prmsi->dwService = RIL_MSGSVCTYPE_PHASE2;
    prmsi->dwParams |= RIL_PARAM_MSI_SERVICE;

    prmsi->dwMsgClasses |= RIL_MSGCLASS_INCOMING;
    prmsi->dwMsgClasses |= RIL_MSGCLASS_OUTGOING;
    prmsi->dwMsgClasses |= RIL_MSGCLASS_BROADCAST;
    prmsi->dwParams |= RIL_PARAM_MSI_MSGCLASSES;

    prmsi->dwReadLocation = RIL_MSGLOC_SIM;
    prmsi->dwReadUsed = 0;
    prmsi->dwReadTotal = 99;
    prmsi->dwParams |= RIL_PARAM_MSI_READLOCATION | RIL_PARAM_MSI_READUSED | RIL_PARAM_MSI_READTOTAL;

    prmsi->dwWriteLocation = RIL_MSGLOC_SIM;
    prmsi->dwWriteUsed= 0;
    prmsi->dwWriteTotal = 99;
    prmsi->dwParams |= RIL_PARAM_MSI_WRITELOCATION | RIL_PARAM_MSI_WRITEUSED | RIL_PARAM_MSI_WRITETOTAL;

    prmsi->dwStoreLocation = RIL_MSGLOC_SIM;
    prmsi->dwStoreUsed= 0;
    prmsi->dwStoreTotal = 99;
    prmsi->dwParams |= RIL_PARAM_MSI_STORELOCATION | RIL_PARAM_MSI_STOREUSED | RIL_PARAM_MSI_STORETOTAL;

    hr = S_OK;
    pBlob = (void*)prmsi;
    cbBlob = sizeof(RILMSGSERVICEINFO);

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(prmsi);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetMsgServiceOptions(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        // CDMA: SMS is handled via module internal memory, so instead of asking the
        // module simply return a fixed value.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETMSGSERVICEOPTIONS, ParseGetMsgServiceOptionsCdma, NULL, hr))
            hr = E_FAIL;
    }
    else if ((g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) {
        // While the SIM is still initializing, these AT commands would fail with an unspecific
        // error messages, which confuses Microsoft's SIM manager. To solve this problem,
        // the RIL doesn't send the AT command at all and returns "SIM busy" right away.
         if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETMSGSERVICEOPTIONS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_SIMBUSY))
            hr = E_FAIL;
    }
    else {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CSMS?;+CPMS?\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETMSGSERVICEOPTIONS,
                      ParseGetMsgServiceOptionsRsp, NULL, hr))
            hr = E_FAIL;
    }

    Error:
    return hr;
}

static HRESULT ParseSMSPhaseConfirmation(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    if (pParam == NULL)
        goto Error;

    // This parsing function is called, when the module has been successfully changed to
    // SMS-Phase 2+. In this case we have to set the AT+CNMI parameters accordingly.
    // If the registry key "StoreSMSonSIMasDefault" is set, then the CNMI must not be
    // changed.
    if (!g_bDefaultCNMIstoreOnSIM)
        CloseHandle(CreateThread(NULL, 0, CNMIReactivationThreadProc, pParam, 0, NULL));

    hr = S_OK;

    Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetMsgServiceOptions(DWORD dwParam, const RILMSGSERVICEINFO* lpMsgServiceInfo)
{
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    UINT nValue = 0;
    RILMSGSTORAGEINFO rmsi; memset(&rmsi,0,sizeof(rmsi)); // zero struct
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (CANT_USE_CPMS || pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        // CDMA: SMS is handled via module internal memory, so there is real action necessary.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETMSGSERVICEOPTIONS, NULL, NULL, hr))
            hr = E_FAIL;
        goto Error;
    }

    // Add "AT"
    (void)strncpyz(szWalk, "AT", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_SERVICE)
    {
        if (RIL_MSGSVCTYPE_PHASE2 == lpMsgServiceInfo->dwService)
        {
            // Before we are going back to SMS Phase 2, we have to change the settings for AT+CNMI.
            if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), USE_STANDARD_CNMI_SETTINGS ? "AT"SMS_CNMI_STORE_ON_SIM_3GPP"\r" :  "AT"SMS_CNMI_STORE_ON_SIM"\r", CMDOPT_NONE, APIID_NONE, 1000, 0, 0))
            {
                hr = E_FAIL;
                goto Error;
            }
            nValue = 0;
        }
        else if (RIL_MSGSVCTYPE_PHASE2PLUS == lpMsgServiceInfo->dwService)
        {
            nValue = 1;
        }

        // Add "+CSMS=<service>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CSMS=%u\r", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_SETMSGSERVICEOPTIONS, 0, 0, 0, (nValue==1?ParseSMSPhaseConfirmation:NULL), NULL, (nValue==1?pHandle->GetDevice():NULL)))
        {
            hr = E_FAIL;
            goto Error;
        }
        Sleep(500);
    }

    if ((lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_READLOCATION) ||
        (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_WRITELOCATION) ||
        (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_STORELOCATION))
    {
        // At least one location is specified
        szWalk = szCmd;
        // Add "+CPMS="
        (void)strncpyz(szWalk, "AT+CPMS=", MAX_ATCMD_LEN - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_READLOCATION)
    {
        nValue = lpMsgServiceInfo->dwReadLocation;
        if (RIL_MSGLOC_UNKNOWN == nValue)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        // Add "<read_storage>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", g_rgszMsgLocations[nValue]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        rmsi.dwReadLocation = nValue;
        rmsi.dwParams |= RIL_PARAM_MSTI_READLOCATION;
    }
    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_WRITELOCATION)
    {
        // Add ","
        (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        nValue = lpMsgServiceInfo->dwWriteLocation;
        if (RIL_MSGLOC_UNKNOWN == nValue)
        {
            // Bug in WM 6: the Microsoft UI calls the RIL with RIL_MSGLOC_UNKNOWN, while
            // it actually means RIL_MSGLOC_SIM. So the RIL doesn't returns an error here
            // but uses RIL_MSGLOC_SIM instead.
            nValue = RIL_MSGLOC_SIM;
            //hr = E_INVALIDARG;
            //goto Error;
        }

        // Add "<write_storage>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", g_rgszMsgLocations[nValue]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        rmsi.dwWriteLocation = nValue;
        rmsi.dwParams |= RIL_PARAM_MSTI_WRITELOCATION;
    }

    if (lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_STORELOCATION)
    {
        if ((lpMsgServiceInfo->dwParams & RIL_PARAM_MSI_WRITELOCATION) == 0)
        {
            // Add ","
            (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

        // Add ","
        (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        nValue = lpMsgServiceInfo->dwStoreLocation;
        if (RIL_MSGLOC_UNKNOWN == nValue)
        {
            // Bug in WM 6: the Microsoft UI calls the RIL with RIL_MSGLOC_UNKNOWN, while
            // it actually means RIL_MSGLOC_SIM. So the RIL doesn't returns an error here
            // but uses RIL_MSGLOC_SIM instead.
            nValue = RIL_MSGLOC_SIM;
            //hr = E_INVALIDARG;
            //goto Error;
        }

        // Add "<store_storage>"
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", g_rgszMsgLocations[nValue]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        rmsi.dwStoreLocation = nValue;
        rmsi.dwParams |= RIL_PARAM_MSTI_STORELOCATION;
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

    if ((g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS))  != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS))
    {
        // Sending this command during SIM initialization would disrupt the initialization
        // process, so we return a 'SIM busy' error message immediately.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETMSGSERVICEOPTIONS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_SIMBUSY))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (rmsi.dwParams)
        {
            pnd = new CNotificationData;
            if (pnd)
            {
                rmsi.cbSize = sizeof(RILMSGSTORAGEINFO);
                if (!pnd->InitFromRealBlob(RIL_NOTIFY_MSGSTORAGECHANGED, &rmsi, sizeof(RILMSGSTORAGEINFO)))
                {
                    delete pnd;
                    pnd = NULL;
                }
            }
        }
        if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_GENERATE_ERROR_CODE, APIID_SETMSGSERVICEOPTIONS, NULL, pnd, hr, 2, 10000))
        {
            hr = E_FAIL;
            goto Error;
        }
        pnd = NULL;
    }

    Error:
    return hr;
}


//
//
//
static HRESULT ParseGetMsgConfigRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    WCHAR wszAddress[MAX_ATCMD_LEN];
    //    char szLanguageRange[MAX_ATCMD_LEN];
    RILMSGCONFIG* prmc = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prmc = (RILMSGCONFIG*)AllocBlob(sizeof(RILMSGCONFIG));
    if (!prmc)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmc, 0x00, sizeof(RILMSGCONFIG));
    prmc->cbSize = sizeof(RILMSGCONFIG);

    // Parse "<prefix>+CSCA: <svcctr_addr>,<type_of_addr><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringBeginning(szRsp, "+CSCA: ", szRsp)              ||
        !ParseUCS2String(szRsp, wszAddress, MAX_ATCMD_LEN, szRsp)        ||
        !MatchStringBeginning(szRsp, ",", szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    if (!ParseUIntAndVerifyAbove(szRsp, TRUE, 0x100, nValue, szRsp))
        nValue = 0;
    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Convert data to address structure
    if (!UCS2StringToRILAddress(wszAddress, (BYTE)nValue, prmc->raSvcCtrAddress))
    {
        hr = E_FAIL;
        goto Error;
    }
    prmc->dwParams |= RIL_PARAM_MC_SVCCTRADDRESS;

    pBlob = (void*)prmc;
    cbBlob = sizeof(RILMSGCONFIG);

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(prmc);
    }
    return hr;

}

const CME_TRANSLATION g_CMET_CSCA_SIM_WA[] = {{RIL_E_SIMNOTINSERTED, RIL_E_SIMBUSY},{NULL,NULL}};

//
//
//
HRESULT RILDrv_GetMsgConfig(DWORD dwParam)
{
    const CME_TRANSLATION *pCME = NULL;

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        // There is no SMS Service Center in CDMA, so the RIL can return an error at this point.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETMSGCONFIG, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_OPNOTSUPPORTED))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (WRONG_SIM_NOT_INSERTED_ON_CSCA)
            pCME = g_CMET_CSCA_SIM_WA;

        if (!QueueCmdCMET(HCCMD_STANDARD, pHandle, "AT+CSCA?\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETMSGCONFIG, ParseGetMsgConfigRsp, NULL, hr, pCME, (WRONG_SIM_NOT_INSERTED_ON_CSCA ? 3 : 2), 10000), NULL)
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetMsgConfig(DWORD dwParam, const RILMSGCONFIG* lpMsgConfigInfo)
{
    char szCmd[MAXLENGTH_MSGIDS + MAX_ATCMD_LEN * 3];
    LPSTR szWalk = szCmd;
    WCHAR wszSvcCtrAddress[MAX_ATCMD_LEN];
    char szEncodedSvcCtrAddress[MAX_ATCMD_LEN];
    BYTE bTypeOfAddress;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    (void)strncpyz(szWalk, "AT", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk += 2;

    if (lpMsgConfigInfo->dwParams & RIL_PARAM_MC_SVCCTRADDRESS)
    {
        hr = RILAddressToUCS2String(lpMsgConfigInfo->raSvcCtrAddress, wszSvcCtrAddress, MAX_ATCMD_LEN, bTypeOfAddress);
        if (FAILED(hr))
        {
            goto Error;
        }
        (void)UCS2ToGSMHex(wszSvcCtrAddress, wcslen(wszSvcCtrAddress), szEncodedSvcCtrAddress, sizeof(szEncodedSvcCtrAddress));
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CSCA=\"%s\",%u", szEncodedSvcCtrAddress, bTypeOfAddress);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_GENERATE_ERROR_CODE, APIID_SETMSGCONFIG, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

static RILCBMSGCONFIG * AllocCbMsgConfig(DWORD dwNumRanges)
{
    DWORD dwSize = sizeof(RILCBMSGCONFIG) + dwNumRanges * sizeof(RILRANGE);

    RILCBMSGCONFIG* prmc = (RILCBMSGCONFIG*)AllocBlob(dwSize);
    if (NULL != prmc)
    {
        memset(prmc, 0x00, dwSize);
        prmc->cbSize = dwSize;
    }

    return prmc;
}

//
//
//
static HRESULT ParseBroadcastMsgIDs(const LPCSTR szBroadcastMsgIDs,
                                    RILCBMSGCONFIG ** pprCbMsgConfig)
{
    HRESULT hr = S_OK;
    DWORD dwNumRanges;
    LPCSTR szDummy;
    DWORD dwNumIDs = 0;

    if (*szBroadcastMsgIDs)
    {
        dwNumIDs++;

        for (LPCSTR szPtr = szBroadcastMsgIDs; '\0' != *szPtr; szPtr++)
        {
            if (',' == *szPtr)
                dwNumIDs++;
        }
    }

    *pprCbMsgConfig = AllocCbMsgConfig(dwNumIDs);
    if (NULL == *pprCbMsgConfig)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!ParseRangesFromString(szBroadcastMsgIDs,
                               (*pprCbMsgConfig)->rgrrBroadcastMsgIDs,
                               dwNumRanges,
                               dwNumIDs,
                               szDummy))
    {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

#ifdef CSCB_IN_MC75_STYLE
//
//
//
static HRESULT ParseGetCellBroadcastMsgConfigStored(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCBMSGCONFIG* prmc = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    hr = ParseBroadcastMsgIDs(g_szReceiveCellBroadCastChannels, &prmc);
    if (FAILED(hr))
    {
        goto Error;
    }

    prmc->fAccept = g_dwReceiveCellBroadCastAccept;
    prmc->dwParams |= (RIL_PARAM_CBMC_BROADCASTMSGIDS | RIL_PARAM_CBMC_ACCEPTIDS);

    if (!ParseBroadcastLangRange(g_szReceiveCellBroadCastDCS, prmc->dwBroadcastMsgLangs)) {
        hr = E_FAIL;
        goto Error;
    }

    prmc->dwParams |= RIL_PARAM_CBMC_BROADCASTMSGLANGS;

    pBlob = (void*)prmc;
    cbBlob = prmc->cbSize;

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(prmc);
    }
    return hr;
}
#endif
//
//
//
static HRESULT ParseGetCellBroadcastMsgConfigRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    BOOL fAccept = FALSE;
    char szLanguageRange[MAX_ATCMD_LEN];
    char szBroadcastMsgIDs[MAXLENGTH_MSGIDS];
    RILCBMSGCONFIG* prmc = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    memset(szLanguageRange, 0, MAX_ATCMD_LEN);
    memset(szBroadcastMsgIDs, 0, MAXLENGTH_MSGIDS);

    // Parse "<prefix>+CSCB: <mode>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSCB: ", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Cache accept value for when we have a structure allocated
    fAccept = (nValue == 0);

    // Parse ",<msg_ids>,<msg_langs><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)                              ||
        !ParseString(szRsp, szBroadcastMsgIDs, MAXLENGTH_MSGIDS, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    (void)strncpyz(g_szBroadcastMsgIDs, szBroadcastMsgIDs, MAXLENGTH_MSGIDS);

    // Parse broadcast message IDs
    // This also allocates the blob
    hr = ParseBroadcastMsgIDs(szBroadcastMsgIDs, &prmc);
    if (FAILED(hr))
    {
        goto Error;
    }

    prmc->fAccept = fAccept;
    prmc->dwParams |= (RIL_PARAM_CBMC_BROADCASTMSGIDS | RIL_PARAM_CBMC_ACCEPTIDS);

    if (MatchStringBeginning(szRsp, ",", szRsp))
    {
        if (!ParseString(szRsp, szLanguageRange, MAX_ATCMD_LEN, szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }

        // Parse accepted languages
        if (!ParseBroadcastLangRange(szLanguageRange, prmc->dwBroadcastMsgLangs)) {
            hr = E_FAIL;
            goto Error;
        }

        prmc->dwParams |= RIL_PARAM_CBMC_BROADCASTMSGLANGS;
    }

    (void)strncpyz(g_szLanguageRange, szLanguageRange, MAX_ATCMD_LEN);

    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prmc;
    cbBlob = prmc->cbSize;

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(prmc);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetCellBroadcastMsgConfig(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

#ifdef CSCB_IN_MC75_STYLE
    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETCBMSGCONFIG, ParseGetCellBroadcastMsgConfigStored, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }
#else
    if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, "AT+CSCB?\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETCBMSGCONFIG, ParseGetCellBroadcastMsgConfigRsp, NULL, hr, 3, 3000))
    {
        hr = E_FAIL;
        goto Error;
    }
#endif

    Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetCellBroadcastMsgConfig(DWORD dwParam, const RILCBMSGCONFIG* lpMsgConfigInfo)
{
#ifndef CSCB_IN_MC75_STYLE
    char szCmd[MAXLENGTH_MSGIDS + MAX_ATCMD_LEN * 3];
    LPSTR szWalk = szCmd;
    BOOL bMustOmit = TRUE;
#endif
    char szCBMsgLangs[MAX_ATCMD_LEN];
    char szBroadcastMsgIDs[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_ACCEPTIDS))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

#ifdef CSCB_IN_MC75_STYLE
    g_dwReceiveCellBroadCast = 1;
    g_dwReceiveCellBroadCastAccept = lpMsgConfigInfo->fAccept;
    g_szReceiveCellBroadCastChannels[0] = '\0';
    g_szReceiveCellBroadCastDCS[0] = '\0';
    if ((lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGIDS))
    {
        MakeBroadcastMsgIDs(lpMsgConfigInfo->rgrrBroadcastMsgIDs,
                            (lpMsgConfigInfo->cbSize - sizeof(RILCBMSGCONFIG)) / sizeof(RILRANGE),
                            szBroadcastMsgIDs,
                            MAX_ATCMD_LEN);

        if (strlen(szBroadcastMsgIDs) < MAX_CBM_STRING)
            (void)strncpyz(g_szReceiveCellBroadCastChannels, szBroadcastMsgIDs, MAX_CBM_STRING);
    }
    if (lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGLANGS)
    {
        MakeBroadcastLangRange(lpMsgConfigInfo->dwBroadcastMsgLangs, szCBMsgLangs, MAX_ATCMD_LEN);

        if (strlen(szCBMsgLangs) < MAX_CBM_STRING)
            (void)strncpyz(g_szReceiveCellBroadCastDCS, szCBMsgLangs, MAX_CBM_STRING);
    }
    if (!lpMsgConfigInfo->fAccept)
    {
        // TODO: reverse selection, as soon the HC25 firmware supports this
    }

    // This is going to be a complicated action, so we schedule a background task
    // and inform the OS about the fact, that we just started the operation
    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETCBMSGCONFIG, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }
    else
    {
        CloseHandle(CreateThread(NULL, 0, ReCheckCBStatusThreadProc, (LPVOID)pHandle->GetDevice(), 0, 0));
    }
#else
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=%d", (lpMsgConfigInfo->fAccept?0:1));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    g_dwReceiveCellBroadCast = 1;
    g_szReceiveCellBroadCastChannels[0] = '\0';
    g_szReceiveCellBroadCastDCS[0] = '\0';

    if ((lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGIDS))
    {
        MakeBroadcastMsgIDs(lpMsgConfigInfo->rgrrBroadcastMsgIDs,
                            (lpMsgConfigInfo->cbSize - sizeof(RILCBMSGCONFIG)) / sizeof(RILRANGE),
                            szBroadcastMsgIDs,
                            MAX_ATCMD_LEN);

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN, ",\"%s\"", szBroadcastMsgIDs);
        if (strlen(szBroadcastMsgIDs) > 0)
            bMustOmit = FALSE;
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        if (strlen(szBroadcastMsgIDs) < MAX_CBM_STRING)
            (void)strncpyz(g_szReceiveCellBroadCastChannels, szBroadcastMsgIDs, MAX_CBM_STRING);

    }

    if (lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGLANGS)
    {
        MakeBroadcastLangRange(lpMsgConfigInfo->dwBroadcastMsgLangs, szCBMsgLangs, MAX_ATCMD_LEN);

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%s,\"%s\"", (lpMsgConfigInfo->dwParams & RIL_PARAM_CBMC_BROADCASTMSGIDS)?"":",", szCBMsgLangs);
        if (strlen(szCBMsgLangs) > 0)
            bMustOmit = FALSE;
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        if (strlen(szCBMsgLangs) < MAX_CBM_STRING)
            (void)strncpyz(g_szReceiveCellBroadCastDCS, szCBMsgLangs, MAX_CBM_STRING);
    }

    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\r");

    // If both strings are empty (bMustOmit == TRUE), we must not send the AT command, because has not effect and
    // causes an irritating error message.
    if (!QueueCmd(HCCMD_STANDARD, pHandle, bMustOmit ? NULL : szCmd, bMustOmit ? CMDOPT_NOOP : CMDOPT_GENERATE_ERROR_CODE, APIID_SETCBMSGCONFIG, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }
#endif

    Error:
    return hr;
}

//
// This function checks the status of the Cell Boradcast and configures it
// newly in case.
//
DWORD WINAPI ReCheckCBStatusThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;
    char szCmd[MAX_ATCMD_LEN];

    // If there is no CBS configuration active, we can abort immediately.
    // We can also abort, if CDMA is currently in use.
    if (g_dwReceiveCellBroadCast != 2 && pHandle->GetCurrentTechnology() != TEC_MODE_CDMA)
    {
        SYNCBLOCK(g_csSCSBInProgress);

        // First check the current status.
        g_szLanguageRange[0] = '\0';
        g_szBroadcastMsgIDs[0] = '\0';
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CSCB?\r", CMDOPT_NONE, APIID_GETCBMSGCONFIG, 0, 0, 0, ParseGetCellBroadcastMsgConfigRsp))
            return 0;

        // Remove all currently configured channels.
        if (strcmp(g_szBroadcastMsgIDs, g_szReceiveCellBroadCastChannels) != 0 || strlen(g_szReceiveCellBroadCastChannels) > 0 || strlen(g_szReceiveCellBroadCastDCS) > 0)
        {
            char *szToken;

            szToken = strtok(g_szBroadcastMsgIDs, ",");
            while (szToken != NULL)
            {
                if (CANNOT_REMOVE_CSCB_SETTINGS_DIRECTLY && strcmp(szToken, "0-65535") == 0) // Special treatment for 0-65535 in HC25 Rel2: must be done in two steps.
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=1,\"1-65535\",\"\";+CSCB=1,\"0\",\"\"\r");
                else
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=1,\"%s\",\"\"\r", szToken);

                // The module might respond with an error, although the command has
                // been executed, so the module response must be ignored.
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCBMSGCONFIG, 0, 0, 0);
                szToken = strtok(NULL, ",");
            }
            if (CANNOT_REMOVE_CSCB_SETTINGS_DIRECTLY)
            {
                // When deleting the last channel, the HC25 Rel2 usually adds a fictional entry
                // instead, which now has to be removed.
                g_szLanguageRange[0] = '\0';
                g_szBroadcastMsgIDs[0] = '\0';
                if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CSCB?\r", CMDOPT_NONE, APIID_GETCBMSGCONFIG, 0, 0, 0, ParseGetCellBroadcastMsgConfigRsp))
                    return 0;
                if (strlen(g_szBroadcastMsgIDs) > 0)
                {
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=1,\"%s\",\"\"\r", g_szBroadcastMsgIDs);
                    // The module might respond with an error, although the command has
                    // been executed, so the module response must be ignored.
                    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCBMSGCONFIG, 0, 0, 0);
                }
            }
        }

        if (g_dwReceiveCellBroadCast == 1)
        {
            // Now write the correct channels.
            if (strlen(g_szReceiveCellBroadCastChannels) > 0 || strlen(g_szReceiveCellBroadCastDCS) > 0)
            {
                if (CANNOT_REMOVE_CSCB_SETTINGS_DIRECTLY && strlen(g_szReceiveCellBroadCastChannels) == 0) // Default value for empty string is 0-65535, but the HC25 Rel2 does transform this into something unpredictable.
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=0,\"0-65535\",\"%s\"\r", g_szReceiveCellBroadCastDCS);
                else
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCB=0,\"%s\",\"%s\"\r", g_szReceiveCellBroadCastChannels, g_szReceiveCellBroadCastDCS);
                if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_GETCBMSGCONFIG, 0, 0, 0))
                    return 0;
            }
        }
    }

    return 0;
}


//
//
//
static HRESULT ParseReadMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    UINT cbMessage;
    BOOL fIncoming;
    RILMESSAGEINFO* prmi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prmi = (RILMESSAGEINFO*)AllocBlob(sizeof(RILMESSAGEINFO));
    if (!prmi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prmi, 0x00, sizeof(RILMESSAGEINFO));
    prmi->cbSize = sizeof(RILMESSAGEINFO);
//  +CMGR: 0,,24<cr><lf>0791448720003023040C9144776359085700113011314175140005E17338ED06<cr><lf>0<cr>
    // Parse "<prefix>+CMGR: <stat>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CMGR: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        // No response at all? Than the HC25 actually meant +CMGR: 0,,0 which is
        // supposed to generate an 'invalid index' error.
        hr = RIL_E_INVALIDINDEX;
        goto Error;
    }

    if (NUM_MSGSTATS > nValue)
    {
        prmi->dwStatus = g_rgdwMsgStats[nValue];
    }
    else
    {
        prmi->dwStatus = RIL_MSGSTATUS_UNKNOWN;
    }
    prmi->dwParams |= RIL_PARAM_MI_STATUS;

    // Parse",<alpha>" (if present)
    if (!MatchStringBeginning(szRsp, ",", szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (INVALID_CMGR_ALPHA) {
        // Bug in PH8 customer sample: instead of the <alpha> parameter there is
        // only a meaningless stream of numbers, which must be skipped-
        if (!MatchStringAnywhere(szRsp, ",", szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // Copy alpha to temp buffer & throw away
        TCHAR szBuf[MAXLENGTH_ADDRESS];
        (void)ParseQuotedEncodedString(ENCODING_UCS2, szRsp, szBuf, szBuf + MAXLENGTH_ADDRESS);
        // Parse ",<length><postfix>"
        if (!MatchStringBeginning(szRsp, ",", szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Calculate the total number of bytes in the message data: (length of the PDU data +
    //    length os SC address (stored in the first 2 bytes) + 1) * 2
    cbMessage = (nValue + 1 + SemiByteCharsToByte(*szRsp, *(szRsp + 1))) * 2;

    // Parse "<pdu>"
    fIncoming = (RIL_MSGSTATUS_RECUNREAD == prmi->dwStatus ||
                 RIL_MSGSTATUS_RECREAD == prmi->dwStatus);
    if (nValue == 0)
    {
        hr = RIL_E_INVALIDINDEX;
        goto Error;
    }
    hr = ParseSMSMessage(szRsp, cbMessage, fIncoming, TRUE, prmi->rmMessage);
    if (FAILED(hr))
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Failed to parse SMS message\r\n")));
        goto Error;
    }
    szRsp += cbMessage;
    prmi->dwParams |= RIL_PARAM_MI_MESSAGE;

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // NOTE: there may be more data in the response (if we also deleted the message),
    //       but we don't care about it

    if (MustFilterSMS(&(prmi->rmMessage)) && pParam != NULL)
    {
        PARSE_PARAM_COMMAND *pCmd;

        pCmd = (PARSE_PARAM_COMMAND *)pParam;
        (void)QueueInternalCmd(HCCMD_STANDARD, pCmd->pHandle , pCmd->szCmd, CMDOPT_NONE, APIID_DELETEMSG, 0, 0, 0, NULL);
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prmi;
    cbBlob = sizeof(RILMESSAGEINFO);

Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(prmi);
    }
    if (pParam != NULL)
        (void)FreeBlob(pParam);

    return hr;
}

//
//
//
static HRESULT ParseReadMsgError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/,LPVOID pParam, DWORD& /*dwCode*/)
{
    // To prevent memory leaks, free memory now, if not already freed.
    if (pParam != NULL)
        (void)FreeBlob(pParam);

    return S_OK;
}

//
//
//
HRESULT RILDrv_ReadMsg(DWORD dwParam, DWORD dwIndex)
{
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    PARSE_PARAM_COMMAND *pParam = NULL;

    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    pParam = (PARSE_PARAM_COMMAND *)AllocBlob(sizeof(PARSE_PARAM_COMMAND));
    if (pParam != NULL)
        pParam->pHandle = pHandle->GetDevice();

#ifdef NO_SMS_INDEX0
    if (dwIndex == 0)
    {
        // Allocated memory no longer needed.
        (void)FreeBlob(pParam);
        pParam = NULL;

        // Force "invalid index" error message.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_READMSG, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_INVALIDINDEX))
            hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGR=%u\r", SMS_INDEX_STARTS_WITH_1 ? dwIndex : dwIndex-1);

    if (pParam != NULL)
        (void)_snprintfz(pParam->szCmd, MAX_ATCMD_LEN, "AT+CMGD=%u\r", SMS_INDEX_STARTS_WITH_1 ? dwIndex : dwIndex-1);
#else
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGR=%u\r", dwIndex);

    if (pParam != NULL)
        (void)_snprintfz(pParam->szCmd, MAX_ATCMD_LEN, "AT+CMGD=%u\r", dwIndex);
#endif
    if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_GENERATE_ERROR_CODE, APIID_READMSG, ParseReadMsg, pnd, hr, 3, 3000, ParseReadMsgError, (LPVOID)pParam))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

    Error:
    if (FAILED(hr) && pParam != NULL)
        (void)FreeBlob(pParam);

    return hr;
}


//
//
//
HRESULT RILDrv_DeleteMsg(DWORD dwParam, DWORD dwIndex)
{
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
#ifdef NO_SMS_INDEX0
    if (dwIndex == 0)
    {
        hr = E_INVALIDARG;
        goto Error;
    }
#endif

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_MSGDELETED, dwIndex))
    {
        delete pnd;
        pnd = NULL;
    }
#ifdef NO_SMS_INDEX0
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGD=%u\r", SMS_INDEX_STARTS_WITH_1 ? dwIndex : dwIndex-1);
#else
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGD=%u\r", dwIndex);
#endif
    if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_GENERATE_ERROR_CODE, APIID_DELETEMSG, NULL, pnd, hr, 3, 3000))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;
    Error:
    return hr;
}


//
//
//
static HRESULT ParseWriteMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    DWORD* pdwIndex = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    pdwIndex = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwIndex)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwIndex, 0x00, sizeof(DWORD));

    // Parse "+CMGW: <index><postfix>"
    if (!MatchStringAnywhere(szRsp, "+CMGW: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        goto Error;
    }

#ifdef NO_SMS_INDEX0
    *pdwIndex = (DWORD)(SMS_INDEX_STARTS_WITH_1 ? nValue : nValue + 1);
#else
    *pdwIndex = (DWORD)nValue;
#endif

    pBlob = (void*)pdwIndex;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(pdwIndex);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_WriteMsg(DWORD dwParam, const RILMESSAGE *lpMessage, DWORD dwStatus)
{
    CNotificationData* pnd = NULL;
    char szCmd[MSG_CMDBUF_LENGTH];
    char szCmd2[MSG_CMDBUF_LENGTH];
    char szMessage[MSG_MSGBUF_LENGTH];
    UINT cbMessage;
    UINT nGSMLength;
    UINT nValue = 0;
    UINT i;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (lpMessage == NULL)
    {
        hr = E_INVALIDARG;
        goto Error;
    }
    if (CANT_USE_CMGW || g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    hr = EncodeSMSMessage(*lpMessage, szMessage, MSG_MSGBUF_LENGTH - 1, cbMessage, nGSMLength);

    if (FAILED(hr))
    {
        goto Error;
    }
    if (!cbMessage)
    {
        hr = E_FAIL;
        goto Error;
    }
    szMessage[cbMessage] = '\0';

    for (i = 0; i < NUM_MSGSTATS; i++)
    {
        if (dwStatus == g_rgdwMsgStats[i])
        {
            nValue = i;
            break;
        }
    }
    if (NUM_MSGSTATS == i)
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (MUST_QUERY_CSCA_BEFORE_SENDING_SMS && pHandle->GetDevice()->GetCurrentTechnology() != TEC_MODE_CDMA)
    {
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CSCA?\r", CMDOPT_NONE, APIID_GETCBMSGCONFIG, 0, 0, 0, NULL))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->DelayInitFromRspBlob(RIL_NOTIFY_MSGSTORED))
    {
        delete pnd;
        pnd = NULL;
    }
    (void)_snprintfz(szCmd, MSG_CMDBUF_LENGTH, "AT+CMGW=%u,%u\r", nGSMLength, nValue);
    (void)_snprintfz(szCmd2, MSG_CMDBUF_LENGTH,  "%s\x1a", szMessage);
    if (!QueueMultipartCmd(HCCMD_STANDARD, pHandle, szCmd, szCmd2, CMDOPT_INTERMEDIATERESPONSE, APIID_WRITEMSG, ParseWriteMsg, pnd, hr))
    {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

    Error:
    return hr;
}


//
//
//
static HRESULT ParseSendMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    DWORD* pdwMsgRef = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    pdwMsgRef = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMsgRef)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwMsgRef, 0x00, sizeof(DWORD));

    if (!MatchStringAnywhere(szRsp, "+CMGS: ", szRsp) &&
        !MatchStringAnywhere(szRsp, "+CMGC: ", szRsp))
    {
        goto Error;
    }

    // Parse "<msgref>[,<ackpdu>]<CR><LF>" (skip <ackpdu> parameter)
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringAnywhere(szRsp, "\r\n", szRsp))
    {
        goto Error;
    }

    g_dwLastSMSCmd = GetTickCount();
    StartCNMICheck((CRilHandle *)pParam);

    *pdwMsgRef = (DWORD)nValue;

    pBlob = (void*)pdwMsgRef;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(pdwMsgRef);
    }
    return hr;
}

//
//
//
static HRESULT ParseSendMsgError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/,LPVOID pParam, DWORD& /*dwCode*/)
{
    // Even if the command failed, it still counts as an executed SMS command.
    g_dwLastSMSCmd = GetTickCount();
    StartCNMICheck((CRilHandle *)pParam);
    return S_OK;
}

//
//
//
HRESULT RILDrv_SendMsg(DWORD dwParam, const RILMESSAGE *lpMessage, DWORD dwOptions)
{
    char szCmd[MSG_CMDBUF_LENGTH];
    char szCmd2[MSG_CMDBUF_LENGTH];
    LPCSTR szATCmd;
    char szMessage[MSG_MSGBUF_LENGTH];
    UINT cbMessage;
    UINT nGSMLength;
    HRESULT hr = S_OK;
    BOOL fIsCDMA = FALSE;
    DWORD dwRetries, dwRetryDelay, dwRetriesMP, dwRetryDelayMP;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA)
        fIsCDMA = TRUE;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSRetries"), &dwRetries))
        dwRetries = 0;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSRetriesMP"), &dwRetriesMP))
        dwRetriesMP = dwRetries;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSRetryDelay"), &dwRetryDelay))
        dwRetryDelay = 0;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSRetryDelayMP"), &dwRetryDelayMP))
        dwRetryDelayMP =  dwRetryDelay;

#ifdef RIL_SENDMSG_DEBUG_TRACE
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: msg type = 0x%X\r\n"), lpMessage->dwType));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: msg flags = 0x%X\r\n"), lpMessage->dwFlags));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: address = %s\r\n"), lpMessage->msgOutSubmit.raDestAddress.wszAddress));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: protocol = %d\r\n"), lpMessage->msgOutSubmit.dwProtocolID));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: DCS type = %d\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwType));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: DCS flags = 0x%X\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwFlags));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: DCS class = %d\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwMsgClass));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: DCS alphabet = %d\r\n"), lpMessage->msgOutSubmit.rmdDataCoding.dwAlphabet));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: VPFormat = %d\r\n"), lpMessage->msgOutSubmit.dwVPFormat));
    RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: msg length = %d\r\n"), lpMessage->msgOutSubmit.cchMsgLength));
    UINT i;
    for(i=0;i<(lpMessage->msgOutSubmit.cchMsgLength);i++)
    {
        RILRetailTrace((TEXT("RilDrv: RILDrv_SendMsg: msg = 0x%X  %c\r\n"), lpMessage->msgOutSubmit.rgbMsg[i], lpMessage->msgOutSubmit.rgbMsg[i]));
    }
    if(lpMessage->msgOutSubmit.rmdDataCoding.dwAlphabet == RIL_DCSALPHABET_UCS2)RILRetailTrace((TEXT("RilDrv: Send UCS2 encoded message\r\n")));
#endif

    // We can only send outgoing messages
    if (RIL_MSGCLASS_OUTGOING != MSGCLASS_FROM_MSGTYPE(lpMessage->dwType))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    hr = EncodeSMSMessage(*lpMessage, szMessage, MSG_MSGBUF_LENGTH - 1, cbMessage, nGSMLength);
    if (FAILED(hr))
    {
        goto Error;
    }
    if (!cbMessage)
    {
        hr = E_FAIL;
        goto Error;
    }
    szMessage[cbMessage] = '\0';

    if ((RIL_MSGTYPE_OUT_SUBMIT == lpMessage->dwType) || (RIL_MSGTYPE_OUT_RAW == lpMessage->dwType))
    {
        if (fIsCDMA) {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Error: sending GSM SMS in CDMA mode\r\n")));
            hr = E_INVALIDARG;
            goto Error;
        }
        szATCmd = "CMGS";
    }
    else if (RIL_MSGTYPE_OUT_COMMAND == lpMessage->dwType && SUPPORTS_CMGC)
    {
        szATCmd = "CMGC";
    }
    else if (RIL_MSGTYPE_OUT_COMMAND == lpMessage->dwType && !SUPPORTS_CMGC)
    {
        hr = E_NOTIMPL;
        goto Error;
    }
    else if (RIL_MSGTYPE_OUT_IS637SUBMIT == lpMessage->dwType)
    {
        if (!fIsCDMA) {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Error: sending CDMA SMS in GSM mode\r\n")));
            hr = E_INVALIDARG;
            goto Error;
        }
        szATCmd = "CMGS";
    }
    else
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (RIL_MSGTYPE_OUT_SUBMIT == lpMessage->dwType && dwRetries != dwRetriesMP && !fIsCDMA)
    {
        BYTE bUDH[MAXLENGTH_HDR];
        UINT uiUDHSize = 0;

        if (ExtractUDHInformation(lpMessage->msgOutSubmit.rgbHdr, lpMessage->msgOutSubmit.cbHdrLength, UDH_ELEMENTTYPE_CONCAT, bUDH, MAXLENGTH_HDR, uiUDHSize))
        {
            // This is a concatenated SMS, so the "SMSRetriesMP" registry key applies here.
            dwRetries = dwRetriesMP;
            dwRetryDelay = dwRetryDelayMP;
        }
    }

    if (MUST_QUERY_CSCA_BEFORE_SENDING_SMS && !fIsCDMA)
    {
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CSCA?\r", CMDOPT_NONE, APIID_GETCBMSGCONFIG, 0, 0, 0, NULL))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    if ((dwOptions&RIL_SENDOPT_PERSISTLINK) != 0 && g_dwSMSPersistLink == SMSPERSISTLINK_TEMPORARY && !fIsCDMA) {
        // The persist link option may be set to permanent mode
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CMMS=1\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);
    }
   (void)_snprintfz(szCmd, MSG_CMDBUF_LENGTH, "AT+%s=%u\r", szATCmd, nGSMLength);

    (void)_snprintfz(szCmd2, MSG_CMDBUF_LENGTH,  "%s\x1a", szMessage);

    // Special problem of all modules: you need a minimum time delay between two SMS related commands,
    // especially between AT+CNMA and AT+CMGS or two AT+CMGS commands.
    if (g_dwLastSMSCmd > 0) {
        DWORD dwCurTime, dwMaxTime = 0;

        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SMSWaitPeriod"), &dwMaxTime) && dwMaxTime > 0) {
            dwCurTime = GetTickCount();

            if (dwCurTime - g_dwLastSMSCmd < dwMaxTime)
                Sleep(dwMaxTime - (dwCurTime - g_dwLastSMSCmd));
        }
    }
    g_dwLastSMSCmd = GetTickCount();

    if (g_bChangeCNMIwhileSMSsend && !g_bDefaultCNMIstoreOnSIM && !fIsCDMA)
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(),  USE_STANDARD_CNMI_SETTINGS ? "AT"SMS_CNMI_STORE_ON_SIM_3GPP"\r" : "AT"SMS_CNMI_STORE_ON_SIM"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    // If we don't have an 3rd port available, we should use the data port to send the command, unless
    // we are in Datamode. This is necessary to pass the GCF SMS collision test.
    if (!QueueMultipartCmdWithRetry(HCCMD_STANDARD,
                pHandle, szCmd, szCmd2, CMDOPT_RETRYONSIMLOCKED | CMDOPT_INTERMEDIATERESPONSE,
                APIID_SENDMSG, ParseSendMsg, NULL, hr, ParseSendMsgError, (LPVOID)pHandle->GetDevice(), dwRetries, dwRetryDelay))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (g_bChangeCNMIwhileSMSsend && !g_bDefaultCNMIstoreOnSIM && !fIsCDMA)
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT"SMS_CNMI_MANUAL_ACK"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    Error:
    return hr;
}


//
//
//
static HRESULT ParseSendStoredMsg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    DWORD* pdwMsgRef = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwMsgRef = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMsgRef)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwMsgRef, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CMSS: <msgref>[,<ackpdu>]<postfix>" (skip <ackpdu> parameter)
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CMSS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringAnywhere(szRsp, "\r\n", szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

#ifdef NO_SMS_INDEX0
    *pdwMsgRef = (DWORD)(SMS_INDEX_STARTS_WITH_1 ? nValue: nValue + 1);
#else
    *pdwMsgRef = (DWORD)nValue;
#endif

    g_dwLastSMSCmd = GetTickCount();
    StartCNMICheck((CRilHandle *)pParam);

    pBlob = (void*)pdwMsgRef;
    cbBlob = sizeof(DWORD);

    Error:
    if (FAILED(hr))
    {
        (void)FreeBlob(pdwMsgRef);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_SendStoredMsg(DWORD dwParam, DWORD dwIndex, DWORD /*dwOptions*/)
{
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_dwDisableSMS == 1)
    {
        hr = E_NOTIMPL;
        goto Error;
    }
#ifdef NO_SMS_INDEX0
    if (dwIndex == 0)
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (NO_UCS2_WHILE_SENDING_SMS)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCS=\"GSM\";+CMSS=%u\r", SMS_INDEX_STARTS_WITH_1 ? dwIndex : dwIndex-1);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMSS=%u\r", SMS_INDEX_STARTS_WITH_1 ? dwIndex : dwIndex-1);
#else
    if (NO_UCS2_WHILE_SENDING_SMS)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CSCS=\"GSM\";+CMSS=%u\r", dwIndex);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMSS=%u\r", dwIndex);
#endif

    if (g_bChangeCNMIwhileSMSsend && !g_bDefaultCNMIstoreOnSIM)
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), USE_STANDARD_CNMI_SETTINGS ? "AT"SMS_CNMI_STORE_ON_SIM_3GPP"\r" : "AT"SMS_CNMI_STORE_ON_SIM"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_RETRYONSIMLOCKED, APIID_SENDSTOREDMSG, ParseSendStoredMsg, NULL, hr, ParseSendMsgError, (LPVOID)pHandle->GetDevice()))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (NO_UCS2_WHILE_SENDING_SMS)
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CSCS=\"UCS2\"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    if (g_bChangeCNMIwhileSMSsend && !g_bDefaultCNMIstoreOnSIM)
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT"SMS_CNMI_MANUAL_ACK"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SendMsgAcknowledgement(DWORD dwParam, BOOL /*fSuccess*/)
{
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CNMA=0\r");

    g_dwLastSMSCmd = GetTickCount();

    if (!QueueCmd(HCCMD_URCPORT_ONLY, pHandle, szCmd, CMDOPT_NONE|CMDOPT_SMSACK, APIID_SENDMSGACKNOWLEDGEMENT, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

//
//
//
static void ParseJunk(UINT nCRLFs, LPCSTR& rszPointer)
{
    LPCSTR szPointerTmp = rszPointer;

    UINT i;

    // Try to match all the CRLFs we should have
    for (i = 0; i < nCRLFs; i++)
    {
        if (!MatchStringAnywhere(szPointerTmp, "\r\n", szPointerTmp))
        {
            break;
        }
    }

    // Back up over the last <CR><LF> iff we found one
    if (szPointerTmp > (rszPointer+2))
    {
        rszPointer = szPointerTmp-2;
    }

    return ;
}

//
// ParseSMSListAndSendAckInternal()
// The syntax of the AT^SMGL command for CDMA.
// < 3.001: old syntax
// = 3.001: syntax unclear
// > 3.001: new syntax
//
// Return value: TRUE if new syntax is used.
//
static BOOL UseNewCdmaSmglSyntax(LPCSTR szOriginalRsp)
{
    BOOL fNewSyntax = TRUE;
    LPCSTR szRsp = szOriginalRsp;

    if (NEW_CDMA_SMGL_SYNTAX) return TRUE;
    if (!UNCLEAR_CDMA_SMGL_SYNTAX) return FALSE;

    // Old syntax: PDU follows directly after the next comma.
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        // There should be a comma in any case,  but if not use old syntag as default.
        return FALSE;
    }

    // Check if there is numeric string of at least 4 characters. If this is
    // a case, it is most likely a PDU (old syntax). Otherwise it is most
    // likely the new one.
    if (strspn(szRsp, "0123456789ABCDEF") >= 4)
        fNewSyntax = FALSE;

    return fNewSyntax;
}

//
// ParseSMSListAndSendAckInternal()
// Parses thorugh the output of the AT^SMGL command to find unread message. The
// function generates a notification for each unread message, depending of the
// content of fDelete (see below).
//
// fDelete [in]: Specifies what to do with unread messages found:
//        TRUE: generate RIL_NOTIFY_MESSAGE notification and delete message
//        FALSE: generate RIL_NOTIFY_MESSAGE_IN_SIM notification and keep message
static HRESULT ParseSMSListAndSendAckInternal(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam, BOOL fDelete)
{
    HRESULT hr = S_OK;
    UINT nIdx;
    CRilHandle* pHandle;
    LPCSTR szDummy;
    BOOL fIsCDMA = FALSE;

    pHandle = (CRilHandle *)pParam;
    if (pHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
        fIsCDMA = TRUE;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, "^SMGL: ", szRsp)) {

        if (!ParseUInt(szRsp, TRUE, nIdx, szRsp)) {
            goto Continue;
        }

        if (!fDelete) {
            CNotificationData* pnd = NULL;
            RILMESSAGE_IN_SIM rmMessageInSim;

            rmMessageInSim.cbSize = sizeof(rmMessageInSim);
            rmMessageInSim.dwParams = RIL_PARAM_MIS_LOCATION | RIL_PARAM_MIS_INDEX;
            rmMessageInSim.dwLocation = RIL_MSGLOC_SIM;
#ifdef NO_SMS_INDEX0
            rmMessageInSim.dwIndex = (SMS_INDEX_STARTS_WITH_1 ? nIdx : nIdx + 1);
#else
            rmMessageInSim.dwIndex = nIdx;
#endif

            pnd = new CNotificationData;
            if (pnd!=NULL && pHandle!=NULL)
            {
                (void)pnd->InitFromRealBlob(RIL_NOTIFY_MESSAGE_IN_SIM, &rmMessageInSim, sizeof(RILMESSAGE_IN_SIM));
                pHandle->BroadcastNotification(pnd);
            }
        }

Continue:

        if (!fDelete) {
            // Find "<postfix>" (skip rest of parameter)
            if (!FindRspPostfix(szRsp, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            if (!fIsCDMA) {
                // Find "<postfix>" (skip PDU)
                if (!FindRspPostfix(szRsp, szRsp)) {
                    hr = E_FAIL;
                    goto Error;
                }
            }
        }

        if (fDelete) {
            LPCSTR szEndOfPDU;
            LPCSTR szStartOfPDU;
            UINT nLength;
            UINT cbMessage;
            BOOL fPrependedSMSC = FALSE;
            RILMESSAGE rmMessage;
            char szCmd[MAX_ATCMD_LEN];
            CNotificationData* pnd = NULL;

            // Scedule this message to be deleted. This command has to be issued with a
            // high priority to make sure, that it is executed, before the SMS message
            // store is rerouted.
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGD=%d\r", nIdx);
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_INIT, APIID_DELETEMSG, 0, 0, 0);

            if (fIsCDMA) {
                // Check, which syntax the module is using.
                if (UseNewCdmaSmglSyntax(szRsp)) {
                    // Parsing of CDMA-style PDUs (new syntax)

                    // Skip rest of the line
                    if (!MatchStringAnywhere(szRsp, "\r\n", szRsp)) {
                        goto Error;
                    }

                    // The PDU follows in the new line.
                    szStartOfPDU = szRsp;
                    if (!FindRspPostfix(szRsp, szRsp)) {
                        // We failed parsing
                        ParseJunk(1, szRsp);
                        goto Error;
                    }
                    cbMessage = (szRsp - szStartOfPDU) -2;

                    if (FAILED(ParseSMSCDMAMessage(szStartOfPDU, cbMessage, TRUE,  rmMessage)))
                    {
                        // We failed parsing
                        ParseJunk(1, szRsp);
                        goto Error;
                    }
                }
                else {
                    // Parsing of CDMA-style PDUs (old syntax)

                    // PDU follows directly after the next comma.
                    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
                        goto Continue;
                    }

                    // The PDU starts right here.
                    szStartOfPDU = szRsp;
                    if (!FindRspPostfix(szRsp, szRsp)) {
                        // We failed parsing
                        ParseJunk(1, szRsp);
                        goto Error;
                    }
                    cbMessage = (szRsp - szStartOfPDU) -2;

                    if (FAILED(ParseSMSCDMAMessage(szStartOfPDU, cbMessage, TRUE,  rmMessage)))
                    {
                        // We failed parsing
                        ParseJunk(1, szRsp);
                        goto Error;
                    }
                }
            }
            else {
                // Parsing of GSM-style PDUs

                // Parse <stat> (optional)
                if (!MatchStringBeginning(szRsp, ",", szRsp)) {
                    goto Continue;
                }
                // Parse <stat> (optional)
                if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
                    goto Continue;
                }

                // Parse <length>
                if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
                    goto Continue;
                }

                // Look for a "<CR><LF>" and a "<postfix>"
                if (!MatchStringAnywhere(szRsp, "\r\n", szDummy) ||
                    !FindRspPostfix(szDummy, szEndOfPDU))
                {
                    // This isn't a complete message notification -- no need to parse it
                    goto Error;
                }

                szEndOfPDU -= 2; // back up over <cr><lf>

                // Parse "<length><CR><LF>"
                if (!ParseUInt(szRsp, FALSE, nLength, szRsp) ||
                    !MatchStringBeginning(szRsp, "\r\n", szRsp))
                {
                    // We failed parsing
                    ParseJunk(2, szRsp);
                    goto Error;
                }

                // Calculate the total number of bytes in the message data: (length of the PDU data +
                //    length os SC address (stored in the first 2 bytes) + 1) * 2

                if ('+' == *szRsp)
                {
                    cbMessage = nLength * 2 + SMS_BOGUS_ADDR_LENGTH;
                    fPrependedSMSC = TRUE;
                }
                else
                {
                    // Start by assuming the PDU contains the correct number of semi-octets.
                    cbMessage = nLength * 2;

                    // If the PDU contains more data than expected, then we probably have
                    // the SMSC address prepended to the PDU.
                    if ((UINT)(szEndOfPDU - szRsp) > cbMessage)
                    {
                        cbMessage += (1 + SemiByteCharsToByte(*szRsp, *(szRsp + 1))) * 2;
                        fPrependedSMSC = TRUE;
                    }
                }

                if ((UINT)(szEndOfPDU - szRsp) < cbMessage)
                {
                    // We have a "complete" response but not enough characters
                    ParseJunk(1, szRsp);
                    goto Error;
                }

                if (FAILED(ParseSMSMessage(szRsp, cbMessage, TRUE, fPrependedSMSC, rmMessage)))
                {
                    // We failed parsing
                    ParseJunk(1, szRsp);
                    goto Error;
                }
                szRsp += cbMessage;
            }

            if (!MustFilterSMS(&rmMessage))
            {
                pnd = new CNotificationData;
                if (pnd!=NULL && pHandle!=NULL)
                {
                    (void)pnd->InitFromRealBlob(RIL_NOTIFY_MESSAGE, &rmMessage, sizeof(RILMESSAGE));
                    pHandle->BroadcastNotification(pnd);
                }
            }
        }

        // Sometimes there is an extra <cr><lf> between the lines
        if (MatchStringBeginning(szRsp, "\r\n", szDummy))
            (void)FindRspPostfix(szRsp, szRsp);
    }

Error:
    return hr;
}

//
// ParseSMSListAndSendAck()
// Parses AT^SMGL output. See ParseSMSListAndSendAckInternal() for details.
//
HRESULT ParseSMSListAndSendAck(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseSMSListAndSendAckInternal(szRsp, pBlob, cbBlob, pParam, FALSE);
}

//
// ParseSMSListAndSendAckDelete()
// Parses AT^SMGL output.
// Parses AT^SMGL output. See ParseSMSListAndSendAckInternal() for details.
//
HRESULT ParseSMSListAndSendAckDelete(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseSMSListAndSendAckInternal(szRsp, pBlob, cbBlob, pParam, TRUE);
}


#ifdef SUPPORTS_EHSX_R1
//
// ParseMsgReadStatus()
// Parses the output of an AT+CRSM command which reads a record of the EF_SMS
// file. The full content of the record will be copied to pParam (max MAX_SMS_SIM_FILE_SIZE
// bytes).
//
static HRESULT ParseMsgReadStatus(LPCSTR szRsp, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    BYTE *pData;
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    UINT uiSize = 0;

    if (pParam == NULL) goto Error;
    pData = (BYTE *)pParam;

    // First fill in the RILSIMRESPONSE structure
    void *pBlobTmp=NULL;
    UINT cbBlobTmp=0;

    hr = ParseSendRestrictedSimCmd(szRsp, pBlobTmp, cbBlobTmp, NULL);
    prsr = (RILSIMRESPONSE *)pBlobTmp;
    if (FAILED(hr)) {
        goto Error;
    }

    // Was there an error of some sort?
    hr = DetermineSimResponseError(prsr->dwStatusWord1, prsr->dwStatusWord2);
    if (FAILED(hr)) {
        goto Error;
    }

    uiSize = prsr->cbSize - sizeof(RILSIMRESPONSE);
    if (uiSize > MAX_SMS_SIM_FILE_SIZE)     // Check if record fits in buffer.
        goto Error;

    memcpy(pData, prsr->pbResponse, uiSize);

Error:
    return hr;
}

//
// ParseReadMessageAndNotify()
// Parses the Output of AT+CMGR. If the command returns a text message, the
// function will generate RIL_NOTIFY_MESSAGE notification containing this text
// message.
//
static HRESULT ParseReadMessageAndNotify(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    CRilHandle* pHandle;
    LPCSTR szDummy;

    pHandle = (CRilHandle *)pParam;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    if (MatchStringBeginning(szRsp, "+CMGR: ", szRsp)) {
        LPCSTR szEndOfPDU;
        UINT nLength;
        UINT cbMessage;
        BOOL fPrependedSMSC = FALSE;
        RILMESSAGE rmMessage;
        CNotificationData* pnd = NULL;

        // Parse <stat> (optional)
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            goto Error;
        }

        // Parse <length>
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            goto Error;
        }

        // Look for a "<CR><LF>" and a "<postfix>"
        if (!MatchStringAnywhere(szRsp, "\r\n", szDummy) ||
            !FindRspPostfix(szDummy, szEndOfPDU))
        {
            // This isn't a complete message notification -- no need to parse it
            goto Error;
        }

        szEndOfPDU -= 2; // back up over <cr><lf>

        // Parse "<length><CR><LF>"
        if (!ParseUInt(szRsp, FALSE, nLength, szRsp) ||
            !MatchStringBeginning(szRsp, "\r\n", szRsp))
        {
            // We failed parsing
            ParseJunk(2, szRsp);
            goto Error;
        }

        // Calculate the total number of bytes in the message data: (length of the PDU data +
        //    length os SC address (stored in the first 2 bytes) + 1) * 2

        if ('+' == *szRsp)
        {
            cbMessage = nLength * 2 + SMS_BOGUS_ADDR_LENGTH;
            fPrependedSMSC = TRUE;
        }
        else
        {
            // Start by assuming the PDU contains the correct number of semi-octets.
            cbMessage = nLength * 2;

            // If the PDU contains more data than expected, then we probably have
            // the SMSC address prepended to the PDU.
            if ((UINT)(szEndOfPDU - szRsp) > cbMessage)
            {
                cbMessage += (1 + SemiByteCharsToByte(*szRsp, *(szRsp + 1))) * 2;
                fPrependedSMSC = TRUE;
            }
        }

        if ((UINT)(szEndOfPDU - szRsp) < cbMessage)
        {
            // We have a "complete" response but not enough characters
            ParseJunk(1, szRsp);
            goto Error;
        }

        if (FAILED(ParseSMSMessage(szRsp, cbMessage, TRUE, fPrependedSMSC, rmMessage)))
        {
            // We failed parsing
            ParseJunk(1, szRsp);
            goto Error;
        }
        szRsp += cbMessage;

        if (!MustFilterSMS(&rmMessage))
        {
            pnd = new CNotificationData;
            if (pnd!=NULL && pHandle!=NULL)
            {
                (void)pnd->InitFromRealBlob(RIL_NOTIFY_MESSAGE, &rmMessage, sizeof(RILMESSAGE));
                pHandle->BroadcastNotification(pnd);
            }
        }

        // Sometimes there is an extra <cr><lf> between the lines
        if (MatchStringBeginning(szRsp, "\r\n", szDummy))
            (void)FindRspPostfix(szRsp, szRsp);
    }

Error:
    return hr;
}
#endif

//
// CheckForMissedSMS():
// An message might have come in in the short period after the module is registered
// to the network and before the RIL is ready to receive URCs. In this function the
// RIL checks the SMS store on the SIM for missed messages.
// EHS5 Rel1: since there is no AT^SMGL command the RIL directly checks the SIM. This
// can work, because per default the incoming SMS are stored on the SIM and not on
// the ME.
//
// Parameters:
// pHandle [in]: pointer to the CRilHandle object.
// bWithDelete [in]: Specifies what to do with unread messages found:
//        TRUE: generate RIL_NOTIFY_MESSAGE notification and delete message
//        FALSE: generate RIL_NOTIFY_MESSAGE_IN_SIM notification and keep message
//
// Return value: TRUE, if operation have been completed successfuly.
BOOL CheckForMissedSMS(CRilHandle *pHandle, BOOL bWithDelete)
{
    BOOL fRet = FALSE;

    if (pHandle == NULL)
        goto Error;

#ifdef SUPPORTS_EHSX_R1
    if (CHECK_FOR_MISSED_SMS_ON_SIM) {
        // There is AT command which lists the unread text messages without setting
        // them to the status read. Therefore the RIL needs to directly read the SIM.
        char szCmd[MAX_ATCMD_LEN];
        char szPath[MAX_PATH];
        BOOL fPath;
        DWORD dwIdx;
        DWORD dwFileSize = 0;

        if (pHandle == NULL) return NULL;
        if (IsForbiddenSIMFile(SIM_EF_SMS)) return NULL;

        // Get numbers of records for the EF_SMS file.
        g_nSIMType = pHandle->GetCurrentSIMType();
        fPath = NeedSIMPath(g_nSIMType, SIM_EF_SMS, szPath, MAX_PATH);
        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28476,0,0,%d,,\"%s\"\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15, szPath);
        else
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28476,0,0,%d\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15);
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimGetResponseInternal, NULL, (LPVOID)&dwFileSize) && dwFileSize == 0) {
            goto Error;
        }

        // Check if SIM record can be stored.
        if ((dwFileSize >> 16) > MAX_SMS_SIM_FILE_SIZE) goto Error;

        // Test every record of EF_SMS.
        for (dwIdx = 1; dwIdx <= (dwFileSize&0xFFFF); dwIdx++) {
            BYTE bData[MAX_SMS_SIM_FILE_SIZE];

            // Read record from EF_SMS.
            memset(bData, 0, MAX_SMS_SIM_FILE_SIZE);
            if (fPath)
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28476,%d,4,%d,,\"%s\"\r", dwIdx, dwFileSize >> 16, szPath);
            else
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28476,%d,4,%d\r", dwIdx, dwFileSize >> 16);
            if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseMsgReadStatus, NULL, (LPVOID)bData)) {
                goto Error;
            }

            if ((bData[0] & 7) == 3) {      // This means: "message received from the network; message to be read"
                if (bWithDelete) {
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGR=%d\r", dwIdx);
                    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_INIT, APIID_DELETEMSG, 0, 0, 0, ParseReadMessageAndNotify, NULL, (LPVOID)pHandle);

                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGD=%d\r", dwIdx);
                    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_INIT, APIID_DELETEMSG, 0, 0, 0);
                }
                else {
                    CNotificationData* pnd = NULL;
                    RILMESSAGE_IN_SIM rmMessageInSim;

                    rmMessageInSim.cbSize = sizeof(rmMessageInSim);
                    rmMessageInSim.dwParams = RIL_PARAM_MIS_LOCATION | RIL_PARAM_MIS_INDEX;
                    rmMessageInSim.dwLocation = RIL_MSGLOC_SIM;
                    rmMessageInSim.dwIndex = dwIdx;

                    pnd = new CNotificationData;
                    if (pnd!=NULL && pHandle!=NULL)
                    {
                        (void)pnd->InitFromRealBlob(RIL_NOTIFY_MESSAGE_IN_SIM, &rmMessageInSim, sizeof(RILMESSAGE_IN_SIM));
                        pHandle->BroadcastNotification(pnd);
                    }
                }
            }
        }

        fRet = TRUE;
    }
    else {
#endif
        // If available use the AT^SMGL command to check for unread text messages.
        if (bWithDelete || pHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
            fRet = QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT^SMGL=0\r", CMDOPT_NONE, APIID_QUERYUNREADSMS, 0, 0, 0, ParseSMSListAndSendAckDelete, NULL, (LPVOID)pHandle);
        else
            fRet = QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT^SMGL=0\r", CMDOPT_URC, APIID_QUERYUNREADSMS, 0, 0, 0, ParseSMSListAndSendAck, NULL, (LPVOID)pHandle);
#ifdef SUPPORTS_EHSX_R1
    }
#endif

Error:
    return fRet;
}


static HRESULT ParseCurrentCNMIStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT            nValue;
    UINT            nMT;
    HRESULT         hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CNMI: <mode>,<mt>,<bm>,<ds>,<bfr><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
      !MatchStringBeginning(szRsp, "+CNMI: ", szRsp)  ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)           ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nMT, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
      !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (pParam != NULL) {
        DWORD *pdwMT = (DWORD *)pParam;

        *pdwMT = nMT;
    }

Error:
    return hr;
}

//
//
//
static DWORD GetRecheckCNMIStatusTimeout()
{
    DWORD dwTimeout = 0;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("TimeoutRecheckCNMIStatus"), &dwTimeout))
        dwTimeout = 30000;
    return dwTimeout;
}

//
// CheckCNMIStatusThreadProc()
// After sending an SMS the module might not correctly shown incoming SMS which
// came in shortly after the outgoing SMS has been sent. This thread waits until
// the last send command has been 30 s ago. Then it checks the CNMI state. If
// the <mt> parameter is 0 this means, that an SMS came in but was not shown
// by the module. In that case, the lost SMS procedure is started.
//
DWORD WINAPI CheckCNMIStatusThreadProc(LPVOID pVoid)
{
    DWORD dwTimeout;
    DWORD dwMT = 0;
    CRilHandle* pHandle;

    s_fCNMIThreadRunning = TRUE;
    pHandle = (CRilHandle *)pVoid;
    dwTimeout = GetRecheckCNMIStatusTimeout();
    // Timeout = 0 means: disable workaround. End thread, but don't set
    // s_fCNMIThreadRunning to FALSE. This way the RIL won't start this
    // thread again.
    if (dwTimeout == 0)
        return 0;

    while (GetTickCount() - g_dwLastSMSCmd < dwTimeout)
        Sleep(100);

    // Check <mt> parameter of AT+CNMI?
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CNMI?\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0, ParseCurrentCNMIStatus, NULL, &dwMT)) {
        // If <mt> is 0 this means, that an SMS has been lost.
        if (dwMT == 0) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Possible lost SMS, run re-check.")));
            // Start the CNMI activation threat, it will check for lost the SMS.
            CNMIReactivationThreadProc(pVoid);
        }
    }

    s_fCNMIThreadRunning = FALSE;
    return 0;
}

//
// StartCNMICheck()
// This function is called if an SMS has been sent (successfulk or unsuccessful).
// It start a thread which will check if this outgoing SMS has caused an incoming
// SMS to be lost.
//
void StartCNMICheck(CRilHandle* pHandle)
{
    if (pHandle == NULL)
        return;

    // Is workaround needed?
    if (!NEEDS_RECHECK_OF_CNMI_AFTER_CMGS && g_dwDisableSMS == 0)
        return;
    // Not needed for CDMA.
    if (pHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
        return;
    // Not needed if SMS is stored on SIM.
    if (!g_bChangeCNMIwhileSMSsend || g_bDefaultCNMIstoreOnSIM)
        return;

    // Start CNMI thread, if it is not already running.
    if (!s_fCNMIThreadRunning)
        CloseHandle(CreateThread(NULL, 0, CheckCNMIStatusThreadProc, (LPVOID)pHandle, 0, NULL));
}

//
// RejectAllPendingSms()
// Workaround for missing SMS notification bug: the module might already
// be in the state, where a SMS notification is supposed to be printed out,
// but isn't. After a timeout this situation could be detected, but this
// timeout isn't over yet. Solution: reject all pending SMS acknowledgments.
// If there is such an acknowledgement pending, the network will try again
// later so nothing is lost. If not, there will be an error message which
// can safely be ignored.
//
void RejectAllPendingSms(CRilHandle* pHandle)
{
    // SMS completly disabled?
    if (g_dwDisableSMS == 0)
        return;
    // Workaround disabled (timeout = 0)?
    if (GetRecheckCNMIStatusTimeout() == 0)
        return;
    // Not needed for CDMA.
    if (pHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
        return;
    // Not needed if SMS is stored on SIM.
    if (!g_bChangeCNMIwhileSMSsend || g_bDefaultCNMIstoreOnSIM)
        return;

    // There might be a pending SMS acknowledgement. Send a reject command. If
    // a SMS acknowledgement is pending, the network will try again later, so
    // the message isn't lost. If there is no acknowledgment pending, the module
    // will return an error message, which the RIL can safely ignore.
    (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle, "AT+CNMA=2\r", CMDOPT_URC, APIID_SENDMSGACKNOWLEDGEMENT, 0, 0, 0);
    return;
}

//
//
//
DWORD WINAPI CNMIReactivationThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;

    // Problem: when the CNMI was reset by the module all following SMS are stored on the SIM
    // without sending any notifications. This applies only, if the module is in SMS Phase 2+.

    // Step 1: Reactivate the URC:
    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT"SMS_CNMI_MANUAL_ACK"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);

    // Step 2: Check SIM, if there are any unread SMS:
    (void)CheckForMissedSMS(pHandle, FALSE);

    return 0;
}

//
// Sometimes it is necessary not to inform the OS about an incoming SMS.
// This function is called, when an SMS is read via AT+CMGR or when a
// +CMT: URC comes in.
//
BOOL MustFilterSMS(RILMESSAGE *pMsg)
{
    BOOL bFilter = FALSE;

    if (g_dwDisableSMS == 1)
        return TRUE;

    if (pMsg != NULL) {
        if (pMsg->dwType == 0) {
            // This might happen, if a GSM PDU appears in PDU mode. In this
            // case filter the message.
            bFilter = TRUE;
        }
        if (pMsg->dwType == RIL_MSGTYPE_IN_DELIVER) {
            if ((pMsg->dwParams&RIL_PARAM_M_PROTOCOLID) && pMsg->msgInDeliver.dwProtocolID == RIL_MSGPROTOCOL_SM_TYPE0) {
                DWORD dwFilter = 0;

                (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("FilterSMSType0"), &dwFilter);
                if (dwFilter == 1) {
                    bFilter = TRUE;
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Type0 SMS filtered\r\n")));
                }
            }
        }
    }
    return bFilter;
}

//
// This function parses the elements of the user data header and extracts the element of
// the type specified with bUDHElementType. If there is no such element in the UDH, the
// function returns FALSE, otherwise TRUE.
// See GSM 03.40, chapter 9.2.3.24.
//
BOOL ExtractUDHInformation(const BYTE *pUDH, UINT uiUDHLen, BYTE bUDHElementType, BYTE *bUDHElement, UINT uiUDHElementBufferLen, UINT& uiUDHElementLen)
{
    BOOL bFound = FALSE;
    UINT uiPos = 0;

    uiUDHElementLen = 0;
    while (uiPos < uiUDHLen-1 && uiUDHLen > 1)
    {
        if (bUDHElementType == pUDH[uiPos])     // UDH element of wanted type?
        {
            uiUDHElementLen = (UINT)pUDH[uiPos+1];      // Get UDH element data length
            if (uiPos + uiUDHElementLen <= uiUDHLen && uiUDHElementBufferLen >= uiUDHLen)
            {
                memcpy(bUDHElement, pUDH+uiPos+2, uiUDHLen);    // Getdata for UDH element
                bFound = TRUE;
            }
            else
            {
                uiUDHElementLen = 0;
            }
            break;
        }
        else
        {
            // Jump to next UDH element.
            uiPos += pUDH[uiPos+1]+2;
        }
    }
    return bFound;
}

// ParseSMSandCheckClass()
// This function parses the output of AT^SMGL=0. It looks for the SMS specified
// with the <param> parameter, parses it and tries to find out the SMS class.
// If the class is found, it is returned via the <param> parameter.
//
// Parameter:
// pParam [in,out]: [in]: pointer to DWORD containing the SMS index
//                  [out]: function writes the class of the message or 0 on error
static HRESULT ParseSMSandCheckClass(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    UINT nIdx;
    DWORD* pdwParam;
    LPCSTR szDummy;
    DWORD dwIndex;

    pdwParam = (DWORD *)pParam;
    dwIndex = *pdwParam;
    *pdwParam = 0;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, "^SMGL: ", szRsp)) {

        if (!ParseUInt(szRsp, TRUE, nIdx, szRsp)) {
            goto Continue;
        }

        // This is the SMS to be checked.
        if (nIdx == dwIndex) {
            LPCSTR szEndOfPDU;
            UINT nLength;
            UINT cbMessage;
            BOOL fPrependedSMSC = FALSE;
            RILMESSAGE rmMessage;

            // Parse <stat> (optional)
            if (!MatchStringBeginning(szRsp, ",", szRsp)) {
                goto Continue;
            }
            // Parse <stat> (optional)
            if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
                goto Continue;
            }

            // Parse <length>
            if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
                goto Continue;
            }

            // Look for a "<CR><LF>" and a "<postfix>"
            if (!MatchStringAnywhere(szRsp, "\r\n", szDummy) ||
                !FindRspPostfix(szDummy, szEndOfPDU))
            {
                // This isn't a complete message notification -- no need to parse it
                goto Error;
            }

            szEndOfPDU -= 2; // back up over <cr><lf>

            // Parse "<length><CR><LF>"
            if (!ParseUInt(szRsp, FALSE, nLength, szRsp) ||
                !MatchStringBeginning(szRsp, "\r\n", szRsp))
            {
                // We failed parsing
                ParseJunk(2, szRsp);
                goto Error;
            }

            // Calculate the total number of bytes in the message data: (length of the PDU data +
            //    length os SC address (stored in the first 2 bytes) + 1) * 2

            if ('+' == *szRsp)
            {
                cbMessage = nLength * 2 + SMS_BOGUS_ADDR_LENGTH;
                fPrependedSMSC = TRUE;
            }
            else
            {
                // Start by assuming the PDU contains the correct number of semi-octets.
                cbMessage = nLength * 2;

                // If the PDU contains more data than expected, then we probably have
                // the SMSC address prepended to the PDU.
                if ((UINT)(szEndOfPDU - szRsp) > cbMessage)
                {
                    cbMessage += (1 + SemiByteCharsToByte(*szRsp, *(szRsp + 1))) * 2;
                    fPrependedSMSC = TRUE;
                }
            }

            if ((UINT)(szEndOfPDU - szRsp) < cbMessage)
            {
                // We have a "complete" response but not enough characters
                ParseJunk(1, szRsp);
                goto Error;
            }

            if (FAILED(ParseSMSMessage(szRsp, cbMessage, TRUE, fPrependedSMSC, rmMessage)))
            {
                // We failed parsing
                ParseJunk(1, szRsp);
                goto Error;
            }
            szRsp += cbMessage;

            if (rmMessage.dwType == RIL_MSGTYPE_IN_DELIVER)
                *pdwParam = rmMessage.msgInDeliver.rmdDataCoding.dwMsgClass;
        }
        else {
            if (!FindRspPostfix(szRsp, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            // Find "<postfix>" (skip PDU)
            if (!FindRspPostfix(szRsp, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
        }

Continue:
        // Sometimes there is an extra <cr><lf> between the lines
        if (MatchStringBeginning(szRsp, "\r\n", szDummy))
            (void)FindRspPostfix(szRsp, szRsp);
    }

Error:
    return hr;
}

// SMSClass2CheckTreadProc()
// Workaround for a problem in Px8 Rel2: In GCF TC 8.2.1 a class 2 SMS is sent.
// This class of SMS is not supposed to be indicated by the module, but the module
// sents an URC. The test case would go fail, if the RIL would read the SMS in,
// so the RIL must use the AT^SMGL=0 command to find out, if the SMS is class 2.
DWORD WINAPI SMSClass2CheckTreadProc(LPVOID pVoid)
{
    BOOL fSendNotification = TRUE;
    DWORD dwClass2Detection;
    CRilHandle* pHandle;
    RILMESSAGE_IN_SIM rmMessageInSim;
    SMS_CLASS2_CHECK_THREAD_DATA* pData = (SMS_CLASS2_CHECK_THREAD_DATA *)pVoid;

    pHandle = pData->pHandle;
    memcpy(&rmMessageInSim, &(pData->rmMessageInSim), sizeof(RILMESSAGE_IN_SIM));
    delete pData;

#ifdef NO_SMS_INDEX0
    dwClass2Detection = rmMessageInSim.dwIndex;
    if (!SMS_INDEX_STARTS_WITH_1)
        dwClass2Detection -= 1;
#else
    dwClass2Detection = rmMessageInSim.dwIndex;
#endif
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT^SMGL=0\r", CMDOPT_URC, APIID_QUERYUNREADSMS, 0, 0, 0, ParseSMSandCheckClass, NULL, &dwClass2Detection)) {
        if (dwClass2Detection == RIL_DCSMSGCLASS_2) {    // SMS Class 2
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Unexpected class 2 SMS detected.\r\n")));
            fSendNotification  = FALSE;
        }
    }

    if (fSendNotification) {
        // This is a valid URC, send out notification.
        (void)pHandle->BroadcastRealBlobNotification(RIL_NOTIFY_MESSAGE_IN_SIM, (LPVOID)&rmMessageInSim, sizeof(RILMESSAGE_IN_SIM));
    }
    return 0;
}

static HRESULT ParseQCSCBStatus(LPCSTR szRsp, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    UINT uiNumParams;
    UINT i;

    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "$QCSCP: <counter>"
    if (!MatchStringBeginning(szRsp, "$QCSCB: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, uiNumParams, szRsp) ||
        !FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (uiNumParams > MAX_CDMA_CSCB_SETTINGS)
        uiNumParams = MAX_CDMA_CSCB_SETTINGS;

    for (i = 0; i < uiNumParams; i++) {
        if (!ParseUnquotedString(szRsp, '\r', s_szCdmaConfig[i], MAX_CDMA_CSCB_SETTING_STRING, szRsp) ||
            !FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    s_nCdmaConfig = uiNumParams;
Error:
    return hr;
}

// ConfigureCdmaCellBroadcast()
// Applies CDMA configuration. Relevant registry keys:
// "ActivateCdmaCellBroadCast": sets AT$QCSCP
// "CdmaCellBroadCastConfig": sets AT$QCSCB
void ConfigureCdmaCellBroadcast(CRilHandle *pHandle)
{
    HKEY hKey;
    DWORD dwDisposition;
    char szCmd[MAX_ATCMD_LEN];
    int nCdmaConfig = 0;
    int nCount;
    BOOL fWriteConfig = FALSE;

    // Apply QCSCP setting
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT$QCSCP=%u\r", g_dwCdmaCBSetting);
    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCBMSGCONFIG, 0, 0, 0);

    // QCSCP setting other than 1 don't requiry an further  configuration.
    if (g_dwCdmaCBSetting != 1) return;

    s_nCdmaConfig = 0;
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT$QCSCB?\r", CMDOPT_NONE, APIID_GETCBMSGCONFIG, 0, 0, 0, ParseQCSCBStatus))
        return;

    if(MyRegCreateKeyEx(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, 0, TEXT(""), 0, KEY_READ, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS)
    {
        DWORD dwSizeReturned = ALLOC_SIZE*sizeof(TCHAR);
        TCHAR szBuffer[ALLOC_SIZE];

        if (RegQueryValueEx(hKey, TEXT("CdmaCellBroadCastConfig"), 0, 0, (BYTE*)szBuffer, &dwSizeReturned) == ERROR_SUCCESS)
        {
            TCHAR *pszTmp = szBuffer;

            while (dwSizeReturned > 0 && dwSizeReturned < ALLOC_SIZE && _tcslen(pszTmp) > 0 && nCdmaConfig < MAX_CDMA_CSCB_SETTINGS)
            {
                (void)_snprintfz(s_szCdmaConfigTemp[nCdmaConfig], MAX_CDMA_CSCB_SETTING_STRING, "%S", pszTmp);
                pszTmp += _tcslen(pszTmp)+1;
                nCdmaConfig++;
            }
        }
        RegCloseKey(hKey);
    }
    else {
        // No access to registry.
        return;
    }

    fWriteConfig = TRUE;
    if (nCdmaConfig == s_nCdmaConfig) {
        char szTmp[MAX_CDMA_CSCB_SETTING_STRING];
        char *pszT;
        int i, j;
        BOOL fFound;

        fWriteConfig = FALSE;
        // Check if current configuration is identical to configuration in registry.
        for (i = 0; i < nCdmaConfig; i++) {
            // The AT$QCSCB? command prints only the first two parameters,
            // separated with blanks instead of commas.
            strcpy(szTmp, s_szCdmaConfigTemp[i]);
            // Remove comma.
            pszT = strchr(szTmp,',');
            if (pszT != NULL) *pszT = ' ';
            // Remove last parameters
            pszT = strchr(szTmp,',');
            if (pszT != NULL) *pszT = '\0';
            fFound = FALSE;
            // Compare to configuration in registry.
            for (j = 0; j < s_nCdmaConfig; j++) {
                if (strcmp(s_szCdmaConfig[j], szTmp) == 0) {
                    // In case of any differenve, write whole configuration new.
                    fFound = TRUE;
                    break;
                }
            }
            if (!fFound) {
                fWriteConfig = TRUE;
                break;
            }
        }
    }
    if (!fWriteConfig) return;

    // Delete old configuration.
    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT$QCSCB=2\r", CMDOPT_NONE, APIID_SETCBMSGCONFIG, 0, 0, 0);
    // Write new configuration
    for (nCount = 0; nCount < nCdmaConfig; nCount++) {
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT$QCSCB=0,%s\r", s_szCdmaConfigTemp[nCount]);
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCBMSGCONFIG, 0, 0, 0);
    }
}

