/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: 3G-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules PD SW 1
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/phonebook.cpp $
 * $Revision: /main/ms-d/12 $
 *     $Date: 2013-05-06 15:07:16 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 6.x
 *****************************************************************************
 * Description:
 *  Cinterion 3G RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/phonebook.cpp$ 
 * 
 * /main/ms-d/12   2013-05-06 15:07:16   pmackent 
 * Save old phonebook selection for blacklist deletion (EHS5)
 *
 * /main/ms-d/11   2013-02-26 15:07:44   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/10   2013-02-04 13:30:31   pmackent
 * Repeat AT+CPBS when writing to FD phonebook (IPIS100084674)
 *
 * /main/ms-d/9   2013-01-30 11:12:19   pmackent
 * Changed handling of SM phonebook while FD lock
 *
 * /main/ms-d/8   2012-11-23 16:51:42   pmackent
 * Handle problems with selecting SM phonebook on EHS5
 *
 * /main/ms-d/7   2012-02-23 10:12:28   ctna0256
 * FD phonebook workaround also needed for deleting entries
 *
 * /main/ms-d/6   2012-02-22 13:26:56   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/5   2012-02-16 13:44:59   ctna0256
 * Comply to CE6 and CE7 compiler warnings
 *
 * /main/ms-d/4   2012-02-14 14:58:02   ctna0256
 * Improved comments
 *
 * /main/ms-d/3   2012-02-10 17:21:03   ctna0256
 * Solved problems with phonebook entries type ASCII and usage of EXT2
 *
 * /main/ms-d/2   2012-02-09 16:44:32   ctna0256
 * Check for missed SMS with AT+CRSM on EHS5, small bug fixes
 *
 * /main/ms-d/1   2012-02-03 13:58:28   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"
#include <simtkit.h>

// For GCF 27.18.3 we need small cache for the names of the FD phonebook entries, since the MMI
// doesn't offer them to be edited.
#define MAX_FDPB_CACHE  5
static WCHAR g_wszFDPBCache[MAX_FDPB_CACHE][MAXLENGTH_PHONEBOOKTEXT];
// Cache for the size of the EF_FDN SIM file.
static DWORD g_nEF_FDN_FileSize = 0;

CRITICAL_SECTION g_csPhonebookCmd;

// Holds the currently selcted phonebook.
DWORD g_dwCurrentPhoneBook = RIL_PBLOC_UNKNOWN;

//
// Some retry options definitions
//
#define PHONEBOOKOPTIONS_RETRIES        1
#define PHONEBOOKOPTIONS_RETRIES_DELAY  5000

// Structure used for ParseReadPhonebookEntriesFromArray()
struct ARRAY_TO_PARSING_DATA {
    LPVOID  pData;
    DWORD dwSize;
};

void ClearFDPBCache(void)
{
    memset(g_wszFDPBCache, 0, MAX_FDPB_CACHE*MAXLENGTH_PHONEBOOKTEXT*sizeof(WCHAR));
    g_nEF_FDN_FileSize = 0;
}

//
//
//
HRESULT ParseGetPhonebookOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT i;
    UINT nValue;
    char szLocation[MAX_ATCMD_LEN];
    RILPHONEBOOKINFO* prpbi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+CPBS: <storage>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CPBS: ", szRsp) ||
        !ParseString(szRsp, szLocation, MAX_ATCMD_LEN, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_PBLOCS; i++) {
        if (!strcmp(szLocation, g_rgszPBLocations[i])) {
            prpbi->dwStoreLocation = i;
            break;
        }
    }
    if (NUM_PBLOCS == i) {
        // We couldn't match the response with anything
        prpbi->dwStoreLocation = RIL_PBLOC_UNKNOWN;
    }
    if (pParam == NULL) {
        g_dwCurrentPhoneBook = prpbi->dwStoreLocation;
    }
    else {
        DWORD *pPB = (DWORD *)pParam;

        *pPB = prpbi->dwStoreLocation;
    }
    prpbi->dwParams |= RIL_PARAM_PBI_STORELOCATION;

    // Parse ","
    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        // Parse "<used>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        prpbi->dwUsed = nValue;
        prpbi->dwParams |= RIL_PARAM_PBI_USED;

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

        prpbi->dwTotal = nValue;
        prpbi->dwParams |= RIL_PARAM_PBI_TOTAL;
    }

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

    pBlob = (void*)prpbi;
    cbBlob = sizeof(RILPHONEBOOKINFO);

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

//
//
//
static HRESULT ParseGetPhonebookOptionsCRSM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILPHONEBOOKINFO* prpbi = NULL;
    HRESULT hr = S_OK;
    DWORD dwNumbers = 0;

    if (g_nSIMType == SIMTYPE_3G_SIM)
        hr = ParseNumbersOfRecordsFromSimRecordStatus3G(szRsp, pBlob, cbBlob, (LPVOID)&dwNumbers);
    else
        hr = ParseNumbersOfRecordsFromSimRecordStatus2G(szRsp, pBlob, cbBlob, (LPVOID)&dwNumbers);

    pBlob = NULL;
    cbBlob = 0;

    if (SUCCEEDED(hr) && dwNumbers > 0) {
        prpbi = (RILPHONEBOOKINFO*)AllocBlob(sizeof(RILPHONEBOOKINFO));
        if (!prpbi) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        memset(prpbi, 0x00, sizeof(RILPHONEBOOKINFO));
        prpbi->cbSize = sizeof(RILPHONEBOOKINFO);

        prpbi->dwStoreLocation = g_dwCurrentPhoneBook;
        prpbi->dwTotal = dwNumbers & 0xFFFF;
        prpbi->dwUsed = dwNumbers & 0xFFFF;    // The RIL doesn't know, how many entries are really used, so WM must do the check.
        prpbi->dwParams = RIL_PARAM_PBI_STORELOCATION|RIL_PARAM_PBI_TOTAL|RIL_PARAM_PBI_USED;

        pBlob = (void*)prpbi;
        cbBlob = sizeof(RILPHONEBOOKINFO);
    }
    else if (SUCCEEDED(hr) && dwNumbers == 0) {
        hr = RIL_E_INVALIDINDEX;
    }

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

//
//
//
HRESULT RILDrv_GetPhonebookOptions(DWORD dwParam)
{
    SYNCBLOCK(g_csPhonebookCmd);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2 && !IsForbiddenSIMFile(SIM_EF_FDN)) {
        char szCmd[MAX_ATCMD_LEN];
        char szPath[MAX_PATH];
        BOOL fPath;

        g_nSIMType = pHandle->GetDevice()->GetCurrentSIMType();
        fPath = NeedSIMPath(g_nSIMType, SIM_EF_FDN, szPath, MAX_PATH);
        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d,,\"%s\"\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15, szPath);
        else
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15);
        if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO|CMDOPT_GENERATE_ERROR_CODE, APIID_GETPHONEBOOKOPTIONS, ParseGetPhonebookOptionsCRSM, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2 && IsForbiddenSIMFile(SIM_EF_FDN)) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NONE, APIID_GETPHONEBOOKOPTIONS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_NETWKTEMPFAILURE)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (NO_SIM_PB_WHILE_FD && g_dwCurrentPhoneBook == RIL_PBLOC_SIMPHONEBOOK) {
            DWORD dwStatus = 0;

            // Getting the SIM phonebook options doesn't work while fixed dialing is activated.
            if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CLCK=\"FD\",2\r", CMDOPT_NONE, APIID_GETLOCKINGSTATUS, 0, 0, 0, ParseGetLockingStatus, NULL, (LPVOID)&dwStatus)) {
                if (dwStatus == RIL_LOCKINGSTATUS_ENABLED) {
                    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETPHONEBOOKOPTIONS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_NOTFOUND))
                        hr = E_FAIL;
                    goto Error;
                }
            }
        }
        if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, "AT+CPBS?\r", CMDOPT_LOWESTPRIO|CMDOPT_GENERATE_ERROR_CODE, APIID_GETPHONEBOOKOPTIONS, ParseGetPhonebookOptions, NULL, hr, PHONEBOOKOPTIONS_RETRIES, PHONEBOOKOPTIONS_RETRIES_DELAY)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetPhonebookOptions(DWORD dwParam, const RILPHONEBOOKINFO* lpPhonebookInfo)
{
    SYNCBLOCK(g_csPhonebookCmd);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
        hr = E_NOTIMPL;
        goto Error;
    }

    // Store location must be specified
    if (!(lpPhonebookInfo->dwParams & RIL_PARAM_PBI_STORELOCATION)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Store location index must be in the valid range
    if (RIL_PBLOC_UNKNOWN == lpPhonebookInfo->dwStoreLocation) {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (NO_SIM_PB_WHILE_FD && RIL_PBLOC_SIMPHONEBOOK == lpPhonebookInfo->dwStoreLocation) {
        DWORD dwStatus = 0;

        // Selecting the SIM phonebook doesn't work while fixed dialing is activated.
        if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CLCK=\"FD\",2\r", CMDOPT_NONE, APIID_GETLOCKINGSTATUS, 0, 0, 0, ParseGetLockingStatus, NULL, (LPVOID)&dwStatus)) {
            if (dwStatus == RIL_LOCKINGSTATUS_ENABLED) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETPHONEBOOKOPTIONS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_NOTFOUND))
                    hr = E_FAIL;
                goto Error;
            }
        }
    }

    g_dwCurrentPhoneBook = lpPhonebookInfo->dwStoreLocation;
    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKSTORAGECHANGED, lpPhonebookInfo->dwStoreLocation)) {
        delete pnd;
        pnd = NULL;
    }

    if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2) {
        // Intel workaround: do nothing now, change to FD phonebook only when the
        // user requests to write to it.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETPHONEBOOKOPTIONS, NULL, pnd, hr)) {
            hr = E_FAIL;
            goto Error;
        }

    }
    else {
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBS=\"%s\"\r", g_rgszPBLocations[lpPhonebookInfo->dwStoreLocation]);
        if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO|CMDOPT_GENERATE_ERROR_CODE, APIID_SETPHONEBOOKOPTIONS, NULL, pnd, hr, (lpPhonebookInfo->dwStoreLocation == RIL_PBLOC_SIMPHONEBOOK ? 5 : 2), 2500)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    pnd = NULL;

Error:
    return hr;
}

//
// ParseExt2EntryFromCRSM()
// Parses the output of a AT+CRSM command with the content of a record from
// the EF_EXT2 SIM file. The pParam parameer holds the pointer to a RILPHONEBOOKENTRY
// structure which holds the corresponding phonebook entry to which the additional
// data from EF_EXT2 has to be added. If there is a further extesion record to this entry
// available, the index will be place in the bit 8..15 of the dwFlags member for the
// phonebook structure.
//
static HRESULT ParseExt2EntryFromCRSM(LPCSTR szRsp, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    RILPHONEBOOKENTRY *pEntry;
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    UINT uiSize = 0;

    if (pParam == NULL) goto Error;
    pEntry = (RILPHONEBOOKENTRY *)pParam;

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

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

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

    uiSize = prsr->cbSize - sizeof(RILSIMRESPONSE);
    if (uiSize != 13)         // This kind of SIM record must have exactly 13 bytes.
        goto Error;

    if ((prsr->pbResponse[0] & 2) == 2) {      // Record contains additional data.
        UINT iIdx;
        char szAddress[MAX_PATH];
        WCHAR wszAddress[MAX_PATH];

        memset(szAddress, 0, sizeof(szAddress));
        if (pEntry->raAddress.dwType == RIL_ADDRTYPE_ALPHANUM) {   // TON/NPI = 209 ? Use ASCII instead of BCD
            for (iIdx = 0; iIdx < (DWORD)min(prsr->pbResponse[1], 10); iIdx++)
                szAddress[iIdx] = prsr->pbResponse[2 + iIdx];
        }
        else {
            for (iIdx = 0; iIdx < (DWORD)min(2*prsr->pbResponse[1], 20); iIdx++) {
                if ((iIdx&1) == 0) szAddress[iIdx] = Nibble2Digit(prsr->pbResponse[2 + (iIdx>>1)] & 0xF);
                else szAddress[iIdx] = Nibble2Digit(prsr->pbResponse[2 + (iIdx>>1)] >> 4);
            }
        }

       (void)_snwprintfz(wszAddress, MAX_PATH, L"%s%S", pEntry->raAddress.wszAddress, szAddress);
       (void)wcsncpy(pEntry->raAddress.wszAddress, wszAddress, MAX_LENGTH_ADDRESS);

        pEntry->dwParams |= (prsr->pbResponse[uiSize-1] << 8);
    }

Error:
    return hr;
}

//
// ParseFDNEntryFromCRSM()
// Parses the output of an AT+CRSM command reading an entry of the EF_FDN file.
// The containing phonebook entry will be stored into a RILPHONEBOOKENTRY structrue
// passed with the pParam parameter. If there is an extesion record to this entry
// available, the index will be place in the bit 8..15 of the dwFlags member for the
// phonebook structure.
//
static HRESULT ParseFDNEntryFromCRSM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    RILPHONEBOOKENTRY *pEntry;
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    UINT uiSize = 0;
    UINT iIdx;
    char szAddress[MAX_PATH];
    WCHAR wszName[MAX_PATH];

    if (pParam == NULL) goto Error;
    pEntry = (RILPHONEBOOKENTRY *)pParam;

    pBlob = NULL;
    cbBlob = 0;

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

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

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

    uiSize = prsr->cbSize - sizeof(RILSIMRESPONSE);
    if (uiSize < 14)         // This kind of SIM record must have at least 14 bytes.
        goto Error;

    if (prsr->pbResponse[uiSize-14] == 0xFF) //Filled with FF? Entry is empty.
        goto Error;

    memset(szAddress, 0, sizeof(szAddress));

    if (prsr->pbResponse[uiSize-13] == 0xD1) {    // TON/NPI = 209: use ASCI instead of BCD
        for (iIdx = 0; iIdx < (UINT)(prsr->pbResponse[uiSize-14] - 1); iIdx++)
            szAddress[iIdx] = prsr->pbResponse[uiSize-12+ iIdx];
    }
    else {
        for (iIdx = 0; iIdx < (UINT)(prsr->pbResponse[uiSize-14]*2) - 1; iIdx++) {
            if ((iIdx&1) == 0) szAddress[iIdx] = Nibble2Digit(prsr->pbResponse[uiSize-12+ (iIdx>>1)] & 0xF);
            else szAddress[iIdx] = Nibble2Digit(prsr->pbResponse[uiSize-12+ (iIdx>>1)] >> 4);
        }
    }

    if (!StringToRILAddress(szAddress, prsr->pbResponse[uiSize-13], pEntry->raAddress))
        goto Error;

    pEntry->dwParams |= RIL_PARAM_PBE_ADDRESS;

    memset(wszName, 0, sizeof(wszName));
    // Name coding according to 3GPP 102.221 Annex A.
    if (prsr->pbResponse[0] == 0x80) {   // UCS2
        iIdx = 1;
        while (iIdx < uiSize - 15) {
            wszName[(iIdx-1)/2] = (prsr->pbResponse[iIdx] << 8) + prsr->pbResponse[iIdx+1];
            if (wszName[(iIdx-1)/2] == 0xFFFF) {    // 0xFFFF indicates end of string
                wszName[(iIdx-1)/2] = 0x0000;
                break;
            }
            iIdx++;
        }
    }
    else if (prsr->pbResponse[0] == 0x81) {   // UCS2 (fixed table)
        UINT iLen = prsr->pbResponse[1];
        UINT iOffset = (prsr->pbResponse[2] << 7);
        UINT iIdxWrite = 0;

        iIdx = 3;
        while (iIdx < uiSize - 15 && iIdxWrite < iLen) {
            if (prsr->pbResponse[iIdx] < 0x7F) wszName[iIdxWrite] = (WCHAR)iOffset + (prsr->pbResponse[iIdx] & 0x7F);
            else wszName[iIdxWrite] = prsr->pbResponse[iIdx];
            iIdx++;
            iIdxWrite++;
        }
    }
    else if (prsr->pbResponse[0] == 0x82) {   // UCS2 (fixed table with offset)
        UINT iLen = prsr->pbResponse[1];
        UINT iOffset = (prsr->pbResponse[2] << 8) + prsr->pbResponse[3];
        UINT iIdxWrite = 0;

        iIdx = 4;
        while (iIdx < uiSize - 15 && iIdxWrite < iLen) {
            if (prsr->pbResponse[iIdx] < 0x7F) wszName[iIdxWrite] = (WCHAR)iOffset + (prsr->pbResponse[iIdx] & 0x7F);
            else wszName[iIdxWrite] = prsr->pbResponse[iIdx];
            iIdx++;
            iIdxWrite++;
        }
    }
    else if (prsr->pbResponse[0] < 0x80) {    // GSM
        iIdx = 0;
        while (iIdx < uiSize - 15) {
            if (prsr->pbResponse[iIdx] == 0xFF)      // 0xFF indicates end of string
                break;
            wszName[iIdx] = prsr->pbResponse[iIdx];
            iIdx++;
        }
    }
    else {
        goto Error;
    }

    if (wcslen(wszName) > 0) {
        (void)wcsncpyz(pEntry->wszText, wszName, MAXLENGTH_PHONEBOOKTEXT);
        pEntry->dwParams |= RIL_PARAM_PBE_TEXT;
    }

    // This is not standard Microsoft: add the index to the EF_EXT2 field. This
    // will be removed, before the record is send to the OS.
    pEntry->dwParams |= (prsr->pbResponse[uiSize-1] << 8);

    pEntry->cbSize = sizeof(RILPHONEBOOKENTRY);

Error:
    return hr;
}

//
// AddExt2InfoToPBEntry()
// Read additional address bytes from the EF_EXT2 SIM file.
//
static void AddExt2InfoToPBEntry(CRilHandle *pRilHandle, RILPHONEBOOKENTRY *pEntry, UINT iExt2)
{
    char szCmd[MAX_ATCMD_LEN];
    char szPath[MAX_PATH];
    BOOL fPath;

    if (pEntry == NULL || pRilHandle == NULL) return;
    if (IsForbiddenSIMFile(SIM_EF_EXT2)) return;

    g_nSIMType = pRilHandle->GetCurrentSIMType();
    fPath = NeedSIMPath(g_nSIMType, SIM_EF_EXT2, szPath, MAX_PATH);

    // The additional bytes for the address may be spread over several records.
    // If there are no more records to read, the record number will be set to FF.
    while (iExt2 != 0xFF) {
        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28491,%d,4,13,,\"%s\"\r", iExt2, szPath);
        else
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28491,%d,4,13\r",iExt2);
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pRilHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseExt2EntryFromCRSM, NULL, (LPVOID)pEntry))
            return;

        iExt2 = pEntry->dwParams >> 8;
        pEntry->dwParams &= 0xFF;       // Remove EXT2 index
    }
}

//
// GetFDNEntriesFromSIM()
// Problem with EHS5: it is not always possible tor read the phonebook entries
// of the FDN phonebook. Solution: read the entries directly from the SIM with
// the AT+CRSM command.
//
// Parameters:
// pRilHandle [in]: pointer to the CRilHandle object
// dwStartIndex [in]: index of the first entry to be read
// dwEndIndex [in]: index of the last entry to be read, must be >= dwStartIndex.
// pdwSize [out]: if entries have been read, the number of bytes in the return value buffer
//
// Return value: a pointer of an array of RILPHONEBOOKENTRY entries. Number of bytes
//        are placed in pdwSize.
//
static RILPHONEBOOKENTRY *GetFDNEntriesFromSIM(CRilHandle *pRilHandle, DWORD dwStartIndex, DWORD dwEndIndex, DWORD *pdwSize)
{
    RILPHONEBOOKENTRY* rgrbpe = NULL;
    char szCmd[MAX_ATCMD_LEN];
    char szPath[MAX_PATH];
    BOOL fPath;
    DWORD dwIdx;
    DWORD dwFileSize = g_nEF_FDN_FileSize;
    UINT nAllocated = 0;
    UINT nUsed = 0;

    if (pdwSize == NULL || pRilHandle == NULL) return NULL;
    if (IsForbiddenSIMFile(SIM_EF_FDN)) return NULL;

    *pdwSize = 0;

    g_nSIMType = pRilHandle->GetCurrentSIMType();
    fPath = NeedSIMPath(g_nSIMType, SIM_EF_FDN, szPath, MAX_PATH);
    if (dwFileSize == 0) {    // Size of the EF_FDN file not know so far?
        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d,,\"%s\"\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15, szPath);
        else
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15);
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pRilHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimGetResponseInternal, NULL, (LPVOID)&dwFileSize) || dwFileSize == 0) {
            goto Error;
        }
        // Store file size to avaoid checking it every a FD phonebook entry is read.
        g_nEF_FDN_FileSize = dwFileSize;
    }

    for (dwIdx = dwStartIndex; dwIdx <= min(dwEndIndex, dwFileSize&0xFFFF); dwIdx++) {
        RILPHONEBOOKENTRY oEntry;

        memset(&oEntry, 0, sizeof(RILPHONEBOOKENTRY));
        oEntry.dwIndex = dwIdx;  // The index is the only parameter not filled out by ParseFDNEntryFromCRSM()
        oEntry.dwParams = RIL_PARAM_PBE_INDEX;
        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28475,%d,4,%d,,\"%s\"\r", dwIdx, dwFileSize >> 16, szPath);
        else
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28475,%d,4,%d\r", dwIdx, dwFileSize >> 16);
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pRilHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseFDNEntryFromCRSM, NULL, (LPVOID)&oEntry)) {
            goto Error;
        }

        if (oEntry.cbSize == sizeof(RILPHONEBOOKENTRY)) {
            UINT iExt2 = oEntry.dwParams >> 8;

            oEntry.dwParams &= 0xFF;       // Remove EXT2 index

            if (iExt2 > 0)
                AddExt2InfoToPBEntry(pRilHandle, &oEntry, iExt2);

            if (nUsed == nAllocated) {
                if (!AllocateOrReallocateStorage((BYTE**)&rgrbpe, sizeof(RILPHONEBOOKENTRY), nUsed, &nAllocated, MISC_ALLOC_NUMBER)) {
                    goto Error;
                }
            }
            memcpy(&rgrbpe[nUsed], &oEntry, sizeof(RILPHONEBOOKENTRY));
            nUsed++;
        }
    }

    *pdwSize = nUsed * sizeof(RILPHONEBOOKENTRY);
Error:
    return rgrbpe;
}

//
//
//
static HRESULT ParseArrayOfPhonebookEntries(LPCSTR szCmd, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    char szAddress[MAXLENGTH_ADDRESS];
    WCHAR wszText[MAXLENGTH_PHONEBOOKTEXT];
    RILPHONEBOOKENTRY* rgrbpe = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, szCmd, szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrbpe, sizeof(RILPHONEBOOKENTRY), nUsed, &nAllocated, MISC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrbpe[nUsed], 0x00, sizeof(RILPHONEBOOKENTRY));
        rgrbpe[nUsed].cbSize = sizeof(RILPHONEBOOKENTRY);

        // Parse "<index>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwIndex = nValue;
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_INDEX;

        // Parse ",<number>,<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
            !MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
            !StringToRILAddress(szAddress, (BYTE)nValue, rgrbpe[nUsed].raAddress)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_ADDRESS;

        // Parse ",<text>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszText, wszText + MAXLENGTH_PHONEBOOKTEXT)) {
            goto Continue;
        }
        (void)wcsncpyz(rgrbpe[nUsed].wszText, wszText, MAXLENGTH_PHONEBOOKTEXT);
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_TEXT;

        if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && rgrbpe[nUsed].dwIndex < MAX_FDPB_CACHE)
        {
            (void)wcsncpyz(g_wszFDPBCache[rgrbpe[nUsed].dwIndex], wszText, MAXLENGTH_PHONEBOOKTEXT);
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Skip over additional empty line in EHS5.
        if (UNNECESSARY_BLANK_LINES_IN_RESPONSE)
            (void)FindRspPostfix(szRsp, szRsp);
    }

    pBlob = (void*)rgrbpe;
    cbBlob = nUsed * sizeof(RILPHONEBOOKENTRY);

Error:
    if (FAILED(hr)) {
        delete[] (BYTE*)rgrbpe;
    }
    return hr;
}


//
//
//
static HRESULT ParseReadPhonebookEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseArrayOfPhonebookEntries("+CPBR: ", szRsp, pBlob, cbBlob);
}

//
//
//
static HRESULT ParseReadPhonebookEntriesFromArray(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    ARRAY_TO_PARSING_DATA *pParams;

    if (pParam == NULL) return E_FAIL;
    pParams = (ARRAY_TO_PARSING_DATA *)pParam;

    pBlob = pParams->pData;
    cbBlob = pParams->dwSize;
    FreeBlob(pParam);

    return S_OK;
}

//
//
//
static HRESULT ParseReadNoPhonebookEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    LPCSTR szDummy;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    // '+CME ERROR: 22' doesn't indicate a real error, it just means that there is
    // currently no entry stored in the specified place.
    if (MatchStringAnywhere(szRsp, "+CME ERROR: 22\r", szDummy))
    {
        // Since this isn't an error, we reconfigure this response to be an 'OK' instead.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
    }
    return S_OK;
}

//
//
//
HRESULT RILDrv_ReadPhonebookEntries(DWORD dwParam, DWORD dwStartIndex, DWORD dwEndIndex)
{
    SYNCBLOCK(g_csPhonebookCmd);
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
        hr = E_NOTIMPL;
        goto Error;
    }

    // Problem with Intel stacks: it is not possible to read entries from the
    // FD phonebook with the AT+CPBR command. Instead it is necessary to directly
    // read the entries from the SIM using the AT+CRSM command,
    if (NO_READ_ON_FD_WO_PIN2 && g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING) {
        RILPHONEBOOKENTRY *pEntries;
        DWORD dwSize = 0;

        // Read entries from the SIM into a cache. The function will reaturn NULL,
        // if nothing have been read.
        pEntries = GetFDNEntriesFromSIM(pHandle->GetDevice(), dwStartIndex, dwEndIndex, &dwSize);
        if (pEntries == NULL) {
            // No entries? Report empty list
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_READPHONEBOOKENTRIES, NULL, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
        else {
            ARRAY_TO_PARSING_DATA *pParams;

            // Allocate memory to hold the parameters. This memory will be freed
            // in ParseReadPhonebookEntriesFromArray().
            pParams = (ARRAY_TO_PARSING_DATA *)AllocBlob(sizeof(ARRAY_TO_PARSING_DATA));
            if (pParams == NULL) {
                FreeBlob(pEntries);
                hr = E_FAIL;
                goto Error;
            }

            pParams->pData = (LPVOID)pEntries;
            pParams->dwSize = dwSize;

            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_READPHONEBOOKENTRIES, ParseReadPhonebookEntriesFromArray, NULL, hr, NULL, (LPVOID)pParams)) {
                FreeBlob(pEntries);
                FreeBlob(pParams);
                hr = E_FAIL;
                goto Error;
            }
        }
    }
    else {
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBR=%u,%u\r", dwStartIndex, dwEndIndex);
        if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO|CMDOPT_GENERATE_ERROR_CODE, APIID_READPHONEBOOKENTRIES, ParseReadPhonebookEntries, NULL, hr, ParseReadNoPhonebookEntries)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}


#ifdef RILDRV_CE7
//
// Returns the number of entries in the response to AT+CPBR=? and returns the
// value to *pParam.
//
static HRESULT ParseGetPBSizeInternal(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;


    // Parse "<prefix>+CPBR: "
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPBR: ", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "(1-"
    if (!MatchStringBeginning(szRsp, "(1-", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "<num entries>", not a required value
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

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

        *pdw = (DWORD)nValue;
    }

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

Error:
    return hr;
}

//
// Parses through the response of an AT+CPBR=1,<max> command and
// returns the first free index in *pParam.
//
static HRESULT ParseFindNextFreeIndex(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    HRESULT hr = S_OK;
    UINT uiFreeIndex = 1;
    UINT uiFound = 0;

    pBlob = NULL;
    cbBlob = 0;

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

    while (MatchStringBeginning(szRsp, "+CPBR: ", szRsp))
    {
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (uiFound == 0) {
            if (uiFreeIndex != nValue) {
                uiFound = uiFreeIndex;

            }
            else {
                uiFreeIndex ++;
            }
        }
        if (!FindRspPostfix(szRsp, szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    if (uiFound == 0)
        uiFound = uiFreeIndex;

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

        *pdw = (DWORD)uiFound;
    }
Error:
    return hr;
}
#endif

//
// RepeatPIN2ifNedded()
// Bug in PHS8 Rel3: the module might forget the PIN2 when writing to the FD phonebook.
// Unfortunately the AT+CPIN command doesn't allow to reenter the PIN2, so it is
// necessary to repeatedly enter the PIN2 using the AT+CPBS command.
//
static void RepeatPIN2ifNedded(CRilHandle *pHandle)
{
    // Check if the PIN2 workaround is needed here.
    if (NEED_CPBS_BEFORE_CPBW_ON_FD && g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING) {
        char szPIN2[MAXLEN_PIN2+1];
        char szCmd[MAX_ATCMD_LEN];

        // Check, if PIN2 has already been entered.
        if (FetchPIN2Secure(szPIN2, MAXLEN_PIN2)) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBS=\"FD\",\"%s\"\r", szPIN2);
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_SETPHONEBOOKOPTIONS, 0, 0, 0);
        }
    }
}

//
//
//
HRESULT RILDrv_WritePhonebookEntry(DWORD dwParam, const RILPHONEBOOKENTRY* lpEntry)
{
    SYNCBLOCK(g_csPhonebookCmd);
    CNotificationData* pnd = NULL;
    BYTE bTypeOfAddress;
    char szAddress[MAXLENGTH_ADDRESS];
    char szCmd[MISC_CMDBUF_LENGTH_PB];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    DWORD dwActualIndex;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (AVOID_CPBW_WHILE_VOICE_CALL && g_iNrCallInfo > 0 && pHandle->GetDevice()->VoiceCallWithoutNumber()) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_WRITEPHONEBOOKENTRY, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_SIMBUSY)) {
            hr = E_FAIL;
        }
        goto Error;
    }

    if (NO_READ_ON_FD_WO_PIN2 && g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING) {
        HRESULT hrError = S_OK;

        // The FD phonebook is suposed to be selected, but actually it might not.
        // For this reason the RIL must select it now before writing to the phone
        // book.
        // If the FD phonebook is selected for the first time, the command will
        // fail with a "PIN2 requiered" error (or "PUK2 required").
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPBS=\"FD\"\r", CMDOPT_GENERATE_ERROR_CODE, APIID_SETPHONEBOOKOPTIONS, 0,0,0, NULL, ParseCPINError, (LPVOID)&hrError);

        // If there has been an error code, report it directly to the OS without
        // trying to actually write a phonebook entry.
        if (hrError != S_OK) {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_WRITEPHONEBOOKENTRY, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)hrError))
                hr = E_FAIL;
            goto Error;
        }
    }

    if (!(lpEntry->dwParams & RIL_PARAM_PBE_INDEX) || !(lpEntry->dwParams & RIL_PARAM_PBE_ADDRESS)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine address and type-of-address byte
    hr = RILAddressToString(lpEntry->raAddress, szAddress, MAXLENGTH_ADDRESS, bTypeOfAddress);
    if (FAILED(hr)) {
        goto Error;
    }

    dwActualIndex = lpEntry->dwIndex;
    if (RIL_PBINDEX_FIRSTAVAILABLE == lpEntry->dwIndex) {
#ifdef RILDRV_CE7
        // The Cellular test under CE7 requires the RIL_NOTIFY_PHONEBOOKENTRYSTORED
        // notification to contain the actual entry number the new entry is stored,
        // when the RIL_PBINDEX_FIRSTAVAILABLE option is used.
        // For this reason the RIL has to find the first available index on its own
        // and stores the new phonebook entry there.
        char szReadCmd[MAX_ATCMD_LEN];
        DWORD dwPBSize = 0;

        // Query numbers of phonebook entries the current phonebook can hold max.
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPBR=?\r", CMDOPT_NONE, APIID_GETDEVCAPS, 0, 0, 0, ParseGetPBSizeInternal, NULL, (LPVOID)&dwPBSize)) {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETDEVCAPS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_MEMORYFULL))
                hr = E_FAIL;
            goto Error;
        }
        // Read all entries to find the first free entry.
        (void)_snprintfz(szReadCmd, MAX_ATCMD_LEN, "AT+CPBR=1,%d\r", dwPBSize);
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szReadCmd, CMDOPT_NONE, APIID_READPHONEBOOKENTRIES, 0, 0, 0, ParseFindNextFreeIndex, NULL, (LPVOID)&dwActualIndex)) {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETDEVCAPS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_MEMORYFULL))
                hr = E_FAIL;
            goto Error;
        }
        // If there has been no free entry found, return the "memory full"
        // error message.
        if (dwActualIndex == 0 || dwActualIndex == RIL_PBINDEX_FIRSTAVAILABLE || dwActualIndex > dwPBSize) {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETDEVCAPS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_MEMORYFULL))
                hr = E_FAIL;
            goto Error;
        }

        // Explicitly write the new phonebook entry to the index found above.
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=%d,\"%s\",%u", dwActualIndex, szAddress, bTypeOfAddress);
#else
        // In difference to the CE7 Cellular test, the WM6 Radiometrics test requires
        // the RIL_NOTIFY_PHONEBOOKENTRYSTORED to contain the constant
        // RIL_PBINDEX_FIRSTAVAILABLE, if the entry has been written to the
        // first available phonebook entry.
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=,\"%s\",%u", szAddress, bTypeOfAddress);
#endif
    } else {
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=%u,\"%s\",%u", lpEntry->dwIndex, szAddress, bTypeOfAddress);
    }
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    // Add text, if supplied
    if (lpEntry->dwParams & RIL_PARAM_PBE_TEXT) {
        (void)strncpyz(szWalk, ",", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        char szEncodedText[MAX_ATCMD_LEN];

        (void)UCS2ToGSMHex((WCHAR *)lpEntry->wszText, wcslen(lpEntry->wszText), szEncodedText, sizeof(szEncodedText));
        (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        (void)strncpyz(szWalk, szEncodedText, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && lpEntry->dwIndex < MAX_FDPB_CACHE && lpEntry->dwIndex != RIL_PBINDEX_FIRSTAVAILABLE)
        {
            (void)wcsncpyz(g_wszFDPBCache[lpEntry->dwIndex], lpEntry->wszText, MAXLENGTH_PHONEBOOKTEXT);
        }
    }
    else
    {
        if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && lpEntry->dwIndex < MAX_FDPB_CACHE && lpEntry->dwIndex != RIL_PBINDEX_FIRSTAVAILABLE)
        {
            (void)strncpyz(szWalk, ",", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

            char szEncodedText[MAX_ATCMD_LEN];

            (void)UCS2ToGSMHex((WCHAR *)g_wszFDPBCache[lpEntry->dwIndex], wcslen(g_wszFDPBCache[lpEntry->dwIndex]), szEncodedText, sizeof(szEncodedText));
            (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            (void)strncpyz(szWalk, szEncodedText, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }
    (void)strncpyz(szWalk, "\r", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30

    // Some modules might need a special handling for FD phonebooks.
    RepeatPIN2ifNedded(pHandle->GetDevice());

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKENTRYSTORED, dwActualIndex)) {
        delete pnd;
        pnd = NULL;
    }
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO|CMDOPT_GENERATE_ERROR_CODE, APIID_WRITEPHONEBOOKENTRY, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_DeletePhonebookEntry(DWORD dwParam, DWORD dwIndex)
{
    SYNCBLOCK(g_csPhonebookCmd);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (NO_READ_ON_FD_WO_PIN2 && g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING) {
        HRESULT hrError = S_OK;

        // The FD phonebook is suposed to be selected, but actually it might not.
        // For this reason the RIL must select it now before deleting in the phone
        // book.
        // If the FD phonebook is selected for the first time, the command will
        // fail with a "PIN2 requiered" error (or "PUK2 required").
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPBS=\"FD\"\r", CMDOPT_GENERATE_ERROR_CODE, APIID_SETPHONEBOOKOPTIONS, 0,0,0, NULL, ParseCPINError, (LPVOID)&hrError);

        // If there has been an error code, report it directly to the OS without
        // trying to actually write a phonebook entry.
        if (hrError != S_OK) {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_WRITEPHONEBOOKENTRY, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)hrError))
                hr = E_FAIL;
            goto Error;
        }
    }

    if (AVOID_CPBW_WHILE_VOICE_CALL && g_iNrCallInfo > 0 && pHandle->GetDevice()->VoiceCallWithoutNumber()) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DELETEPHONEBOOKENTRY, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_SIMBUSY)) {
            hr = E_FAIL;
        }
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKENTRYDELETED, dwIndex)) {
        delete pnd;
        pnd = NULL;
    }
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBW=%u\r", dwIndex);

    if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && dwIndex < MAX_FDPB_CACHE)
        g_wszFDPBCache[dwIndex][0] = 0;

    // Some modules might need a special handling for FD phonebooks.
    RepeatPIN2ifNedded(pHandle->GetDevice());

    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO|CMDOPT_GENERATE_ERROR_CODE, APIID_DELETEPHONEBOOKENTRY, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}


