/**************************************************************************** Copyright (c) 1998-1999 Microsoft Corporation Module Name: card.cpp Abstract: Calling Card Object implementation Author: noela - 09/11/98 Notes: Rev History: ****************************************************************************/ #include #include #if WINNT #else #include #endif #include #include #include #include "tapi.h" #include "tspi.h" #include "utils.h" #include "cplResource.h" #include "client.h" #include "clntprivate.h" #include "card.h" #include "location.h" #include #include #include "rules.h" #include "tregupr2.h" #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) #ifdef DBG #define assert(condition) if(condition);else \ { DebugAssertFailure (__FILE__, __LINE__, #condition); } static void DebugAssertFailure (LPCSTR file, DWORD line, LPCSTR condition); #else #define assert(condition) #endif #define CLIENT_FREE(x) \ if (x) { ClientFree(x); (x)=NULL;} else; #define TRACE_DWERROR() LOG((TL_ERROR, "Error %x at line %d in file %hs", dwError, __LINE__, __FILE__)) #define TRACE_HRESULT() LOG((TL_ERROR, "Error %x at line %d in file %hs", Result, __LINE__, __FILE__)) #define EXIT_IF_DWERROR() \ if(dwError !=ERROR_SUCCESS) \ { \ TRACE_DWERROR(); \ goto forced_exit; \ } #define EXIT_IF_FAILED() \ if(FAILED(Result)) \ { \ TRACE_HRESULT(); \ goto forced_exit; \ } #define MAX_NUMBER_LEN 15 const TCHAR gszCardsPath[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Cards"); const TCHAR gszTelephonyPath[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Telephony"); const TCHAR gszCardW[] = TEXT("Card"); const TCHAR gszCardNameW[] = TEXT("Name"); const TCHAR gszLocalRuleW[] = TEXT("LocalRule"); const TCHAR gszLDRuleW[] = TEXT("LDRule"); const TCHAR gszInternationalRuleW[] = TEXT("InternationalRule"); const TCHAR gszLocalAccessNumberW[] = TEXT("LocalAccessNumber"); const TCHAR gszLDAccessNumberW[] = TEXT("LDAccessNumber"); const TCHAR gszInternationalAccessNumberW[] = TEXT("InternationalAccessNumber"); const TCHAR gszAccountNumberW[] = TEXT("AccountNumber"); const TCHAR gszPinW[] = TEXT("Pin"); const TCHAR gszFlags[] = TEXT("Flags"); const TCHAR gszCard[] = TEXT("Card"); const TCHAR gszCards[] = TEXT("Cards"); const TCHAR gszNextID[] = TEXT("NextID"); const TCHAR gszCardListVersion[] = TEXT("CardListVersion"); const TCHAR gszResourceLibrary[] = TEXT("TAPIUI.DLL"); const TCHAR gszSystemSetupPath[] = TEXT("System\\Setup"); const TCHAR gszSystemSetupInProgress[] = TEXT("SystemSetupInProgress"); static BOOL ValidValue(PWSTR); /**************************************************************************** Class : CCallingCard Method : Constructor ****************************************************************************/ CCallingCard::CCallingCard() { m_dwCardID = 0; m_pszCardName = NULL;; m_pszPIN = NULL; m_pszEncryptedPIN = NULL; m_pszAccountNumber = NULL; m_pszLocalAccessNumber = NULL; m_pszLongDistanceAccessNumber = NULL; m_pszInternationalAccessNumber = NULL; m_pszCardPath = NULL; m_hCard = NULL; m_bDirty = FALSE; m_bDeleted = FALSE; m_dwFlags = 0; // by default m_dwCryptInitResult = TapiCryptInitialize(); } /**************************************************************************** Class : CCallingCard Method : Destructor ****************************************************************************/ CCallingCard::~CCallingCard() { CleanUp(); TapiCryptUninitialize(); } /**************************************************************************** Class : CCallingCard Method : CleanUp Clean up memory allocations ****************************************************************************/ void CCallingCard::CleanUp(void) { CLIENT_FREE(m_pszCardName); CLIENT_FREE(m_pszPIN); CLIENT_FREE(m_pszEncryptedPIN); CLIENT_FREE(m_pszAccountNumber); CLIENT_FREE(m_pszLocalAccessNumber); CLIENT_FREE(m_pszLongDistanceAccessNumber); CLIENT_FREE(m_pszInternationalAccessNumber); CLIENT_FREE(m_Rules.m_pszInternationalRule); CLIENT_FREE(m_Rules.m_pszLongDistanceRule); CLIENT_FREE(m_Rules.m_pszLocalRule); CLIENT_FREE(m_pszCardPath); CloseCardKey(); } /**************************************************************************** Class : CCallingCard Method : Initialize - external parameters Only for new calling cards (not present in registry) ****************************************************************************/ HRESULT CCallingCard::Initialize ( DWORD dwCardID, PWSTR pszCardName, DWORD dwFlags, PWSTR pszPIN, PWSTR pszAccountNumber, PWSTR pszInternationalRule, PWSTR pszLongDistanceRule, PWSTR pszLocalRule, PWSTR pszInternationalAccessNumber, PWSTR pszLongDistanceAccessNumber, PWSTR pszLocalAccessNumber ) { HRESULT Result = S_OK; #define CALLING_CARD_INIT(param) \ m_##param = ClientAllocString(param); \ if(m_##param == NULL) \ { \ LOG((TL_ERROR, "Initialize create m_%s failed", _T(#param))); \ CleanUp(); \ return E_OUTOFMEMORY; \ } CALLING_CARD_INIT(pszCardName) CALLING_CARD_INIT(pszPIN) CALLING_CARD_INIT(pszAccountNumber) CALLING_CARD_INIT(pszInternationalAccessNumber) CALLING_CARD_INIT(pszLongDistanceAccessNumber) CALLING_CARD_INIT(pszLocalAccessNumber) #undef CALLING_CARD_INIT ////////////////////////////////////////////////// // copy the set of three Rules // Result = m_Rules.Initialize( pszInternationalRule, pszLongDistanceRule, pszLocalRule ); if(FAILED(Result)) { LOG((TL_ERROR, "Initialize create m_Rules failed" )); CleanUp(); return Result; } m_dwCardID = dwCardID; m_dwFlags = dwFlags; m_bDirty = TRUE; return Result; } /**************************************************************************** Class : CCallingCard Method : Initialize - from registry ****************************************************************************/ HRESULT CCallingCard::Initialize ( DWORD dwCardID ) { DWORD dwError; DWORD dwType; DWORD dwLength; m_dwCardID = dwCardID; // open the registry key dwError = OpenCardKey(FALSE); if(dwError != ERROR_SUCCESS) { // If the key does not exist, it will be created at the first save if(dwError==ERROR_FILE_NOT_FOUND) { m_bDirty = TRUE; } return(HRESULT_FROM_WIN32(dwError)); } #define READ_CARD_VAL(Member, Name) \ dwError = ReadOneStringValue(&##Member, Name); \ if(dwError != ERROR_SUCCESS) \ { \ TRACE_DWERROR(); \ CleanUp(); \ return HRESULT_FROM_WIN32(dwError); \ } READ_CARD_VAL(m_pszCardName, gszCardNameW); READ_CARD_VAL(m_pszEncryptedPIN, gszPinW); // READ_CARD_VAL(m_pszPIN, gszPinW); READ_CARD_VAL(m_pszAccountNumber, gszAccountNumberW); READ_CARD_VAL(m_pszLocalAccessNumber, gszLocalAccessNumberW); READ_CARD_VAL(m_pszLongDistanceAccessNumber, gszLDAccessNumberW); READ_CARD_VAL(m_pszInternationalAccessNumber, gszInternationalAccessNumberW); READ_CARD_VAL(m_Rules.m_pszLocalRule, gszLocalRuleW); READ_CARD_VAL(m_Rules.m_pszLongDistanceRule, gszLDRuleW); READ_CARD_VAL(m_Rules.m_pszInternationalRule, gszInternationalRuleW); #undef READ_CARD_VAL // Decrypt the PIN number dwError = DecryptPIN(); if(dwError != ERROR_SUCCESS) { TRACE_DWERROR(); CleanUp(); return HRESULT_FACILITY(dwError)==0 ? HRESULT_FROM_WIN32(dwError) : dwError; } dwLength = sizeof(m_dwFlags); dwError = RegQueryValueEx ( m_hCard, gszFlags, NULL, &dwType, (PBYTE)&m_dwFlags, &dwLength ); if(dwError != ERROR_SUCCESS) { TRACE_DWERROR(); CleanUp(); return HRESULT_FROM_WIN32(dwError); } CloseCardKey(); return S_OK; } /**************************************************************************** Class : CCallingCard Method : SaveToRegistry ****************************************************************************/ HRESULT CCallingCard::SaveToRegistry(void) { DWORD dwError; if(!m_bDirty) // nothing to save return S_OK; //open/create the registry key dwError = OpenCardKey(TRUE); if(dwError != ERROR_SUCCESS) return HRESULT_FROM_WIN32(dwError); assert(m_hCard); assert(m_pszCardPath); if(m_bDeleted) { CloseCardKey(); dwError = RegDeleteKey (HKEY_CURRENT_USER, m_pszCardPath); if(dwError != ERROR_SUCCESS) return HRESULT_FROM_WIN32(dwError); m_bDirty = FALSE; return S_OK; } // Encrypt the PIN number dwError = EncryptPIN(); if(dwError != ERROR_SUCCESS) { TRACE_DWERROR(); return HRESULT_FACILITY(dwError)==0 ? HRESULT_FROM_WIN32(dwError) : dwError; } // Save ! #define CARD_SAVE_STRING(Member, Name) \ dwError = TAPIRegSetValueExW( m_hCard, \ Name, \ 0, \ REG_SZ, \ (PBYTE) Member, \ (wcslen(Member)+1)*sizeof(WCHAR) \ ) ; \ if(dwError != ERROR_SUCCESS) \ { \ TRACE_DWERROR(); \ CloseCardKey(); \ return HRESULT_FROM_WIN32(dwError); \ } CARD_SAVE_STRING(m_pszCardName, gszCardNameW); CARD_SAVE_STRING(m_pszEncryptedPIN, gszPinW); CARD_SAVE_STRING(m_pszAccountNumber, gszAccountNumberW); CARD_SAVE_STRING(m_pszLocalAccessNumber, gszLocalAccessNumberW); CARD_SAVE_STRING(m_pszLongDistanceAccessNumber, gszLDAccessNumberW); CARD_SAVE_STRING(m_pszInternationalAccessNumber, gszInternationalAccessNumberW); CARD_SAVE_STRING(m_Rules.m_pszLocalRule, gszLocalRuleW); CARD_SAVE_STRING(m_Rules.m_pszLongDistanceRule, gszLDRuleW); CARD_SAVE_STRING(m_Rules.m_pszInternationalRule, gszInternationalRuleW); #undef CARD_SAVE_STRING dwError = RegSetValueEx ( m_hCard, gszFlags, 0, REG_DWORD, (PBYTE)&m_dwFlags, sizeof(m_dwFlags) ); if(dwError != ERROR_SUCCESS) { TRACE_DWERROR(); CloseCardKey(); return HRESULT_FROM_WIN32(dwError); } m_bDirty = FALSE; CloseCardKey(); return S_OK; } /**************************************************************************** Class : CCallingCard Method : MarkDeleted Mark the card as deleted ****************************************************************************/ HRESULT CCallingCard::MarkDeleted(void) { m_bDirty = TRUE; if (m_dwFlags & CARD_BUILTIN) { // a builtin card is only hidden, not deleted m_dwFlags |= CARD_HIDE; return S_OK; } m_bDeleted = TRUE; return S_OK; } /**************************************************************************** Class : CCallingCard Method : Validate Returns 0 if the card contains complete and valid rules, or a series of flags if the card is missing any required data. ****************************************************************************/ DWORD CCallingCard::Validate(void) { DWORD dwResult = 0; // Does the card have a name? if (!*m_pszCardName) { dwResult |= CCVF_NOCARDNAME; } // Does the card have any rules? if (!*(m_Rules.m_pszInternationalRule) && !*(m_Rules.m_pszLocalRule) && !*(m_Rules.m_pszLongDistanceRule) ) { dwResult |= CCVF_NOCARDRULES; } else { DWORD dwFieldsToCheck = 0;; struct { PWSTR pwszRule; DWORD dwAccesFlag; } aData[] = { {m_Rules.m_pszInternationalRule, CCVF_NOINTERNATIONALACCESSNUMBER}, {m_Rules.m_pszLocalRule, CCVF_NOLOCALACCESSNUMBER}, {m_Rules.m_pszLongDistanceRule, CCVF_NOLONGDISTANCEACCESSNUMBER}, }; // Foreach rule, is the required rule data available? We need to check // for use of the PIN number, CardNumber, and all three access numbers. // If any of these fields is used, then we need to verify that data has // been entered for those fields. The data is only required if it is // actually used in a rule. for (int i=0; ibeyond_tail() ) { delete node->value(); node = node->next(); } m_CallingCardList.flush(); node = m_DeletedCallingCardList.head(); while( !node->beyond_tail() ) { delete node->value(); node = node->next(); } m_DeletedCallingCardList.flush(); if(m_hCards) RegCloseKey(m_hCards); } /**************************************************************************** Class : CCallingCards Method : Initialize Checks the presence of the Cards key. If not present, it creates and populates it from string resources. Verify the format of the registry config. If it is the old one, it converts it to the new one. Reads the cards from registry ****************************************************************************/ HRESULT CCallingCards::Initialize(void) { DWORD dwError; DWORD dwDisp; BOOL bNewCards = FALSE; DWORD dwIndex; DWORD dwLength; DWORD dwType; CCallingCard *pCard = NULL; HRESULT Result = S_OK; BOOL bNeedToConvert = FALSE; BOOL bNeedToGenerateDefault = FALSE; // Test the presence of the Cards key dwError = RegOpenKeyEx( HKEY_CURRENT_USER, gszCardsPath, 0, KEY_READ, &m_hCards ); if(dwError==ERROR_SUCCESS) { // Read the NextID value dwLength = sizeof(m_dwNextID); dwError = RegQueryValueEx ( m_hCards, gszNextID, NULL, &dwType, (PBYTE)&m_dwNextID, &dwLength ); if(dwError==ERROR_SUCCESS) { if(dwType == REG_DWORD) { // Test the registry format and upgrade if necessary if(IsCardListInOldFormat(m_hCards)) { bNeedToConvert = TRUE; } } else { dwError = ERROR_INVALID_DATA; } } } if(dwError != ERROR_SUCCESS) bNeedToGenerateDefault = TRUE; if(bNeedToGenerateDefault || bNeedToConvert) { // If the Cards key is missing or it has bad info, so we create a fresh, default set of cards. // If the cards key is in the old format, it have to be converted. // There's an excepted case, though: during the system setup no cards should be created or converted. // So detect setup case first: // // HKEY hSetupKey; DWORD dwSetupValue; // close the cards key handle if(m_hCards) { RegCloseKey(m_hCards); m_hCards = NULL; } dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, gszSystemSetupPath, 0, KEY_QUERY_VALUE, &hSetupKey ); if(dwError == ERROR_SUCCESS) { dwLength = sizeof(dwSetupValue); dwSetupValue = 0; dwError = RegQueryValueEx( hSetupKey, gszSystemSetupInProgress, NULL, &dwType, (PBYTE)&dwSetupValue, &dwLength ); RegCloseKey(hSetupKey); if(dwError == ERROR_SUCCESS && dwType == REG_DWORD) { if(dwSetupValue == 1) { // Setup time ! dwError = ERROR_SUCCESS; goto forced_exit; } } } if(dwError != ERROR_SUCCESS) { // Hmm, cannot open the key or read the value... TRACE_DWERROR(); } if(bNeedToConvert) { dwError = ConvertCallingCards(HKEY_CURRENT_USER); if(dwError!=ERROR_SUCCESS) { // fallback to default cards bNeedToGenerateDefault = TRUE; } } if(bNeedToGenerateDefault) { dwError = CreateFreshCards(); EXIT_IF_DWERROR(); } // Reopen Cards key dwError = RegOpenKeyEx( HKEY_CURRENT_USER, gszCardsPath, 0, KEY_READ, &m_hCards ); EXIT_IF_DWERROR(); // ReRead the NextID value dwLength = sizeof(m_dwNextID); dwError = RegQueryValueEx ( m_hCards, gszNextID, NULL, &dwType, (PBYTE)&m_dwNextID, &dwLength ); EXIT_IF_DWERROR(); } // Read all entries dwIndex = 0; while(TRUE) { TCHAR szKeyName[ARRAYSIZE(gszCard)+MAX_NUMBER_LEN]; DWORD dwKeyLength; DWORD dwCardID; // Enumerate next entry dwKeyLength = sizeof(szKeyName)/sizeof(TCHAR); dwError = RegEnumKeyEx (m_hCards, dwIndex, szKeyName, &dwKeyLength, NULL, NULL, NULL, NULL ); if(dwError == ERROR_NO_MORE_ITEMS) { m_dwNumEntries = dwIndex; dwError = ERROR_SUCCESS; break; } EXIT_IF_DWERROR(); // Create a CCallingCard object dwCardID = StrToInt(szKeyName+ARRAYSIZE(gszCard)-1); pCard = new CCallingCard; if(pCard != NULL) { // Read the card information from registry Result = pCard->Initialize(dwCardID); if(SUCCEEDED(Result)) { // Add it to the list m_CallingCardList.tail()->insert_after(pCard); } else { LOG((TL_ERROR, "Error %x reading card %d", Result, dwCardID)); delete pCard; pCard=NULL; } } dwIndex++; } forced_exit: if(m_hCards) { RegCloseKey(m_hCards); m_hCards = NULL; } if(dwError != ERROR_SUCCESS) Result = HRESULT_FROM_WIN32(dwError); return Result; } /**************************************************************************** Class : CCallingCards Method : RemoveCard ****************************************************************************/ void CCallingCards::RemoveCard(CCallingCard *pCard) { CCallingCardNode *node = m_CallingCardList.head(); while( !node->beyond_tail() ) { if ( pCard == node->value() ) { InternalDeleteCard(node); return; } node = node->next(); } assert(FALSE); } /**************************************************************************** Class : CCallingCards Method : RemoveCard ****************************************************************************/ void CCallingCards::RemoveCard(DWORD dwID) { CCallingCardNode *node = m_CallingCardList.head(); while( !node->beyond_tail() ) { if ( dwID == (node->value())->GetCardID() ) { InternalDeleteCard(node); return; } node = node->next(); } assert(FALSE); } /**************************************************************************** Class : CCallingCards Method : GetCallingCard ****************************************************************************/ CCallingCard * CCallingCards::GetCallingCard(DWORD dwID) { CCallingCardNode *node = m_CallingCardList.head(); while( !node->beyond_tail() ) { if ( dwID == (node->value())->GetCardID() ) { return node->value(); } node = node->next(); } return NULL; } /**************************************************************************** Class : CCallingCards Method : SaveToRegistry ****************************************************************************/ HRESULT CCallingCards::SaveToRegistry(void) { DWORD dwError = ERROR_SUCCESS; HRESULT Result = S_OK; CCallingCardNode *node; // Open the Cards key dwError = RegOpenKeyEx (HKEY_CURRENT_USER, gszCardsPath, 0, KEY_SET_VALUE, &m_hCards); EXIT_IF_DWERROR(); //first - save the next ID dwError = RegSetValueEx ( m_hCards, gszNextID, 0, REG_DWORD, (PBYTE)&m_dwNextID, sizeof(m_dwNextID) ); EXIT_IF_DWERROR(); // save all cards (from both lists) node = m_CallingCardList.head(); while( !node->beyond_tail() ) { Result = node->value()->SaveToRegistry(); EXIT_IF_FAILED(); node = node->next(); } node = m_DeletedCallingCardList.head(); while( !node->beyond_tail() ) { Result = node->value()->SaveToRegistry(); EXIT_IF_FAILED(); node = node->next(); } forced_exit: if(m_hCards) { RegCloseKey(m_hCards); m_hCards = NULL; } if(dwError != ERROR_SUCCESS) Result = HRESULT_FROM_WIN32(dwError); return Result; } /**************************************************************************** Class : CCallingCards Method : Reset ****************************************************************************/ HRESULT CCallingCards::Reset(BOOL bInclHidden) { m_hEnumNode = m_CallingCardList.head(); m_bEnumInclHidden = bInclHidden; return S_OK; } /**************************************************************************** Class : CCallingCards Method : Next ****************************************************************************/ HRESULT CCallingCards::Next(DWORD NrElem, CCallingCard **ppCard, DWORD *pNrElemFetched) { DWORD dwIndex = 0; if(pNrElemFetched == NULL && NrElem != 1) return E_INVALIDARG; if(ppCard==NULL) return E_INVALIDARG; while( !m_hEnumNode->beyond_tail() && dwIndexvalue(); if(m_bEnumInclHidden || !pCard->IsMarkedHidden()) { *ppCard++ = pCard; dwIndex++; } m_hEnumNode = m_hEnumNode->next(); } if(pNrElemFetched!=NULL) *pNrElemFetched = dwIndex; return dwIndexbeyond_tail() && dwIndexvalue()->IsMarkedHidden()) { dwIndex++; } m_hEnumNode = m_hEnumNode->next(); } return dwIndexInitialize( dwIndex, pszName, dwFlags, pszPin, pszAccountNumber, pszInternationalRule, pszLDRule, pszLocalRule, pszInternationalAccessNumber, pszLDAccessNumber, pszLocalAccessNumber ); EXIT_IF_FAILED(); // save it Result = pCard->SaveToRegistry(); EXIT_IF_FAILED(); delete pCard; pCard = NULL; } // Write NextID value dwError = RegSetValueEx ( m_hCards, gszNextID, 0, REG_DWORD, (PBYTE)&dwNumCards, sizeof(dwNumCards) ); EXIT_IF_DWERROR(); forced_exit: if(hTelephony) RegCloseKey(hTelephony); if(m_hCards) { RegCloseKey(m_hCards); m_hCards = NULL; } if(pCard) delete pCard; if(hTapiui) FreeLibrary(hTapiui); if(FAILED(Result)) dwError = HRESULT_CODE(Result); return dwError; } /**************************************************************************** Class : CCallingCards Method : InternalDeleteCard ****************************************************************************/ void CCallingCards::InternalDeleteCard(CCallingCardNode *pNode) { CCallingCard *pCard = pNode->value(); pCard->MarkDeleted(); if(!pCard->IsMarkedHidden()) { pNode->remove(); m_dwNumEntries--; m_DeletedCallingCardList.tail()->insert_after(pCard); } } /**************************************************************************** Helpers ****************************************************************************/ BOOL ValidValue(PWSTR pwszString) { // An empty string or with spaces only is not valid WCHAR const * pwcCrt = pwszString; while(*pwcCrt) if(*pwcCrt++ != L' ') return TRUE; return FALSE; } #ifdef DBG static void DebugAssertFailure (LPCSTR file, DWORD line, LPCSTR condition) { DbgPrt (0, TEXT("%hs(%d) : Assertion failed, condition: %hs\n"), file, line, condition); DebugBreak(); } #endif