/* Copyright (c) 1995, Microsoft Corporation, all rights reserved ** ** entry.c ** Remote Access Common Dialog APIs ** RasPhonebookEntryDlg APIs ** ** 06/20/95 Steve Cobb */ #include "rasdlgp.h" #include "entry.h" #include // for SERIAL_TXT #include // for MprAdmin API declarations #include // for NetUserAdd declarations /*---------------------------------------------------------------------------- ** Local prototypes (alphabetically) **---------------------------------------------------------------------------- */ BOOL EuCommit( IN EINFO* pInfo ); DWORD EuCommitCredentials( IN EINFO* pInfo ); DWORD EuLoadScpScriptsList( OUT DTLLIST** ppList ); /* Target machine for RouterEntryDlg{A,W} */ static WCHAR g_wszServer[ MAX_COMPUTERNAME_LENGTH + 3] = L""; /*---------------------------------------------------------------------------- ** External entry points **---------------------------------------------------------------------------- */ BOOL APIENTRY RasEntryDlgA( IN LPSTR lpszPhonebook, IN LPSTR lpszEntry, IN OUT LPRASENTRYDLGA lpInfo ) /* Win32 ANSI entrypoint that displays the modal Phonebook Entry property ** sheet. 'LpszPhonebook' is the full path to the phonebook file or NULL ** to use the default phonebook. 'LpszEntry' is the entry to edit or the ** default name of the new entry. 'LpInfo' is caller's additional ** input/output parameters. ** ** Returns true if user presses OK and succeeds, false on error or Cancel. */ { WCHAR* pszPhonebookW; WCHAR* pszEntryW; RASENTRYDLGW infoW; BOOL fStatus; TRACE("RasEntryDlgA"); if (!lpInfo) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } if (lpInfo->dwSize != sizeof(RASENTRYDLGA)) { lpInfo->dwError = ERROR_INVALID_SIZE; return FALSE; } /* Thunk "A" arguments to "W" arguments. */ if (lpszPhonebook) { pszPhonebookW = StrDupTFromA( lpszPhonebook ); if (!pszPhonebookW) { lpInfo->dwError = ERROR_NOT_ENOUGH_MEMORY; return FALSE; } } else pszPhonebookW = NULL; if (lpszEntry) { pszEntryW = StrDupTFromA( lpszEntry ); if (!pszEntryW) { Free0( pszPhonebookW ); { lpInfo->dwError = ERROR_NOT_ENOUGH_MEMORY; return FALSE; } } } else pszEntryW = NULL; ZeroMemory( &infoW, sizeof(infoW) ); infoW.dwSize = sizeof(infoW); infoW.hwndOwner = lpInfo->hwndOwner; infoW.dwFlags = lpInfo->dwFlags; infoW.xDlg = lpInfo->xDlg; infoW.yDlg = lpInfo->yDlg; infoW.reserved = lpInfo->reserved; infoW.reserved2 = lpInfo->reserved2; /* Thunk to the equivalent "W" API. */ fStatus = RasEntryDlgW( pszPhonebookW, pszEntryW, &infoW ); Free0( pszPhonebookW ); Free0( pszEntryW ); /* Thunk "W" results to "A" results. */ WideCharToMultiByte( CP_ACP, 0, infoW.szEntry, -1, lpInfo->szEntry, RAS_MaxEntryName + 1, NULL, NULL ); lpInfo->dwError = infoW.dwError; return fStatus; } BOOL APIENTRY RasEntryDlgW( IN LPWSTR lpszPhonebook, IN LPWSTR lpszEntry, IN OUT LPRASENTRYDLGW lpInfo ) /* Win32 Unicode entrypoint that displays the modal Phonebook Entry ** property sheet. 'LpszPhonebook' is the full path to the phonebook file ** or NULL to use the default phonebook. 'LpszEntry' is the entry to edit ** or the default name of the new entry. 'LpInfo' is caller's additional ** input/output parameters. ** ** Returns true if user presses OK and succeeds, false on error or Cancel. */ { DWORD dwErr; EINFO einfo; BOOL fStatus; HWND hwndOwner; DWORD dwOp; TRACE("RasEntryDlgW"); if (!lpInfo) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } if (lpInfo->dwSize != sizeof(RASENTRYDLGW)) { lpInfo->dwError = ERROR_INVALID_SIZE; return FALSE; } /* Eliminate some invalid flag combinations up front. */ if (lpInfo->dwFlags & RASEDFLAG_CloneEntry) lpInfo->dwFlags &= ~(RASEDFLAG_NewEntry | RASEDFLAG_NoRename); /* Pre-initialize the entry common context block. The initialization is ** completed later after the property sheet or wizard has been positioned ** so that "waiting for services" can be centered. */ dwErr = EuInit0( lpszPhonebook, lpszEntry, lpInfo, &einfo, &dwOp ); if (dwErr == 0) { if ((lpInfo->dwFlags & RASEDFLAG_NewEntry) && einfo.pUser->fNewEntryWizard) { if (!einfo.fRouter) AeWizard( &einfo ); else AiWizard( &einfo ); if (einfo.fPadSelected) { /* Explain to the user that an address must be entered, */ MsgDlg( lpInfo->hwndOwner, SID_EnterX25Address, NULL ); einfo.fChainPropertySheet = TRUE; } if (einfo.fChainPropertySheet && lpInfo->dwError == 0) PePropertySheet( &einfo ); if (einfo.fPadSelected) { /* Now we clear 'fChainPropertySheet' if we only set it ** because an X25 pad was selected. ** This way, the user credentials are committed as required ** in EuCommit below. */ einfo.fChainPropertySheet = FALSE; } } else { PePropertySheet( &einfo ); } } else { ErrorDlg( lpInfo->hwndOwner, dwOp, dwErr, NULL ); lpInfo->dwError = dwErr; } fStatus = (einfo.fCommit && EuCommit( &einfo )); EuFree( &einfo ); return fStatus; } BOOL APIENTRY RouterEntryDlgA( IN LPSTR lpszServer, IN LPSTR lpszPhonebook, IN LPSTR lpszEntry, IN OUT LPRASENTRYDLGA lpInfo ) { BOOL fSuccess; DWORD dwErr; PWCHAR pszServerW = NULL; TRACE("RouterEntryDlgA"); // // Set the RPC server. // if (!lpszServer) g_wszServer[0] = L'\0'; else { MultiByteToWideChar( CP_ACP, 0, lpszServer, -1, g_wszServer, MAX_COMPUTERNAME_LENGTH+3 ); } dwErr = LoadRasRpcDll(g_wszServer); if (dwErr) { lpInfo->dwError = dwErr; return FALSE; } // // Load MprApi entrypoints // dwErr = LoadMpradminDll(); if (dwErr) { LoadRasRpcDll(NULL); lpInfo->dwError = dwErr; return FALSE; } // // Call the existing UI. // fSuccess = RasEntryDlgA(lpszPhonebook, lpszEntry, lpInfo); // // Unload MprApi entrypoints // UnloadMpradminDll(); // // Unset the RPC server. // dwErr = LoadRasRpcDll(NULL); if (dwErr) { lpInfo->dwError = dwErr; return FALSE; } return fSuccess; } WCHAR pszRemoteHelpFmt[] = L"\\\\%s\\admin$\\system32\\%s"; DWORD UpdateRemoteHelpFile( IN PWCHAR lpszServer, IN DWORD dwSid, OUT PWCHAR* ppszFile) { PWCHAR pszFile = NULL, pszMachine = NULL; DWORD dwSize = 0; pszFile = PszFromId( g_hinstDll, dwSid ); if (pszFile == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } if ((*lpszServer) && (*lpszServer == L'\\')) { pszMachine = lpszServer + 2; } else { pszMachine = lpszServer; } dwSize = lstrlen(pszRemoteHelpFmt) + lstrlen(pszMachine) + lstrlen(pszFile) + 1; dwSize *= 2; // Free the previous setting // Free0(*ppszFile); *ppszFile = (PWCHAR) Malloc(dwSize); if (*ppszFile == NULL) { Free0(pszFile); return ERROR_NOT_ENOUGH_MEMORY; } wsprintfW(*ppszFile, pszRemoteHelpFmt, pszMachine, pszFile); Free0(pszFile); return NO_ERROR; } BOOL APIENTRY RouterEntryDlgW( IN LPWSTR lpszServer, IN LPWSTR lpszPhonebook, IN LPWSTR lpszEntry, IN OUT LPRASENTRYDLGW lpInfo ) { BOOL fSuccess; DWORD dwErr; TRACE("RouterEntryDlgW"); TRACEW1(" s=%s",(lpszServer)?lpszServer:TEXT("")); TRACEW1(" p=%s",(lpszPhonebook)?lpszPhonebook:TEXT("")); TRACEW1(" e=%s",(lpszEntry)?lpszEntry:TEXT("")); if (!lpszServer) g_wszServer[0] = L'\0'; else lstrcpyW(g_wszServer, lpszServer); // // Reset the global router help file to be the one // already installed on the nt4 machine. // if (lpszServer) { dwErr = UpdateRemoteHelpFile( lpszServer, SID_HelpFile, &g_pszHelpFile); if (dwErr != NO_ERROR) { return dwErr; } dwErr = UpdateRemoteHelpFile( lpszServer, SID_RouterHelpFile, &g_pszRouterHelpFile); if (dwErr != NO_ERROR) { return dwErr; } } // // Set the RPC server before calling RasEntryDlg. // dwErr = LoadRasRpcDll(lpszServer); if (dwErr) { lpInfo->dwError = dwErr; return FALSE; } // // Load MprApi entrypoints // dwErr = LoadMpradminDll(); if (dwErr) { LoadRasRpcDll(NULL); lpInfo->dwError = dwErr; return FALSE; } // // Call the existing UI. // fSuccess = RasEntryDlgW(lpszPhonebook, lpszEntry, lpInfo); // // Unload MprApi entrypoints // UnloadMpradminDll(); // // Unset the RPC server. // dwErr = LoadRasRpcDll(NULL); if (dwErr) { lpInfo->dwError = dwErr; return FALSE; } return fSuccess; } /*---------------------------------------------------------------------------- ** Phonebook Entry common routines ** Listed alphabetically **---------------------------------------------------------------------------- */ BOOL EuCommit( IN EINFO* pInfo ) /* Commits the new or changed entry node to the phonebook file and list. ** Also adds the area code to the per-user list, if indicated. 'PInfo' is ** the common entry information block. ** ** Returns true if successful, false otherwise. */ { DWORD dwErr; BOOL fEditMode; BOOL fChangedNameInEditMode; /* Delete all disabled link nodes. */ if (DtlGetNodes( pInfo->pEntry->pdtllistLinks ) > 1) { DTLNODE* pNode; pNode = DtlGetFirstNode( pInfo->pEntry->pdtllistLinks ); while (pNode) { PBLINK* pLink = (PBLINK* )DtlGetData( pNode ); DTLNODE* pNextNode = DtlGetNextNode( pNode ); if (!pLink->fEnabled) { DtlRemoveNode( pInfo->pEntry->pdtllistLinks, pNode ); DestroyLinkNode( pNode ); } pNode = pNextNode; } } /* Add the area code to the per-user list. */ if (pInfo->pEntry->pszAreaCode) { TCHAR* pszNewAreaCode = NULL; DTLNODE* pNodeNew; DTLNODE* pNode; /* Create a new node for the current area code and add it to the list ** head. */ pszNewAreaCode = StrDup( pInfo->pEntry->pszAreaCode ); if (!pszNewAreaCode) return FALSE; pNodeNew = DtlCreateNode( pszNewAreaCode, 0 ); if (!pNodeNew) { Free( pszNewAreaCode ); return FALSE; } DtlAddNodeFirst( pInfo->pUser->pdtllistAreaCodes, pNodeNew ); /* Delete any other occurrence of the same area code later in the ** list. */ pNode = DtlGetNextNode( pNodeNew ); while (pNode) { TCHAR* pszAreaCode; DTLNODE* pNodeNext; pNodeNext = DtlGetNextNode( pNode ); pszAreaCode = (TCHAR* )DtlGetData( pNode ); if (lstrcmp( pszAreaCode, pszNewAreaCode ) == 0) { DtlRemoveNode( pInfo->pUser->pdtllistAreaCodes, pNode ); DestroyPszNode( pNode ); } pNode = pNodeNext; } Free0( pszNewAreaCode ); pInfo->pUser->fDirty = TRUE; } /* Notice if user changed his area-code/country-code preference. */ if ((pInfo->pApiArgs->dwFlags & RASEDFLAG_NewEntry) && pInfo->pUser->fUseAreaAndCountry != pInfo->pEntry->fUseCountryAndAreaCode) { pInfo->pUser->fUseAreaAndCountry = pInfo->pEntry->fUseCountryAndAreaCode; pInfo->pUser->fDirty = TRUE; } /* Save preferences if they've changed. */ if (pInfo->pUser->fDirty) { if (g_pSetUserPreferences( pInfo->pUser, (pInfo->fRouter) ? UPM_Router : UPM_Normal ) != 0) { return FALSE; } } /* Save the changed phonebook entry. */ pInfo->pEntry->fDirty = TRUE; /* The final name of the entry is output to caller via API structure. */ lstrcpy( pInfo->pApiArgs->szEntry, pInfo->pEntry->pszEntryName ); /* Delete the old node if in edit mode, then add the new node. */ EuGetEditFlags( pInfo, &fEditMode, &fChangedNameInEditMode ); if (fEditMode) DtlDeleteNode( pInfo->pFile->pdtllistEntries, pInfo->pOldNode ); DtlAddNodeLast( pInfo->pFile->pdtllistEntries, pInfo->pNode ); pInfo->pNode = NULL; /* Write the change to the phone book file. */ dwErr = WritePhonebookFile( pInfo->pFile, (fChangedNameInEditMode) ? pInfo->szOldEntryName : NULL ); if (dwErr != 0) { ErrorDlg( pInfo->pApiArgs->hwndOwner, SID_OP_WritePhonebook, dwErr, NULL ); return FALSE; } /* If the user is creating a new router-phonebook entry, ** and the user is using the router wizard to create it, ** and the user did not edit properties directly, ** save the dial-out credentials, and optionally, the dial-in credentials. */ if ((pInfo->pApiArgs->dwFlags & RASEDFLAG_NewEntry) && pInfo->fRouter && pInfo->pUser->fNewEntryWizard && !pInfo->fChainPropertySheet) { dwErr = EuCommitCredentials(pInfo); } /* If the user edited/created a router-phonebook entry, ** store the bitmask of selected network-protocols in 'reserved2'. */ if (pInfo->fRouter) pInfo->pApiArgs->reserved2 = ((NP_Ip|NP_Ipx) & ~pInfo->pEntry->dwfExcludedProtocols); pInfo->pApiArgs->dwError = 0; return TRUE; } DWORD EuCommitCredentials( IN EINFO* pInfo ) /* Commits the credentials and user-account for a router interface. */ { DWORD dwErr; DWORD dwPos; HANDLE hServer; HANDLE hInterface; WCHAR* pwszInterface = NULL; TRACE("EuCommitCredentials"); /* Connect to the router service */ dwErr = g_pMprAdminServerConnect(g_wszServer, &hServer); if (dwErr != NO_ERROR) return dwErr; do { RAS_USER_0 ru0; USER_INFO_1 ui1; MPR_INTERFACE_0 mi0; /* Initialize the interface-information structure. */ ZeroMemory(&mi0, sizeof(mi0)); mi0.dwIfType = ROUTER_IF_TYPE_FULL_ROUTER; mi0.fEnabled = TRUE; pwszInterface = StrDupWFromT(pInfo->pEntry->pszEntryName); if (!pwszInterface) { dwErr = ERROR_NOT_ENOUGH_MEMORY; break; } lstrcpyW(mi0.wszInterfaceName, pwszInterface); /* Create the interface. */ dwErr = g_pMprAdminInterfaceCreate( hServer, 0, (BYTE*)&mi0, &hInterface ); if (dwErr) { TRACE1("EuCommitCredentials: MprAdminInterfaceCreate error %d", dwErr); break; } /* Set the dial-out credentials for the interface. ** Stop after this if an error occurs, or if we don't need ** to add a user-account. */ dwErr = g_pMprAdminInterfaceSetCredentials( g_wszServer, pwszInterface, pInfo->pszRouterUserName, pInfo->pszRouterDomain, pInfo->pszRouterPassword ); if (dwErr || !pInfo->fAddUser) { if(dwErr) TRACE1("EuCommitCredentials: MprAdminInterfaceSetCredentials error %d", dwErr); break; } /* Initialize user-information structure. */ ZeroMemory(&ui1, sizeof(ui1)); ui1.usri1_name = pwszInterface; ui1.usri1_password = StrDupWFromT(pInfo->pszRouterDialInPassword); ui1.usri1_priv = USER_PRIV_USER; ui1.usri1_comment = PszFromId(g_hinstDll, SID_RouterDialInAccount); ui1.usri1_flags = UF_SCRIPT|UF_NORMAL_ACCOUNT|UF_DONT_EXPIRE_PASSWD; /* Add the user-account. */ { WCHAR pszComputer[256], *pszMachine = NULL; if (g_wszServer == NULL) { pszMachine = NULL; } else { pszMachine = pszComputer; if (*g_wszServer == L'\\') { wcscpy(pszMachine, g_wszServer); } else { wcscpy(pszMachine, L"\\\\"); wcscpy(pszMachine + 2, g_wszServer); } } dwErr = NetUserAdd(pszMachine, 1, (BYTE*)&ui1, &dwPos); ZeroMemory( ui1.usri1_password, lstrlen(ui1.usri1_password) * sizeof(TCHAR)); Free0(ui1.usri1_password); Free0(ui1.usri1_comment); if (dwErr) { TRACE1("EuCommitCredentials: NetUserAdd error %d", dwErr); break; } } /* Initialize the RAS user-settings structure. */ ZeroMemory(&ru0, sizeof(ru0)); ru0.bfPrivilege = RASPRIV_NoCallback|RASPRIV_DialinPrivilege; /* Enable dial-in access for the user-account. */ dwErr = g_pRasAdminUserSetInfo( g_wszServer, pwszInterface, 0, (BYTE*)&ru0); if(dwErr) TRACE1("EuCommitCredentials: RasAdminUserSetInfo error %d", dwErr); } while(FALSE); if (pwszInterface) Free(pwszInterface); g_pMprAdminServerDisconnect(hServer); return dwErr; } VOID EuEditScpScript( IN HWND hwndOwner, IN TCHAR* pszScript ) /* Starts notepad.exe on the 'pszScript' script path. 'HwndOwner' is the ** window to center any error popup on. 'PEinfo' is the common entry ** context. */ { TCHAR szCmd[ (MAX_PATH * 2) + 50 + 1 ]; STARTUPINFO si; PROCESS_INFORMATION pi; BOOL f; wsprintf( szCmd, TEXT("notepad.exe %s"), pszScript ); ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); TRACEW1("EuEditScp-cmd=%s",szCmd); f = CreateProcess( NULL, szCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); if (f) { CloseHandle( pi.hThread ); CloseHandle( pi.hProcess ); } else { ErrorDlg( hwndOwner, SID_OP_LoadSwitchEditor, GetLastError(), NULL ); } } VOID EuEditSwitchInf( IN HWND hwndOwner ) /* Starts notepad.exe on the system script file, switch.inf. 'HwndOwner' ** is the window to center any error popup on. */ { TCHAR szCmd[ (MAX_PATH * 2) + 50 + 1 ]; TCHAR szSysDir[ MAX_PATH + 1 ]; STARTUPINFO si; PROCESS_INFORMATION pi; BOOL f; szSysDir[ 0 ] = TEXT('\0'); g_pGetSystemDirectory( szSysDir, MAX_PATH ); wsprintf( szCmd, TEXT("notepad.exe %s\\ras\\switch.inf"), szSysDir ); ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); TRACEW1("EuEditInf-cmd=%s",szCmd); f = CreateProcess( NULL, szCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); if (f) { CloseHandle( pi.hThread ); CloseHandle( pi.hProcess ); } else { ErrorDlg( hwndOwner, SID_OP_LoadSwitchEditor, GetLastError(), NULL ); } } VOID EuFillAreaCodeList( IN EINFO* pEinfo, IN HWND hwndClbAreaCodes ) /* Fill the area code list 'hwndClbAreaCodes' and set the selection to the ** area code in the entry, unless it's already been filled. 'PEinfo' is ** the common entry context. */ { DTLLIST* pList; DTLNODE* pNode; PBENTRY* pEntry; TRACE("EuFillAreaCodeList"); if (ComboBox_GetCount( hwndClbAreaCodes ) > 0) return; pList = pEinfo->pUser->pdtllistAreaCodes; ASSERT(pList); pEntry = pEinfo->pEntry; ASSERT(pEntry); ComboBox_LimitText( hwndClbAreaCodes, RAS_MaxAreaCode ); /* Add this entry's area code first. */ if (pEntry->pszAreaCode) ComboBox_AddString( hwndClbAreaCodes, pEntry->pszAreaCode ); /* Append the per-user list of area codes, skipping the one we already ** added. */ for (pNode = DtlGetFirstNode( pList ); pNode; pNode = DtlGetNextNode( pNode )) { TCHAR* pszAreaCode = (TCHAR* )DtlGetData( pNode ); if (!pEntry->pszAreaCode || lstrcmp( pszAreaCode, pEntry->pszAreaCode ) != 0) { ComboBox_AddString( hwndClbAreaCodes, pszAreaCode ); } } ComboBox_AutoSizeDroppedWidth( hwndClbAreaCodes ); /* Select the first item, which will be this entry's code, if any. */ if (ComboBox_GetCount( hwndClbAreaCodes ) > 0) ComboBox_SetCurSel( hwndClbAreaCodes, 0 ); } VOID EuFillCountryCodeList( IN EINFO* pEinfo, IN HWND hwndLbCountryCodes, IN BOOL fComplete ) /* Fill the country code list 'hwndLbCountryCodes' and set the selection ** to the one in the entry, unless it's filled already. If 'fComplete' is ** set the list is completedly filled, otherwise only the current entry's ** country code is loaded. 'PEinfo' is the common entry context. 'HwndDlg' is ** the dialog owning the */ { DWORD dwErr; COUNTRY* pCountries; COUNTRY* pCountry; DWORD cCountries; DWORD i; TRACE1("EuFillCountryCodeList(f=%d)",fComplete); /* There are 3 items in a partial list, the single visible country code ** and the dummy items before and after used to give correct behavior when ** left/right arrows are pressed. */ cCountries = ComboBox_GetCount( hwndLbCountryCodes ); if (cCountries > 3 || (!fComplete && cCountries == 3)) return; /* Release old data buffer if already partially loaded. */ if (pEinfo->pCountries) FreeCountryInfo( pEinfo->pCountries, pEinfo->cCountries ); pCountries = NULL; cCountries = 0; dwErr = GetCountryInfo( &pCountries, &cCountries, (fComplete) ? 0 : pEinfo->pEntry->dwCountryID ); if (dwErr == 0) { ComboBox_ResetContent( hwndLbCountryCodes ); if (!fComplete) { /* Add dummy item first in partial list so left arrow selection ** change can be handled correctly. See CBN_SELCHANGE handling. */ ComboBox_AddItem( hwndLbCountryCodes, TEXT("AAAAA"), (VOID* )-1 ); } for (i = 0, pCountry = pCountries; i < cCountries; ++i, ++pCountry) { INT iItem; TCHAR szBuf[ 512 ]; wsprintf( szBuf, TEXT("%s (%d)"), pCountry->pszName, pCountry->dwCode ); iItem = ComboBox_AddItem( hwndLbCountryCodes, szBuf, pCountry ); /* If it's the one in the entry, select it. */ if (pCountry->dwId == pEinfo->pEntry->dwCountryID) ComboBox_SetCurSel( hwndLbCountryCodes, iItem ); } if (!fComplete) { /* Add dummy item last in partial list so right arrow selection ** change can be handled correctly. See CBN_SELCHANGE handling. */ ComboBox_AddItem( hwndLbCountryCodes, TEXT("ZZZZZ"), (VOID* )1 ); } ComboBox_AutoSizeDroppedWidth( hwndLbCountryCodes ); if (dwErr == 0 && cCountries == 0) dwErr = ERROR_TAPI_CONFIGURATION; } if (dwErr != 0) { ErrorDlg( GetParent( hwndLbCountryCodes ), SID_OP_LoadTapiInfo, dwErr, NULL ); return; } if (ComboBox_GetCurSel( hwndLbCountryCodes ) < 0) { /* The entry's country code was not added to the list, so as an ** alternate select the first country in the list, loading the whole ** list if necessary...should be extremely rare, a diddled phonebook ** or TAPI country list strangeness. */ if (ComboBox_GetCount( hwndLbCountryCodes ) > 0) ComboBox_SetCurSel( hwndLbCountryCodes, 0 ); else { FreeCountryInfo( pCountries, cCountries ); EuFillCountryCodeList( pEinfo, hwndLbCountryCodes, TRUE ); return; } } /* Will be freed by EuFree. */ pEinfo->pCountries = pCountries; pEinfo->cCountries = cCountries; } VOID EuFillScriptsList( IN EINFO* pEinfo, IN HWND hwndLbScripts, IN TCHAR* pszSelection ) /* Fill scripts list in working entry of common entry context 'pEinfo'. ** The old list, if any, is freed. Select the script from user's entry. ** 'HwndLbScripts' is the script dropdown. 'PszSelection' is the selected ** name from the phonebook or NULL for "(none)". If the name is non-NULL ** but not found in the list it is appended. */ { DWORD dwErr; DTLNODE* pNode; INT nIndex; DTLLIST* pList; TRACE("EuFillScriptsList"); ComboBox_ResetContent( hwndLbScripts ); ComboBox_AddItemFromId( g_hinstDll, hwndLbScripts, SID_NoneSelected, NULL ); ComboBox_SetCurSel( hwndLbScripts, 0 ); pList = NULL; dwErr = LoadScriptsList( &pList ); if (dwErr != 0) { ErrorDlg( GetParent( hwndLbScripts ), SID_OP_LoadScriptInfo, dwErr, NULL ); return; } DtlDestroyList( pEinfo->pListScripts, DestroyPszNode ); pEinfo->pListScripts = pList; for (pNode = DtlGetFirstNode( pEinfo->pListScripts ); pNode; pNode = DtlGetNextNode( pNode )) { TCHAR* psz; psz = (TCHAR* )DtlGetData( pNode ); nIndex = ComboBox_AddString( hwndLbScripts, psz ); if (pszSelection && lstrcmp( psz, pszSelection ) == 0) ComboBox_SetCurSel( hwndLbScripts, nIndex ); } if (pszSelection && ComboBox_GetCurSel( hwndLbScripts ) <= 0) { nIndex = ComboBox_AddString( hwndLbScripts, pszSelection ); if (nIndex >= 0) ComboBox_SetCurSel( hwndLbScripts, nIndex ); } ComboBox_AutoSizeDroppedWidth( hwndLbScripts ); } VOID EuFillDoubleScriptsList( IN EINFO* pEinfo, IN HWND hwndLbScripts, IN TCHAR* pszSelection ) /* Fill double scripts list (switch.inf entries and .SCP files) in working ** entry of common entry context 'pEinfo'. The old list, if any, is ** freed. Select the script from user's entry. 'HwndLbScripts' is the ** script combobox. 'PszSelection' is the selected name from the ** phonebook or NULL for "(none)". If the name is non-NULL but not found ** in the list it is appended. */ { DWORD dwErr; DTLNODE* pNode; INT nIndex; DTLLIST* pList; DTLLIST* pListScp; TRACE("EuFillDoubleScriptsList"); ComboBox_ResetContent( hwndLbScripts ); ComboBox_AddItemFromId( g_hinstDll, hwndLbScripts, SID_NoneSelected, NULL ); ComboBox_SetCurSel( hwndLbScripts, 0 ); pList = NULL; dwErr = LoadScriptsList( &pList ); if (dwErr != 0) { ErrorDlg( GetParent( hwndLbScripts ), SID_OP_LoadScriptInfo, dwErr, NULL ); return; } pListScp = NULL; dwErr = EuLoadScpScriptsList( &pListScp ); if (dwErr == 0) { while (pNode = DtlGetFirstNode( pListScp )) { DtlRemoveNode( pListScp, pNode ); DtlAddNodeLast( pList, pNode ); } DtlDestroyList( pListScp, NULL ); } DtlDestroyList( pEinfo->pListDoubleScripts, DestroyPszNode ); pEinfo->pListDoubleScripts = pList; for (pNode = DtlGetFirstNode( pEinfo->pListDoubleScripts ); pNode; pNode = DtlGetNextNode( pNode )) { TCHAR* psz; psz = (TCHAR* )DtlGetData( pNode ); nIndex = ComboBox_AddString( hwndLbScripts, psz ); if (pszSelection && lstrcmp( psz, pszSelection ) == 0) ComboBox_SetCurSel( hwndLbScripts, nIndex ); } if (pszSelection && ComboBox_GetCurSel( hwndLbScripts ) <= 0) { nIndex = ComboBox_AddString( hwndLbScripts, pszSelection ); if (nIndex >= 0) ComboBox_SetCurSel( hwndLbScripts, nIndex ); } ComboBox_AutoSizeDroppedWidth( hwndLbScripts ); } VOID EuFree( IN EINFO* pInfo ) /* Releases memory associated with 'pInfo'. */ { TCHAR* psz; INTERNALARGS* piargs; piargs = (INTERNALARGS* )pInfo->pApiArgs->reserved; /* Don't clean up the phonebook and user preferences if they arrived ** via the secret hack. */ if (!piargs) { if (pInfo->pFile) ClosePhonebookFile( pInfo->pFile ); if (pInfo->pUser) DestroyUserPreferences( pInfo->pUser ); } DtlDestroyList( pInfo->pListScripts, DestroyPszNode ); DtlDestroyList( pInfo->pListDoubleScripts, DestroyPszNode ); if (pInfo->pNode) DestroyEntryNode( pInfo->pNode ); if (pInfo->pCountries) FreeCountryInfo( pInfo->pCountries, pInfo->cCountries ); /* Free router-information */ Free0(pInfo->pszRouter); Free0(pInfo->pszRouterUserName); Free0(pInfo->pszRouterDomain); if (psz = pInfo->pszRouterPassword) { ZeroMemory(psz, lstrlen(psz) * sizeof(TCHAR)); Free(psz); } if (psz = pInfo->pszRouterDialInPassword) { ZeroMemory(psz, lstrlen(psz) * sizeof(TCHAR)); Free(psz); } } VOID EuGetEditFlags( IN EINFO* pEinfo, OUT BOOL* pfEditMode, OUT BOOL* pfChangedNameInEditMode ) /* Sets '*pfEditMode' true if in edit mode, false otherwise. Set ** '*pfChangedNameInEditMode' true if the entry name was changed while in ** edit mode, false otherwise. 'PEinfo' is the common entry context. */ { if ((pEinfo->pApiArgs->dwFlags & RASEDFLAG_NewEntry) || (pEinfo->pApiArgs->dwFlags & RASEDFLAG_CloneEntry)) { *pfEditMode = *pfChangedNameInEditMode = FALSE; } else { *pfEditMode = TRUE; *pfChangedNameInEditMode = (lstrcmpi( pEinfo->szOldEntryName, pEinfo->pEntry->pszEntryName ) != 0); } } DWORD EuInit0( IN TCHAR* pszPhonebook, IN TCHAR* pszEntry, IN RASENTRYDLG* pArgs, OUT EINFO* pInfo, OUT DWORD* pdwOp ) /* Initializes 'pInfo' data just enough so the user preferences are ** available and it can be safely EuFree()ed, for use by the property ** sheet or wizard. 'PszPhonebook', 'pszEntry', and 'pArgs', are the ** arguments passed by user to the API. '*pdwOp' is set to the operation ** code associated with any error. ** ** See EuInit. ** ** Returns 0 if successful or an error code. */ { DWORD dwErr; *pdwOp = 0; ZeroMemory( pInfo, sizeof(*pInfo ) ); pInfo->pszPhonebook = pszPhonebook; pInfo->pszEntry = pszEntry; pInfo->pApiArgs = pArgs; pInfo->fRouter = RasRpcDllLoaded(); if (pInfo->fRouter) pInfo->pszRouter = StrDupTFromW(g_wszServer); /* Load the user preferences, or figure out that caller has already loaded ** them. */ if (pArgs->reserved) { INTERNALARGS* piargs; /* We've received user preferences and the "no user" status via the ** secret hack. */ piargs = (INTERNALARGS* )pArgs->reserved; pInfo->pUser = piargs->pUser; pInfo->fNoUser = piargs->fNoUser; } else { /* Read user preferences from registry. */ dwErr = g_pGetUserPreferences( &pInfo->user, (pInfo->fRouter) ? UPM_Router : UPM_Normal ); if (dwErr != 0) { *pdwOp = SID_OP_LoadPrefs; return dwErr; } pInfo->pUser = &pInfo->user; } return 0; } DWORD EuInit( OUT EINFO* pInfo, OUT DWORD* pdwOp ) /* Initializes 'pInfo' data for use by the property sheet or wizard. ** 'PszPhonebook', 'pszEntry', and 'pArgs', are the arguments passed by ** user to the API. '*pdwOp' is set to the operation code associated with ** any error. ** ** Assumes EuInit0 has been previously called. ** ** Returns 0 if successful, or an error code. */ { DWORD dwErr; DTLLIST* pListPorts; *pdwOp = 0; /* Load the phonebook file or figure out that caller already loaded it. */ if (pInfo->pApiArgs->reserved) { INTERNALARGS* piargs; /* We've received an open phonebook file, user preferences, and ** possibly user-less information via the secret hack. */ piargs = (INTERNALARGS* )pInfo->pApiArgs->reserved; pInfo->pFile = piargs->pFile; } else { /* Load and parse the phonebook file. */ dwErr = ReadPhonebookFile( pInfo->pszPhonebook, &pInfo->user, NULL, (pInfo->fRouter) ? RPBF_Router : 0, &pInfo->file ); if (dwErr != 0) { *pdwOp = SID_OP_LoadPhonebook; return dwErr; } pInfo->pFile = &pInfo->file; } /* Load the list of ports. */ dwErr = LoadPortsList2( &pListPorts, pInfo->fRouter ); if (dwErr != 0) { TRACE1("LoadPortsList=%d",dwErr); *pdwOp = SID_OP_RetrievingData; return dwErr; } if (DtlGetNodes( pListPorts ) <= 0) pInfo->fNoPortsConfigured = TRUE; /* Set up work entry node. */ if (pInfo->pApiArgs->dwFlags & RASEDFLAG_NewEntry) { DTLNODE* pNodeL; DTLNODE* pNodeP; PBLINK* pLink; PBPORT* pPort; /* New entry mode, so 'pNode' set to default settings. */ pInfo->pNode = CreateEntryNode( TRUE ); if (!pInfo->pNode) { TRACE("CreateEntryNode failed"); *pdwOp = SID_OP_RetrievingData; return dwErr; } /* Store entry within work node stored in context for convenience ** elsewhere. */ pInfo->pEntry = (PBENTRY* )DtlGetData( pInfo->pNode ); ASSERT(pInfo->pEntry); if (pInfo->fRouter) { /* Set router specific defaults. */ pInfo->pEntry->dwIpNameSource = ASRC_None; } /* Use caller's last choice for area and country code. */ if (pInfo->pUser->fUseAreaAndCountry) pInfo->pEntry->fUseCountryAndAreaCode = TRUE; /* Use caller's default name, if any. */ if (pInfo->pszEntry) pInfo->pEntry->pszEntryName = StrDup( pInfo->pszEntry ); /* Set an appropriate default device. */ pNodeL = DtlGetFirstNode( pInfo->pEntry->pdtllistLinks ); ASSERT(pNodeL); pLink = (PBLINK* )DtlGetData( pNodeL ); ASSERT(pLink); pNodeP = DtlGetFirstNode( pListPorts ); if (!pNodeP) { TRACE("No ports configured"); pNodeP = CreatePortNode(); } if (pNodeP) { pPort = (PBPORT* )DtlGetData( pNodeP ); if (pInfo->fNoPortsConfigured) { /* Make up a bogus COM port with unknown Unimodem attached. ** Hereafter, this will behave like an entry whose modem has ** been de-installed. */ pPort->pszPort = PszFromId( g_hinstDll, SID_DefaultPort ); pPort->fConfigured = FALSE; pPort->pszMedia = StrDup( TEXT(SERIAL_TXT) ); pPort->pbdevicetype = PBDT_Modem; } CopyToPbport( &pLink->pbport, pPort ); if (pLink->pbport.pbdevicetype == PBDT_Modem) SetDefaultModemSettings( pLink ); } if (!pNodeP || !pLink->pbport.pszPort || !pLink->pbport.pszMedia) { *pdwOp = SID_OP_RetrievingData; return ERROR_NOT_ENOUGH_MEMORY; } } else { DTLNODE* pNode; /* Edit or clone entry mode, so 'pNode' set to entry's current ** settings. */ pInfo->pOldNode = EntryNodeFromName( pInfo->pFile->pdtllistEntries, pInfo->pszEntry ); if (!pInfo->pOldNode) { *pdwOp = SID_OP_RetrievingData; return ERROR_CANNOT_FIND_PHONEBOOK_ENTRY; } if (pInfo->pApiArgs->dwFlags & RASEDFLAG_CloneEntry) pInfo->pNode = CloneEntryNode( pInfo->pOldNode ); else pInfo->pNode = DuplicateEntryNode( pInfo->pOldNode ); if (!pInfo->pNode) { TRACE("DuplicateEntryNode failed"); *pdwOp = SID_OP_RetrievingData; return ERROR_NOT_ENOUGH_MEMORY; } /* Store entry within work node stored in context for convenience ** elsewhere. */ pInfo->pEntry = (PBENTRY* )DtlGetData( pInfo->pNode ); /* Save original entry name for comparison later. */ lstrcpy( pInfo->szOldEntryName, pInfo->pEntry->pszEntryName ); /* For router, want unconfigured ports to show up as "unavailable" so ** they stand out to user who has been directed to change them. */ if (pInfo->fRouter) { DTLNODE* pNodeL; PBLINK* pLink; pNodeL = DtlGetFirstNode( pInfo->pEntry->pdtllistLinks ); pLink = (PBLINK* )DtlGetData( pNodeL ); if (!pLink->pbport.fConfigured) { Free0( pLink->pbport.pszDevice ); pLink->pbport.pszDevice = NULL; } } } /* Append links containing all remaining configured ports to the list of ** links with the new links marked "unenabled". This lets us make the ** port<->port and single-link<->multi-link transitions behave more as ** user expects. */ { DTLNODE* pNodeP; DTLNODE* pNodeL; for (pNodeP = DtlGetFirstNode( pListPorts ); pNodeP; pNodeP = DtlGetNextNode( pNodeP )) { PBPORT* pPort; BOOL fPortUsed; pPort = (PBPORT* )DtlGetData( pNodeP ); fPortUsed = FALSE; for (pNodeL = DtlGetFirstNode( pInfo->pEntry->pdtllistLinks ); pNodeL; pNodeL = DtlGetNextNode( pNodeL )) { PBLINK* pLink = (PBLINK* )DtlGetData( pNodeL ); ASSERT( pPort->pszPort ); ASSERT( pLink->pbport.pszPort ); if (lstrcmp( pLink->pbport.pszPort, pPort->pszPort ) == 0) { fPortUsed = TRUE; break; } } if (!fPortUsed) { DTLNODE* pNode; pNode = CreateLinkNode(); if (pNode) { PBLINK* pLink = (PBLINK* )DtlGetData( pNode ); if (CopyToPbport( &pLink->pbport, pPort ) != 0) DestroyLinkNode( pNode ); else { if (pPort->pbdevicetype == PBDT_Modem) SetDefaultModemSettings( pLink ); pLink->fEnabled = FALSE; DtlAddNodeLast( pInfo->pEntry->pdtllistLinks, pNode ); } } } } } DtlDestroyList( pListPorts, DestroyPortNode ); if (pInfo->fRouter) { pInfo->pEntry->dwfExcludedProtocols |= NP_Nbf; pInfo->pEntry->fIpPrioritizeRemote = FALSE; } return 0; } VOID EuLbCountryCodeSelChange( IN EINFO* pEinfo, IN HWND hwndLbCountryCodes ) /* Called when the country list selection has changed. 'PEinfo' is the ** common entry context. 'HwndLbCountryCode' is the country code ** combobox. */ { LONG lSign; LONG i; TRACE("EuLbCountryCodeSelChange"); /* Make sure all the country codes are loaded. When a partial list is ** there are dummy entries placed before and after the single country ** code. This allows us to give transparent behavior when user presses ** left/right arrows to change selection. */ lSign = (LONG )ComboBox_GetItemData( hwndLbCountryCodes, ComboBox_GetCurSel( hwndLbCountryCodes ) ); if (lSign != -1 && lSign != 1) lSign = 0; EuFillCountryCodeList( pEinfo, hwndLbCountryCodes, TRUE ); i = (LONG )ComboBox_GetCurSel( hwndLbCountryCodes ); if (ComboBox_SetCurSel( hwndLbCountryCodes, i + lSign ) < 0) ComboBox_SetCurSel( hwndLbCountryCodes, i ); } DWORD EuLoadScpScriptsList( OUT DTLLIST** ppList ) /* Loads '*ppList' with a list of Psz nodes containing the pathnames of ** the .SCP files in the RAS directory. It is caller's responsibility to ** call DtlDestroyList on the returned list. ** ** Returns 0 if successful or an error code. */ { UINT cch; TCHAR szPath[ MAX_PATH ]; TCHAR* pszFile; WIN32_FIND_DATA data; HANDLE h; DTLLIST* pList; cch = g_pGetSystemDirectory( szPath, MAX_PATH ); if (cch == 0) return GetLastError(); pList = DtlCreateList( 0L ); if (!pList) return ERROR_NOT_ENOUGH_MEMORY; lstrcat( szPath, TEXT("\\ras\\*.scp") ); h = FindFirstFile( szPath, &data ); if (h != INVALID_HANDLE_VALUE) { /* Find the address of the file name part of the path since the 'data' ** provides only the filename and not the rest of the path. */ pszFile = szPath + lstrlen( szPath ) - 5; do { DTLNODE* pNode; /* Ignore any directories that happen to match. */ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; /* Create a Psz node with the path to the found file and append it ** to the end of the list. */ lstrcpy( pszFile, data.cFileName ); pNode = CreatePszNode( szPath ); if (!pNode) continue; DtlAddNodeLast( pList, pNode ); } while (FindNextFile( h, &data )); FindClose( h ); } *ppList = pList; return 0; } VOID EuPhoneNumberStashFromEntry( IN EINFO* pEinfo, IN OUT DTLLIST** ppListPhoneNumbers, OUT BOOL* pfPromoteHuntNumbers ) /* Replace single link stashed phone number settings with first link ** versions. 'PEinfo' is the common entry context. '*ppListPhoneNumbers' ** is the current stash phone number list or NULL if none. ** 'PfPromoteHuntNumber' receives the "promote hunt number" flag. */ { DTLLIST* pList; DTLNODE* pNode; PBLINK* pLink; TRACE("EuPhoneNumberStashFromEntry"); pNode = DtlGetFirstNode( pEinfo->pEntry->pdtllistLinks ); ASSERT(pNode); pLink = (PBLINK* )DtlGetData( pNode ); ASSERT(pLink); ASSERT(pLink->pdtllistPhoneNumbers); pList = DtlDuplicateList( pLink->pdtllistPhoneNumbers, DuplicatePszNode, DestroyPszNode ); if (pList) { DtlDestroyList( *ppListPhoneNumbers, DestroyPszNode ); *ppListPhoneNumbers = pList; } *pfPromoteHuntNumbers = pLink->fPromoteHuntNumbers; } VOID EuPhoneNumberStashToEntry( IN EINFO* pEinfo, IN DTLLIST* pListPhoneNumbers, IN BOOL fPromoteHuntNumbers, IN BOOL fAllEnabled ) /* Replace first link, or all enabled links if 'fAllEnabled' is set, phone ** number settings with the single link stash versions. 'PEinfo' is the ** common entry context. 'PListPhoneNumbers' is the current stash list. ** 'fPromoteHuntNumbers' is the current "promote hunt number" flag. */ { DTLLIST* pList; DTLNODE* pNode; PBLINK* pLink; TRACE("EuPhoneNumberStashToEntry"); for (pNode = DtlGetFirstNode( pEinfo->pEntry->pdtllistLinks ); pNode; pNode = DtlGetNextNode( pNode )) { pLink = (PBLINK* )DtlGetData( pNode ); ASSERT(pLink); ASSERT(pListPhoneNumbers ); if (fAllEnabled && !pLink->fEnabled) continue; pList = DtlDuplicateList( pListPhoneNumbers, DuplicatePszNode, DestroyPszNode ); if (pList) { DtlDestroyList( pLink->pdtllistPhoneNumbers, DestroyPszNode ); pLink->pdtllistPhoneNumbers = pList; } pLink->fPromoteHuntNumbers = fPromoteHuntNumbers; if (!fAllEnabled) break; } } VOID EuSaveCountryInfo( IN EINFO* pEinfo, IN HWND hwndLbCountryCodes ) /* Save the country code and ID from 'hwndLbCountryCodes' into the working ** entry in common entry context 'pEinfo'. */ { if (pEinfo->pCountries) { COUNTRY* pCountry; INT iSel; iSel = ComboBox_GetCurSel( hwndLbCountryCodes ); if (iSel < 0) return; pCountry = (COUNTRY* )ComboBox_GetItemDataPtr( hwndLbCountryCodes, iSel ); ASSERT(pCountry); pEinfo->pEntry->dwCountryID = pCountry->dwId; pEinfo->pEntry->dwCountryCode = pCountry->dwCode; } } BOOL EuValidateAreaCode( IN HWND hwndOwner, IN EINFO* pEinfo ) /* Validates the area code in the working buffer popping up a message if ** invalid. 'HwndOwner' is the window that owns the popup message, if ** any. 'PEinfo' is the common entry context. ** ** Returns true if valid, false if not. */ { if (!ValidateAreaCode( pEinfo->pEntry->pszAreaCode )) { /* Invalid area code. If it's disabled anyway, just silently lose it. */ if (pEinfo->pEntry->fUseCountryAndAreaCode) { MsgDlg( hwndOwner, SID_BadAreaCode, NULL ); return FALSE; } else *pEinfo->pEntry->pszAreaCode = TEXT('\0'); } return TRUE; } BOOL EuValidateName( IN HWND hwndOwner, IN EINFO* pEinfo ) /* Validates the working entry name and pops up a message if invalid. ** 'HwndOwner' is the window to own the error popup. 'PEinfo' is the ** common dialog context containing the name to validate. ** ** Returns true if the name is valid, false if not. */ { PBENTRY* pEntry; BOOL fEditMode; BOOL fChangedNameInEditMode; pEntry = pEinfo->pEntry; /* Validate the sheet data. */ if (!ValidateEntryName( pEinfo->pEntry->pszEntryName )) { /* Invalid entry name. */ MsgDlg( hwndOwner, SID_BadEntry, NULL ); return FALSE; } EuGetEditFlags( pEinfo, &fEditMode, &fChangedNameInEditMode ); if ((fChangedNameInEditMode || !fEditMode) && EntryNodeFromName( pEinfo->pFile->pdtllistEntries, pEntry->pszEntryName )) { /* Duplicate entry name. */ MSGARGS msgargs; ZeroMemory( &msgargs, sizeof(msgargs) ); msgargs.apszArgs[ 0 ] = pEntry->pszEntryName; MsgDlg( hwndOwner, SID_DuplicateEntry, &msgargs ); return FALSE; } return TRUE; }