/*++ Copyright (c) 1996 Microsoft Corporation Module Name: tapiutil.c Abstract: Utility functions for working with TAPI Environment: Windows fax driver user interface Revision History: 09/18/96 -davidx- Created it. 22/07/99 -v-sashab- Replaced a direct access to TAPI by Server calls mm/dd/yy -author- description --*/ #include "faxui.h" #include "tapiutil.h" #define STRSAFE_NO_DEPRECATE #include #define UNKNOWN_DIALING_LOCATION (0xffffffff) static HLINEAPP g_hLineApp = 0; static DWORD g_dwTapiVersion = 0x00020000; static DWORD g_dwDefaultDialingLocation = UNKNOWN_DIALING_LOCATION; BOOL CurrentLocationUsesCallingCard (); VOID CALLBACK TapiLineCallback( DWORD hDevice, DWORD dwMessage, ULONG_PTR dwInstance, ULONG_PTR dwParam1, ULONG_PTR dwParam2, ULONG_PTR dwParam3 ) /*++ Routine Description: TAPI line callback function: Even though we don't actually have anything to do here, we must provide a callback function to keep TAPI happy. Arguments: hDevice - Line or call handle dwMessage - Reason for the callback dwInstance - LINE_INFO index dwParam1 - Callback parameter #1 dwParam2 - Callback parameter #2 dwParam3 - Callback parameter #3 Return Value: NONE --*/ { } BOOL InitTapi () { // // TAPI does not work properly on Win95 // and does not relevant for remote fax connection // #ifdef UNICODE DWORD nLineDevs; LONG status; LINEINITIALIZEEXPARAMS lineInitParams; Assert (!g_hLineApp); if (g_hLineApp) { return TRUE; } ZeroMemory(&lineInitParams, sizeof(lineInitParams)); lineInitParams.dwTotalSize = lineInitParams.dwNeededSize = lineInitParams.dwUsedSize = sizeof(lineInitParams); status = lineInitializeEx (&g_hLineApp, g_hModule, TapiLineCallback, TEXT("Fax Send Wizard"), &nLineDevs, &g_dwTapiVersion, &lineInitParams); if (NO_ERROR != status) { Error(("lineInitializeEx failed: %x\n", status)); g_hLineApp = 0; return FALSE; } #endif // UNICODE return TRUE; } void ShutdownTapi () { if (!g_hLineApp) { return; } // // Restore the last dialing location the user selected // if (UNKNOWN_DIALING_LOCATION != g_dwDefaultDialingLocation) { SetCurrentLocation (g_dwDefaultDialingLocation); } lineShutdown (g_hLineApp); g_hLineApp = 0; } // ShutdownTapi DWORD GetDefaultCountryID( VOID ) /*++ Routine Description: Return the default country ID for the current location Arguments: NONE Return Value: The current ID for the current location --*/ { // // We assume the correct information has already been saved to the // registry during the installation process. // return 0; } PFAX_TAPI_LINECOUNTRY_ENTRY FindCountry( PFAX_TAPI_LINECOUNTRY_LIST pCountryList, DWORD countryId ) /*++ Routine Description: Find the specified country from a list of all countries and return a pointer to the corresponding FAX_TAPI_LINECOUNTRY_ENTRY structure Arguments: pCountryList - pointer to the country list countryId - Specifies the country ID we're interested in Return Value: Pointer to a FAX_TAPI_LINECOUNTRY_ENTRY structure corresponding to the specified country ID NULL if there is an error --*/ { DWORD dwIndex; if (pCountryList == NULL || countryId == 0) return NULL; // // Look at each FAX_TAPI_LINECOUNTRY_ENTRY structure and compare its country ID with // the specified country ID // for (dwIndex=0; dwIndex < pCountryList->dwNumCountries; dwIndex++) { if (pCountryList->LineCountryEntries[dwIndex].dwCountryID == countryId) return &pCountryList->LineCountryEntries[dwIndex]; } return NULL; } DWORD GetCountryIdFromCountryCode( PFAX_TAPI_LINECOUNTRY_LIST pCountryList, DWORD dwCountryCode ) /*++ Routine Description: Arguments: pCountryList - pointer to the country list dwCountryCode - Specifies the country code we're interested in Return Value: Country ID --*/ { DWORD dwIndex; if (pCountryList == NULL || dwCountryCode == 0) return 0; // // Look at each FAX_TAPI_LINECOUNTRY_ENTRY structure and compare its country ID with // the specified country ID // for (dwIndex=0; dwIndex < pCountryList->dwNumCountries; dwIndex++) { if (pCountryList->LineCountryEntries[dwIndex].dwCountryCode == dwCountryCode) return pCountryList->LineCountryEntries[dwIndex].dwCountryID; } return 0; } INT AreaCodeRules( PFAX_TAPI_LINECOUNTRY_ENTRY pEntry ) /*++ Routine Description: Given a FAX_TAPI_LINECOUNTRY_ENTRY structure, determine if area code is needed in that country Arguments: pEntry - Points to a FAX_TAPI_LINECOUNTRY_ENTRY structure Return Value: AREACODE_DONTNEED - Area code is not used in the specified country AREACODE_OPTIONAL - Area code is optional in the specified country AREACODE_REQUIRED - Area code is required in the specified country --*/ { if ((pEntry != NULL) && (pEntry->lpctstrLongDistanceRule != 0)) { // // Area code is required in this country // if (_tcschr(pEntry->lpctstrLongDistanceRule, TEXT('F')) != NULL) return AREACODE_REQUIRED; // // Area code is not needed in this country // if (_tcschr(pEntry->lpctstrLongDistanceRule, TEXT('I')) == NULL) return AREACODE_DONTNEED; } // // Default case: area code is optional in this country // return AREACODE_OPTIONAL; } VOID AssemblePhoneNumber( OUT LPTSTR pAddress, IN UINT cchAddress, IN DWORD countryCode, IN LPTSTR pAreaCode, IN LPTSTR pPhoneNumber ) /*++ Routine Description: Assemble a canonical phone number given the following: country code, area code, and phone number Arguments: pAddress - Specifies a buffer to hold the resulting fax address cchAddress - The size of the pAddress OUT buffer in TCHARs countryCode - Specifies the country code pAreaCode - Specifies the area code string pPhoneNumber - Specifies the phone number string Return Value: NONE --*/ { // // Country code if neccessary // HRESULT hRc; if (countryCode != 0) { hRc = StringCchPrintf(pAddress, cchAddress, TEXT("+%d "), countryCode); if(FAILED(hRc)) { Assert(0); return; } } // // Area code if necessary // if (pAreaCode && !IsEmptyString(pAreaCode)) { if (countryCode != 0) { hRc = StringCchCat(pAddress, cchAddress, TEXT("(") ); if(FAILED(hRc)) { Assert(0); return; } } hRc = StringCchCat(pAddress, cchAddress, pAreaCode); if(FAILED(hRc)) { Assert(0); return; } if (countryCode != 0) { hRc = StringCchCat(pAddress, cchAddress, TEXT(")")); if(FAILED(hRc)) { Assert(0); return; } } hRc = StringCchCat(pAddress, cchAddress, TEXT(" ")); if(FAILED(hRc)) { Assert(0); return; } } // // Phone number at last // Assert(pPhoneNumber != NULL); hRc = StringCchCat(pAddress, cchAddress, pPhoneNumber); if(FAILED(hRc)) { Assert(0); } } VOID UpdateAreaCodeField( HWND hwndAreaCode, PFAX_TAPI_LINECOUNTRY_LIST pCountryList, DWORD countryId ) /*++ Routine Description: Update any area code text field associated with a country list box Arguments: hwndAreaCode - Specifies the text field associated with the country list box pCountryList - pointer to the country list countryId - Currently selected country ID Return Value: NONE --*/ { if (hwndAreaCode == NULL) return; if (AreaCodeRules(FindCountry(pCountryList,countryId)) == AREACODE_DONTNEED) { SendMessage(hwndAreaCode, WM_SETTEXT, 0, (LPARAM) TEXT("")); EnableWindow(hwndAreaCode, FALSE); } else EnableWindow(hwndAreaCode, TRUE); } DWORD GetLocalCountryCode() /*++ Routine Description: Retrieve local country code Arguments: None Return Value: Local country code if success NULL if failed --*/ { HMODULE hTapi = NULL; TCHAR tszLocalCountryCode[16] = {0}; TCHAR tszLocalCityCode[16] = {0}; DWORD dwCountryCode = 0; typedef LONG (WINAPI *TAPI_GET_LOCATION_INFO)(LPTSTR, LPTSTR); TAPI_GET_LOCATION_INFO pfnTapiGetLocationInfo; hTapi = LoadLibrary(TEXT("tapi32.dll")); if(!hTapi) { Error(("LoadLibrary(tapi32.dll) failed. ec = 0x%X\n",GetLastError())); return dwCountryCode; } #ifdef UNICODE pfnTapiGetLocationInfo = (TAPI_GET_LOCATION_INFO)GetProcAddress(hTapi, "tapiGetLocationInfoW"); #else pfnTapiGetLocationInfo = (TAPI_GET_LOCATION_INFO)GetProcAddress(hTapi, "tapiGetLocationInfoA"); if(!pfnTapiGetLocationInfo) { pfnTapiGetLocationInfo = (TAPI_GET_LOCATION_INFO)GetProcAddress(hTapi, "tapiGetLocationInfo"); } #endif if(pfnTapiGetLocationInfo) { if(0 == pfnTapiGetLocationInfo(tszLocalCountryCode, tszLocalCityCode)) { if (1 != _stscanf(tszLocalCountryCode, TEXT("%u"), &dwCountryCode)) { Error(("_stscanf failed.")); dwCountryCode = 0; } } } else { Error(("tapiGetLocationInfo failed. ec = 0x%X\n",GetLastError())); } FreeLibrary(hTapi); return dwCountryCode; } // GetLocalCountryCode VOID InitCountryListBox( PFAX_TAPI_LINECOUNTRY_LIST pCountryList, HWND hwndList, HWND hwndAreaCode, LPTSTR lptstrCountry, DWORD countryId, BOOL bAddCountryCode ) /*++ Routine Description: Initialize the country list box Arguments: pCountryList - pointer to the country list hwndList - Handle to the country list box window hwndAreaCode - Handle to an associated area code text field lptstrCountry - Country that should be selected or NULL countryId - Initially selected country ID bAddCountryCode - if TRUE add a country code to a country name Return Value: NONE --*/ #define MAX_COUNTRY_NAME 256 { DWORD dwIndex; TCHAR buffer[MAX_COUNTRY_NAME]={0}; if(0 == countryId) { // // if no country selected, select the local // countryId = GetLocalCountryCode(); if(0 == countryId) { // // The default location is not configured. // DoTapiProps(hwndList); countryId = GetLocalCountryCode(); } } // // Disable redraw on the list box and reset its content // SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); SendMessage(hwndList, CB_RESETCONTENT, FALSE, 0); // // Loop through FAX_TAPI_LINECOUNTRY_ENTRY structures and add the available selections to // the country list box. // if (pCountryList) { TCHAR szFormat[64] = { TEXT("%s (%d)") }; #ifdef UNICODE if(pCountryList->dwNumCountries && IsWindowRTL(hwndList) && !StrHasRTLChar(LOCALE_SYSTEM_DEFAULT, pCountryList->LineCountryEntries[0].lpctstrCountryName)) { // // The Combo Box has RTL layout // but the country name has not RTL characters. // So, we add LEFT-TO-RIGHT OVERRIDE UNICODE character. // _tcscpy(szFormat, TEXT("\x202D%s (%d)")); } #endif for (dwIndex=0; dwIndex < pCountryList->dwNumCountries; dwIndex++) { if (pCountryList->LineCountryEntries[dwIndex].lpctstrCountryName) { if(bAddCountryCode) { _sntprintf(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, szFormat, pCountryList->LineCountryEntries[dwIndex].lpctstrCountryName, pCountryList->LineCountryEntries[dwIndex].dwCountryCode); } else { _tcsncpy(buffer, pCountryList->LineCountryEntries[dwIndex].lpctstrCountryName, sizeof(buffer)/sizeof(buffer[0]) - 1); } if (lptstrCountry && _tcsstr(buffer,lptstrCountry) && !countryId) { // search for a first occurence of lptstrCountry countryId = pCountryList->LineCountryEntries[dwIndex].dwCountryID; } SendMessage(hwndList, CB_SETITEMDATA, SendMessage(hwndList, CB_ADDSTRING, 0, (LPARAM) buffer), pCountryList->LineCountryEntries[dwIndex].dwCountryID); } } } // // Figure out which item in the list should be selected // if (pCountryList != NULL) { for (dwIndex=0; dwIndex <= pCountryList->dwNumCountries; dwIndex++) { if ((DWORD) SendMessage(hwndList, CB_GETITEMDATA, dwIndex, 0) == countryId) break; } if (dwIndex > pCountryList->dwNumCountries) { dwIndex = countryId = 0; } } else { dwIndex = countryId = 0; } SendMessage(hwndList, CB_SETCURSEL, dwIndex, 0); SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); // // Update the associated area code text field // UpdateAreaCodeField(hwndAreaCode, pCountryList, countryId); } VOID SelChangeCountryListBox( HWND hwndList, HWND hwndAreaCode, PFAX_TAPI_LINECOUNTRY_LIST pCountryList ) /*++ Routine Description: Handle dialog selection changes in the country list box Arguments: hwndList - Handle to the country list box window hwndAreaCode - Handle to an associated area code text field pCountryList - pointer to the country list Return Value: NONE --*/ { UpdateAreaCodeField(hwndAreaCode, pCountryList, GetCountryListBoxSel(hwndList)); } DWORD GetCountryListBoxSel( HWND hwndList ) /*++ Routine Description: Return the current selection of country list box Arguments: hwndList - Handle to the country list box window Return Value: Currently selected country ID --*/ { INT msgResult; if ((msgResult = (INT)SendMessage(hwndList, CB_GETCURSEL, 0, 0)) == CB_ERR || (msgResult = (INT)SendMessage(hwndList, CB_GETITEMDATA, msgResult, 0)) == CB_ERR) { return 0; } return msgResult; } BOOL DoTapiProps( HWND hDlg ) { DWORD dwRes; dwRes = lineTranslateDialog(g_hLineApp, 0, // Device ID g_dwTapiVersion, hDlg, NULL); // Address if(0 != dwRes) { Error(("lineTranslateDialog failed. ec = 0x%X\n", dwRes)); return FALSE; } return TRUE; } // DoTapiProps LPLINETRANSLATECAPS GetTapiLocationInfo( HWND hWnd ) /*++ Routine Description: Get a list of locations from TAPI Arguments: NONE Return Value: Pointer to a LINETRANSLATECAPS structure, NULL if there is an error --*/ #define INITIAL_LINETRANSLATECAPS_SIZE 5000 { LPLINETRANSLATECAPS pTranslateCaps = NULL; // // TAPI does not work properly on Win95 // and does not relevant for remote fax connection // #ifdef UNICODE DWORD cbNeeded = INITIAL_LINETRANSLATECAPS_SIZE; LONG status; INT i; if (!g_hLineApp) { return NULL; } for (i = 0; i < 2; i++) { // // Free any existing buffer and allocate a new one with larger size // MemFree(pTranslateCaps); if (! (pTranslateCaps = MemAlloc(cbNeeded))) { Error(("Memory allocation failed\n")); return NULL; } // // Get the LINETRANSLATECAPS structure from TAPI // pTranslateCaps->dwTotalSize = cbNeeded; status = lineGetTranslateCaps(g_hLineApp, g_dwTapiVersion, pTranslateCaps); // // Try to bring up UI if there are no locations. // if (LINEERR_INIFILECORRUPT == status) { if (lineTranslateDialog( g_hLineApp, 0, g_dwTapiVersion, hWnd, NULL )) { MemFree(pTranslateCaps); return NULL; } continue; } if ((pTranslateCaps->dwNeededSize > pTranslateCaps->dwTotalSize) || (LINEERR_STRUCTURETOOSMALL == status) || (LINEERR_NOMEM == status)) { // // Retry since our initial estimated buffer size was too small // if (cbNeeded >= pTranslateCaps->dwNeededSize) { cbNeeded = cbNeeded * 5; } else { cbNeeded = pTranslateCaps->dwNeededSize; } Warning(("LINETRANSLATECAPS resized to: %d\n", cbNeeded)); } else { // // Either success of real error - break now and let the code after the loop handle it. // break; } } if (NO_ERROR != status) { Error(("lineGetTranslateCaps failed: %x\n", status)); MemFree(pTranslateCaps); SetLastError (status); pTranslateCaps = NULL; } if (pTranslateCaps) { // // Update the current default dialing location. // We save it here and restore it when the wizard exists in ShutdownTapi(). // g_dwDefaultDialingLocation = pTranslateCaps->dwCurrentLocationID; } #endif // UNICODE return pTranslateCaps; } // GetTapiLocationInfo BOOL SetCurrentLocation( DWORD locationID ) /*++ Routine Description: Change the default TAPI location Arguments: locationID - The permanant ID for the new default TAPI location Return Value: TRUE if successful, FALSE if there is an error --*/ { LONG lResult; Assert (g_hLineApp); if (!g_hLineApp) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } lResult = lineSetCurrentLocation(g_hLineApp, locationID); if (NO_ERROR == lResult) { Verbose(("Current location changed: ID = %d\n", locationID)); return TRUE; } else { Error(("Couldn't change current TAPI location\n")); SetLastError (lResult); return FALSE; } } // SetCurrentLocation BOOL TranslateAddress ( LPCTSTR lpctstrCanonicalAddress, DWORD dwLocationId, LPTSTR *lpptstrDialableAndDisplayableAddress ) /*++ Routine name : TranslateAddress Routine description: Translates a canonical address Author: Eran Yariv (EranY), Feb, 2001 Arguments: lpctstrCanonicalAddress [in] - Canonical address string dwLocationId [in] - Location id to use lpptstrDialableAndDisplayableAddress [out] - Allocated string holding a combination of translated dialable and displayable addresses Return Value: TRUE if successful, FALSE otherwise (sets last error0. --*/ { // // TAPI does not work properly on Win95 // and does not relevant for remote fax connection // #ifdef UNICODE DWORD dwLineTransOutSize = sizeof(LINETRANSLATEOUTPUT) + 4096; LPLINETRANSLATEOUTPUT lpTranslateOutput = NULL; LONG lRslt = ERROR_SUCCESS; DWORD dwRes; LPTSTR lptstrTranslatedDialableString; LPTSTR lptstrTranslatedDisplayableString; DWORD dwTranslatedStringsSize; BOOL bCanonicCheck; dwRes = IsCanonicalAddress(lpctstrCanonicalAddress, &bCanonicCheck, NULL, NULL, NULL); if (ERROR_SUCCESS != dwRes) { SetLastError (dwRes); return FALSE; } Assert (bCanonicCheck); if (!bCanonicCheck) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } Assert (g_hLineApp); if (!g_hLineApp) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } if (!SetCurrentLocation(dwLocationId)) { return FALSE; } lpTranslateOutput = MemAlloc (dwLineTransOutSize); if (!lpTranslateOutput) { Error(("Couldn't allocate translation results buffer\n")); return FALSE; } lpTranslateOutput->dwTotalSize = dwLineTransOutSize; lRslt = lineTranslateAddress( g_hLineApp, 0, g_dwTapiVersion, lpctstrCanonicalAddress, 0, LINETRANSLATEOPTION_CANCELCALLWAITING, lpTranslateOutput ); if ((lpTranslateOutput->dwNeededSize > lpTranslateOutput->dwTotalSize) || (LINEERR_STRUCTURETOOSMALL == lRslt) || (LINEERR_NOMEM == lRslt)) { // // Retry since our initial estimated buffer size was too small // if (dwLineTransOutSize >= lpTranslateOutput->dwNeededSize) { dwLineTransOutSize = dwLineTransOutSize * 5; } else { dwLineTransOutSize = lpTranslateOutput->dwNeededSize; } // // Re-allocate the LineTransCaps structure // dwLineTransOutSize = lpTranslateOutput->dwNeededSize; MemFree(lpTranslateOutput); lpTranslateOutput = (LPLINETRANSLATEOUTPUT) MemAlloc(dwLineTransOutSize); if (!dwLineTransOutSize) { Error(("Couldn't allocate translation results buffer\n")); return FALSE; } lpTranslateOutput->dwTotalSize = dwLineTransOutSize; lRslt = lineTranslateAddress( g_hLineApp, 0, g_dwTapiVersion, lpctstrCanonicalAddress, 0, LINETRANSLATEOPTION_CANCELCALLWAITING, lpTranslateOutput ); } if (ERROR_SUCCESS != lRslt) { // // Other error // Error(("lineGetTranslateAddress() failed, ec=0x%08x\n", lRslt)); MemFree (lpTranslateOutput); SetLastError (lRslt); return FALSE; } // // We now hold the valid translated address in lpTranslateOutput // // // Calc required buffer size to hold combined strings. // if (CurrentLocationUsesCallingCard ()) { // // Calling card is used. // TAPI returns credit card numbers in the displayable string. // return the input canonical number as the displayable string. // lptstrTranslatedDisplayableString = (LPTSTR)lpctstrCanonicalAddress; } else { // // Calling card isn't used - use displayable string as is. // Assert (lpTranslateOutput->dwDisplayableStringSize > 0); lptstrTranslatedDisplayableString = (LPTSTR)((LPBYTE)lpTranslateOutput + lpTranslateOutput->dwDisplayableStringOffset); } dwTranslatedStringsSize = _tcslen (lptstrTranslatedDisplayableString); Assert (lpTranslateOutput->dwDialableStringSize > 0); lptstrTranslatedDialableString = (LPTSTR)((LPBYTE)lpTranslateOutput + lpTranslateOutput->dwDialableStringOffset); dwTranslatedStringsSize += _tcslen (lptstrTranslatedDialableString); // // Add NULL + Formatting extra length // dwTranslatedStringsSize += COMBINED_TRANSLATED_STRING_EXTRA_LEN + 1; // // Allocate return buffer // *lpptstrDialableAndDisplayableAddress = (LPTSTR)MemAlloc (dwTranslatedStringsSize * sizeof (TCHAR)); if (!*lpptstrDialableAndDisplayableAddress) { MemFree (lpTranslateOutput); Error(("Couldn't allocate translation results buffer\n")); return FALSE; } _stprintf (*lpptstrDialableAndDisplayableAddress, COMBINED_TRANSLATED_STRING_FORMAT, lptstrTranslatedDialableString, lptstrTranslatedDisplayableString); MemFree (lpTranslateOutput); return TRUE; #endif // UNICODE return FALSE; } // TranslateAddress BOOL CurrentLocationUsesCallingCard () { LPLINETRANSLATECAPS pTranslateCaps = GetTapiLocationInfo (NULL); DWORD dwIndex; BOOL bRes = TRUE; LPLINELOCATIONENTRY pLocationEntry = NULL; if (!pTranslateCaps) { return TRUE; } // // Find current location // pLocationEntry = (LPLINELOCATIONENTRY) ((PBYTE) pTranslateCaps + pTranslateCaps->dwLocationListOffset); for (dwIndex = 0; dwIndex < pTranslateCaps->dwNumLocations; dwIndex++) { if (pLocationEntry->dwPermanentLocationID == pTranslateCaps->dwCurrentLocationID) { // // We found the current calling location // Let's see if it uses calling cards. // if (pLocationEntry->dwPreferredCardID) { bRes = TRUE; goto exit; } else { // // Not using calling card // bRes = FALSE; goto exit; } } pLocationEntry++; } exit: MemFree (pTranslateCaps); return bRes; } // CurrentLocationUsesCallingCard