//+---------------------------------------------------------------------------- // // File: phbk.cpp // // Module: CMPBK32.DLL // // Synopsis: Implementation of CPhoneBook // // Copyright (c) 1998-1999 Microsoft Corporation // // Author: quintinb created header 08/17/99 // //+---------------------------------------------------------------------------- // ############################################################################ // Phone book APIs #include "cmmaster.h" const TCHAR* const c_pszInfDefault = TEXT("INF_DEFAULT"); const TCHAR* const c_pszInfSuffix = TEXT(".CMS"); //#define ReadVerifyPhoneBookDW(x) CMASSERTMSG(ReadPhoneBookDW(&(x),pcCSVFile),"Invalid DWORD in phone book"); #define ReadVerifyPhoneBookDW(x) if (!ReadPhoneBookDW(&(x),pcCSVFile)) \ { CMASSERTMSG(0,"Invalid DWORD in phone book"); \ goto DataError; } #define ReadVerifyPhoneBookW(x) if (!ReadPhoneBookW(&(x),pcCSVFile)) \ { CMASSERTMSG(0,"Invalid WORD in phone book"); \ goto DataError; } #define ReadVerifyPhoneBookB(x) if (!ReadPhoneBookB(&(x),pcCSVFile)) \ { CMASSERTMSG(0,"Invalid BYTE in phone book"); \ goto DataError; } #define ReadVerifyPhoneBookSZ(x,y) if (!ReadPhoneBookSZ(&x[0],y+sizeof('\0'),pcCSVFile)) \ { CMASSERTMSG(0,"Invalid STRING in phone book"); \ goto DataError; } #define CHANGE_BUFFER_SIZE 50 #define ERROR_USERBACK 32766 #define ERROR_USERCANCEL 32767 // ############################################################################ void CPhoneBook::EnumNumbersByCountry(DWORD dwCountryID, PPBFS pFilter, CB_PHONEBOOK pfnNumber, DWORD_PTR dwParam) { MYDBG(("CPhoneBook::EnumNumbersByCountry")); PACCESSENTRY pAELast, pAE = NULL; PIDLOOKUPELEMENT pIDLookUp; IDLOOKUPELEMENT LookUpTarget; LookUpTarget.dwID = dwCountryID; pIDLookUp = NULL; pIDLookUp = (PIDLOOKUPELEMENT)CmBSearch(&LookUpTarget,m_rgIDLookUp, (size_t) m_pLineCountryList->dwNumCountries,sizeof(IDLOOKUPELEMENT),CompareIDLookUpElements); if (pIDLookUp) pAE = IdxToPAE(pIDLookUp->iFirstAE); // Fill the list for whatever AE's we found // if (pAE) { pAELast = &(m_rgPhoneBookEntry[m_cPhoneBookEntries - 1]); while (pAELast >= pAE) { if (pAE->dwCountryID == dwCountryID && pAE->wStateID == 0) { if (PhoneBookMatchFilter(pFilter,pAE->fType)) { pfnNumber((unsigned int) (pAE - m_rgPhoneBookEntry),dwParam); } } pAE++; } // Select the first item // } } // ############################################################################ void CPhoneBook::EnumNumbersByCountry(DWORD dwCountryID, DWORD dwMask, DWORD fType, CB_PHONEBOOK pfnNumber, DWORD_PTR dwParam) { MYDBG(("CPhoneBook::EnumNumbersByCountry")); PhoneBookFilterStruct sFilter = {1,{{dwMask,fType}}}; EnumNumbersByCountry(dwCountryID,&sFilter,pfnNumber,dwParam); } // ############################################################################ BOOL CPhoneBook::FHasPhoneType(PPBFS pFilter) { MYDBG(("CPhoneBook::FHasPhoneType")); PACCESSENTRY pAELast, pAE = NULL; pAE = &(m_rgPhoneBookEntry[0]); // pAE points to the first phone book entry // // Examine each entry until we find a match or exhaust the entries // if (pAE) { pAELast = &(m_rgPhoneBookEntry[m_cPhoneBookEntries - 1]); while (pAELast >= pAE) { // // See if this pop passes the specified filter // if (PhoneBookMatchFilter(pFilter, pAE->fType)) { return TRUE; } pAE++; } } return FALSE; } // ############################################################################ void CPhoneBook::EnumNumbersByRegion(unsigned int nRegion, DWORD dwCountryID, PPBFS pFilter, CB_PHONEBOOK pfnNumber, DWORD_PTR dwParam) { MYDBG(("CPhoneBook::EnumNumbersByRegion")); PACCESSENTRY pAELast, pAE = NULL; pAE = &m_rgPhoneBookEntry[0]; // pAE points to the first phone book entry // Fill the list for whatever AE's we found if (pAE) { pAELast = &(m_rgPhoneBookEntry[m_cPhoneBookEntries - 1]); while (pAELast >= pAE) { // choose phone number of the same region OR with region ID = 0(which means ALL regions) if (pAE->dwCountryID == dwCountryID && ((pAE->wStateID == nRegion+1) || (pAE->wStateID == 0))) { if (PhoneBookMatchFilter(pFilter,pAE->fType)) pfnNumber((unsigned int) (pAE - m_rgPhoneBookEntry), dwParam); } pAE++; } // Select the first item // } } // ############################################################################ void CPhoneBook::EnumNumbersByRegion(unsigned int nRegion, DWORD dwCountryID, DWORD dwMask, DWORD fType, CB_PHONEBOOK pfnNumber, DWORD_PTR dwParam) { MYDBG(("CPhoneBook::EnumNumbersByRegion")); PhoneBookFilterStruct sFilter = {1,{{dwMask,fType}}}; EnumNumbersByRegion(nRegion,dwCountryID,&sFilter,pfnNumber,dwParam); } // ############################################################################ void CPhoneBook::EnumRegions(DWORD dwCountryID, PPBFS pFilter, CB_PHONEBOOK pfnRegion, DWORD_PTR dwParam) { unsigned int idx; MYDBG(("CPhoneBook::EnumRegions")); for (idx=0;idx= pAE) { if (pAE->dwCountryID == dwCountryID && pAE->wStateID == idx+1) { if (PhoneBookMatchFilter(pFilter,pAE->fType)) goto AddRegion; } pAE++; } // while continue; // start the next 'for' loop AddRegion: pfnRegion(idx,dwParam); } } // ############################################################################ void CPhoneBook::EnumRegions(DWORD dwCountryID, DWORD dwMask, DWORD fType, CB_PHONEBOOK pfnRegion, DWORD_PTR dwParam) { MYDBG(("CPhoneBook::EnumRegions")); PhoneBookFilterStruct sFilter = {1,{{dwMask,fType}}}; EnumRegions(dwCountryID,&sFilter,pfnRegion,dwParam); } // ############################################################################ void CPhoneBook::EnumCountries(PPBFS pFilter, CB_PHONEBOOK pfnCountry, DWORD_PTR dwParam) { unsigned int idx; MYDBG(("CPhoneBook::EnumCountries")); for (idx=0;idxdwNumCountries;idx++) { if (FHasPhoneNumbers(m_rgNameLookUp[idx].pLCE->dwCountryID,pFilter)) { pfnCountry(idx,dwParam); } } } // ############################################################################ void CPhoneBook::EnumCountries(DWORD dwMask, DWORD fType, CB_PHONEBOOK pfnCountry, DWORD_PTR dwParam) { MYDBG(("CPhoneBook::EnumCountries")); PhoneBookFilterStruct sFilter = {1,{{dwMask,fType}}}; EnumCountries(&sFilter,pfnCountry,dwParam); } // ############################################################################ BOOL CPhoneBook::FHasPhoneNumbers(DWORD dwCountryID, PPBFS pFilter) { PIDLOOKUPELEMENT pIDLookUp; IDLOOKUPELEMENT LookUpTarget; PACCESSENTRY pAE = NULL, pAELast = NULL; DWORD dwTmpCountryID; LookUpTarget.dwID = dwCountryID; pIDLookUp = NULL; pIDLookUp = (PIDLOOKUPELEMENT)CmBSearch(&LookUpTarget,m_rgIDLookUp, (size_t) m_pLineCountryList->dwNumCountries,sizeof(IDLOOKUPELEMENT),CompareIDLookUpElements); if (!pIDLookUp) return FALSE; // no such country pAE = IdxToPAE(pIDLookUp->iFirstAE); if (!pAE) return FALSE; // no phone numbers at all dwTmpCountryID = pAE->dwCountryID; pAELast = &(m_rgPhoneBookEntry[m_cPhoneBookEntries - 1]); while (pAELast >= pAE) { if (pAE->dwCountryID == dwTmpCountryID) { if (PhoneBookMatchFilter(pFilter,pAE->fType)) return TRUE; } pAE++; } return FALSE; // no phone numbers of the right type // return ((BOOL)(pIDLookUp->pFirstAE)); } // ############################################################################ BOOL CPhoneBook::FHasPhoneNumbers(DWORD dwCountryID, DWORD dwMask, DWORD fType) { MYDBG(("CPhoneBook::FHasPhoneNumbers")); PhoneBookFilterStruct sFilter = {1,{{dwMask,fType}}}; return FHasPhoneNumbers(dwCountryID,&sFilter); } // ############################################################################ CPhoneBook::CPhoneBook() { m_rgPhoneBookEntry = NULL; m_cPhoneBookEntries =0; m_rgLineCountryEntry=NULL; m_rgState=NULL; m_cStates=0; m_rgIDLookUp = NULL; m_rgNameLookUp = NULL; m_pLineCountryList = NULL; MYDBG(("CPhoneBook::CPhoneBook")); ZeroMemory(&m_szINFFile[0],MAX_PATH); ZeroMemory(&m_szPhoneBook[0],MAX_PATH); } // ############################################################################ CPhoneBook::~CPhoneBook() { MYDBG(("CPhoneBook::~CPhoneBook")); CmFree(m_rgPhoneBookEntry); m_rgPhoneBookEntry = NULL; CmFree(m_pLineCountryList); m_pLineCountryList = NULL; CmFree(m_rgIDLookUp); m_rgIDLookUp = NULL; CmFree(m_rgNameLookUp); m_rgNameLookUp = NULL; CmFree(m_rgState); m_rgState = NULL; } // ############################################################################ BOOL CPhoneBook::ReadPhoneBookDW(DWORD *pdw, CCSVFile *pcCSVFile) { char szTempBuffer[TEMP_BUFFER_LENGTH]; if (!pcCSVFile->ReadToken(szTempBuffer,TEMP_BUFFER_LENGTH)) return FALSE; return (FSz2Dw(szTempBuffer,pdw)); } // ############################################################################ BOOL CPhoneBook::ReadPhoneBookW(WORD *pw, CCSVFile *pcCSVFile) { char szTempBuffer[TEMP_BUFFER_LENGTH]; if (!pcCSVFile->ReadToken(szTempBuffer,TEMP_BUFFER_LENGTH)) return FALSE; return (FSz2W(szTempBuffer,pw)); } // ############################################################################ BOOL CPhoneBook::ReadPhoneBookB(BYTE *pb, CCSVFile *pcCSVFile) { char szTempBuffer[TEMP_BUFFER_LENGTH]; if (!pcCSVFile->ReadToken(szTempBuffer,TEMP_BUFFER_LENGTH)) return FALSE; return (FSz2B(szTempBuffer,pb)); } // ############################################################################ BOOL CPhoneBook::ReadPhoneBookSZ(LPSTR psz, DWORD dwSize, CCSVFile *pcCSVFile) { if (!pcCSVFile->ReadToken(psz,dwSize)) return FALSE; return TRUE; } // ############################################################################ BOOL CPhoneBook::ReadPhoneBookNL(CCSVFile *pcCSVFile) { if (!pcCSVFile->ClearNewLines()) return FALSE; return TRUE; } // // Note: the new fUnicode parameter has been added so that Whistler and newer releases // take advantage of the Unicode TAPI functions where available, so that MUI works. // static LONG PBlineGetCountry(DWORD dwCountryID, DWORD dwAPIVersion, LPLINECOUNTRYLIST lpLineCountryList, BOOL fUnicode) { HINSTANCE hInst; LONG lRes; // Try to load the TAPI DLL hInst = LoadLibrary("tapi32"); if (!hInst) { return (LINEERR_NOMEM); } // Get the proc address for GetCountry LONG (WINAPI *pfn)(DWORD,DWORD,LPLINECOUNTRYLIST); pfn = (LONG (WINAPI *)(DWORD,DWORD,LPLINECOUNTRYLIST)) GetProcAddress(hInst, fUnicode ? "lineGetCountryW" : "lineGetCountryA"); if (!pfn) { FreeLibrary(hInst); return (LINEERR_NOMEM); } // Get the country list lRes = pfn(dwCountryID,dwAPIVersion,lpLineCountryList); FreeLibrary(hInst); return (lRes); } // ############################################################################ HRESULT CPhoneBook::Init(LPCSTR pszISPCode) { char szTempBuffer[TEMP_BUFFER_LENGTH]; LPLINECOUNTRYLIST pLineCountryTemp = NULL; HRESULT hr = ERROR_NOT_ENOUGH_MEMORY; DWORD dwLastState = 0; DWORD dwLastCountry = 0; DWORD dwNumAllocated; PACCESSENTRY pCurAccessEntry; LPLINECOUNTRYENTRY pLCETemp; DWORD idx; LPTSTR pszTemp; LPTSTR pszCmpDir = NULL; CCSVFile *pcCSVFile=NULL; PSTATE ps,psLast; //faster to use pointers. DWORD dwAlloc = 0; PACCESSENTRY pTempAccessEntry = NULL; MYDBG(("CPhoneBook::Init")); // Get TAPI country list m_pLineCountryList = (LPLINECOUNTRYLIST)CmMalloc(sizeof(LINECOUNTRYLIST)); if (!m_pLineCountryList) { goto InitExit; } m_pLineCountryList->dwTotalSize = sizeof(LINECOUNTRYLIST); // // Note: For Whistler and newer releases, we take advantage of the Unicode TAPI // functions where available, so that MUI works. Hence the final parameter // to PBlineGetCountry, and the two different QSorts below. // // get ALL country information idx = PBlineGetCountry(0,0x10003, m_pLineCountryList, OS_NT51); if (idx && idx != LINEERR_STRUCTURETOOSMALL) { goto InitExit; } MYDBGASSERT(m_pLineCountryList->dwNeededSize); // reallocate memory for country list pLineCountryTemp = (LPLINECOUNTRYLIST)CmMalloc(m_pLineCountryList->dwNeededSize); if (!pLineCountryTemp) { goto InitExit; } pLineCountryTemp->dwTotalSize = m_pLineCountryList->dwNeededSize; CmFree(m_pLineCountryList); m_pLineCountryList = pLineCountryTemp; pLineCountryTemp = NULL; if (PBlineGetCountry(0,0x10003, m_pLineCountryList, OS_NT51)) { goto InitExit; } // Load Look Up arrays // keyword: country ID, // keyvalue: pointer to the country entry in m_pLineCountryList // #ifdef DEBUG m_rgIDLookUp = (IDLOOKUPELEMENT*)CmMalloc(sizeof(IDLOOKUPELEMENT)*m_pLineCountryList->dwNumCountries+5); #else m_rgIDLookUp = (IDLOOKUPELEMENT*)CmMalloc(sizeof(IDLOOKUPELEMENT)*m_pLineCountryList->dwNumCountries); #endif if (!m_rgIDLookUp) { goto InitExit; } // pLCETemp points to the first country information entry pLCETemp = (LPLINECOUNTRYENTRY)((DWORD_PTR) m_pLineCountryList + m_pLineCountryList->dwCountryListOffset); for (idx=0;idxdwNumCountries;idx++) { m_rgIDLookUp[idx].dwID = pLCETemp[idx].dwCountryID; m_rgIDLookUp[idx].pLCE = &pLCETemp[idx]; } // sort the country lines CmQSort(m_rgIDLookUp, (size_t) m_pLineCountryList->dwNumCountries,sizeof(IDLOOKUPELEMENT), CompareIDLookUpElements); // m_rgNameLookUp: look-up list for country name // keyword: country name // keyvalue: pointer to the country entry in m_pLineCountryList m_rgNameLookUp = (CNTRYNAMELOOKUPELEMENT*)CmMalloc(sizeof(CNTRYNAMELOOKUPELEMENT) * m_pLineCountryList->dwNumCountries); if (!m_rgNameLookUp) { goto InitExit; } for (idx=0;idxdwNumCountries;idx++) { m_rgNameLookUp[idx].psCountryName = (LPSTR)((DWORD_PTR)m_pLineCountryList + (DWORD)pLCETemp[idx].dwCountryNameOffset); m_rgNameLookUp[idx].dwNameSize = pLCETemp[idx].dwCountryNameSize; m_rgNameLookUp[idx].pLCE = &pLCETemp[idx]; } // sort the country names if (OS_NT51) { CmQSort(m_rgNameLookUp,(size_t) m_pLineCountryList->dwNumCountries,sizeof(CNTRYNAMELOOKUPELEMENTW), CompareCntryNameLookUpElementsW); } else { CmQSort(m_rgNameLookUp,(size_t) m_pLineCountryList->dwNumCountries,sizeof(CNTRYNAMELOOKUPELEMENT), CompareCntryNameLookUpElementsA); } // // Locate ISP's INF file (aka .CMS) // if (!SearchPath(NULL, (LPCTSTR) pszISPCode, c_pszInfSuffix, MAX_PATH, m_szINFFile, &pszTemp)) { wsprintf(szTempBuffer,"Can not find:%s%s (%d)",pszISPCode,c_pszInfSuffix,GetLastError()); CMASSERTMSG(0,szTempBuffer); hr = ERROR_FILE_NOT_FOUND; goto InitExit; } // Load Region file, get region file name char szStateFile[sizeof(szTempBuffer)/sizeof(szTempBuffer[0])]; GetPrivateProfileString(c_pszCmSectionIsp, c_pszCmEntryIspRegionFile, NULL, szStateFile, sizeof(szStateFile)-1, m_szINFFile); // // Can't assume current directory, construct path to PBK directory // pszCmpDir = GetBaseDirFromCms(m_szINFFile); // // Look for the .PBR file, using CMP dir as base path for search // if (!SearchPath(pszCmpDir, szStateFile, NULL, TEMP_BUFFER_LENGTH, szTempBuffer, &pszTemp)) { // CMASSERTMSG(0,"STATE.ICW not found"); CMASSERTMSG(0,"region file not found"); hr = ERROR_FILE_NOT_FOUND; goto InitExit; } // open region file pcCSVFile = new CCSVFile; if (!pcCSVFile) { goto InitExit; } if (!pcCSVFile->Open(szTempBuffer)) { // CMASSERTMSG(0,"Can not open STATE.ICW"); CMASSERTMSG(0,"Can not open region file"); delete pcCSVFile; pcCSVFile = NULL; goto InitExit; } // first token in region file is the number of regions if (!pcCSVFile->ClearNewLines() || !pcCSVFile->ReadToken(szTempBuffer,TEMP_BUFFER_LENGTH)) { goto InitExit; } if (!FSz2Dw(szTempBuffer,&m_cStates)) { // CMASSERTMSG(0,"STATE.ICW count is invalid"); CMASSERTMSG(0,"region count is invalid"); goto InitExit; } // Now read in all the regions if there are any if (0 != m_cStates) { m_rgState = (PSTATE)CmMalloc(sizeof(STATE)*m_cStates); if (!m_rgState) { goto InitExit; } for (ps = m_rgState, psLast = &m_rgState[m_cStates - 1]; ps <= psLast;++ps) { if (pcCSVFile->ClearNewLines()) { pcCSVFile->ReadToken(ps->szStateName,cbStateName); } } } pcCSVFile->Close(); // load Phone Book Name if (!GetPrivateProfileString(c_pszCmSectionIsp, c_pszCmEntryIspPbFile,c_pszInfDefault, szTempBuffer,TEMP_BUFFER_LENGTH,m_szINFFile)) { CMASSERTMSG(0,"PhoneBookFile not specified in INF file"); hr = ERROR_FILE_NOT_FOUND; goto InitExit; } #ifdef DEBUG if (!lstrcmp(szTempBuffer,c_pszInfDefault)) { wsprintf(szTempBuffer, "%s value not found in ISP file", c_pszCmEntryIspPbFile); CMASSERTMSG(0,szTempBuffer); } #endif // // Look for the .PBK file, using CMP dir as base path for search // if (!SearchPath(pszCmpDir,szTempBuffer,NULL,MAX_PATH,m_szPhoneBook,&pszTemp)) { CMASSERTMSG(0,"ISP phone book not found"); hr = ERROR_FILE_NOT_FOUND; goto InitExit; } // read in phone book entries if (!pcCSVFile->Open(m_szPhoneBook)) { CMASSERTMSG(0,"Can not open phone book"); hr = GetLastError(); goto InitExit; } dwNumAllocated = 0; do { MYDBGASSERT (dwNumAllocated >= m_cPhoneBookEntries); if (m_rgPhoneBookEntry) { // If we already have an array, make sure its big enough if (dwNumAllocated == m_cPhoneBookEntries) { // We're maxed out, allocate some more memory dwNumAllocated += PHONE_ENTRY_ALLOC_SIZE; dwAlloc = (DWORD) dwNumAllocated * sizeof(ACCESSENTRY); MYDBG(("PhoneBook::Init - Grow ReAlloc = %lu",dwAlloc)); // Realloc pTempAccessEntry = (PACCESSENTRY)CmRealloc(m_rgPhoneBookEntry, dwAlloc); if (!pTempAccessEntry) { MYDBG(("PhoneBook::Init - Grow ReAlloc of %lu failed", dwAlloc)); goto InitExit; } m_rgPhoneBookEntry = pTempAccessEntry; pTempAccessEntry = NULL; MYDBG(("Grow phone book to %d entries",dwNumAllocated)); pCurAccessEntry = m_rgPhoneBookEntry + m_cPhoneBookEntries; } } else { // Initialization for the first time through DWORD dwSize = (DWORD) sizeof(ACCESSENTRY); dwAlloc = (DWORD) dwSize * PHONE_ENTRY_ALLOC_SIZE; MYDBG(("PhoneBook::Init - sizeof(ACCESSENTRY) = %lu",dwSize)); MYDBG(("PhoneBook::Init - PHONE_ENTRY_ALLOC_SIZE = %d",PHONE_ENTRY_ALLOC_SIZE)); MYDBG(("PhoneBook::Init - Initial Alloc = %lu",dwAlloc)); // Allocate intial array of PHONE_ENTRY_ALLOC_SIZE items m_rgPhoneBookEntry = (PACCESSENTRY)CmMalloc(dwAlloc); if (!m_rgPhoneBookEntry) { MYDBG(("PhoneBook::Init - Initial Alloc of %lu failed",dwAlloc)); goto InitExit; } dwNumAllocated = PHONE_ENTRY_ALLOC_SIZE; pCurAccessEntry = m_rgPhoneBookEntry; } // Read a line from the phonebook hr = ReadOneLine(pCurAccessEntry,pcCSVFile); if (hr == ERROR_NO_MORE_ITEMS) { break; } else if (hr != ERROR_SUCCESS) { MYDBG(("PhoneBook::Init - ReadOneLine failed")); goto InitExit; } hr = ERROR_NOT_ENOUGH_MEMORY; // check the first index pointer to prevent it from being overwritten // by the second appearance that's scattered around somewhere else -- added by byao if (pCurAccessEntry->dwCountryID != dwLastCountry) { PIDLOOKUPELEMENT pIDLookUpElement; // NOTE: Not sure about the first parameter here. pIDLookUpElement = (PIDLOOKUPELEMENT)CmBSearch(&pCurAccessEntry->dwCountryID, m_rgIDLookUp,(size_t) m_pLineCountryList->dwNumCountries,sizeof(IDLOOKUPELEMENT), CompareIDLookUpElements); if (!pIDLookUpElement) { // bad country ID, but we can't assert here MYDBG(("Bad country ID in phone book %d\n",pCurAccessEntry->dwCountryID)); continue; } else { // for a given country ID this is the first phone number // don't overwrite existing index if (!pIDLookUpElement->iFirstAE) { pIDLookUpElement->iFirstAE = PAEToIdx(pCurAccessEntry); dwLastCountry = pCurAccessEntry->dwCountryID; } } } // Check to see if this is the first phone number for a given state // the code has been changed accordingly if (pCurAccessEntry->wStateID && (pCurAccessEntry->wStateID != dwLastState)) { idx = pCurAccessEntry->wStateID - 1; // // don't overwrite existing index // if ((idx < m_cStates) && !m_rgState[idx].iFirst) { m_rgState[idx].dwCountryID = pCurAccessEntry->dwCountryID; m_rgState[idx].iFirst = PAEToIdx(pCurAccessEntry); } dwLastState = pCurAccessEntry->wStateID; } pCurAccessEntry++; m_cPhoneBookEntries++; } while (TRUE); MYDBG(("PhoneBook::Init - %lu Entries read",m_cPhoneBookEntries)); if (m_cPhoneBookEntries == 0) { // // Phone book is empty // goto InitExit; } // Trim the phone book for unused memory dwAlloc = m_cPhoneBookEntries * sizeof(ACCESSENTRY); MYDBG(("PhoneBook::Init - Trim ReAlloc = %lu",dwAlloc)); MYDBGASSERT(m_cPhoneBookEntries); // Realloc pTempAccessEntry = (PACCESSENTRY)CmRealloc(m_rgPhoneBookEntry, dwAlloc); MYDBGASSERT(pTempAccessEntry); if (!pTempAccessEntry) { MYDBG(("PhoneBook::Init - Trim ReAlloc of %lu failed",dwAlloc)); goto InitExit; } m_rgPhoneBookEntry = pTempAccessEntry; pTempAccessEntry = NULL; hr = ERROR_SUCCESS; // Exit InitExit: // If something failed release everything if (hr != ERROR_SUCCESS) { CmFree(m_pLineCountryList); m_pLineCountryList = NULL; CmFree(m_rgPhoneBookEntry); m_rgPhoneBookEntry = NULL; m_cPhoneBookEntries = 0 ; CmFree(m_rgIDLookUp); m_rgIDLookUp=NULL; CmFree(m_rgNameLookUp); m_rgNameLookUp=NULL; CmFree(m_rgState); m_rgState = NULL; m_cStates = 0; } if (pcCSVFile) { pcCSVFile->Close(); delete pcCSVFile; } if (pszCmpDir) { CmFree(pszCmpDir); } return hr; } // ############################################################################ HRESULT CPhoneBook::Merge(LPCSTR pszChangeFile) { char szTempBuffer[TEMP_BUFFER_LENGTH]; char szTempFileName[MAX_PATH]; CCSVFile *pcCSVFile = NULL; ACCESSENTRY aeChange; PIDXLOOKUPELEMENT rgIdxLookUp = NULL; PIDXLOOKUPELEMENT pCurIdxLookUp; DWORD dwAllocated; DWORD dwOriginalSize; HRESULT hr = ERROR_NOT_ENOUGH_MEMORY; DWORD dwIdx; DWORD cch, cchWritten; HANDLE hFile = INVALID_HANDLE_VALUE; MYDBG(("CPhoneBook::Merge")); // We'll grow the phone book on the first add record (this minimizes the number // of places in the code where we have to grow the phone book) - so, for now, // just stay with the current size. dwAllocated = m_cPhoneBookEntries; // Create index to loaded phone book, sorted by index rgIdxLookUp = (PIDXLOOKUPELEMENT)CmMalloc(sizeof(IDXLOOKUPELEMENT) * dwAllocated); MYDBGASSERT(rgIdxLookUp); if (!rgIdxLookUp) { goto MergeExit; } for (dwIdx = 0; dwIdx < m_cPhoneBookEntries; dwIdx++) { rgIdxLookUp[dwIdx].iAE = PAEToIdx(&m_rgPhoneBookEntry[dwIdx]); rgIdxLookUp[dwIdx].dwIndex = IdxToPAE(rgIdxLookUp[dwIdx].iAE)->dwIndex; } dwOriginalSize = m_cPhoneBookEntries; CmQSort(rgIdxLookUp,(size_t) dwOriginalSize,sizeof(IDXLOOKUPELEMENT),CompareIdxLookUpElements); // Load changes to phone book pcCSVFile = new CCSVFile; MYDBGASSERT(pcCSVFile); if (!pcCSVFile) { goto MergeExit; } if (!pcCSVFile->Open(pszChangeFile)) { delete pcCSVFile; pcCSVFile = NULL; goto MergeExit; } do { // Read a change record ZeroMemory(&aeChange,sizeof(ACCESSENTRY)); hr = ReadOneLine(&aeChange, pcCSVFile); if (hr == ERROR_NO_MORE_ITEMS) { break; // no more enteries } else if (hr != ERROR_SUCCESS) { goto MergeExit; } hr = ERROR_NOT_ENOUGH_MEMORY; /* if (!ReadPhoneBookDW(&aeChange.dwIndex,pcCSVFile)) break; // no more enteries ReadVerifyPhoneBookDW(aeChange.dwCountryID); ReadVerifyPhoneBookW(aeChange.wStateID); ReadVerifyPhoneBookSZ(aeChange.szCity,cbCity); ReadVerifyPhoneBookSZ(aeChange.szAreaCode,cbAreaCode); // NOTE: 0 is a valid area code and ,, is a valid entry for an area code if (!FSz2Dw(aeChange.szAreaCode,&aeChange.dwAreaCode)) aeChange.dwAreaCode = NO_AREA_CODE; ReadVerifyPhoneBookSZ(aeChange.szAccessNumber,cbAccessNumber); ReadVerifyPhoneBookDW(aeChange.dwConnectSpeedMin); ReadVerifyPhoneBookDW(aeChange.dwConnectSpeedMax); ReadVerifyPhoneBookB(aeChange.bFlipFactor); ReadVerifyPhoneBookDW(aeChange.fType); ReadVerifyPhoneBookSZ(aeChange.szDataCenter,cbDataCenter); */ pCurIdxLookUp = (PIDXLOOKUPELEMENT) CmBSearch(&aeChange, rgIdxLookUp, (size_t) dwOriginalSize, sizeof(IDXLOOKUPELEMENT), CompareIdxLookUpElements); // Determine if this is a delete, add, or merge record if (aeChange.szAccessNumber[0] == '0' && aeChange.szAccessNumber[1] == '\0') { // This is a delete record CMASSERTMSG(pCurIdxLookUp,"Attempting to delete a record that does not exist. The change file and phone book versions do not match."); if (pCurIdxLookUp) { CMASSERTMSG(IdxToPAE(pCurIdxLookUp->iAE),"Attempting to delete a record that has already been deleted."); pCurIdxLookUp->iAE = PAEToIdx(NULL); //Create a dead entry in the look up table } } else if (pCurIdxLookUp) { // This is a change record CMASSERTMSG(IdxToPAE(pCurIdxLookUp->iAE),"Attempting to change a record which has been deleted."); if (IdxToPAE(pCurIdxLookUp->iAE)) { CopyMemory(IdxToPAE(pCurIdxLookUp->iAE),&aeChange,sizeof(ACCESSENTRY)); } } else { // This is an add entry // Make sure we have enough room if (m_cPhoneBookEntries >= dwAllocated) { // Grow phone book dwAllocated += CHANGE_BUFFER_SIZE; DWORD dwNewAlloc = (DWORD) sizeof(ACCESSENTRY) * dwAllocated; PACCESSENTRY pTempAccessEntry = (PACCESSENTRY)CmRealloc(m_rgPhoneBookEntry, dwNewAlloc); MYDBGASSERT(pTempAccessEntry); if (!pTempAccessEntry) { MYDBG(("PhoneBook::Merge - Grow ReAlloc of %lu failed",dwNewAlloc)); goto MergeExit; } m_rgPhoneBookEntry = pTempAccessEntry; pTempAccessEntry = NULL; MYDBG(("Grow phone book to %lu entries",dwAllocated)); // Grow look up index MYDBGASSERT(rgIdxLookUp); PIDXLOOKUPELEMENT pTempLookupElement = (PIDXLOOKUPELEMENT)CmRealloc(rgIdxLookUp, sizeof(IDXLOOKUPELEMENT)*dwAllocated); MYDBGASSERT(pTempLookupElement); if (!pTempLookupElement) { goto MergeExit; } rgIdxLookUp = pTempLookupElement; } //Add entry to the end of the phonebook and to end of look up index CopyMemory(&m_rgPhoneBookEntry[m_cPhoneBookEntries],&aeChange,sizeof(ACCESSENTRY)); rgIdxLookUp[m_cPhoneBookEntries].iAE = PAEToIdx(&m_rgPhoneBookEntry[m_cPhoneBookEntries]); rgIdxLookUp[m_cPhoneBookEntries].dwIndex = IdxToPAE(rgIdxLookUp[m_cPhoneBookEntries].iAE)->dwIndex; m_cPhoneBookEntries++; // NOTE: because the entry is added to the end of the list, we can't add // and delete entries in the same change file. } } while (TRUE); // The CompareIdxLookupElementFileOrder() function needs the iAE member to be // a PACCESSENTRY, and not an index. So we convert 'em here, and then we'll // convert 'em back later. for (dwIdx=0;dwIdxdwIndex, pAE->dwCountryID, (DWORD) pAE->wStateID, pAE->szCity, pAE->szAreaCode, pAE->szAccessNumber, pAE->dwConnectSpeedMin, pAE->dwConnectSpeedMax, (DWORD) pAE->bFlipFactor, (DWORD) pAE->fType, pAE->szDataCenter); if (!WriteFile(hFile,szTempBuffer,cch,&cchWritten,NULL)) { // something went wrong, get rid of the temporary file hr = GetLastError(); CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; DeleteFile(szTempFileName); goto MergeExit; } MYDBGASSERT(cch == cchWritten); } } CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; // Move new phone book over old if (!DeleteFile(m_szPhoneBook)) { hr = GetLastError(); goto MergeExit; } if (!MoveFile(szTempFileName,m_szPhoneBook)) { hr = GetLastError(); goto MergeExit; } // discard the phonebook in memory CmFree(m_rgPhoneBookEntry); m_rgPhoneBookEntry = NULL; m_cPhoneBookEntries = 0; CmFree(m_pLineCountryList); CmFree(m_rgIDLookUp); CmFree(m_rgNameLookUp); CmFree(m_rgState); m_pLineCountryList = NULL; m_rgIDLookUp = NULL; m_rgNameLookUp = NULL; m_rgState = NULL; m_cStates = 0; lstrcpy(szTempBuffer,m_szINFFile); m_szINFFile[0] = '\0'; m_szPhoneBook[0] = '\0'; // Reload it (and rebuild look up arrays) hr = Init(szTempBuffer); MergeExit: if (pcCSVFile) { pcCSVFile->Close(); delete pcCSVFile; } if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); } CmFree(rgIdxLookUp); return hr; } // ############################################################################ HRESULT CPhoneBook::ReadOneLine(PACCESSENTRY pAccessEntry, CCSVFile *pcCSVFile) { HRESULT hr = ERROR_SUCCESS; // // Skip newlines (trailing or leading) and read first DW token // If either fail, then consider this the end of the file // if (!ReadPhoneBookNL(pcCSVFile) || !ReadPhoneBookDW(&pAccessEntry->dwIndex,pcCSVFile)) { hr = ERROR_NO_MORE_ITEMS; // no more enteries MYDBG(("CPhoneBook::ReadOneLine - No More items")); goto ReadExit; } ReadVerifyPhoneBookDW(pAccessEntry->dwCountryID); ReadVerifyPhoneBookW(pAccessEntry->wStateID); ReadVerifyPhoneBookSZ(pAccessEntry->szCity,cbCity); ReadVerifyPhoneBookSZ(pAccessEntry->szAreaCode,cbAreaCode); // NOTE: 0 is a valid area code and ,, is a valid entry for an area code if (!FSz2Dw(pAccessEntry->szAreaCode,&pAccessEntry->dwAreaCode)) pAccessEntry->dwAreaCode = NO_AREA_CODE; ReadVerifyPhoneBookSZ(pAccessEntry->szAccessNumber,cbAccessNumber); ReadVerifyPhoneBookDW(pAccessEntry->dwConnectSpeedMin); ReadVerifyPhoneBookDW(pAccessEntry->dwConnectSpeedMax); ReadVerifyPhoneBookB(pAccessEntry->bFlipFactor); ReadVerifyPhoneBookDW(pAccessEntry->fType); // // Attempt to read datacenter, if read fails, find out why before reacting // if (!ReadPhoneBookSZ(pAccessEntry->szDataCenter, cbDataCenter + 1, pcCSVFile)) { // // If the last read was successful, then we must have some bad sz data // if (!pcCSVFile->ReadError()) { CMASSERTMSG(0,"Invalid STRING in phone book"); goto DataError; } } ReadExit: return hr; DataError: hr = ERROR_INVALID_DATA; goto ReadExit; } // ############################################################################ HRESULT CPhoneBook::GetCanonical (PACCESSENTRY pAE, char *psOut) { HRESULT hr = ERROR_SUCCESS; PIDLOOKUPELEMENT pIDLookUp; pIDLookUp = (PIDLOOKUPELEMENT)CmBSearch(&pAE->dwCountryID,m_rgIDLookUp, (size_t) m_pLineCountryList->dwNumCountries,sizeof(IDLOOKUPELEMENT),CompareIdxLookUpElements); if (!pIDLookUp) { hr = ERROR_INVALID_PARAMETER; } else { if (!psOut) { hr = ERROR_INVALID_PARAMETER; } else { *psOut = 0; SzCanonicalFromAE (psOut, pAE, pIDLookUp->pLCE); } } return hr; } // ############################################################################ HRESULT CPhoneBook::GetNonCanonical (PACCESSENTRY pAE, char *psOut) { HRESULT hr = ERROR_SUCCESS; PIDLOOKUPELEMENT pIDLookUp; pIDLookUp = (PIDLOOKUPELEMENT)CmBSearch(&pAE->dwCountryID,m_rgIDLookUp, (size_t) m_pLineCountryList->dwNumCountries,sizeof(IDLOOKUPELEMENT),CompareIdxLookUpElements); if (!pIDLookUp) { hr = ERROR_INVALID_PARAMETER; } else { if (!psOut) { hr = ERROR_INVALID_PARAMETER; } else { *psOut = 0; SzNonCanonicalFromAE (psOut, pAE, pIDLookUp->pLCE); } } return hr; } // ############################################################################ DllExportH PhoneBookLoad(LPCSTR pszISPCode, DWORD_PTR *pdwPhoneID) { HRESULT hr = ERROR_NOT_ENOUGH_MEMORY; CPhoneBook *pcPhoneBook; MYDBG(("CM_PHBK_DllExport - PhoneBookLoad")); if (!g_hInst) g_hInst = GetModuleHandleA(NULL); // validate parameters MYDBGASSERT(pszISPCode && *pszISPCode && pdwPhoneID); *pdwPhoneID = NULL; // allocate phone book pcPhoneBook = new CPhoneBook; // initialize phone book if (pcPhoneBook) hr = pcPhoneBook->Init(pszISPCode); // in case of failure if (hr && pcPhoneBook) { delete pcPhoneBook; MYDBG(("PhoneBookLoad() - init failed")); } else { *pdwPhoneID = (DWORD_PTR)pcPhoneBook; } return hr; } // ############################################################################ DllExportH PhoneBookUnload(DWORD_PTR dwPhoneID) { MYDBG(("CM_PHBK_DllExport - PhoneBookUnload")); MYDBGASSERT(dwPhoneID); // Release contents delete (CPhoneBook*)dwPhoneID; return ERROR_SUCCESS; } // ############################################################################ DllExportH PhoneBookMergeChanges(DWORD_PTR dwPhoneID, LPCSTR pszChangeFile) { MYDBG(("CM_PHBK_DllExport - PhoneBookMergeChanges")); return ((CPhoneBook*)dwPhoneID)->Merge(pszChangeFile); }