/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: 3G-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules PD SW 1
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/URC_Parsing.cpp $
 * $Revision: /main/ms-d/45 $
 *     $Date: 2014-06-16 13:44:42 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 6.x
 *****************************************************************************
 * Description:
 *  Cinterion 3G RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/URC_Parsing.cpp$ 
 * 
 * /main/ms-d/45   2014-06-16 13:44:42   pmackent 
 * Use RIL_E_FDNRESTRICTED error code if available (IPIS100098571)
 *
 * /main/ms-d/44   2014-05-26 16:30:17   pmackent
 * Correction for incorrectly reported system type while module is in CDMA mode and in no-coverage area (IPIS100097887)
 *
 * /main/ms-d/43   2014-02-11 14:30:10   pmackent
 * Fix for wrong handling of signal strength notifications in CDMA
 *
 * /main/ms-d/42   2013-11-29 12:43:25   pmackent
 * Prepare for DTMF detection (possible new PHS8 feature)
 *
 * /main/ms-d/41   2013-09-25 15:21:30   pmackent
 * New methods for signal strength display in 3G networks (IPIS100091577)
 *
 * /main/ms-d/40   2013-09-13 09:26:53   pmackent
 * Cell Broadcast for CDMA (IPIS100091379)
 *
 * /main/ms-d/39   2013-06-11 10:13:00   pmackent
 * Restart on +CREG: 3 without cause (IPIS100088819)
 *
 * /main/ms-d/38   2013-05-21 11:45:49   pmackent
 * Allow user defined OMADM textes
 *
 * /main/ms-d/37   2013-05-21 09:49:13   pmackent
 * Removed unnecessary GPRS setting in CDMA
 *
 * /main/ms-d/36   2013-05-15 10:39:00   pmackent
 * Avoid 1xRTT icon during call attempts in no-coverage area(IPIS100087532)
 *
 * /main/ms-d/35   2013-05-13 15:24:02   pmackent
 * Store NITZ DST settings (IPIS100087449)
 *
 * /main/ms-d/34   2013-05-10 14:35:28   pmackent
 * Display "1xRTT" icon during voice call in EVDO (IPIS100087443)
 *
 * /main/ms-d/33   2013-04-26 09:28:14   pmackent
 * Remove unused variable
 *
 * /main/ms-d/32   2013-04-26 09:17:14   pmackent
 * Parsing of CDMA status reports added
 *
 * /main/ms-d/31   2013-04-24 11:44:30   pmackent
 * New CDMA/CNMI setting and CDMA/CLCC workaround
 *
 * /main/ms-d/30   2013-04-12 16:44:22   pmackent
 * Workaround for wrong CGREG status in PXS8 multimode
 *
 * /main/ms-d/29   2013-04-12 09:21:38   pmackent
 * Adaptions for PXS8 2.910
 *
 * /main/ms-d/28   2013-03-20 15:32:10   pmackent
 * Change handling of data calls in CDMA in Windows Mobile (IPIS100085910)
 *
 * /main/ms-d/27   2013-03-06 10:14:17   pmackent
 * corrected typo in constant name
 *
 * /main/ms-d/26   2013-02-26 15:08:04   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/25   2013-02-07 14:04:58   pmackent
 * Special OMADM notification
 *
 * /main/ms-d/24   2013-02-01 14:24:14   pmackent
 * Handling of OMADM URC
 *
 * /main/ms-d/23   2012-12-12 15:23:49   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/22   2012-11-29 15:29:45   pmackent
 * Badly coded <alpha parameter in +CLIP for EHS5
 *
 * /main/ms-d/21   2012-11-27 11:49:29   pmackent
 * Remove CE6 warnings
 *
 * /main/ms-d/20   2012-10-22 11:21:19   pmackent
 * Workaround for from NITZ indication in CDMA
 *
 * /main/ms-d/19   2012-10-08 16:18:40   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/18   2012-10-01 09:58:31   pmackent
 * Handle class 2 SMS for PTCRB 8.2.1 (IPIS100082634)
 *
 * /main/ms-d/17   2012-09-25 10:26:35   pmackent
 * Workaround for double CDMA activation URC
 *
 * /main/ms-d/16   2012-08-27 16:20:34   pmackent
 * Wait before searching for CDMA SMS
 *
 * /main/ms-d/15   2012-07-16 15:46:48   pmackent
 * Perform reset after CDMA activation
 *
 * /main/ms-d/14   2012-06-19 13:53:55   pmackent
 * Handle case when muted call gets disconnected and next call uses DTMF (IPIS100078740)
 *
 * /main/ms-d/13   2012-06-07 13:59:05   pmackent
 * Adaptions for multimode
 * Workaround for CREG=0 bug in PXS8
 *
 * /main/ms-d/12   2012-06-05 11:47:50   pmackent
 * Add handling of undocumented CDMA activation URC (IPIS100079730)
 *
 * /main/ms-d/11   2012-04-26 13:02:21   pmackent
 * Further CDMA specific code
 *
 * /main/ms-d/10   2012-04-18 13:46:36   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/9   2012-02-27 10:55:55   ctna0256
 * Handle EHS5 specilities with DTMF, RLP, call control
 *
 * /main/ms-d/8   2012-02-24 14:21:28   ctna0256
 * Workaround for missing BUSY in EHS5
 *
 * /main/ms-d/7   2012-02-23 16:10:14   ctna0256
 * Special handling of NO CARRIER in Intel-AT-Set
 *
 * /main/ms-d/6   2012-02-22 15:09:32   ctna0256
 * Hide Intel style CEER parsing
 *
 * /main/ms-d/5   2012-02-22 13:27:27   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/4   2012-02-16 13:44:51   ctna0256
 * Comply to CE6 and CE7 compiler warnings
 *
 * /main/ms-d/3   2012-02-14 14:52:12   ctna0256
 * Improved comments
 *
 * /main/ms-d/2   2012-02-10 12:59:42   ctna0256
 * Store Idle Mode Status in registry (IPIS100077395)
 *
 * /main/ms-d/1   2012-02-03 15:05:45   ctna0256
 * Move URC parsing functions in separate file
 *
 * /main/ms-d/1   2012-02-03 13:58:22   ctna0256
 * Phonebook and SIM access now in separate files
 *
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 Gemalto M2M GmbH
//
// 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"

// These commands don't need a timoeout definition per registry.
#define CUSD_CANCEL_TIMEOUT  15000     // Timeout (in ms) for AT+CUSD=2 command
#define WAIT_FOR_GPRS_ATTACH  3000     // Time (in ms) to wait before sending AT+CGATT after +CGREG URC

#define WAIT_AFTER_CREG_URC 1500       // Wait for further actions after +CREG URC (in ms)

#define MAX_CDMA_PDU_SIZE 1024         // Maximum PDU size for CDMA cell broadcast messages.

//
// CallerID validities
//
static const DWORD g_rgdwValidities[] =
{
    RIL_REMOTEPARTYINFO_VALID,          // 0
    RIL_REMOTEPARTYINFO_WITHHELD,       // 1
    RIL_REMOTEPARTYINFO_UNAVAILABLE,    // 2
};
#define NUM_VALIDITIES      (sizeof(g_rgdwValidities) / sizeof(DWORD))

//
// Supplementary service data status values
//
static const DWORD g_rgdwSupSvcDataStats[] =
{
    RIL_SUPSVCDATASTATUS_NOINFOREQUIRED,        // 0
    RIL_SUPSVCDATASTATUS_FURTHERINFOREQUIRED,   // 1
    RIL_SUPSVCDATASTATUS_TERMINATED,            // 2
    RIL_SUPSVCDATASTATUS_OTHERCLIENTRESPONDED,  // 3
    RIL_SUPSVCDATASTATUS_UNSUPPORTED,           // 4
    RIL_SUPSVCDATASTATUS_TIMEOUT,               // 5
};
#define NUM_SUPSVCDATASTATS     (sizeof(g_rgdwSupSvcDataStats) / sizeof(DWORD))

#ifdef RIL_INDICATE_SS_INFORMATION
//
// Supplementyry service indications
//
static const SSINDICATION g_rgCSSIIndicationsTable[] =
{
    {0, IDS_SS_INDICATION_CSSI_0},
    {1, IDS_SS_INDICATION_CSSI_1},
    {2, IDS_SS_INDICATION_CSSI_2},
    {3, IDS_SS_INDICATION_CSSI_3}
};
#define NUM_CSSIINDICATIONSCAUSES   (sizeof(g_rgCSSIIndicationsTable) / sizeof(SSINDICATION))
static const SSINDICATION g_rgCSSUIndicationsTable[] =
{
    {0,  IDS_SS_INDICATION_CSSU_0},
    {10, IDS_SS_INDICATION_CSSU_10}
};
#define NUM_CSSUINDICATIONSCAUSES   (sizeof(g_rgCSSUIndicationsTable) / sizeof(SSINDICATION))
#endif

// Used for handling the +CTZV and +CTZDST URCs
static BOOL g_fNitzTimeAvailable = FALSE;
static char g_szNitzTime[MAX_PATH];
static UINT g_nTimeZone = 0;

#ifdef RIL_ENABLE_ALS
UINT           g_dwALSLine = 0;
#endif


//
//
//
BOOL CResponse::ParseExtRing(LPCSTR& rszPointer, DWORD *pdwCallType)
{
    DWORD dwDefaultAddressId = 0;
    LPCSTR szDummy;
    RILRINGINFO rri; memset(&rri,0,sizeof(rri)); // zero struct
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete ring notification -- no need to parse it
        goto Error;
    }

#ifdef RIL_ENABLE_ALS
    dwDefaultAddressId = g_dwALSLine;
    // The ^SALS and the +CRING URC show up within the same Mux frame, so the URC is already in the buffer.
    if (MatchStringAnywhere(rszPointer, "^SALS: 2", szDummy) )
        dwDefaultAddressId = 1;
#endif

    // Start by assuming that this ring is on the first line.
    rri.dwAddressId = dwDefaultAddressId;
    rri.dwParams |= RIL_PARAM_RI_ADDRESSID;

    if (MatchStringBeginning(rszPointer, "ASYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = FALSE;
        rri.rsiServiceInfo.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "SYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = TRUE;
        rri.rsiServiceInfo.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "REL ASYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = FALSE;
        rri.rsiServiceInfo.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "REL SYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = TRUE;
        rri.rsiServiceInfo.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "FAX\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_FAX;
    }
    else if (MatchStringBeginning(rszPointer, "VOICE\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_VOICE;
    }
    else if (MatchStringBeginning(rszPointer, "AUX VOICE\r\n", rszPointer))
    {
        rri.dwAddressId = 1;        // AUX VOICE: voice call on 2nd line.
        rri.dwCallType = RIL_CALLTYPE_VOICE;
    }
    else
    {
        // Skip to the next <postfix>
        (void)FindRspPostfix(rszPointer, rszPointer);
        rri.dwCallType = RIL_CALLTYPE_UNKNOWN;
    }

    *pdwCallType = rri.dwCallType;

    // Walk back over the <CR><LF>
    rszPointer -= 2;

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_RING;
    rri.cbSize = sizeof(RILRINGINFO);
    rri.dwParams |= RIL_PARAM_RI_CALLTYPE;
    if (RIL_CALLTYPE_DATA == rri.dwCallType)
    {
        // For data calls, set connection service info
        rri.dwParams |= RIL_PARAM_RI_SERVICEINFO;
        rri.rsiServiceInfo.cbSize = sizeof(RILSERVICEINFO);
        rri.rsiServiceInfo.dwParams |= RIL_PARAM_SVCI_ALL;
    }

    if (!SetBlob((void*)&rri, sizeof(RILRINGINFO)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL  CResponse::ParseServiceInfo(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    RILSERVICEINFO rsi; memset(&rsi,0,sizeof(rsi)); // zero struct
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete ring notification -- no need to parse it
        goto Error;
    }

    if (MatchStringBeginning(rszPointer, "ASYNC", rszPointer))
    {
        rsi.fSynchronous = FALSE;
        rsi.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "SYNC", rszPointer))
    {
        rsi.fSynchronous = TRUE;
        rsi.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "REL ASYNC", rszPointer))
    {
        rsi.fSynchronous = FALSE;
        rsi.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "REL SYNC", rszPointer))
    {
        rsi.fSynchronous = TRUE;
        rsi.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "GPRS", rszPointer))
    {
        rsi.fSynchronous = FALSE;
        rsi.fTransparent = FALSE;
    }
    else
    {
        // "+CR: " must be followed by something valid
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }


    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_DATASVCNEGOTIATED;
    rsi.cbSize = sizeof(RILSERVICEINFO);
    rsi.dwParams |= RIL_PARAM_SVCI_ALL;
    if (!SetBlob((void*)&rsi, sizeof(RILSERVICEINFO)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseRemotePartyInfo(LPCSTR& rszPointer, BOOL fCLIP)
{
    LPCSTR szDummy;
    UINT nValue;
    char szAddress[MAXLENGTH_ADDRESS];
    char szSubAddress[MAXLENGTH_SUBADDR];
    RILREMOTEPARTYINFO rrpi ; memset(&rrpi,0,sizeof(rrpi)); // zero struct
    BOOL fAddressPresent = FALSE;
    BOOL fSubAddressPresent = FALSE;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete Caller ID notification -- no need to parse it
        goto Error;
    }

    rrpi.cbSize = sizeof(RILREMOTEPARTYINFO);

    // Parse "<address>
    if (ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer))
    {
        fAddressPresent = TRUE;
    }

    // Parse ","
    if (!MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        goto Error;
    }

    // Parse "<type>"
    if (ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer) &&
        fAddressPresent                                                       &&
        StringToRILAddress(szAddress, (BYTE)nValue, rrpi.raAddress))
    {
        rrpi.dwParams |= RIL_PARAM_RPI_ADDRESS;
    }
    // Parse ","
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        // Workaround: it the subaddress string is empty and the subaddress type is 0,
        // the module forgets to print the comma
        if (MatchStringBeginning(rszPointer, "\"\"0", szDummy))
        {
            fSubAddressPresent = FALSE;
            (void)MatchStringBeginning(rszPointer, "\"\"0", rszPointer);
        }
        else
        {
            // Parse "<subaddr>"
            if (ParseString(rszPointer, szSubAddress, MAXLENGTH_SUBADDR, rszPointer))
            {
                fSubAddressPresent = TRUE;
            }

            if (!MISSING_PARAMETER_IN_COLP || fCLIP)
            {
                // Parse ","
                if (!MatchStringBeginning(rszPointer, ",", rszPointer))
                {
                    goto Error;
                }

                // Parse "<subaddr_type>"
                if (ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer) &&
                    fSubAddressPresent                                                    &&
                    StringToRILSubAddress(szSubAddress, (BYTE)nValue, rrpi.rsaSubAddress))
                {
                    rrpi.dwParams |= RIL_PARAM_RPI_SUBADDRESS;
                }
            }
        }

        // Parse ","
        if (MatchStringBeginning(rszPointer, ",", rszPointer))
        {
            BOOL fSkipRest = FALSE;

            if (POSSIBLE_WRONG_ALPHA_IN_CLIP) {
                // Bug in HC25: the <cr> might come before the <">. In this case we
                // have an undecodable <alpha> parameter, which must be skipped.
                if (MatchStringBeginning(rszPointer, "\"", szDummy))
                {
                    LPCSTR szDummy1, szDummy2;
                    if (!MatchStringAnywhere(szDummy, "\"", szDummy1))
                        szDummy1 = NULL;
                    if (!MatchStringAnywhere(szDummy, "\r", szDummy2))
                        szDummy2 = NULL;
                    if (szDummy1 > szDummy2 || (szDummy1 == NULL && szDummy2 != NULL))
                        fSkipRest = TRUE;
                }
            }
            if (MISSING_PARAMETER_IN_COLP && !fCLIP)
                fSkipRest = TRUE;
            if (fSkipRest)
            {
                if (MatchStringAnywhere(rszPointer, "\r", rszPointer))
                    rszPointer --;
            }
            else
            {
                // Parse "<description>"
                if (CLCC_ALPHA_ALWAYS_GSM) {  // Applies for +CLIP/+COLP as well.
                    char szGSM[MAXLENGTH_DESCRIPTION+1];

                    if (ParseString(rszPointer, szGSM, MAXLENGTH_DESCRIPTION, rszPointer)) {
                        (void)_snwprintfz(rrpi.wszDescription, MAXLENGTH_DESCRIPTION, L"%S", szGSM);
                        rrpi.dwParams |= RIL_PARAM_RPI_DESCRIPTION;
                    }
                }
                else {
                    if (ParseQuotedEncodedString(   ENCODING_UCS2,
                                                    rszPointer,
                                                    rrpi.wszDescription,
                                                    rrpi.wszDescription + MAXLENGTH_DESCRIPTION))
                    {
                        rrpi.dwParams |= RIL_PARAM_RPI_DESCRIPTION;
                    }
                }

                // Parse ","
                if ((fCLIP || (!fCLIP && TOO_MANY_PARAMETERS_IN_COLP)) && MatchStringBeginning(rszPointer, ",", rszPointer))
                {
                    // Parse "<validity>" (might be omited)
                    if (ParseUIntAndVerifyAbove(rszPointer, FALSE, NUM_VALIDITIES, nValue, rszPointer))
                    {
                        rrpi.dwValidity = g_rgdwValidities[nValue];
                        rrpi.dwParams |= RIL_PARAM_RPI_VALIDITY;
                    }
                }
            }
        }
    }

    if (!fCLIP && strlen(m_pRilHandle->GetLastDialedNumber())>0 )
    {
        if (wcslen(rrpi.raAddress.wszAddress)==0)
            (void)_snwprintfz(rrpi.raAddress.wszAddress, MAXLENGTH_ADDRESS, L"%S", m_pRilHandle->GetLastDialedNumber());

        m_pRilHandle->DeleteLastDialedNumber();
    }

    // Store, if a call without number comes in for problems with AT+CPBW.
    if (fCLIP && wcslen(rrpi.raAddress.wszAddress) == 0)
        m_pRilHandle->SetReceivedVoiceCallWithoutNumber();

    m_fUnsolicited = TRUE;
    m_dwCode = (fCLIP ? RIL_NOTIFY_CALLERID : RIL_NOTIFY_DIALEDID);
    if (!SetBlob((void*)&rrpi, sizeof(RILREMOTEPARTYINFO)))
    {
        goto Error;
    }
#ifdef RIL_INDICATE_COLP
    if (!fCLIP && m_pRilHandle->DisplayAdvancedNotifications())
    {
        WCHAR wszText[MAX_RESOURCE_STRING_LEN];
        CNotificationData* pnd = NULL;
        RILSUPSERVICEDATA *prssd;
        UINT cbStruct;

        if (LoadString((HINSTANCE)hInstance, IDS_INDICATION_COLP, wszText, MAX_RESOURCE_STRING_LEN) > 0)
        {
            WCHAR wszOut[MAX_RESOURCE_STRING_LEN];

            (void)_snwprintfz(wszOut, MAX_RESOURCE_STRING_LEN, wszText, szAddress);
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszOut) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            if (prssd != NULL)
            {
                memcpy(prssd->pbData, (BYTE *)wszOut, (wcslen(wszOut) * sizeof(WCHAR)) + sizeof(WCHAR));
                prssd->dwParams = RIL_PARAM_SSDI_DATA;
                prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                prssd->cbSize = cbStruct;
                pnd = new CNotificationData;
                if (pnd)
                {
                    if (pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                        m_pRilHandle->BroadcastNotification(pnd);
                    else
                        delete pnd;
                }
                FreeBlob(prssd);
            }
        }
    }
#endif
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseCallWaitingInfo(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue;
    BOOL fAddressPresent = FALSE;
    char szAddress[MAXLENGTH_ADDRESS];
    RILCALLWAITINGINFO rcwi ; memset(&rcwi,0,sizeof(rcwi)); // zero struct
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete Caller ID notification -- no need to parse it
        goto Error;
    }

    rcwi.cbSize = sizeof(RILCALLWAITINGINFO);

    // Parse "<address>
    if (ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer))
    {
        fAddressPresent = TRUE;
    }

    // Parse ","
    if (!MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        goto Error;
    }

    // Parse "<type>"
    if (ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer) &&
        fAddressPresent                                                       &&
        StringToRILAddress(szAddress, (BYTE)nValue, rcwi.rrpiCallerInfo.raAddress))
    {
        rcwi.dwParams |= RIL_PARAM_CWI_CALLERINFO;
        rcwi.rrpiCallerInfo.dwParams |= RIL_PARAM_RPI_ADDRESS;
    }

    // Parse ",<class>"
    if (!MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        goto Error;
    }
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        nValue = 1;             // If this parameter is missing, it should be handled as a voice call.

    if (1 == nValue)
    {
        rcwi.dwCallType = RIL_CALLTYPE_VOICE;
    }
    else if (2 == nValue || 16 == nValue || 32 == nValue || 64 == nValue || 128 == nValue)
    {
        rcwi.dwCallType = RIL_CALLTYPE_DATA;
    }
    else if (4 == nValue)
    {
        rcwi.dwCallType = RIL_CALLTYPE_FAX;
    }
    else
    {
        rcwi.dwCallType = RIL_CALLTYPE_UNKNOWN;
    }
    rcwi.dwParams |= RIL_PARAM_CWI_CALLTYPE;

    // Parse ","
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        // Parse ","
        if (MatchStringBeginning(rszPointer, ",", rszPointer))
        {
            // Parse "<validity>" (optional)
            if (ParseUIntAndVerifyAbove(rszPointer, FALSE, NUM_VALIDITIES, nValue, rszPointer))
            {
                rcwi.rrpiCallerInfo.dwValidity = g_rgdwValidities[nValue];
                rcwi.dwParams |= RIL_PARAM_CWI_CALLERINFO;
                rcwi.rrpiCallerInfo.dwParams |= RIL_PARAM_RPI_VALIDITY;
            }
        }
    }

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_CALLWAITING;
    if (!SetBlob((void*)&rcwi, sizeof(RILCALLWAITINGINFO)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseUnsolicitedSSInfo(LPCSTR& szPointer)
{
// Parsing for the +CCSU notification. The format is:
//    "<code2>[,<index>[,<address>,<type>[,<subaddress>,<satype>]]]"

    LPCSTR szDummy;
    UINT nDummy;
    DWORD dwTemp;
    LPCSTR szPostfix;
    BOOL fRet = FALSE;
#ifdef RIL_INDICATE_SS_INFORMATION
    WCHAR wszText[MAX_RESOURCE_STRING_LEN];
    UINT uiText = 0;
    UINT i;
#endif

    if (!FindRspPostfix(szPointer, szPostfix))
    {
        // This isn't a complete Supplementary services notification -- no need to parse it
        goto Error;
    }

    // parse <code2>
    if (!ParseUIntAndVerifyAbove(szPointer, FALSE, RIL_UNSSSCODE_MAX+1, nDummy, szPointer))
    {
        goto Error;
    }
    if(5 == nDummy)
    {
        // A notification indicating that a call on hold has been remotely hung up
        // Find "<postfix>"
        if (!ParseRspPostfix(szPointer, szDummy))
        {
            goto Error;
        }
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_DISCONNECT;
        dwTemp = RIL_DISCINIT_REMOTE;
        if (!SetBlob((void*)&dwTemp, sizeof(DWORD)))
        {
            goto Error;
        }
        else
        {
#ifdef RILDRV_CE6
            SendDisconnectNotification(m_pRilHandle, RIL_RESULT_CALLABORTED);
#endif
            fRet = TRUE;
            if (m_pRilHandle->IsVoiceCallDialing())
            {
                CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)m_pRilHandle, 0, 0));
                m_pRilHandle->SetVoiceCallDialing(FALSE);
            }
        }
    }
    else
    {
        // A "real" SS Notification
        RILUNSOLICITEDSSINFO russi;
        BOOL fHaveAddress = FALSE;
        char szAddress[MAXLENGTH_ADDRESS];

        russi.cbSize = sizeof(RILUNSOLICITEDSSINFO);
        russi.dwNotificationCode = nDummy;
        russi.dwParams = RIL_PARAM_UNSSS_NOTIFICATIONCODE;

        // parse [,<index>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Continue;
        }
        if (ParseUInt(szPointer, TRUE, nDummy, szPointer))
        {
            russi.dwCallUserGroupIndex = nDummy;
            russi.dwParams |= RIL_PARAM_UNSSS_CUGINDEX;
        }

        // parse [,<number>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Continue;
        }
        if(ParseString(szPointer, szAddress, MAXLENGTH_ADDRESS, szPointer))
        {
             fHaveAddress = TRUE;
        }
        // Parse "," and look for <type>
        if (MatchStringBeginning(szPointer, ",", szPointer))
        {
            UINT nType = 0;
            if(ParseUInt(szPointer, FALSE, nType, szPointer))
            {
                if(( nType < 0x100) && fHaveAddress)
                {
                    if(StringToRILAddress(szAddress, (BYTE)nType, russi.raAddress))
                    {
                        russi.dwParams |= RIL_PARAM_UNSSS_ADDRESS;
                    }
                    else
                    {
                        goto Error;
                    }
                }
            }
        }

        // parse [,<subaddr>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Continue;
        }
        fHaveAddress = FALSE;
        if(ParseString(szPointer, szAddress, MAXLENGTH_ADDRESS, szPointer))
        {
             fHaveAddress = TRUE;
        }
        // Parse "," and look for <satype>
        if (MatchStringBeginning(szPointer, ",", szPointer))
        {
            UINT nType = 0;

            // The <sattype> parameter is not expected.
            (void)ParseUInt(szPointer, FALSE, nType, szPointer);
        }

Continue:
        // Find "<postfix>"
        if (!ParseRspPostfix(szPointer, szDummy))
        {
            goto Error;
        }
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_UNSOLICITEDSS;
        if (!SetBlob((void*)&russi, sizeof(RILUNSOLICITEDSSINFO)))
        {
            goto Error;
        }
        else
        {
            fRet = TRUE;
        }
    }

#ifdef RIL_INDICATE_SS_INFORMATION
    if (m_pRilHandle->DisplayAdvancedNotifications())
    {
        for (i = 0; i < NUM_CSSUINDICATIONSCAUSES; i++)
        {
            if (g_rgCSSUIndicationsTable[i].dwSS_ind == nDummy)
            {
                uiText = g_rgCSSUIndicationsTable[i].uiErrorText;
                break;
            }
        }

        if (uiText != 0)
        {
            CNotificationData* pnd = NULL;
            RILSUPSERVICEDATA *prssd;
            INT cbStruct;

            if (LoadString((HINSTANCE)hInstance, uiText, wszText, MAX_RESOURCE_STRING_LEN) > 0)
            {
                cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                if (prssd != NULL)
                {
                    memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                    prssd->dwParams = RIL_PARAM_SSDI_DATA;
                    prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                    prssd->cbSize = cbStruct;
                    pnd = new CNotificationData;
                    if (pnd)
                    {
                        if (pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                            m_pRilHandle->BroadcastNotification(pnd);
                        else
                            delete pnd;
                    }
                    FreeBlob(prssd);
                }
            }
        }
    }
#endif

Error:

    return fRet;
}

//
//
//
BOOL CResponse::ParseIntermediateSSInfo(LPCSTR& szPointer)
{
// Parsing for the +CCSI notification. The format is:
//    "<code1>[,<index>]"
// <index> is only present when <code1> is 4 (CUG call)

    LPCSTR szDummy;
    UINT nDummy;
    LPCSTR szPostfix;
    BOOL fRet = FALSE;
    RILINTERMEDIATESSINFO rissi;
#ifdef RIL_INDICATE_SS_INFORMATION
    WCHAR wszText[MAX_RESOURCE_STRING_LEN];
    UINT uiText = NULL;
    UINT i;
#endif

    if (!FindRspPostfix(szPointer, szPostfix))
    {
        // This isn't a complete Supplementary services notification -- no need to parse it
        goto Error;
    }

    // parse <code1>
    if (!ParseUIntAndVerifyAbove(szPointer, FALSE, RIL_INTSSCODE_MAX+1, nDummy, szPointer))
    {
        goto Error;
    }

    rissi.cbSize = sizeof(RILINTERMEDIATESSINFO);
    rissi.dwNotificationCode = nDummy;
    rissi.dwParams = RIL_PARAM_INTSS_NOTIFICATIONCODE;

    if (4 == nDummy)
    {
        // parse ,<index>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Error;
        }
        if (ParseUInt(szPointer, TRUE, nDummy, szPointer))
        {
            rissi.dwCallUserGroupIndex = nDummy;
            rissi.dwParams |= RIL_PARAM_INTSS_CUGINDEX;
        }
        else
        {
            goto Error;
        }
    }

    // Find "<postfix>"
    if (!ParseRspPostfix(szPointer, szDummy))
    {
        goto Error;
    }
    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_INTERMEDIATESS;
    if (!SetBlob((void*)&rissi, sizeof(RILINTERMEDIATESSINFO)))
    {
        goto Error;
    }
    else
    {
        fRet = TRUE;
    }

#ifdef RIL_INDICATE_SS_INFORMATION
    if (m_pRilHandle->DisplayAdvancedNotifications())
    {
        for (i = 0; i < NUM_CSSIINDICATIONSCAUSES; i++)
        {
            if (g_rgCSSIIndicationsTable[i].dwSS_ind == nDummy)
            {
                uiText = g_rgCSSIIndicationsTable[i].uiErrorText;
                break;
            }
        }

        if (uiText != 0)
        {
            CNotificationData* pnd = NULL;
            RILSUPSERVICEDATA *prssd;
            INT cbStruct;

            if (LoadString((HINSTANCE)hInstance, uiText, wszText, MAX_RESOURCE_STRING_LEN) > 0)
            {
                cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                if (prssd != NULL)
                {
                    memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                    prssd->dwParams = RIL_PARAM_SSDI_DATA;
                    prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                    prssd->cbSize = cbStruct;
                    pnd = new CNotificationData;
                    if (pnd)
                    {
                        if (pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                            m_pRilHandle->BroadcastNotification(pnd);
                        else
                            delete pnd;
                    }
                    FreeBlob(prssd);
                }
            }
        }
    }
#endif

Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseCallMeter(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete CCM notification -- no need to parse it
        goto Error;
    }

    // The parameter of the +CCCM-URC may or may not be in quotation
    // marks.
    (void)MatchStringBeginning(rszPointer, "\"", rszPointer);
    // Parse "<ccm>"
    if (!ParseHexUInt(rszPointer, TRUE, nValue, rszPointer))
    {
        // Parsing failure
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }
    (void)MatchStringBeginning(rszPointer, "\"", rszPointer);

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_CALLMETER;
    if (!SetBlob((void*)&nValue, sizeof(DWORD)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
// Some networks are indicated by the module as roaming, but not be reported
// this wait to the MMI.
//
DWORD WINAPI CheckRoamingNetworkThreadProc(LPVOID pVoid)
{
    DWORD dwRegStatus = RIL_REGSTAT_ROAMING;
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;
    //Sleep(WAIT_AFTER_CREG_URC);

    // We query the current operator, the result will be placed in a global variable.
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+COPS=3,2;+COPS?\r", CMDOPT_NONE, APIID_GETCURRENTOPERATOR, 0, 0, 0, ParseGetCurrentOperator))
    {
        // Test, if this operator is supposed to be shown as 'non roaming'.
        if (pHandle->IsNoRoamingNetwork(g_iLastCOPSOpr))
            dwRegStatus = RIL_REGSTAT_HOME;
    }

    // If the registration status changed again, or the radio is switched off meanwhile, it doesn't make sense to send the notification now.
    if (g_dwRegStatus == RIL_REGSTAT_ROAMING && !g_PowerManager.IsRadioOff())
        pHandle->GenerateRegStatusNotification(dwRegStatus);
    return 0;
}

//
// Parsing function for GPRSReattachThreadProc().
// Returns TRUE/FALSE to *pParam, when GPRS attach is possible.
//
static HRESULT ParseTestGPRSReattach(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    BOOL* pfReattachPossible = NULL;
    UINT nValue;
    HRESULT hr = S_OK;

    pfReattachPossible = (BOOL *)pParam;
    pBlob = NULL;
    cbBlob = 0;
    if (pfReattachPossible != NULL)
        *pfReattachPossible = FALSE;

    // Parse "<prefix>^SIND: psinfo,<mode>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "^SIND: psinfo,", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

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

    // If nValue is >0 the current cell supports some kind of packed switched data.
    if (pfReattachPossible != NULL)
        *pfReattachPossible = (nValue == 0 ? FALSE : TRUE);

Error:
    return hr;
}

//
// This thread checks, if a GPRS-Reattach is possible and performs it.
//
DWORD WINAPI GPRSReattachThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;
    BOOL fReattachPossible = FALSE;

    pHandle = (CRilHandle *)pVoid;
    Sleep(WAIT_AFTER_CREG_URC);
    // A reattach is possible if module is registered to a network and it reports GPRS is available
    if ((g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING) && !NO_CHECK_BEFORE_GPRS_REATTACH && (!NO_SIND_PSINFO_IN_CDMA || pHandle->GetCurrentTechnology() != TEC_MODE_CDMA) &&
        QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT^SIND=psinfo,2\r", CMDOPT_NONE, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0, ParseTestGPRSReattach, NULL, (LPVOID)&fReattachPossible)) {
    }
    // For the EHS5 it is no problem to try the re-attach independent of the XREG state.
    if (NO_CHECK_BEFORE_GPRS_REATTACH)
        fReattachPossible = TRUE;
    // If a Re-attach is possible, perform it
    if (fReattachPossible)
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT+CGATT=1\r", CMDOPT_NONE, APIID_SETGPRSATTACHED, 0, 0, 0, NULL);
    return 0;
}

#ifndef RIL_NO_CREG_LOCATION_UPDATES
//
// Sometimes the location information is missing in an +CREG URC. In this case we read the value manually.
//
static DWORD WINAPI CheckLocationUpdateThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;
    Sleep(WAIT_AFTER_CREG_URC);
    // To be on the safe side, we not only read the current value, we re-activate the URC itself.
    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT+CREG=2;+CREG?\r", CMDOPT_NONE, APIID_GETREGISTRATIONSTATUS, 0, 0, 0, ParseGetRegistrationStatus, NULL, pVoid);
    return 0;
}
#endif

#ifdef RIL_USE_CSQ_NOTIFICATIONS
//
// In this thread we send a notification about a change of the signal strength, also the radio
// hasn't reported one. This is necessary after changes of the registration change, since
// the MMI expects a new notification of the signal strength, even when the old one is still
// valid.
//
DWORD WINAPI ReSendSignalThreadProc(LPVOID pVoid)
{
    CNotificationData* pnd = NULL;
    RILSIGNALQUALITY rsq;
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;

    Sleep(750);
    FillRSQStruct(&rsq);
    pnd = new CNotificationData;
    if (pnd)
    {
        if (pnd->InitFromRealBlob(RIL_NOTIFY_SIGNALQUALITY, (void*)&rsq, rsq.cbSize))
            pHandle->BroadcastNotification(pnd);
        else
            delete pnd;
    }
    return 0;
}
#endif

//
//
//
static HRESULT ParseGetExtendedErrorReport(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    char *pszStr = (char *)pParam;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CEER: <errString><cr><lf>"
    if(!MatchStringBeginning(szRsp, "+CEER: ", szRsp) ||
        !ParseUnquotedString(szRsp, '\r', pszStr, MAX_ATCMD_LEN, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    // Find "<postfix>"
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetExtendedErrorReportIntel(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    char *pszStr = (char *)pParam;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CEER: <errString>[,<cause>,<description>]<cr><lf>"
    if(!MatchStringBeginning(szRsp, "+CEER: ", szRsp) ||
        !ParseUnquotedString(szRsp, '\r', pszStr, MAX_ATCMD_LEN, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

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

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetExtendedErrorReportNeer(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    char *pszStr = (char *)pParam;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+NEER:<errString><cr><lf>"  (no space)
    if(!MatchStringBeginning(szRsp, "+NEER:", szRsp) ||
        !ParseUnquotedString(szRsp, '\r', pszStr, MAX_ATCMD_LEN, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    // Find "<postfix>"
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
// CheckNoCarrierReasonThreadProc()
// This thread is started when an ATD command returns with "NO CARRIER". It
// tries to find out about the cause by sending AT+CEER and generates an
// apropiate error code.
//
DWORD WINAPI CheckNoCarrierReasonThreadProc(LPVOID pVoid)
{
    CHECK_NOCARRIER_THREAD_DATA* pData = (CHECK_NOCARRIER_THREAD_DATA *)pVoid;
    char szCEERerror[MAX_ATCMD_LEN];
    UINT cbBlob = 0;
    void *pBlob = NULL;

    memset(szCEERerror, 0, MAX_ATCMD_LEN);

    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pData->pHandle->GetDevice(), "AT+CEER\r", CMDOPT_NONE, APIID_QUERYCEERERROR, 0, 0, 0, ParseGetExtendedErrorReportIntel, NULL, (LPVOID)szCEERerror)) {
        // If the call gets blocked by an active FD lock, AT+CEER will print out this:
        if (strcmp(szCEERerror, "\"CC setup error\", 279, \"FDN failed\"") == 0) {
            pBlob = AllocBlob(sizeof(DWORD));
            if (pBlob != NULL) {
                pData->dwResponseCode = RIL_RESULT_ERROR;
                cbBlob = sizeof(DWORD);
#if defined(RIL_E_FDNRESTRICT)
                *(DWORD *)pBlob = (DWORD)RIL_E_FDNRESTRICT;
#else
                // Older Windows Mobile versions doesn't know the error code RIL_E_FDNRESTRICT.
                // This error code generates a similar error message on the UI.
                *(DWORD *)pBlob = (DWORD)RIL_E_DESTINATIONBARRED;
#endif
            }
        }
    }

    // Trace the new result
    TraceIOCTLCallAsynchReturn(pData->hrID,  pData->apiid, pData->dwResponseCode, pBlob, cbBlob);

    // Send out the resposne to the owning handle
    pData->pHandle->Notify(pData->dwResponseCode, pData->hrID, pBlob, cbBlob);

    FreeBlob(pVoid);
    return 0;
}

//
//
//
static DWORD QueryCEERStatus(CRilHandle* pHandle, const DWORD dwNotification)
{
    PFN_CMD_PARSE pfnParsing;
    char szCEERerror[MAX_ATCMD_LEN];

    memset(szCEERerror, 0, MAX_ATCMD_LEN);

    if (INTEL_STYLE_CEER)
        pfnParsing = ParseGetExtendedErrorReportIntel;
    else
        pfnParsing = ParseGetExtendedErrorReport;

     // A call has ben ended and we now wand to find out about the reason. If the reason is,
    // that we reached the AC limit, we have to send the RIL_NOTIFY_CALLMETERMAXREACHED
    // notification. Other possible reason: barring of incoming call.
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CEER\r", CMDOPT_NONE, APIID_QUERYCEERERROR, 0, 0, 0, pfnParsing, NULL, (LPVOID)szCEERerror))
    {
        DWORD dwReg = 0;

        if (strstr(szCEERerror, "Normal call clearing") == NULL && strstr(szCEERerror, "No cause information available") == NULL)
        {
            dwReg = 0;
            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("DisplayCEERerrorMessages"), &dwReg);
            if (dwReg == 1)
            {
                WCHAR wszText[MAX_RESOURCE_STRING_LEN];
                INT cbStruct;
                RILSUPSERVICEDATA *prssd;
                CNotificationData* pnd2 = NULL;

                (void)_snwprintfz(wszText, MAX_RESOURCE_STRING_LEN, L"%S", szCEERerror);
                cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                if (prssd != NULL)
                {
                    memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                    prssd->dwParams = RIL_PARAM_SSDI_DATA;
                    prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                    prssd->cbSize = cbStruct;
                    pnd2 = new CNotificationData;
                    if (pnd2)
                    {
                        if (pnd2->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                            pHandle->BroadcastNotification(pnd2);
                        else
                            delete pnd2;
                    }
                    FreeBlob(prssd);
                }
            }
        }
        if (NEED_CHECK_NO_CARRIER_REASON && strstr(szCEERerror, "\"User busy\"") != NULL) {
            // The "BUSY" URC doesn't show up as specified on the EHS5 R1, so
            // AT+CEER indicated "User busy", generate a "busy" notification.
            (void)pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_BUSY);
        }

        dwReg = 1;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("NotifyCEERerrorMessages"), &dwReg);
        if (dwReg == 1)
        {
            CNotificationData* pnd3 = NULL;

            pnd3 = new CNotificationData;
            if (pnd3)
            {
                // Send string including terminating '\0'.
                if (pnd3->InitFromRealBlob(dwNotification, (void*)szCEERerror, strlen(szCEERerror)+1))
                    pHandle->BroadcastNotification(pnd3);
                else
                    delete pnd3;
            }
        }

        if (strstr(szCEERerror, "ACM >= ACMmax") != NULL)  // means: call meter maximum reached
        {
            CNotificationData* pnd = new CNotificationData;

            if (pnd)
            {
#ifdef RIL_INDICATE_AOCMAX
                INT cbStruct;
                RILSUPSERVICEDATA *prssd;
                WCHAR wszText[MAX_RESOURCE_STRING_LEN];
                CNotificationData* pnd2 = NULL;
#endif

                if (pnd->InitFromRealBlob(RIL_NOTIFY_CALLMETERMAXREACHED, NULL, 0))
                    pHandle->BroadcastNotification(pnd);
                else
                    delete pnd;

#ifdef RIL_INDICATE_AOCMAX
                if (pHandle->DisplayAdvancedNotifications())
                {
                    if (LoadString((HINSTANCE)hInstance, IDS_AOC_INDICATION_AOCMAX, wszText, MAX_RESOURCE_STRING_LEN) > 0)
                    {
                        cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                        prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                        if (prssd != NULL)
                        {
                            memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                            prssd->dwParams = RIL_PARAM_SSDI_DATA;
                            prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                            prssd->cbSize = cbStruct;
                            pnd2 = new CNotificationData;
                            if (pnd2)
                            {
                                if (pnd2->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                                    pHandle->BroadcastNotification(pnd2);
                                else
                                    delete pnd2;
                            }
                            FreeBlob(prssd);
                        }
                    }
                }
#endif
            }
        }
        if (strstr(szCEERerror, "Operator determined barring") != NULL || strstr(szCEERerror, "Call rejected") != NULL)  // means: remote party activated barring of incoming call
        {
            CNotificationData* pnd = new CNotificationData;

            if (pnd)
            {
#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
                INT cbStruct;
                RILSUPSERVICEDATA *prssd;
                WCHAR wszText[MAX_RESOURCE_STRING_LEN];
                CNotificationData* pnd2 = NULL;
#endif

                if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_BARRING_INCOMING_CALL, NULL, 0))
                    pHandle->BroadcastNotification(pnd);
                else
                    delete pnd;

#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
                if (pHandle->DisplayAdvancedNotifications())
                {
                    if (LoadString((HINSTANCE)hInstance, IDS_ADV_CALL_BARRING_INCOMMING_CALL, wszText, MAX_RESOURCE_STRING_LEN) > 0)
                    {
                        cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                        prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                        if (prssd != NULL)
                        {
                            memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                            prssd->dwParams = RIL_PARAM_SSDI_DATA;
                            prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                            prssd->cbSize = cbStruct;
                            pnd2 = new CNotificationData;
                            if (pnd2)
                            {
                                if (pnd2->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                                    pHandle->BroadcastNotification(pnd2);
                                else
                                    delete pnd2;
                            }
                            FreeBlob(prssd);
                        }
                    }
                }
#endif
            }
        }

        if (dwNotification == RIL_NOTIFY_DEVSPECIFIC_NETWORK_REJECT_CAUSE && strstr(szCEERerror, "No cause information available") != NULL)
        {
            // The module might deregister from the network showing the +CREG: 3 URC
            // showing no cause information. If this is the case the only way to get
            // back on the network is to reset the module.
            dwReg = 0;
            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("RestartOnCREG3noCause"), &dwReg);
            if (dwReg == 1) {
                // Simply turn off the module, the resilience feature will restart
                // the module.
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: +CREG: 3 without cause information: initiate automatic restart\r\n")));
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT^SMSO\r", CMDOPT_NONE, APIID_NONE, 3000, 0, 0, NULL, NULL, NULL);
            }
        }
    }

    return 0;
}

static DWORD QueryNEERStatus(CRilHandle* pHandle, const DWORD dwNotification)
{
    char szNEERerror[MAX_ATCMD_LEN];

    memset(szNEERerror, 0, MAX_ATCMD_LEN);

     // A call has ben ended and we now wand to find out about the reason. If the reason is,
    // that we reached the AC limit, we have to send the RIL_NOTIFY_CALLMETERMAXREACHED
    // notification. Other possible reason: barring of incoming call.
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+NEER\r", CMDOPT_NONE, APIID_QUERYCEERERROR, 0, 0, 0, ParseGetExtendedErrorReportNeer, NULL, (LPVOID)szNEERerror))
    {
        DWORD dwReg = 0;

        if (strstr(szNEERerror, "No Extended Error available") == NULL)
        {
            dwReg = 0;
            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("DisplayCEERerrorMessages"), &dwReg);
            if (dwReg == 1)
            {
                WCHAR wszText[MAX_RESOURCE_STRING_LEN];
                INT cbStruct;
                RILSUPSERVICEDATA *prssd;
                CNotificationData* pnd2 = NULL;

                (void)_snwprintfz(wszText, MAX_RESOURCE_STRING_LEN, L"%S", szNEERerror);
                cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                if (prssd != NULL)
                {
                    memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                    prssd->dwParams = RIL_PARAM_SSDI_DATA;
                    prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                    prssd->cbSize = cbStruct;
                    pnd2 = new CNotificationData;
                    if (pnd2)
                    {
                        if (pnd2->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                            pHandle->BroadcastNotification(pnd2);
                        else
                            delete pnd2;
                    }
                    FreeBlob(prssd);
                }
            }
        }

        dwReg = 1;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("NotifyCEERerrorMessages"), &dwReg);
        if (dwReg == 1)
        {
            CNotificationData* pnd3 = NULL;

            pnd3 = new CNotificationData;
            if (pnd3)
            {
                // Send string including terminating '\0'.
                if (pnd3->InitFromRealBlob(dwNotification, (void*)szNEERerror, strlen(szNEERerror)+1))
                    pHandle->BroadcastNotification(pnd3);
                else
                    delete pnd3;
            }
        }
    }

    return 0;
}

DWORD WINAPI ACMMaxThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    return QueryCEERStatus(pHandle, RIL_NOTIFY_DEVSPECIFIC_CALL_RELEASE_CAUSE);
}

static DWORD WINAPI NetworkRejectThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    if (INTEL_STYLE_CEER)
        return QueryNEERStatus(pHandle, RIL_NOTIFY_DEVSPECIFIC_NETWORK_REJECT_CAUSE);
    else
        return QueryCEERStatus(pHandle, RIL_NOTIFY_DEVSPECIFIC_NETWORK_REJECT_CAUSE);
}

//
// Recheck the registration status.
//
static DWORD WINAPI RecheckRegistrationState(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;
    DWORD dwOldState = g_dwRegStatus;

    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CREG?\r", CMDOPT_NONE, APIID_GETREGISTRATIONSTATUS, 0, 0, 0, ParseGetRegistrationStatus, NULL, pVoid);

    // If state have been changed, send the new registration state notification.
    if (dwOldState != g_dwRegStatus)
        (void)pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_REGSTATUSCHANGED, g_dwRegStatus);


    return 0;
}

//
//
//
BOOL CResponse::ParseCallListNotification(LPCSTR& rszPointer)
{
    char szAddress[MAXLENGTH_ADDRESS];
    LPCSTR szDummy;
    UINT nValue;
    BOOL bContinue = FALSE;
    BOOL bIncrement = FALSE;
    BOOL bRet = TRUE;
    BOOL bDialingActive = FALSE;
    BOOL bIsNewFirstCall = FALSE;
    INT iNrVoiceCallsOriginally = 0;
#ifdef RIL_ENABLE_ALS
    BOOL bNoRING = TRUE;
#endif
#ifdef RILDRV_CE6
    RILCALLINFO oCallInfo[MAX_NUMBERCALLS];
    INT iNrCallInfo;

    EnterCriticalSection(&g_CallInfoSection);
    iNrCallInfo = g_iNrCallInfo;
    memcpy(oCallInfo, g_CallInfo, MAX_NUMBERCALLS * sizeof(RILCALLINFO));
    LeaveCriticalSection(&g_CallInfoSection);
#endif

    EnterCriticalSection(&g_CallInfoSection);
    // Count current numbers of voice calls.
    for (nValue = 0; nValue < (UINT)g_iNrCallInfo; nValue++) {
        if (g_CallInfo[nValue].dwType != RIL_CALLTYPE_DATA)
            iNrVoiceCallsOriginally++;
    }
    if (g_iNrCallInfo == 0)
        bIsNewFirstCall = TRUE;
    // The RIL generates a preliminary call progress notification when the
    // +CRING: URC appears the 1st time. This notification always use the
    // ID 1, but the now following ^SLCC URC might use a differnt ID, which
    // might be needed to be corrected.
    if (g_iNrCallInfo == 1 && g_CallInfo[0].dwStatus == RIL_CALLSTAT_INCOMING)
        bIsNewFirstCall = TRUE;
    LeaveCriticalSection(&g_CallInfoSection);

    if (!MatchStringAnywhere(rszPointer, "^SLCC: \r\n", szDummy) && !MatchStringBeginning(rszPointer, "\r\n", szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        bRet = FALSE;
        goto Error;
    }

    for (;;) {
        BOOL fSetToIncoming = FALSE;

        // Parse "<id>"
        if (!ParseUInt(rszPointer, TRUE, nValue, rszPointer)) {
            RILCALLINFO oAddInfo;

            // Check status of CDMA data connection, it might be necessary
            // to add it to the list of active calls.
            if (CheckCDMADataConnStatus(m_pRilHandle, m_CallInfo, m_iNrCallInfo, &oAddInfo) && m_iNrCallInfo < MAX_NUMBERCALLS) {
                memcpy(&m_CallInfo[m_iNrCallInfo], &oAddInfo, sizeof(RILCALLINFO));
                m_iNrCallInfo++;
            }
            EnterCriticalSection(&g_CallInfoSection);
            g_iNrCallInfo = (int)m_iNrCallInfo;
            if (g_iNrCallInfo == 0)
                g_iIdxForFirstCall = -1;
            memcpy(g_CallInfo, m_CallInfo, MAX_NUMBERCALLS * sizeof(RILCALLINFO));
            if (g_iIdxForFirstCall > 0 && g_iNrCallInfo > 0) {
                INT iIdx;

                for (iIdx = 0; iIdx < g_iNrCallInfo; iIdx++) {
                    if (g_CallInfo[iIdx].dwID == 1)
                        g_CallInfo[iIdx].dwID = REPLACEMENT_CALL_ID;
                    if (g_CallInfo[iIdx].dwID == (DWORD)g_iIdxForFirstCall) {
                        g_CallInfo[iIdx].dwID = 1;
                        break;
                    }
                }
            }
            if (bIsNewFirstCall && ID_INCREMENTS_AFTER_GPRS && g_iNrCallInfo == 1) {
                // Bug in HC25: after a GPRS context is deativated all following voice and
                // CSD calls are get an incremented ID. The TAPI of CE6 and the radiometrics
                // test require, that the IDs start with 1 for the 1st call. Other IDs are
                // acceptable for the other calls in multiparty calls, so the RIL only
                // adjusts the 1st call to the ID 1.
                if (g_CallInfo[0].dwID != 1) {
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: changed call ID from %d to 1\r\n"),g_CallInfo[0].dwID));
                    g_iIdxForFirstCall = (INT)g_CallInfo[0].dwID;
                    g_CallInfo[0].dwID = 1;
                }
            }
            LeaveCriticalSection(&g_CallInfoSection);
            if (!bDialingActive)
                m_pRilHandle->SetVoiceCallDialing(FALSE);
            m_iNrCallInfo = 0;
            goto Error;
        }
        fSetToIncoming = FALSE;
        if (WRONG_CLCC_IDX_10_IN_CDMA && nValue == 10) {
            // Bug in PXS8 in CDMA mode: <idx>=10 is an illegal value, the correct
            // value is 1. If this is the case also the <dir> parameter is wrong. It
            // is supposed to be "incoming" in this case.
            if (m_pRilHandle->GetCurrentTechnology() == TEC_MODE_CDMA) {
                nValue = 1;
                fSetToIncoming = TRUE;
            }
        }
        memset(&m_CallInfo[m_iNrCallInfo], 0x00, sizeof(RILCALLINFO));
        m_CallInfo[m_iNrCallInfo].cbSize = sizeof(RILCALLINFO);
        m_CallInfo[m_iNrCallInfo].raAddress.cbSize = sizeof(RILADDRESS);

        m_CallInfo[m_iNrCallInfo].dwID = nValue;
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_ID;

        // Parse ",<direction>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, 2, nValue, rszPointer)) {
            goto NextURC;
        }
        // The <direction> parameter might be wrong (see "wrong CLCC index 10"
        // workaround above).
        if (fSetToIncoming)
            nValue = 1;
        m_CallInfo[m_iNrCallInfo].dwDirection = (nValue ? RIL_CALLDIR_INCOMING : RIL_CALLDIR_OUTGOING);
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_DIRECTION;

        // Parse ",<status>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, NUM_CALLSTATS, nValue, rszPointer)) {
            goto NextURC;
        }
        m_CallInfo[m_iNrCallInfo].dwStatus = g_rgdwCallStats[nValue];
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_STATUS;

        if (m_CallInfo[m_iNrCallInfo].dwDirection == RIL_CALLDIR_OUTGOING && (m_CallInfo[m_iNrCallInfo].dwStatus == RIL_CALLSTAT_ALERTING || m_CallInfo[m_iNrCallInfo].dwStatus == RIL_CALLSTAT_DIALING))
            bDialingActive = TRUE;

        // Parse ",<type>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, NUM_CALLTYPES, nValue, rszPointer)) {
            goto NextURC;
        }

        m_CallInfo[m_iNrCallInfo].dwType = g_rgdwCallTypes[nValue];
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_TYPE;

        // Parse ",<multiparty>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, 2, nValue, rszPointer)) {
            goto NextURC;
        }
        m_CallInfo[m_iNrCallInfo].dwMultiparty = (nValue ? RIL_CALL_MULTIPARTY : RIL_CALL_SINGLEPARTY);
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_MULTIPARTY;

        // Parse ",<tca>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, 2, nValue, rszPointer)) {
            goto NextURC;
        }

#ifdef RIL_ENABLE_ALS
        if (m_CallInfo[m_iNrCallInfo].dwStatus == RIL_CALLSTAT_INCOMING)
            bNoRING = FALSE;
#endif

        bIncrement = TRUE;

        if (CLCC_NUMBER_AS_NUMERIC) {
            // Bug in PXS8: while dealing with a multipary CDMA call, the module
            // indicates an empty <address> parameter as 0 (numeric).
            if (MatchStringBeginning(rszPointer, ",0,0\r\n", szDummy))
                (void)MatchStringBeginning(rszPointer, ",0,0", rszPointer) ;
        }

        // Parse ","
        if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
            // Parse "<address>,<type>"
            if (ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer)) {
                if(!MatchStringBeginning(rszPointer, ",", rszPointer))
                    bContinue = TRUE;

                if(!ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x1000, nValue, rszPointer))
                    bContinue = TRUE;

                nValue = (nValue > 0x100) ? nValue/10 : nValue;

                if(!StringToRILAddress(szAddress, (BYTE)nValue, m_CallInfo[m_iNrCallInfo].raAddress))
                    bContinue = TRUE;

                if(bContinue)
                    goto NextURC;
                m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_ADDRESS;
            }
            else {
                // If we couldn't parse an address, then it might be empty,
                // meaning the ID is blocked. Since the address parameter
                // is present, make sure the type also exists before continuing.
                if (!MatchStringBeginning(rszPointer, ",", rszPointer)                 ||
                    !ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer)) {
                    goto NextURC;
                }
            }

            // Parse ","
            if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
            // Parse "<description>"
                if (!ParseQuotedEncodedString(ENCODING_UCS2, rszPointer, m_CallInfo[m_iNrCallInfo].wszDescription, m_CallInfo[m_iNrCallInfo].wszDescription + MAXLENGTH_DESCRIPTION)) {
                    goto NextURC;
                }
                m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_DESCRIPTION;
            }
        }
NextURC:
        if (bIncrement && m_iNrCallInfo < MAX_NUMBERCALLS-1)
            m_iNrCallInfo++;
        if (!MatchStringBeginning(rszPointer, "\r\n^SLCC: ", rszPointer))
            break;
    }
Error:
    if (bRet)
        g_LastATD = 0;      // needed for RILDrv_GetCallList(), see comments there.

#ifdef RIL_ENABLE_ALS
    if (bNoRING)
        g_dwALSLine = 0;
#endif

    // Workaround: the TAPI misinterprets the call states of CSD calls. So we
    // simply do not report this fact to the OS.
    if (bRet && !(g_iNrCallInfo==1 && (g_CallInfo[0].dwType==RIL_CALLTYPE_DATA || g_CallInfo[0].dwType==RIL_CALLTYPE_FAX))) {
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CALLSTATECHANGED;
        (void)SetBlob(NULL, 0);
        //RHADeactivateVoicePath(m_pRilHandle, FALSE);
    }
    // Special case: since the +CRING-URC is only activated on the URC-port, but incomming CSD calls are
    // signalized on the data port only, we have to emulate the +CRING: REL ASYNC URC here.
    if (bRet && (g_iNrCallInfo==1 && g_CallInfo[0].dwType==RIL_CALLTYPE_DATA && g_CallInfo[0].dwStatus==RIL_CALLSTAT_INCOMING)) {
        RILRINGINFO rri; memset(&rri,0,sizeof(rri)); // zero struct

        rri.dwAddressId = 0;
        rri.dwParams |= RIL_PARAM_RI_ADDRESSID;
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = TRUE;
        rri.rsiServiceInfo.fTransparent = FALSE;
        rri.cbSize = sizeof(RILRINGINFO);
        rri.dwParams |= RIL_PARAM_RI_CALLTYPE;
        // For data calls, set connection service info
        rri.dwParams |= RIL_PARAM_RI_SERVICEINFO;
        rri.rsiServiceInfo.cbSize = sizeof(RILSERVICEINFO);
        rri.rsiServiceInfo.dwParams |= RIL_PARAM_SVCI_ALL;
        (void)SetBlob((void*)&rri, sizeof(RILRINGINFO));
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_RING;
    }
    // If there is no more call, the mute status gets reseted.
    if (g_iNrCallInfo == 0)
        g_dwLastMuteSetting = 0;
#ifdef RILDRV_CE6
    if (bRet) {
        SendCE6CallProgressInfo(m_pRilHandle, oCallInfo, iNrCallInfo);
    }
#endif
    if (MISSING_1XRTT_INDICATION_FOR_CDMA_VC && m_pRilHandle->GetCurrentTechnology() == TEC_MODE_CDMA) {
        DWORD dwReg = 1;
        INT iNrVoiceCallsNow = 0;

        // CDMA specific: when the module operates in the 1xRTT/EVDO hybrid mode
        // all voice calls are actually made in 1xRTT. The module doesn't inform
        // about this fact, so the RIL must send the notifications by itself.
        // This behaviour can be suppresed via registry key.
        EnterCriticalSection(&g_CallInfoSection);
        // Count current numbers of voice calls.
        for (nValue = 0; nValue < (UINT)g_iNrCallInfo; nValue++) {
            if (g_CallInfo[nValue].dwType != RIL_CALLTYPE_DATA)
                iNrVoiceCallsNow++;
        }
        LeaveCriticalSection(&g_CallInfoSection);
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaSwitch1xRTTVoiceCall"), &dwReg);
        if (dwReg == 1 && iNrVoiceCallsOriginally == 0 && iNrVoiceCallsNow > 0 && (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)) {
            // Report technology "1xRTT" to the OS.
            (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCHANGED, g_rgdwPSInfos[11]);
            (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCAPSCHANGED, g_rgdwPSCaps[11]);
        }
        if (dwReg == 1 && iNrVoiceCallsOriginally > 0 && iNrVoiceCallsNow == 0) {
            // Query actual technology and report it to the OS.
            (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilHandle, "AT^SIND=psinfo,2\r", CMDOPT_URC, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0, ParseInitialPSInfo, ParseInitialPSInfoError, (LPVOID)m_pRilHandle);
        }

    }

    return bRet;
}

//
//
//
BOOL CResponse::ParseIndicatorEventValue(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    BOOL fRet = FALSE;
    UINT nValue;
#ifndef RIL_NOTIFY_EONS
    DWORD dwRegStatus;
#endif
#ifdef RIL_NOTIFY_NITZ
    UINT nTimeZone= 0;
    UINT nDayLightSaving = 0;
    BOOL bIsDLSset = FALSE;
    BOOL bOK = FALSE;
    RILNITZINFO oNITZ;
    WCHAR wszTime[NITZ_TIME_STRING_LEN];
    char szTemp[MAX_ATCMD_LEN];

    memset(wszTime, 0, sizeof(wszTime));
    memset(&oNITZ, 0, sizeof(RILNITZINFO));
#endif

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "eons,"
    if (MatchStringBeginning(rszPointer, "eons,", rszPointer))
    {
        // We have an eons notification -> assume that the opeartor name has changed
        // and create a RIL_NOTIFY_REGSTATUSCHANGED in any case
        m_fUnsolicited = TRUE;
#ifdef RIL_NOTIFY_EONS
        m_dwCode = RIL_NOTIFY_EONS;
#else // RIL_NOTIFY_EONS
        // Obsolete: The following code is only relevant for Windows Mobile 5.
        dwRegStatus = g_dwRegStatus;
        m_dwCode = RIL_NOTIFY_REGSTATUSCHANGED;
        if (!SetBlob((void*)&dwRegStatus, sizeof(DWORD)))
        {
            goto Error;
        }
#endif // RIL_NOTIFY_EONS
        fRet = FindRspPostfix(rszPointer, rszPointer);
        if (fRet) {
          // Step back over found "<CR><LF>" which is parsed in ParseNotification() again
          rszPointer = rszPointer - 2;
        }
    }
    else if (MatchStringBeginning(rszPointer, "nitz,", rszPointer))
    {
        m_fUnsolicited = TRUE;
#ifdef RIL_NOTIFY_NITZ
        // Parse "<time>,<timezoneoffset>"
        if (!ParseString(rszPointer, szTemp, MAX_ATCMD_LEN, szDummy))
            szTemp[0] = 0;

        if ((strlen(szTemp) % 4) == 0) {    // We got an URC in UCS2 format
            bOK = ParseUCS2String(rszPointer, wszTime, NITZ_TIME_STRING_LEN, rszPointer);
        }
        else {      // String is in GSM format
            if (strlen(szTemp) <= NITZ_TIME_STRING_LEN) {
                bOK = ParseString(rszPointer, szTemp, MAX_ATCMD_LEN, rszPointer);
                (void)_snwprintfz(wszTime, NITZ_TIME_STRING_LEN, L"%S", szTemp);
                bOK = TRUE;
            }
        }

        if (bOK && MatchStringBeginning(rszPointer, ",", rszPointer) &&
             ParseIntAsUInt(rszPointer, nTimeZone, rszPointer, TRUE)) {

            // Parse "<daylightsavingoffset>"
            if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
                 bIsDLSset = ParseIntAsUInt(rszPointer, nDayLightSaving, rszPointer);
                 if (bIsDLSset) m_pRilHandle->StoreNitzDST(nDayLightSaving);
            }
            if (!bIsDLSset)
                bIsDLSset = m_pRilHandle->GetStoredNitzDST(&nDayLightSaving);

            m_dwCode = RIL_NOTIFY_NITZ;
            oNITZ.cbSize = sizeof(RILNITZINFO);
            if (CDMA_NITZ_TZ_IN_60_MINUTES && m_pRilHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
                oNITZ.TimeZoneOffsetMinutes = 60 * (int)nTimeZone;
            else
                oNITZ.TimeZoneOffsetMinutes = 15 * (int)nTimeZone;
            oNITZ.dwParams = RIL_PARAM_NITZ_TIMEZONEOFFSET|RIL_PARAM_NITZ_DAYLIGHTSAVINGOFFSET;
            oNITZ.DaylightSavingOffsetMinutes = 0;
            if (bIsDLSset) {
                oNITZ.DaylightSavingOffsetMinutes = 60 * (int)nDayLightSaving;
                if (CDMA_NITZ_TZ_IN_60_MINUTES && m_pRilHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
                    oNITZ.TimeZoneOffsetMinutes = oNITZ.TimeZoneOffsetMinutes - oNITZ.DaylightSavingOffsetMinutes;
            }
            if (ParseSystemTimeNITZ(&oNITZ.SysTime, wszTime))
                oNITZ.dwParams |= RIL_PARAM_NITZ_SYSTEMTIME;
            oNITZ.dwNotificationCode = RIL_NOTIFY_NITZ;
            if (!SetBlob((void*)&oNITZ, sizeof(oNITZ)))
            {
                goto Error;
            }
        }

#endif // RIL_NOTIFY_NITZ
        fRet = FindRspPostfix(rszPointer, rszPointer);
        if (fRet) {
          // Step back over found "<CR><LF>" which is parsed in ParseNotification() again
          rszPointer = rszPointer - 2;
        }
    }
    else if (MatchStringBeginning(rszPointer, "signal,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
#ifdef RIL_USE_CSQ_NOTIFICATIONS
            RILSIGNALQUALITY rsq;
#endif

            g_bCINDIsActive = TRUE;
            g_iLastSignal = nValue;

#ifdef RIL_USE_CSQ_NOTIFICATIONS
            if (g_dwSignalStrenth3G == SS_3G_RSSI || m_pRilHandle->GetCurrentTechnology() == TEC_MODE_CDMA) {
                FillRSQStruct(&rsq);

                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
                if (!SetBlob((void*)&rsq, sizeof(RILSIGNALQUALITY)))
                {
                    m_fUnsolicited = FALSE;
                }
            }
            else {
                CloseHandle(CreateThread(NULL, 0, ReCheckSignalStrengthThreadProc, (LPVOID)m_pRilHandle, 0, 0));
            }
#endif
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "rssi,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
#ifdef RIL_USE_CSQ_NOTIFICATIONS
            RILSIGNALQUALITY rsq;
#endif

            g_bCINDIsActive = TRUE;
            g_iLastRSSI = nValue;

#ifdef RIL_USE_CSQ_NOTIFICATIONS
            if (g_dwSignalStrenth3G == SS_3G_RSSI || m_pRilHandle->GetCurrentTechnology() == TEC_MODE_CDMA) {
                FillRSQStruct(&rsq);

                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
                if (!SetBlob((void*)&rsq, sizeof(RILSIGNALQUALITY)))
                {
                    m_fUnsolicited = FALSE;
                }
            }
#endif
            if (g_dwSignalStrenth3G != SS_3G_RSSI && m_pRilHandle->GetCurrentTechnology() != TEC_MODE_CDMA) {
                CloseHandle(CreateThread(NULL, 0, ReCheckSignalStrengthThreadProc, (LPVOID)m_pRilHandle, 0, 0));
            }
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "call,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            m_pRilHandle->SetLastCallURC(nValue);
#ifdef RIL_ALLOW_TRACE_PPP_DATA
            if (nValue == 1)
                g_ComManager.TraceExtDataCall1();
#endif
            if (g_iNrCallInfo == -1) {
                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_CALLSTATECHANGED;
                (void)SetBlob(NULL, 0);
            }

#ifdef RILDRV_CE6
            // "+CIEV: call,0" always indicates call disconnected whether it was done remotely or locally.
            if ((0 == nValue) && (g_iLastIdxDisconnected > 0))
                SendDisconnectNotification(m_pRilHandle, RIL_RESULT_NOCARRIER);
 #endif
            CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)m_pRilHandle, 0, 0));
         }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "smsfull,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            // The "smsfull" indication can be usefull for GCF 34.2.3
            if (nValue == 1)
            {
                DWORD dwState;

                dwState = RIL_SIMSTATUSCHANGED_FULL;
                m_dwCode = RIL_NOTIFY_SIMSTATUSCHANGED;
                (void)SetBlob(&dwState, sizeof(DWORD));
                m_fUnsolicited = TRUE;
            }
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "simstatus,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            g_iCurrentSIMStatus = nValue;

            if (nValue == 5)
                m_pRilHandle->SignalInternalSIMReady();
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "vcall,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            // If a video call comes in, it must be rejected immediately.
            if (nValue == 1)
                CloseHandle(CreateThread(NULL, 0, RejectVCallThreadProc, (LPVOID)m_pRilHandle, 0, 0));
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "audio,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            if (nValue == 1)
            {
                if (!m_pRilHandle->IgnoreCIEVAudioSounder())
                {
                    m_pRilHandle->SetCIEVaudioActive(TRUE);
                    RHAActivateVoicePath(m_pRilHandle);
                }
                m_pRilHandle->SetIgnoreNextCIEVAudioSounderTimer(0);
            }
            else
            {
                m_pRilHandle->SetCIEVaudioActive(FALSE);
                if (!m_pRilHandle->IsSLCCAudioActive() && !m_pRilHandle->IsCIEVAudioActive())
                    RHADeactivateVoicePath(m_pRilHandle, TRUE, FALSE);
            }
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "psinfo,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            DWORD dwState;

            if (nValue < NUM_PSPINFOS)
            {
                nValue = m_pRilHandle->GetCorrectNetworkTechnology(nValue);
                dwState = m_pRilHandle->GetSystemTypeFromPSInfo(nValue);
                m_dwCode = RIL_NOTIFY_SYSTEMCHANGED;
                (void)SetBlob(&dwState, sizeof(DWORD));
                m_fUnsolicited = TRUE;
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCAPSCHANGED, g_rgdwPSCaps[nValue]);

                // Problem with the HC25: if the registration was lost and is now
                // found again, the expected "+CREG: 1" might not come. What does
                // come in this situation, however, is the "+CIEV: psinfo" URC. If
                // this happens, the RIL will now re-check the registration state.
                if (CREG1_MIGHT_BE_MISSING && nValue > 0 && (g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING))
                    CloseHandle(CreateThread(NULL, 0, RecheckRegistrationState, (LPVOID)m_pRilHandle, 0, 0));
            }
            if (WRONG_CGREG_IN_MULTIMODE && m_pRilHandle->GetMultiModeSetting() == MULTIMODE_CONFIG_MULTIMODE) {
                // Bug in PXS8 (multimode): the +CGREG status is not correct, try to
                // substitute with the current psinfo setting.
                if (nValue > 0 && nValue < 11) {
                    g_dwGPRSRegStatus = ((nValue & 1) == 0 ? RIL_REGSTAT_HOME: RIL_REGSTAT_UNREGISTERED);
                    if(g_bFakeRegStatusDeniedToHome && (g_dwGPRSRegStatus == RIL_REGSTAT_DENIED))
                        g_dwGPRSRegStatus = RIL_REGSTAT_HOME;
                    (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_GPRSREGSTATUSCHANGED, g_dwGPRSRegStatus);

                }
            }
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "dtmf,", rszPointer))
    {
        char szDTMF[MAX_ATCMD_LEN];

        if (ParseString(rszPointer, szDTMF, MAX_ATCMD_LEN, rszPointer))
        {
            // Check: is there at least one character detected?
            if (strlen(szDTMF) > 0) {
                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_DTMFSIGNAL;
                (void)SetBlob(szDTMF, strlen(szDTMF)+1);
            }
        }
        // Skip over the rest of the URC, the rest of the parameters are of no
        // interest for Windows Mobile.
        fRet = FindRspPostfix(rszPointer, rszPointer);
        if (fRet) {
          // Step back over found "<CR><LF>" which is parsed in ParseNotification() again
          rszPointer = rszPointer - 2;
        }
        fRet = TRUE;
    }
    else
    {
        // Simply throw away all other +CIEV URCs for now
        fRet = FindRspPostfix(rszPointer, rszPointer);
        if (fRet) {
          // Step back over found "<CR><LF>" which is parsed in ParseNotification() again
          rszPointer = rszPointer - 2;
        }
    }

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseTimeZoneNotification(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    BOOL fRet = FALSE;

    g_fNitzTimeAvailable = FALSE;
    memset(g_szNitzTime, 0, MAX_PATH);
    g_nTimeZone = 0;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete CTZV notification -- no need to parse it
        goto Error;
    }

    // Parse "<tz>,<time>"
    if (!ParseIntAsUInt(rszPointer, g_nTimeZone, rszPointer, TRUE))
    {
        // Parsing failure
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }
    if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
        if (!ParseString(rszPointer, g_szNitzTime, MAX_PATH, rszPointer))
        {
            // Parsing failure
            ParseJunk(1, rszPointer);
            fRet = TRUE;
            goto Error;
        }
    }

    // Store received values and wait for the +CTZDST URC to complete
    // the NITZ notification.
    g_fNitzTimeAvailable = TRUE;
    m_fUnsolicited = TRUE;
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseDayLightSavingNotification(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nDST;
    BOOL fRet = FALSE;
    RILNITZINFO oNITZ;

    memset(&oNITZ, 0, sizeof(RILNITZINFO));

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete CTZDST notification -- no need to parse it
        goto Error;
    }

    // Parse "<dst>"
    if (!ParseUInt(rszPointer, TRUE, nDST, rszPointer))
    {
        // Parsing failure
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }

    // If data from the previous +CTZV URC have been stored, generate the
    // complete NITZ notification.
    if (g_fNitzTimeAvailable) {
        WCHAR wszTime[NITZ_TIME_STRING_LEN];

        (void)_snwprintfz(wszTime, NITZ_TIME_STRING_LEN, L"%S", g_szNitzTime);
        m_dwCode = RIL_NOTIFY_NITZ;
        oNITZ.cbSize = sizeof(RILNITZINFO);
        oNITZ.TimeZoneOffsetMinutes = 15 * (int)g_nTimeZone;
        oNITZ.dwParams = RIL_PARAM_NITZ_TIMEZONEOFFSET|RIL_PARAM_NITZ_DAYLIGHTSAVINGOFFSET;
        oNITZ.DaylightSavingOffsetMinutes = 60 * (int)nDST;
        if (ParseSystemTimeNITZ(&oNITZ.SysTime, wszTime)) {
            INT iMinutes;

            oNITZ.dwParams |= RIL_PARAM_NITZ_SYSTEMTIME;
            // The time give by the module is the local time, but Windows Mobile expects
            // the UTC, so the RIL has to adjust the time.
            iMinutes = ((INT)oNITZ.SysTime.wHour * 60) + (INT)oNITZ.SysTime.wMinute;
            iMinutes += 24 * 60;
            iMinutes -= oNITZ.TimeZoneOffsetMinutes;
            iMinutes -= oNITZ.DaylightSavingOffsetMinutes;
            if (iMinutes >= 24 * 60)  iMinutes = iMinutes- (24 * 60);
            oNITZ.SysTime.wMinute = (WORD)(iMinutes % 60);
            oNITZ.SysTime.wHour = (WORD)(iMinutes / 60);
        }
        oNITZ.dwNotificationCode = RIL_NOTIFY_NITZ;
        if (!SetBlob((void*)&oNITZ, sizeof(oNITZ)))
        {
            goto Error;
        }
    }

    m_fUnsolicited = TRUE;
    fRet = TRUE;

    // Delete the used
    g_fNitzTimeAvailable = FALSE;
    memset(g_szNitzTime, 0, MAX_PATH);
    g_nTimeZone = 0;

    Error:
    return fRet;
}

#ifdef SUPPORTS_EHSX_R1
//
//
//
BOOL CResponse::ParseXCievNotification(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue, nRSSI;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<rssi>"
    if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
#ifdef RIL_USE_CSQ_NOTIFICATIONS
        RILSIGNALQUALITY rsq;
#endif

        // The <rssi> value of "+XCIEV" is not identical to the one from "+CIEV: rssi".
        switch (nValue) {
            case 1:
                nRSSI = 1;
                break;
            case 2:
                nRSSI = 1;
                break;
            case 3:
                nRSSI = 2;
                break;
            case 4:
                nRSSI = 2;
                break;
            case 5:
                nRSSI = 3;
                break;
            case 6:
                nRSSI = 4;
                break;
            case 7:
                nRSSI = 5;
                break;
            default:
                nRSSI = 0;
        }

        g_bCINDIsActive = TRUE;
        g_iLastRSSI = nRSSI;

#ifdef RIL_USE_CSQ_NOTIFICATIONS
        FillRSQStruct(&rsq);

        m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
        if (!SetBlob((void*)&rsq, sizeof(RILSIGNALQUALITY)))
        {
            m_fUnsolicited = FALSE;
        }
#endif
    }
    // There might be an <batt> parameter following, but it is of no interesst.
    (void)FindRspPostfix(rszPointer, rszPointer);

    m_fUnsolicited = TRUE;

    fRet = TRUE;

    Error:
    return fRet;
}

BOOL CResponse::ParseXregNotification(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue, nState;
    char szBand[MAXLENGTH_OPERATOR_LONG];
    BOOL fRet = FALSE;

    memset(szBand, 0, MAXLENGTH_OPERATOR_LONG);

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<state>,<band>"
    if (!ParseUInt(rszPointer, FALSE, nState, rszPointer) ||
         !MatchStringBeginning(rszPointer, ",", rszPointer) ||
         !ParseUnquotedString(rszPointer, '\r', szBand, MAXLENGTH_OPERATOR_LONG, rszPointer))
    {
        goto Error;
    }

    // If we didn't find a <cr><lf>, this is not a notification, but a response, which has
    // a different format and which we don't handle here.
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }
    // If there is a comma in the band parameter, this is not an URC but a response.
    if (strchr(szBand, ',') != NULL)
    {
        goto Error;
    }

    // Converte the EHS5 <state> values to the PH8 style <value>
    switch (nState) {
        case 1:             // GPRS
            nValue = 2;
            break;
        case 2:             // EDGE
            nValue = 4;
            break;
        case 3:             // UMTS
            nValue = 6;
            break;
        case 4:             // HSDPA
            nValue = 8;
            break;
        case 5:             // HSUPA
        case 6:             // HSPA
            nValue = 10;
            break;
        default:
            nValue = 0;
    }

    if (nValue < NUM_PSPINFOS)
    {
        DWORD dwState;

        nValue = m_pRilHandle->GetCorrectNetworkTechnology(nValue);
        dwState = m_pRilHandle->GetSystemTypeFromPSInfo(nValue);
        m_dwCode = RIL_NOTIFY_SYSTEMCHANGED;
        (void)SetBlob(&dwState, sizeof(DWORD));
        m_fUnsolicited = TRUE;
        (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCAPSCHANGED, g_rgdwPSCaps[nValue]);
    }

    fRet = TRUE;

    Error:
    return fRet;
}
#endif

#ifdef RIL_ENABLE_ALS
//
//
//
BOOL CResponse::ParseALSStatusStatus(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue;
    DWORD dwALSStatus;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (nValue > 0)
    {
        dwALSStatus = nValue-1;     // Cinterion: ALS starts with 1; Microsoft: ALS starts with 0
    }
    else
    {
        dwALSStatus = 0;
    }

    // If we didn't find a <cr><lf>, this is not a notification, but a response, which has
    // a different format and which we don't handle here.
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    g_dwALSLine = dwALSStatus; // It was the URC -> incomming call comes from this line

    m_fUnsolicited = FALSE;

    fRet = TRUE;

    Error:
    return fRet;
}
#endif


HRESULT ParsePsInfoForCReg(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    DWORD *pdwSystemType = NULL;
    UINT nValue;
    HRESULT hr = S_OK;

    pdwSystemType = (DWORD *)pParam;
    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>^SIND: psinfo,<mode>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "^SIND: psinfo,", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

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

    if (pdwSystemType != NULL)
        *pdwSystemType = nValue;

Error:
    return hr;
}

//
//
//
DWORD WINAPI RecheckCREGThreadProc(LPVOID pVoid)
{
    CRilHandle*    pRilHandle;
    DWORD dwPSinfo = 0;
    DWORD dwRegStatus = RIL_REGSTAT_UNREGISTERED;
    DWORD dwOldRegStatus;

    pRilHandle = (CRilHandle *)pVoid;

    // The module reported "+CREG: 0", but this might not be true. Check the
    // PSINFO status: if it reports GPRS/EDGE/UMTS/HSDPA the module is actually
    // registered.
    if (!NO_SIND_PSINFO_IN_CDMA || pRilHandle->GetCurrentTechnology() != TEC_MODE_CDMA)
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pRilHandle, "AT^SIND=psinfo,2\r", CMDOPT_NONE, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0, ParsePsInfoForCReg, NULL, (LPVOID)&dwPSinfo);

    // If the module indicates GPRS/EDGE/UMTS/HSDPA as current network technology,
    // the radio is actually registered to the network. It might be roaming, but
    // there is no way to find out, so the RIL assumes it is the home network.
    if (dwPSinfo > 0)
        dwRegStatus = RIL_REGSTAT_HOME;
    // If the technology indicates CDMA, the RIL can rely on the ^SREG-URC, so
    // there is nothing more to do.
    if (dwPSinfo >= 11)
        return 0;

    // Now send the new registration status to the OS.
    dwOldRegStatus = 0xFFFFFFFF;
    dwOldRegStatus = g_dwRegStatus;
    g_dwRegStatus = dwRegStatus;
    // Special check (needed for +CREG only): any "+CREG: 3" (access denied) is followed by a "+CREG: 0", which is
    // not correct. As a solution the RIL will still report "access denied" until the module has been registered to a
    // network, the SIM is removed or the module has been resetted.
    if (g_dwRegStatus == RIL_REGSTAT_DENIED && CREG0_FOLLOWS_CREG3)
        g_fRegStatus3detected = TRUE;
    if ((g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING) && g_fRegStatus3detected)
        g_dwRegStatus = RIL_REGSTAT_DENIED;
    if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
        g_fRegStatus3detected = FALSE;
    dwRegStatus = g_dwRegStatus;

    // Set the "network rejection" flag to FALSE, when the module sucessfully
    // registered to a network.
    if (dwRegStatus == RIL_REGSTAT_HOME)
        pRilHandle->SetNetworkRejectionState(FALSE);

    pRilHandle->GenerateRegStatusNotification(dwRegStatus, FALSE);
#if defined(RIL_NOTIFY_EONS)
    if ((pRilHandle->GetEons()->GetEONSState() == EONS_EMULATED || CHECK_EONS_AFTER_CELL_CHANGE) && dwRegStatus == RIL_REGSTAT_HOME)
    {
        // Emulated EONS only: when the location changed, the EONS name might changed, too.
        // So we notify the OS, so it can check for a change of the name.
        (void)pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_EONS, NULL, 0);
    }
#endif

    // Notify about new technology (if necessary)
    if (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)
        pRilHandle->NotifyNewTechnologyMode(TEC_MODE_LTE);

#ifdef RIL_USE_CSQ_NOTIFICATIONS
    if (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)
    {
        // Strange behaviour of the Microsoft MMI: if an RIL_NOTIFY_REGSTATUSCHANGED shows up, the MMI
        // forgets about all signal strength URCs so far, so we have to resend it
        CloseHandle(CreateThread(NULL, 0, ReSendSignalThreadProc, (LPVOID)pRilHandle, 0, 0));
    }
#endif

    return 0;
}

//
//
//
BOOL CResponse::ParseRegistrationStatus(LPCSTR& rszPointer, DWORD const dwNotifyCode)
{
    LPCSTR szDummy;
    UINT nValue;
    DWORD dwRegStatus;
    DWORD dwOldRegStatus;
    BOOL fRet = FALSE;
    BOOL bSuppressNotification = FALSE;
    UINT dwLAC,dwCI, dwAcT;
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    RILLOCATIONINFO sLI;
#endif

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }

    if (NUM_REGSTATS > nValue)
    {
        dwRegStatus = g_rgdwRegStats[nValue];

        if(g_bFakeRegStatusDeniedToHome && (g_rgdwRegStats[nValue] == RIL_REGSTAT_DENIED))
            g_dwGPRSRegStatus = RIL_REGSTAT_HOME;
    }
    else
    {
        dwRegStatus = RIL_REGSTAT_UNKNOWN;
    }

    dwLAC = 0;
    dwCI = 0;
    // If we didn't find a <cr><lf>, this is not a notification, but a response, which has
    // a different format and which we don't handle here.
    // e.g. the response looks like "+CREG: 1,1<cr><lf>OK<cr>"
    // but the notication looks like: "+CREG: 2<cr><lf>"
    // Exception: if +CREG=2, than there are two extra arguments, we have to ignore. In
    // this case, the 2nd argument starts with a quotation mark.
    if (MatchStringBeginning(rszPointer, ",\"", rszPointer))
    {
        (void)ParseHexUInt(rszPointer, TRUE, dwLAC, rszPointer);
        (void)MatchStringBeginning(rszPointer, "\",\"", rszPointer);
        (void)ParseHexUInt(rszPointer, TRUE, dwCI, rszPointer);
        (void)MatchStringBeginning(rszPointer, "\"", rszPointer);
        // There may be an additional parameter <AcT>
        if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
            (void)ParseUInt(rszPointer, TRUE, dwAcT, rszPointer);
        }
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    // Problem with the PXS8 in multimode: the radio might indicate "+CREG: 0",
    // while it actually is registered to the network. The RIL now will have to
    // use other commands to find out, if the radio is registered.
    if (CREG0_MIGHT_BE_WRONG &&  m_pRilHandle->GetMultiModeSetting() == MULTIMODE_CONFIG_MULTIMODE
        && dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && dwRegStatus == RIL_REGSTAT_UNREGISTERED) {
        CloseHandle(CreateThread(NULL, 0, RecheckCREGThreadProc, (LPVOID)m_pRilHandle, 0, 0));
    }
    if (WRONG_CGREG_IN_MULTIMODE && m_pRilHandle->GetMultiModeSetting() == MULTIMODE_CONFIG_MULTIMODE
        && dwNotifyCode == RIL_NOTIFY_GPRSREGSTATUSCHANGED) {
        // Bug in PXS8 (multimode): the +CGREG status is not correct, so it
        // shouldn't be reported. Instead the handler of the "+CIEV: psinfo"
        // URC will send the notification.
        m_fUnsolicited = TRUE;
        fRet = TRUE;
        goto Error;
    }

    if (dwRegStatus == RIL_REGSTAT_ROAMING && m_pRilHandle->IsNoRoamingIndicator())
        dwRegStatus = RIL_REGSTAT_HOME;

    dwOldRegStatus = 0xFFFFFFFF;
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED)
    {
        dwOldRegStatus = g_dwRegStatus;
        g_dwRegStatus = dwRegStatus;
        // Special check (needed for +CREG only): any "+CREG: 3" (access denied) is followed by a "+CREG: 0", which is
        // not correct. As a solution the RIL will still report "access denied" until the module has been registered to a
        // network, the SIM is removed or the module has been resetted.
        if (g_dwRegStatus == RIL_REGSTAT_DENIED && CREG0_FOLLOWS_CREG3)
            g_fRegStatus3detected = TRUE;
        if ((g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING) && g_fRegStatus3detected)
            g_dwRegStatus = RIL_REGSTAT_DENIED;
        if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
            g_fRegStatus3detected = FALSE;
        dwRegStatus = g_dwRegStatus;
    }
    else if (dwNotifyCode == RIL_NOTIFY_GPRSREGSTATUSCHANGED)
    {
        dwOldRegStatus = g_dwGPRSRegStatus;
        g_dwGPRSRegStatus = dwRegStatus;
    }

    m_fUnsolicited = TRUE;
    m_dwCode = NULL;

    // It is possible to configure with registry, what not all networks should be
    // considered as "roaming networks". If this is the case, the RIL won't generate
    // the registration change notification now, instead a thread will be started
    // which checks, which network the module is now registered to, and than
    // sends the apropiate notification (roaming/home).
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && dwRegStatus == RIL_REGSTAT_ROAMING && m_pRilHandle->NeedsCheckForNoRoamingNetwork() && !m_pRilHandle->MustFakeUnregisterdState())
    {
        bSuppressNotification = TRUE;
        CloseHandle(CreateThread(NULL, 0, CheckRoamingNetworkThreadProc, (LPVOID)m_pRilHandle, 0, 0));
    }
    // Set the "network rejection" flag to FALSE, when the module sucessfully
    // registered to a network.
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING))
        m_pRilHandle->SetNetworkRejectionState(FALSE);
    // If the network rejects us, find out about the cause.
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && dwRegStatus == RIL_REGSTAT_DENIED)
    {
        // Check for network rejection cause only once
        if (!m_pRilHandle->GetNetworkRejectionState()) {
            m_pRilHandle->SetNetworkRejectionState(TRUE);
            // Find out the cause of the rejection by the network (for AT&T approval).
            CloseHandle(CreateThread(NULL, 0, NetworkRejectThreadProc, (LPVOID)m_pRilHandle, 0, 0));
        }
    }
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING) && dwLAC!=0 && dwCI!=0)
    {
        if ((g_fReportLocationUpdateAsRegistrationState || dwOldRegStatus != dwRegStatus) && !bSuppressNotification)
            m_pRilHandle->GenerateRegStatusNotification(dwRegStatus);

        m_dwCode = RIL_NOTIFY_LOCATIONUPDATE;

        sLI.cbSize = sizeof(RILLOCATIONINFO);
        sLI.dwLocationAreaCode = dwLAC;
        sLI.dwCellID = dwCI;
        if (!SetBlob((void*)&sLI, sizeof(RILLOCATIONINFO)))
        {
            goto Error;
        }
    }
    else
    {
#endif

    // g_fReportLocationUpdateAsRegistrationState: if we change between two roaming
    // networks, the change will be indicated by a +CREG: 5 URC with new location
    // information. Since the last +CREG-URC was also a +CREG: 5 URC, this would
    // normally not be reported as a regitration state change. In this case the
    // RIL sets g_fReportLocationUpdateAsRegistrationState to TRUE. Otherwise the
    // RIL will generate a notification only if the registration state has been
    // changed.
    if ((g_fReportLocationUpdateAsRegistrationState || dwOldRegStatus != dwRegStatus) && !bSuppressNotification)
    {
        DWORD dwRegStatusNotify;

        dwRegStatusNotify = dwRegStatus;
        if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && m_pRilHandle->MustFakeUnregisterdState() && (dwRegStatus == RIL_REGSTAT_ATTEMPTING|| dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING))
            dwRegStatusNotify = RIL_REGSTAT_UNREGISTERED;
        m_dwCode = dwNotifyCode;
        if (!SetBlob((void*)&dwRegStatusNotify, sizeof(DWORD)))
        {
            goto Error;
        }
        if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED)
            m_pRilHandle->GenerateRegStatusNotification(dwRegStatus, TRUE);
    }
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    }
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING) && (dwLAC==0 || dwCI==0))
    {
        // It may happen, that location info is missing in URC. In this case, we have to read them
        // manually and notify the OS about the result.
        CloseHandle(CreateThread(NULL, 0, CheckLocationUpdateThreadProc, (LPVOID)m_pRilHandle, 0, 0));
    }
#endif
#if defined(RIL_NOTIFY_EONS)
    if ((m_pRilHandle->GetEons()->GetEONSState() == EONS_EMULATED || CHECK_EONS_AFTER_CELL_CHANGE) && dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING))
    {
        // Emulated EONS only: when the location changed, the EONS name might changed, too.
        // So we notify the OS, so it can check for a change of the name.
        (void)m_pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_EONS, NULL, 0);
    }
#endif

    // Notify about new technology (if necessary)
    if (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)
        m_pRilHandle->NotifyNewTechnologyMode(TEC_MODE_LTE);


    fRet = TRUE;

#ifdef RILDRV_CE6
    if (dwNotifyCode == RIL_NOTIFY_GPRSREGSTATUSCHANGED)
    {
        BOOL bAttached = (g_dwGPRSRegStatus == RIL_REGSTAT_HOME || g_dwGPRSRegStatus == RIL_REGSTAT_ROAMING ? TRUE : FALSE);

        (void)m_pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSATTACHED, (LPVOID)&bAttached, sizeof(BOOL));
    }
#endif

#ifdef RIL_USE_CSQ_NOTIFICATIONS
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING))
    {
        // Strange behaviour of the Microsoft MMI: if an RIL_NOTIFY_REGSTATUSCHANGED shows up, the MMI
        // forgets about all signal strength URCs so far, so we have to resend it
        CloseHandle(CreateThread(NULL, 0, ReSendSignalThreadProc, (LPVOID)m_pRilHandle, 0, 0));
    }
#endif
    // Check if Auto-GPRS-ReAttach is activated
    if (dwNotifyCode == RIL_NOTIFY_GPRSREGSTATUSCHANGED && g_dwEnableGPRSReAttach == 1 && g_dwEnableGPRSAttach == 1) {
        // Check, if status changed from attached to detached
        if ((g_dwGPRSRegStatus != RIL_REGSTAT_HOME && g_dwGPRSRegStatus != RIL_REGSTAT_ROAMING) && (dwOldRegStatus == RIL_REGSTAT_HOME || dwOldRegStatus == RIL_REGSTAT_ROAMING)) {
            // Start Thread, which performs a re-attach, if possible.
            CloseHandle(CreateThread(NULL, 0, GPRSReattachThreadProc, (LPVOID)m_pRilHandle, 0, 0));
        }
    }
    Error:
    return fRet;
}

BOOL CResponse::ParseCdmaRegistrationStatus(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue;
    DWORD dwRegStatus;
    DWORD dwOldRegStatus;
    BOOL fRet = FALSE;
    BOOL bSuppressNotification = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (NEW_SREG_PARAM) {
        UINT nValueEVDO;

        // New parameter <statusEVDO>
        if (!MatchStringBeginning(rszPointer, ",", rszPointer)       ||
            !ParseUInt(rszPointer, TRUE, nValueEVDO, rszPointer))         {
            goto Error;
        }
        if ((nValueEVDO == 1 || nValueEVDO ==5) && nValue != 1 && nValue != 5)
            nValue = nValueEVDO;
    }
    if (NUM_REGSTATS > nValue)
    {
        dwRegStatus = g_rgdwRegStats[nValue];
    }
    else
    {
        dwRegStatus = RIL_REGSTAT_UNKNOWN;
    }

    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    if (dwRegStatus == RIL_REGSTAT_ROAMING && m_pRilHandle->IsNoRoamingIndicator())
        dwRegStatus = RIL_REGSTAT_HOME;

    dwOldRegStatus = g_dwRegStatus;
    g_dwRegStatus = dwRegStatus;
    // Special check (needed for +CREG only): any "+CREG: 3" (access denied) is followed by a "+CREG: 0", which is
    // not correct. As a solution the RIL will still report "access denied" until the module has been registered to a
    // network, the SIM is removed or the module has been resetted.
    if (g_dwRegStatus == RIL_REGSTAT_DENIED && CREG0_FOLLOWS_CREG3)
        g_fRegStatus3detected = TRUE;
    if ((g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING) && g_fRegStatus3detected)
        g_dwRegStatus = RIL_REGSTAT_DENIED;
    if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
        g_fRegStatus3detected = FALSE;
    dwRegStatus = g_dwRegStatus;

    m_fUnsolicited = TRUE;
    m_dwCode = NULL;

    // Set the "network rejection" flag to FALSE, when the module sucessfully
    // registered to a network.
    if (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)
        m_pRilHandle->SetNetworkRejectionState(FALSE);
    // If the network rejects us, find out about the cause.
    if (dwRegStatus == RIL_REGSTAT_DENIED)
    {
        // Check for network rejection cause only once
        if (!m_pRilHandle->GetNetworkRejectionState()) {
            m_pRilHandle->SetNetworkRejectionState(TRUE);
            // Find out the cause of the rejection by the network (for AT&T approval).
            CloseHandle(CreateThread(NULL, 0, NetworkRejectThreadProc, (LPVOID)m_pRilHandle, 0, 0));
        }
    }

    // g_fReportLocationUpdateAsRegistrationState: if we change between two roaming
    // networks, the change will be indicated by a +CREG: 5 URC with new location
    // information. Since the last +CREG-URC was also a +CREG: 5 URC, this would
    // normally not be reported as a regitration state change. In this case the
    // RIL sets g_fReportLocationUpdateAsRegistrationState to TRUE. Otherwise the
    // RIL will generate a notification only if the registration state has been
    // changed.
    if ((g_fReportLocationUpdateAsRegistrationState || dwOldRegStatus != dwRegStatus) && !bSuppressNotification)
    {
        DWORD dwRegStatusNotify;

        dwRegStatusNotify = dwRegStatus;
        if (m_pRilHandle->MustFakeUnregisterdState() && (dwRegStatus == RIL_REGSTAT_ATTEMPTING|| dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING))
            dwRegStatusNotify = RIL_REGSTAT_UNREGISTERED;
        m_dwCode = RIL_NOTIFY_REGSTATUSCHANGED;
        if (!SetBlob((void*)&dwRegStatusNotify, sizeof(DWORD)))
        {
            goto Error;
        }
        m_pRilHandle->GenerateRegStatusNotification(dwRegStatus, TRUE);
    }

    // Notify about new technology
    if (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)
        m_pRilHandle->NotifyNewTechnologyMode(TEC_MODE_CDMA);

    fRet = TRUE;

#ifdef RIL_USE_CSQ_NOTIFICATIONS
    if (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)
    {
        // Strange behaviour of the Microsoft MMI: if an RIL_NOTIFY_REGSTATUSCHANGED shows up, the MMI
        // forgets about all signal strength URCs so far, so we have to resend it
        CloseHandle(CreateThread(NULL, 0, ReSendSignalThreadProc, (LPVOID)m_pRilHandle, 0, 0));
    }
#endif
    Error:
    return fRet;
}

DWORD WINAPI PromptForSimAndRequeueCmdProc(LPVOID lpParameter);

DWORD WINAPI SIMReInsertedThreadProc(LPVOID pVoid)
{
    HANDLE hThread;
    CRilHandle*    pRilHandle;
    DWORD dwRetries;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("InitSIMReadyRetries"), &dwRetries))
        dwRetries = MAX_SIM_READY_TRIES_DEFAULT;

    pRilHandle = (CRilHandle *)pVoid;
    // Wait for MMI to process new SIM state.
    Sleep(5000);
    // Notify MMI, that every SIM file is different now (we might have a new SIM now)
    (void)pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SIM_DATACHANGE, RIL_SIM_DATACHANGE_ALL);
    // Increase thread priority, to make sure, that the SIM-PIN dialog is handled with priority.
    (void)SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
    // Make sure, the SIM ready thread is running
    pRilHandle->StartReadyStateQuery();
    // We wait until AT+CPIN? is able to anwer. If we start the SIM-PIN dialog too early, it
    // would show 'unknown SIM state' if the PIN is diabled.
    // Regarding POSSIBLE_WRONG_SIMNOTINSTERTED_ERROR: since the SCKS URC already
    // showed up, we know, the the SIM is inserted, so there is no need to query it.
    // If the last try fails, make one last try without assuming POSSIBLE_WRONG_SIMNOTINSTERTED_ERROR.
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pRilHandle, "AT+CPIN?\r", CMDOPT_SIMQUERY, APIID_UNLOCKPHONE, 0, dwRetries, SIM_READY_RETRY_INTERVAL, ParseGetPhoneLockedState, NULL, (LPVOID)pRilHandle, NULL, POSSIBLE_WRONG_SIMNOTINSTERTED_ERROR || pRilHandle->IgnoreSCKSsupport() ? g_CMET_CPINWithSIM : g_CMET_CPINNoSIM) && POSSIBLE_WRONG_SIMNOTINSTERTED_ERROR)
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pRilHandle, "AT+CPIN?\r", CMDOPT_SIMQUERY, APIID_UNLOCKPHONE, 0, 0, 0, ParseGetPhoneLockedState, NULL, (LPVOID)pRilHandle, NULL, g_CMET_CPINNoSIM);
    // Now start SIM-PIN dialog (if necessary)
    hThread = CreateThread(NULL, 0, PromptForSimAndRequeueCmdProc, (LPVOID)NULL, 0, NULL);
    (void)CloseHandle(hThread);
    return 0;
}

//
//
//
BOOL CResponse::ParseSIMCardStatus(LPCSTR& rszPointer, BOOL bSCKSURC)
{
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM card notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    if (!bSCKSURC)       // The '^SCRD:' URC shows, that the radio doesn't accept the SIM. The RIL can ignore the <mcc> parameter.
        nValue = 2;      // Treat '^SCRC:' URC as '^SCKS: 2' URC.

    if (nValue == 0 || nValue >= 2)    // ^SCKS: 0  -> SIM-Card removed or invalid, so we send the "SIM not accessible" message; ^SCKS: 2 -> SIM-Card broken, same action (same for ^SCKS: 3 on PH8)
    {
        g_dwRegStatus = RIL_REGSTAT_UNKNOWN;
        g_fRegStatus3detected = FALSE;
        g_uiECCCacheSize = 0;
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_SIMNOTACCESSIBLE;
        g_iPPLMNBufferPos = PPLMN_BUFFER_NOT_FILLED;
        g_dwSIMLockedState = RIL_LOCKEDSTATE_UNKNOWN;
        ClearGPRSParamCache();
        m_pRilHandle->SetCurrentSIMType(SIMTYPE_NOT_INSERTED);
        m_pRilHandle->SetNetworkRejectionState(FALSE);
        m_pRilHandle->GetSimToolkit()->ResetIdleModeText();

        if (!SetBlob(NULL, 0))
        {
            goto Error;
        }
        UpdateSIMState((DWORD)RIL_E_SIMNOTINSERTED);
    }
    else if (nValue == 1)    // ^SCKS: 1  -> we start a re-initialization thread
    {
        RILEQUIPMENTSTATE res;

        if (WRONG_SIMSTATUS_AFTER_SAT_SIM_RESET)   // This bug also applies to the "^SCKS: 1" URC
            m_pRilHandle->IgnoreNextSIMStatus();

        CloseHandle(CreateThread(NULL, 0, SIMReInsertedThreadProc, (LPVOID)m_pRilHandle, 0, 0));
        m_pRilHandle->SendReadyStateNotification(RIL_READYSTATE_NONE, FALSE);
        m_pRilHandle->SendReadyStateNotification(RIL_READYSTATE_INITIALIZED, FALSE);
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED;
        memset(&res, 0x00, sizeof(RILEQUIPMENTSTATE));
        res.cbSize = sizeof(RILEQUIPMENTSTATE);
        res.dwParams = RIL_PARAM_EQUIPMENTSTATE_READYSTATE;
        res.dwReadyState = RIL_READYSTATE_INITIALIZED;
        if (!SetBlob((void *)&res, sizeof(RILEQUIPMENTSTATE)))
        {
            goto Error;
        }
    }


    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseExtendedShutdown(LPCSTR& rszPointer, BOOL &bShutdown) const
{
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    bShutdown = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete shutdown notification -- no need to parse it
        goto Error;
    }

    // Parse "<cause>"
    if (!ParseIntAsUInt(rszPointer, nValue, rszPointer))
    {
        goto Error;
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    if (!SHUTDOWN_MEANS_PLANED_RESTART) {
        switch (nValue)
        {
            case 1:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_UNDERTEMPERATURE);
                bShutdown = TRUE;
                break;
            case 2:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN_WARN, EMERG_SHUTDOWN_UNDERTEMPERATURE);
                break;
            case 3:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN_WARN, EMERG_SHUTDOWN_NONE);
                break;
            case 4:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN_WARN, EMERG_SHUTDOWN_OVERTEMPERATURE);
                break;
            case 5:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_OVERTEMPERATURE);
                bShutdown = TRUE;
                break;
            case 6:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN_WARN, EMERG_SHUTDOWN_UNDERVOLTAGE);
                break;
            case 7:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN_WARN, EMERG_SHUTDOWN_OVERVOLTAGE);
                break;
            case 8:
                (void)m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_OVERVOLTAGE);
                bShutdown = TRUE;
                break;
        }
    }

    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseTemperatureWarning(LPCSTR& rszPointer, INT &iCause) const
{
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseIntAsUInt(rszPointer, nValue, rszPointer))
    {
        goto Error;
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    iCause = (INT)nValue;

    fRet = TRUE;

    Error:
    return fRet;
}


// Problem: if an +CUSD-URC doesn't contain a DCS, we do not know, if this is
// GSM or UCS2 coded. To solve this problem, we have to make a clever guess.
BOOL CResponse::IsProbablyNotUCS2(LPCSTR szDataString) const
{
    BOOL bRet = FALSE;
    UINT i, iLen;

    iLen = strlen(szDataString);
    if (iLen == 0 || (iLen % 4) != 0)
        bRet = TRUE;

    for (i=0; i<iLen && !bRet; i++)
    {
        char szSearch[2];

        szSearch[0] = szDataString[i];
        szSearch[1] = 0;
        if (strstr("0123456789ABCDEFabcdef", szSearch) == NULL)
            bRet = TRUE;
    }
    return bRet;
}

//
//
//
BOOL CResponse::ParseUSSDInfo(LPCSTR& rszPointer, BOOL &fExpectNothing)
{
    LPSTR szDataString = NULL;
    UINT cbDataString;
    UINT cbStruct;
    UINT nStatus;
    UINT nDCS;
    // UINT i;
    // BYTE* pbDataWalk;
    // LPSTR pchDataStringWalk;
    LPCSTR szDummy;
    RILSUPSERVICEDATA* prssd = NULL;
    BOOL fRet = FALSE;
    BOOL bDCSunknown = FALSE;
    BOOL fCUSD4Workaround;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete USSD notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUIntAndVerifyAbove(rszPointer, TRUE, NUM_SUPSVCDATASTATS, nStatus, rszPointer))
    {
        goto Error;
    }

    // Bug in some HC28 versions: sometimes the comma is missing.
    fCUSD4Workaround = MatchStringBeginning(rszPointer, "\"", szDummy);
    // Parse ","
    if (fCUSD4Workaround ? FALSE : !MatchStringBeginning(rszPointer,  ",", rszPointer))
    {
        // No data parameter present
        prssd = (RILSUPSERVICEDATA*)AllocBlob(sizeof(RILSUPSERVICEDATA));
        if (!prssd)
        {
            goto Error;
        }
        cbStruct = sizeof(RILSUPSERVICEDATA);
        prssd->dwParams = 0;
    }
    else
    {
        // Parse "<data>,<dcs>"
        // NOTE: we take ownerswhip of allocated szDataString
        if (!ParseUnlimitedString(rszPointer, szDataString, cbDataString, rszPointer))
        {
            goto Error;
        }
        if(!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUInt(rszPointer, TRUE, nDCS, rszPointer))
        {
            // default nDCS.  this parameter is supposed to be mandatory but we've
            // seen it missing from otherwise normal USSD responses.
            bDCSunknown = TRUE;
            nDCS = 15;        // The default coding scheme is 15 (= unspecific language)
        }

        // See GSM 03.38 for Cell Broadcast DCS details
        // Currently support only default alphabet in all languages (i.e., the DCS range 0x00..0x0f)
        // Also applies to uncompressed default alphabet (DCS=64) and uncompressed 8 bit data (DCS=68)
        // We also support the Czech language version (DCS=32)
        // Since the USSD URC always uses UCS2 on the Cinterion driver, we can also treat
        // uncompressed UCS2 messages (DCS=72) here.
        // Only exception: when DCS is unkown. In this case the USSD strings comes as GSM, even when
        // +CSCS is set to "UCS2".
        if (bDCSunknown && IsProbablyNotUCS2(szDataString))
        {
            // Default alphabet encoding (which has been requested in our init string to allow direct copying of bytes)

            // At this point, cbDataString has the size of the szDataString which ParseUnlimitedString allocated for us and
            // which holds the TE-encoded characters.
            // However, this byte count also includes an extra byte for the NULL character at the end, which we're not going to translate.
            // Therefore, decrement cbDataString by 1
            cbDataString--;

            // Now figure out the number of Unicode characters needed to hold the string after it's converted
            size_t cbUnicode;
            if (!EncodedStringSizeToUnicodeStringSize(ENCODING_TECHARSET, cbDataString, cbUnicode))
            {
                goto Error;
            }

            // cbUnicode now has the number of bytes we need to hold the Unicode string generated when we
            // convert from the TE-encoded string.
            // However, it doesn't include space for the terminating NULL, so we need to allow for that also.
            // (Note that ParseEncodedString will append the terminating NULL for us automatically.)
            cbUnicode += sizeof(TCHAR);

            // Allocate a structure big enough to hold the header and the null-terminated unicode string
            cbStruct = sizeof(RILSUPSERVICEDATA) + cbUnicode;
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            if (!prssd)
            {
                goto Error;
            }

            // Now convert the TE-encoded string into Unicode in the buffer we just allocated
            if (!ParseEncodedString(ENCODING_TECHARSET,
                                    szDataString,
                                      szDataString + cbDataString,
                                      (LPWSTR)(prssd->pbData),
                                      (LPWSTR)(prssd->pbData + cbUnicode)))
            {
                goto Error;
            }

            prssd->dwParams = RIL_PARAM_SSDI_DATA;
        }
        // We can also handle uncompressed UCS2 data, but we have to convert them
        else if (((0x00 <= nDCS) && (nDCS <= 0x0f)) || (nDCS == 68) || (nDCS == 64) || (nDCS == 32) || (nDCS == 72) || bDCSunknown)
        {
            cbDataString--;
            size_t cbUnicode;
            if (!EncodedStringSizeToUnicodeStringSize(ENCODING_UCS2, cbDataString, cbUnicode))
            {
                goto Error;
            }

            cbUnicode += sizeof(TCHAR);

            // Allocate a structure big enough to hold the header and the null-terminated unicode string
            cbStruct = sizeof(RILSUPSERVICEDATA) + cbUnicode;
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            if (!prssd)
            {
                goto Error;
            }

            // Now convert the UCS2-encoded string into Unicode in the buffer we just allocated
            if (!ParseEncodedString(ENCODING_UCS2,
                                    szDataString,
                                      szDataString + cbDataString,
                                      (LPWSTR)(prssd->pbData),
                                      (LPWSTR)(prssd->pbData + cbUnicode)))
            {
                goto Error;
            }

            prssd->dwParams = RIL_PARAM_SSDI_DATA;
        }
        else
        {
            goto Error;
        }
    }

    prssd->cbSize = cbStruct;
    prssd->dwStatus = g_rgdwSupSvcDataStats[nStatus];
    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_SUPSERVICEDATA;
    if (!SetBlob((void*)prssd, cbStruct))
    {
        goto Error;
    }
    fRet = TRUE;
    if (g_rgdwSupSvcDataStats[nStatus] == RIL_SUPSVCDATASTATUS_FURTHERINFOREQUIRED)
    {
        // USSD-URC of type RIL_SUPSVCDATASTATUS_FURTHERINFOREQUIRED is followed by an ">" sign:
        if (MatchStringAnywhere(rszPointer, ">", rszPointer))
        {
            m_pRilHandle->EnterUSSDAnswerMode(m_PortType);
            fExpectNothing = TRUE;
        }
    }

    Error:
    delete[] szDataString;
    (void)FreeBlob(prssd);
    return fRet;
}

//
//
//
BOOL CResponse::ParseMessage(LPCSTR& rszPointer, const DWORD dwNotifyCode)
{
    LPCSTR szDummy;
    LPCSTR szEndOfPDU;
    UINT nLength;
    UINT cbMessage;
    BOOL fPrependedSMSC = FALSE;
    RILMESSAGE rmMessage;
    HRESULT hr;
    BOOL fRet = FALSE;

    // Look for a "<CR><LF>" and a "<postfix>"
    if (!MatchStringAnywhere(rszPointer, "\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>

    // There might be a leading comma (see: 3GPP 27.005, +CMT:)
    (void)MatchStringBeginning(rszPointer, ",", rszPointer);

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

    if (RIL_NOTIFY_BCMESSAGE == dwNotifyCode)
    {
        cbMessage = nLength * 2;
    }
    else
    {
        // 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 ('+' == *rszPointer)
        {
            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 - rszPointer) > cbMessage)
            {
                cbMessage += (1 + SemiByteCharsToByte(*rszPointer, *(rszPointer + 1))) * 2;
                fPrependedSMSC = TRUE;
            }
        }
    }

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

    // Parse "<pdu>"
    if (RIL_NOTIFY_BCMESSAGE == dwNotifyCode)
    {
        if (0 == cbMessage)
        {
            cbMessage = szEndOfPDU - rszPointer;
        }
        hr = ParseCellBroadcastMessage(rszPointer, cbMessage, rmMessage);
    }
    else
    {
        hr = ParseSMSMessage(rszPointer, cbMessage, TRUE, fPrependedSMSC, rmMessage);
    }
    if (FAILED(hr))
    {
        // We failed parsing
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }
    rszPointer += cbMessage;

    if (dwNotifyCode == RIL_NOTIFY_MESSAGE)
    {
        if (MustFilterSMS(&rmMessage))
        {
            fRet = TRUE;
            goto Error;
        }
    }

    m_fUnsolicited = TRUE;
    m_dwCode = dwNotifyCode;
    if (!SetBlob((void*)&rmMessage, sizeof(RILMESSAGE)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseCdmaMessage(LPCSTR& rszPointer, const DWORD dwNotifyCode)
{
    LPCSTR szDummy;
    LPCSTR szEndOfPDU;
    UINT nLength;
    UINT cbMessage;
    RILMESSAGE rmMessage;
    HRESULT hr;
    BOOL fRet = FALSE;

    // Look for a "<CR><LF>" and a "<postfix>"
    if (!MatchStringAnywhere(rszPointer, "\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(rszPointer, FALSE, nLength, rszPointer) ||
        !MatchStringBeginning(rszPointer, "\r\n", rszPointer))
    {
        // We failed parsing
        ParseJunk(2, rszPointer);
        fRet = TRUE;
        goto Error;
    }

    cbMessage = (UINT)(szEndOfPDU - rszPointer);

    if ((UINT)(szEndOfPDU - rszPointer) < nLength * 2)
    {
        // Bug in PXS8: the message length reported by the module might be wrong,
        // so the RIL just prints a warning and parses the PDU nevertheless.
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Reported length doesn't match actual PDU length. RIL will decode message nevertheless.")));
    }

    if (RIL_NOTIFY_BCMESSAGE == dwNotifyCode)
    {
        // Windows CE / Mobile doesn't support cell broadcast for CDMA.
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: CDMA Cell Broadcast not supported.")));
        hr = E_FAIL;
    }
    else if (RIL_NOTIFY_STATUSMESSAGE == dwNotifyCode)
    {
        hr = ParseCdmaStatusMessage(rszPointer, cbMessage, rmMessage);
    }
    else
    {
        hr = ParseSMSCDMAMessage(rszPointer, cbMessage, TRUE, rmMessage);
    }

    // Parse "<pdu>"
    if (FAILED(hr))
    {
        // We failed parsing
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }
    rszPointer += cbMessage;

    m_fUnsolicited = TRUE;
    m_dwCode = dwNotifyCode;
    if (!SetBlob((void*)&rmMessage, sizeof(RILMESSAGE)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseCdmaQCBMessage(LPCSTR& rszPointer)
{
    const char *cszIncorrectTeleservice = "00000250000";
    const char *cszCorrectTeleservice = "0000021002";
    char szPDU[MAX_CDMA_PDU_SIZE];
    RILMESSAGE rmMessage;
    HRESULT hr;
    BOOL fRet = FALSE;

    // Parse "<PDU><CR><LF>"
    if (!ParseUnquotedString(rszPointer, '\r', szPDU, MAX_CDMA_PDU_SIZE, rszPointer) ||
        !FindRspPostfix(rszPointer, rszPointer))
    {
        goto Error;
    }

    if (INCORRECT_CDMA_CB_PDU && strncmp(szPDU, cszIncorrectTeleservice, strlen(cszIncorrectTeleservice)) == 0) {
        // Bug in PXS8: The first 11 characters of the PDU are wrong and 1
        // character too many. The RIL will replace them with 10 plausible
        // characters.
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ParseCdmaQCBMessage() - incorrect teleservice (0x5000), try compensating\r\n")));
        memmove(szPDU, szPDU + strlen(cszIncorrectTeleservice), MAX_CDMA_PDU_SIZE - (strlen(cszIncorrectTeleservice) +1));
        memmove(szPDU + strlen(cszCorrectTeleservice), szPDU, MAX_CDMA_PDU_SIZE - (strlen(szPDU) +1));
        memcpy(szPDU, cszCorrectTeleservice, strlen(cszCorrectTeleservice));
    }
    hr = ParseSMSCDMAMessage(szPDU, strlen(szPDU), TRUE, rmMessage);
    if (FAILED(hr))
    {
        fRet = TRUE;
        goto Error;
    }

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_BCMESSAGE;
    if (!SetBlob((void*)&rmMessage, sizeof(RILMESSAGE)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseMessageInSim(LPCSTR& rszPointer, const DWORD dwNotifyCode, BOOL fNoNotification)
{
    RILMESSAGE_IN_SIM rmMessageInSim;
    BOOL fRet = FALSE;
    LPCSTR szDummy;
    UINT Location;
    UINT Index;

    // Look for a "<postfix>" to be sure we got a whole message
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    // Look for a "<Location>,<Index>"
    if ( (!ParseGetLocation(rszPointer, Location, rszPointer)) ||
         (!MatchStringBeginning(rszPointer, ",", rszPointer)) ||
         (!ParseUInt(rszPointer, FALSE, Index, rszPointer)))
    {
        goto Error;
    }

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

    m_fUnsolicited = TRUE;
#ifdef RILDRV_CE6
// Visual Studio sees a possible memory leak through an exception
// which actually doesn't exists.
#pragma warning (push)
#pragma warning ( disable : 6211 )
#endif
    if (!fNoNotification)
    {
        DWORD dwReg = 1;

        GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("PerformSMSClass2Check"), &dwReg);
        if (INCORRECT_URC_FOR_SMS_CLASS2 &&
               dwNotifyCode == RIL_NOTIFY_MESSAGE_IN_SIM &&   // Applies only to +CMTI: URCs
               Location == RIL_MSGLOC_SIM &&      // valid only for "SM" store
               m_pRilHandle->GetCurrentTechnology() != TEC_MODE_CDMA && // Is not required for CDMA
               dwReg == 1) {
            SMS_CLASS2_CHECK_THREAD_DATA *pData;

            // Bug in PH8 Rel2: the module might have incorrectly indicate an incoming
            // class 2 message, which is not supposed to happen. In this case, the RIL
            // must not notifiy the OS.
            pData = new SMS_CLASS2_CHECK_THREAD_DATA;
            if (pData != NULL) {
                pData->pHandle = m_pRilHandle;
                memcpy(&(pData->rmMessageInSim), &rmMessageInSim, sizeof(RILMESSAGE_IN_SIM));

                CloseHandle(CreateThread(NULL, 0, SMSClass2CheckTreadProc, (LPVOID)pData, 0, 0));
            }
        }
        else {
            m_dwCode = dwNotifyCode;
            if (!SetBlob((void*)&rmMessageInSim, sizeof(rmMessageInSim)))
            {
                goto Error;
            }
        }
    }
#ifdef RILDRV_CE6
#pragma warning (pop)
#endif
    fRet = TRUE;

    Error:
    return fRet;
}

struct INCOMINGCDMASMS_THREAD_DATA {
    CRilHandle* pHandle;
    UINT uiIndex;
};


//
//
//
static DWORD WINAPI ReadIncomingCdmaMsgThreadProc(LPVOID pVoid)
{
    INCOMINGCDMASMS_THREAD_DATA *pData;
    CRilHandle* pHandle;
    UINT uiIndex;

    pData = (INCOMINGCDMASMS_THREAD_DATA *)pVoid;
    uiIndex = pData->uiIndex;
    pHandle = pData->pHandle;
    FreeBlob(pData);

    // Bug in PXS8: a newly incoming SMS needs to by read in order to become
    // unread.
    if (CDMA_NEW_SMS_ARE_READ) {
        char szCmd[MAX_ATCMD_LEN];

        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMGR=%d\r", uiIndex);
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_INIT, APIID_READMSG, 0, 0, 0);
    }
    else {
        // The module needs some time to actually write the text message.
        Sleep(200);
    }

    // There is at least one new message in memory. Read it, notify the OS and
    // then delete it.
    (void)CheckForMissedSMS(pHandle, TRUE);

    return 0;
}

//
//
//
BOOL CResponse::ParseCdmaMessageInMemory(LPCSTR& rszPointer)
{
    INCOMINGCDMASMS_THREAD_DATA *pData = NULL;
    BOOL fRet = FALSE;
    LPCSTR szDummy;
    UINT Location = RIL_MSGLOC_UNKNOWN;
    UINT Index;

    // Look for a "<postfix>" to be sure we got a whole message
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    // Look for a "<Location>,<Index>"
    if ( (!ParseGetLocation(rszPointer, Location, rszPointer)) ||
         (!MatchStringBeginning(rszPointer, ",", rszPointer)) ||
         (!ParseUInt(rszPointer, FALSE, Index, rszPointer)))
    {
        goto Error;
    }

    pData = (INCOMINGCDMASMS_THREAD_DATA *)AllocBlob(sizeof(INCOMINGCDMASMS_THREAD_DATA));
    if (pData != NULL) {
        pData->pHandle = m_pRilHandle;
        pData->uiIndex = Index;

        // Complete URC? Start a the SMS location thread to read the message
        // and notify the OS.
        CloseHandle(CreateThread(NULL, 0, ReadIncomingCdmaMsgThreadProc, (LPVOID)pData, 0, NULL));
    }

    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseCdmaActivation(LPCSTR& rszPointer)
{
    RILSUPSERVICEDATA* prssd = NULL;
    UINT cbStruct;
    char szActivationResponse[MAX_ATCMD_LEN];
    BOOL fRet = FALSE;
    LPCSTR szDummy;
    DWORD dwReg = 1;
    BOOL fSendNotification = TRUE;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaActivationAsUSSD"), &dwReg);
    memset(szActivationResponse, 0, MAX_ATCMD_LEN);

    // Look for a "<postfix>" to be sure we got a whole message
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    // Look for the "<response>" parameter
    if (!ParseUnquotedString(rszPointer, '\r', szActivationResponse, MAX_ATCMD_LEN, rszPointer))
    {
        goto Error;
    }

    if (DOUBLE_CDMA_ACTIVATION_URC) {
        if (m_pRilHandle->GetGotCDMAActivationURC())
            fSendNotification = FALSE;
        else
            m_pRilHandle->SetGotCDMAActivationURC(TRUE);
    }

    // Remove leading blanks
    while (szActivationResponse[0] == ' ')
        memmove(szActivationResponse, szActivationResponse+1, MAX_ATCMD_LEN-1);

    // If configured, generate an USSD notification with the result of the activation.
    if (fSendNotification && dwReg == 1) {
        WCHAR wszActivationResponse[MAX_ATCMD_LEN];

        memset(wszActivationResponse, 0, MAX_ATCMD_LEN * sizeof(WCHAR));
        (void)_snwprintfz(wszActivationResponse, MAX_ATCMD_LEN, L"ACTIVATION: %S", szActivationResponse);
        cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszActivationResponse) * sizeof(WCHAR)) + sizeof(WCHAR);
        prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
        if (prssd == NULL)
        {
            goto Error;
        }
        memcpy(prssd->pbData, (BYTE *)wszActivationResponse, (wcslen(wszActivationResponse) * sizeof(WCHAR)) + sizeof(WCHAR));
        prssd->dwParams = RIL_PARAM_SSDI_DATA;
        prssd->dwStatus = RIL_SUPSVCDATASTATUS_NOINFOREQUIRED;
        prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
        prssd->cbSize = cbStruct;

        (void)m_pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_SUPSERVICEDATA, (LPVOID)prssd, cbStruct);
    }

    m_dwCode = RIL_NOTIFY_DEVSPECIFIC_CDMA_ACTIVATION;
    if (!SetBlob((void*)szActivationResponse, strlen(szActivationResponse)+1))
    {
        goto Error;
    }

    fRet = TRUE;

#ifdef RIL_RADIO_RESILIENCE
    // It is recommended to restart the radio after the CDMA registration.
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ResetAfterCdmaActivation"), &dwReg))
        dwReg = 1;
    if (fSendNotification && dwReg == 1) {
        // If Now trigger restart of the radio to complete CDMA activation.
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Trigger reset of module to finish CDMA activation.\r\n")));
        (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilHandle, "AT+CFUN=1,1\r", CMDOPT_NONE, APIID_NONE, 3000, 0, 0);
        (void)m_pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_RADIORESET, NULL, 0);
    }
#endif

    Error:
    if (prssd != NULL)
        FreeBlob(prssd);
    return fRet;
}

//
// TransformOMADMText()
// The RIL allows the user to modify the OMADM messages displayed as USSD.
//
void CResponse::TransformOMADMText(const char* szOMADMResponse, WCHAR *wszActivationResponse, UINT uiBufferLen)
{
    WCHAR wszRegKeyName[MAX_PATH];
    UINT iIdxRead = 0;
    UINT iIdxWrite = 0;

    // Generate registry key name (module text without spaces)
    memset(wszRegKeyName, 0, MAX_PATH * sizeof(WCHAR));
    for (iIdxRead = 0; iIdxRead < strlen(szOMADMResponse); iIdxRead++) {
        if ((szOMADMResponse[iIdxRead] >= 'A' && szOMADMResponse[iIdxRead] <= 'Z') ||
            (szOMADMResponse[iIdxRead] >= 'a' && szOMADMResponse[iIdxRead] <= 'z') ||
            (szOMADMResponse[iIdxRead] >= '0' && szOMADMResponse[iIdxRead] <= '9')) {
            wszRegKeyName[iIdxWrite] = (WCHAR)szOMADMResponse[iIdxRead];
            iIdxWrite++;
        }
        if (iIdxWrite >= MAX_PATH) break;
    }
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\RIL\\OmaDmText"), wszRegKeyName, wszActivationResponse, uiBufferLen)) {
        // Default: use original text from module.
        (void)_snwprintfz(wszActivationResponse, MAX_ATCMD_LEN, L"OMADM: %S", szOMADMResponse);
    }
}

//
//
//
BOOL CResponse::ParseOMADMNotification(LPCSTR& rszPointer)
{
    RILSUPSERVICEDATA* prssd = NULL;
    UINT cbStruct;
    char szOMADMResponse[MAX_ATCMD_LEN];
    BOOL fRet = FALSE;
    LPCSTR szDummy;
    DWORD dwReg = 1;
    BOOL fSendNotification = TRUE;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaOmaDmAsUSSD"), &dwReg);
    memset(szOMADMResponse, 0, MAX_ATCMD_LEN);

    // Look for a "<postfix>" to be sure we got a whole message
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    // Look for the "<response>" parameter
    if (!ParseUnquotedString(rszPointer, '\r', szOMADMResponse, MAX_ATCMD_LEN, rszPointer))
    {
        goto Error;
    }

    // Remove leading blanks
    while (szOMADMResponse[0] == ' ')
        memmove(szOMADMResponse, szOMADMResponse+1, MAX_ATCMD_LEN-1);

    // If configured, generate an USSD notification with the result of the activation.
    if (fSendNotification && dwReg == 1) {
        WCHAR wszActivationResponse[MAX_ATCMD_LEN];

        memset(wszActivationResponse, 0, MAX_ATCMD_LEN * sizeof(WCHAR));
        TransformOMADMText(szOMADMResponse, wszActivationResponse, MAX_ATCMD_LEN);
        if (wcslen(wszActivationResponse) > 0) {  // Don't generate notification if no text is specified.
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszActivationResponse) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            if (prssd == NULL)
            {
                goto Error;
            }
            memcpy(prssd->pbData, (BYTE *)wszActivationResponse, (wcslen(wszActivationResponse) * sizeof(WCHAR)) + sizeof(WCHAR));
            prssd->dwParams = RIL_PARAM_SSDI_DATA;
            prssd->dwStatus = RIL_SUPSVCDATASTATUS_NOINFOREQUIRED;
            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
            prssd->cbSize = cbStruct;

            (void)m_pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_SUPSERVICEDATA, (LPVOID)prssd, cbStruct);
        }
    }

    m_dwCode = RIL_NOTIFY_DEVSPECIFIC_CDMA_OMADM_UPDATE;
    if (!SetBlob((void*)szOMADMResponse, strlen(szOMADMResponse)+1))
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    if (prssd != NULL)
        FreeBlob(prssd);
    return fRet;
}

//
//
//
BOOL CResponse::ParseSIMToolkitCmdOrRsp(LPCSTR& rszPointer, const DWORD dwNotifyCode)
{
    UINT cbDataString;
    LPSTR szDataString = NULL;
    LPSTR pchDataStringWalk;
    UINT cbData;
    BYTE* pbData = NULL;
    BYTE* pbDataWalk;
    LPCSTR szDummy;
    UINT i;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM Toolkit notification -- no need to parse it
        goto Error;
    }

    // Parse "<data>"
    // NOTE: we take ownerswhip of allocated szDataString
    if (!ParseUnlimitedUnquotedString(rszPointer, '\r', szDataString, cbDataString, rszPointer))
    {
        goto Error;
    }

    if ( (szDataString[0]=='\0') && (dwNotifyCode==RIL_NOTIFY_SIMTOOLKITCMD) )
    {
        m_dwCode=RIL_NOTIFY_SIMTOOLKITSESSIONEND;
    }
    else
    {
        cbData = (cbDataString - 1) / 2;
        pbData = (BYTE*)AllocBlob(cbData);
        if (!pbData)
        {
            goto Error;
        }

        pbDataWalk = pbData;
        pchDataStringWalk = szDataString;
        for (i = 0; i < cbData; i++)
        {
            *pbDataWalk = SemiByteCharsToByte(*pchDataStringWalk, *(pchDataStringWalk + 1));
            pbDataWalk++;
            pchDataStringWalk += 2;
        }

        if (!SetBlob((void*)pbData, cbData))
        {
            goto Error;
        }
        m_dwCode = dwNotifyCode;
    }

    m_fUnsolicited = TRUE;
    fRet = TRUE;

    Error:
    delete[] szDataString;
    (void)FreeBlob(pbData);
    return fRet;
}


//
//
//
BOOL CResponse::ParseSIMToolkitCallSetup(LPCSTR& rszPointer)
{
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Ignore any blank characters at the beginning
    if (*rszPointer == ' ') rszPointer++;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM Toolkit notification -- no need to parse it
        goto Error;
    }

    if (rszPointer + 2 == szDummy)
    {
        //
        // The "<timeout>" value is not specified
        //

        nValue = 0;
    }
    else
    {
        // Parse "<timeout>"
        if (!MatchStringBeginning(rszPointer, "\"", rszPointer) ||
            !ParseUInt(rszPointer, FALSE, nValue, rszPointer)   ||
            !MatchStringBeginning(rszPointer, "\"", rszPointer))
        {
            // Parsing failure
            ParseJunk(1, rszPointer);
            fRet = TRUE;
            goto Error;
        }
    }

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_SIMTOOLKITCALLSETUP;
    if (!SetBlob((void*)&nValue, sizeof(DWORD)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

