//+----------------------------------------------------------------------------
//
// File:     ras.cpp     
//
// Module:   CMDIAL32.DLL
//
// Synopsis: This module contains the functions to allow Connection Manager to
//           interact with RAS.
//
// Copyright (c) 1997-1999 Microsoft Corporation
//
// Author:   byao       created         04/29/97
//           quintinb   created Header  08/16/99
//
//+----------------------------------------------------------------------------

#include "cmmaster.h"
#include "compchck.h"
#include "dial_str.h"
#include "dun_str.h"
#include "tunl_str.h"
#include "stp_str.h"
#include "ras_str.h"
#include "dialogs.h"

#include <cmdefs.h> // located in net\inc

//
// CMS flags use to specify DUN settings. These entries are specific
// to this code module, shared entries are stored on dun_str.h
//

const TCHAR* const c_pszCmSectionDunPhone                   = TEXT("Phone");
const TCHAR* const c_pszCmEntryDunPhoneDialAsIs             = TEXT("Dial_As_Is");
const TCHAR* const c_pszCmEntryDunPhonePhoneNumber          = TEXT("Phone_Number");
const TCHAR* const c_pszCmEntryDunPhoneAreaCode             = TEXT("Area_Code");
const TCHAR* const c_pszCmEntryDunPhoneCountryCode          = TEXT("Country_Code");
const TCHAR* const c_pszCmEntryDunPhoneCountryId            = TEXT("Country_ID");
const TCHAR* const c_pszCmSectionDunDevice                  = TEXT("Device");
const TCHAR* const c_pszCmEntryDunDeviceType                = TEXT("Type");
const TCHAR* const c_pszCmEntryDunDeviceName                = TEXT("Name");
const TCHAR* const c_pszCmEntryHideTrayIcon                 = TEXT("HideTrayIcon");
const TCHAR* const c_pszCmEntryInternetConnection           = TEXT("InternetConnection");

//
// the following reg key and value control whether Dial-Up Networking on Win95
// will start the Wizard.  Note that these are explicitly chars instead of TCHARs
//
const CHAR* const c_pszRegRemoteAccess = "RemoteAccess";
const CHAR* const c_pszRegWizard       = "wizard";

#define ICM_RAS_REG_WIZARD_VALUE        0x00000080

//
// .CMS flags used only by ras.cpp
//

const TCHAR* const c_pszCmEntryDialExtraPercent         = TEXT("DialExtraPercent"); 
const TCHAR* const c_pszCmEntryDialExtraSampleSeconds   = TEXT("DialExtraSampleSeconds"); 
const TCHAR* const c_pszCmEntryHangUpExtraPercent         = TEXT("HangUpExtraPercent"); 
const TCHAR* const c_pszCmEntryHangUpExtraSampleSeconds   = TEXT("HangUpExtraSampleSeconds"); 

//
//  This file includes the definitions of c_ArrayOfRasFuncsW and c_ArrayOfRasFuncsUA below
//
#include "raslink.cpp"

//+----------------------------------------------------------------------------
//
// Function:  LinkToRas
//
// Synopsis: Establishes the RAS linkage by populating the inputted RAS Linkage structure
//           with function pointers from Rasapi32.dll (on NT) or from cmutoa.dll (Unicode
//           to ANSI wrapper functions used on win9x).  Most of the actual work is done
//           in LinkToDll, this function just does setup work to make sure the correct
//           entry points are searched for and that Cmutoa.dll is initialized properly (since it
//           needs to link to rasapi32.dll itself to get the actual ANSI RAS API's to work with).
//
// Arguments: RasLinkageStruct *prlsRasLink - pointer to a RAS Linkage structure.  This
//                                            structure contains storage for pointers to
//                                            the RAS dll and all of the needed RAS
//                                            function pointers.
//
// Returns:   BOOL - FALSE if *any* entry point is still not resolved.
//
// History:   quintinb Created Header    01/04/2000
//
//+----------------------------------------------------------------------------
BOOL LinkToRas(RasLinkageStruct *prlsRasLink) 
{
    BOOL bReturn = TRUE;

    //
    //  Check inputs
    //
    if (NULL == prlsRasLink)
    {
        return FALSE;
    }

    if (OS_NT)
    {
        //
        //  On NT we get our RAS APIs from rasapi32.dll and we ask for the W version
        //  of the API.
        //
        if (OS_NT4) 
        {
            c_ArrayOfRasFuncsW[11] = NULL; //RasDeleteSubEntry
            c_ArrayOfRasFuncsW[12] = NULL; //RasSetCustomAuthData
            c_ArrayOfRasFuncsW[13] = NULL; //RasGetEapUserIdentity
            c_ArrayOfRasFuncsW[14] = NULL; //RasFreeEapUserIdentity
            c_ArrayOfRasFuncsW[15] = NULL; //RasInvokeEapUI
            c_ArrayOfRasFuncsW[16] = NULL; //pfnGetCredentials
            c_ArrayOfRasFuncsW[17] = NULL; //pfnSetCredentials
        }
        else if (OS_W2K)
        {
            //
            //  Special-casing for APIs that changed after Windows2000 shipped
            //
            c_ArrayOfRasFuncsW[11] = "DwDeleteSubEntry";   //RasDeleteSubEntry is DwDeleteSubEntry on Win2k
        }

        bReturn = LinkToDll(&prlsRasLink->hInstRas, "RASAPI32.DLL", c_ArrayOfRasFuncsW,
                            prlsRasLink->apvPfnRas);
    }
    else
    {
        //
        //  On Win9x we still want the W version of the API but since it isn't available we
        //  call the wrappers in cmutoa.dll instead.  Thus we use cmutoa.dll as our RAS API dll
        //  and call the UA APIs.  We also have an extra step because we want to make sure 
        //  that cmutoa.dll can actually initialize the RAS dll's that it uses for the UA 
        //  conversion functions.  Thus we call cmutoa's InitCmRasUtoA function to set up 
        //  its internal RAS linkage.  If this function fails, we must fail the RAS link.

        typedef BOOL (WINAPI *pfnInitCmRasUtoASpec)(void);

        pfnInitCmRasUtoASpec InitCmRasUtoA;
        HMODULE hCmUtoADll = LoadLibraryExA("cmutoa.DLL", NULL, 0); // REVIEW: this should use getmodulehandle so as not to change the refcount on the dll.
        
        if (!hCmUtoADll)
        {            
            return FALSE;
        }

        // Get Initialization routine from the DLL
        InitCmRasUtoA = (pfnInitCmRasUtoASpec) GetProcAddress(hCmUtoADll, "InitCmRasUtoA") ;

        if (InitCmRasUtoA)
        {
            bReturn = InitCmRasUtoA();
            if (bReturn)
            {
                if (!OS_MIL)
                {
                    c_ArrayOfRasFuncsUA[10] = NULL; //RasSetSubEntryProperties
                    c_ArrayOfRasFuncsUA[11] = NULL; //RasDeleteSubEntry
                }

                bReturn = LinkToDll(&prlsRasLink->hInstRas, "CMUTOA.DLL", c_ArrayOfRasFuncsUA, 
                                    prlsRasLink->apvPfnRas);
            }
        }

        FreeLibrary(hCmUtoADll); // we want this to stay in memory but the refcount should also be correct
    }

    return bReturn;
}

BOOL IsRasLoaded(const RasLinkageStruct * const prlsRasLink)
{
    UINT uIndex = 0;

    //
    //  Did we get a valid pointer passed in and does that
    //  struct contain a pointer to a RAS dll?
    //
    BOOL bReturn = (NULL != prlsRasLink) && (NULL != prlsRasLink->hInstRas);

    //
    //  The list of functions we are checking for is different on NT
    //  and Win9x.  Note that we also assume that LinkToRas has already
    //  been called so that the list of functions we are expecting will
    //  have been modified for the exact platform that we are one.  If
    //  LinkToRas hasn't been called then the hInstRas param should be
    //  NULL.
    //
    if (OS_NT)
    {
        while (bReturn && (NULL != c_ArrayOfRasFuncsW[uIndex]))
        {
            //
            //  Check for a NULL function pointer when we have
            //  a valid function name.
            //
            if (NULL == prlsRasLink->apvPfnRas[uIndex])
            {
                bReturn = FALSE;
            }

           uIndex++;
        }
    }
    else
    {
        while (bReturn && (NULL != c_ArrayOfRasFuncsUA[uIndex]))
        {
            //
            //  Check for a NULL function pointer when we have
            //  a valid function name.
            //
            if (NULL == prlsRasLink->apvPfnRas[uIndex])
            {
                bReturn = FALSE;
            }

           uIndex++;
        }    
    }

    return bReturn;
}

//+----------------------------------------------------------------------------
//
// Function:  UnlinkFromRas
//
// Synopsis:  This function tears down the linkage with RAS by freeing RAS dll's, calling
//           the cmutoa unklinkage function (if necessary), and zeroing the RAS Linkage
//           structure passed in.
//
// Arguments: RasLinkageStruct *prlsRasLink - pointer to a RAS Linkage structure.  This
//                                        structure contains storage for pointers to
//                                        the RAS dll and all of the needed RAS
//                                        function pointers.
//
// Returns:   Nothing
//
// History:   quintinb Created Header    01/04/2000
//
//+----------------------------------------------------------------------------
void UnlinkFromRas(RasLinkageStruct *prlsRasLink) 
{
    if (!OS_NT)
    {
        HMODULE hCmUtoADll = LoadLibraryExA("cmutoa.dll", NULL, 0);
        
        if (!hCmUtoADll)
        {         
            CMASSERTMSG(FALSE, TEXT("UnlinkFromRas -- Unable to load cmutoa."));
            return;
        }

        FARPROC FreeCmRasUtoA = GetProcAddress(hCmUtoADll, "FreeCmRasUtoA");

        if (FreeCmRasUtoA)
        {
            FreeCmRasUtoA();
        }

        FreeLibrary(hCmUtoADll);
    }

    if (prlsRasLink->hInstRas) 
    {
        FreeLibrary(prlsRasLink->hInstRas);
    }

    memset(prlsRasLink,0,sizeof(RasLinkageStruct));
}

//
// GetRasModems: get a list of modem devices from RAS
//
//+----------------------------------------------------------------------------
//
// Function:  GetRasModems
//
// Synopsis:  Enumerates the available RAS devices.  The device list is allocated and passed
//           back to the caller through the pprdiRasDevInfo pointer.  This allocated memory
//           must be freed by the caller.  The count of available devices is stored in the
//           pdwCnt input parameter.
//
// Arguments: RasLinkageStruct *prlsRasLink - pointer to the RAS Linkage structure
//            LPRASDEVINFO *pprdiRasDevInfo - pointer to hold the RAS device list
//            LPDWORD pdwCnt - pointer to hold the count of devices
//
// Returns:   BOOL - FALSE if unable to return the enumerated device list.
//
// History:   quintinb Created Header    01/04/2000
//
//+----------------------------------------------------------------------------

BOOL GetRasModems(const RasLinkageStruct *prlsRasLink, 
                                  LPRASDEVINFO *pprdiRasDevInfo, 
                                  LPDWORD pdwCnt) 
{
    DWORD dwLen;
    DWORD dwRes;
    DWORD dwCnt;

    if (pprdiRasDevInfo) 
    {
        *pprdiRasDevInfo = NULL;
    }
        
    if (pdwCnt) 
    {
        *pdwCnt = 0;
    }
        
    if (!prlsRasLink->pfnEnumDevices) 
    {
        return (FALSE);
    }
        
    dwLen = 0;
    dwRes = prlsRasLink->pfnEnumDevices(NULL,&dwLen,&dwCnt);

    CMTRACE3(TEXT("GetRasModems() RasEnumDevices(NULL,pdwLen,&dwCnt) returns %u, dwLen=%u, dwCnt=%u."), 
        dwRes, dwLen, dwCnt);
        
    if (((dwRes != ERROR_SUCCESS) && (dwRes != ERROR_BUFFER_TOO_SMALL)) || (dwLen < sizeof(**pprdiRasDevInfo))) 
    {
        return (FALSE);
    }

    if (!pprdiRasDevInfo) 
    {
        if (pdwCnt)
        {
            *pdwCnt = dwCnt;
        }
        return (TRUE);
    }
        
    *pprdiRasDevInfo = (LPRASDEVINFO) CmMalloc(__max(dwLen,sizeof(**pprdiRasDevInfo)));

    if (*pprdiRasDevInfo)
    {
        (*pprdiRasDevInfo)->dwSize = sizeof(**pprdiRasDevInfo);
        dwRes = prlsRasLink->pfnEnumDevices(*pprdiRasDevInfo,&dwLen,&dwCnt);

        CMTRACE3(TEXT("GetRasModems() RasEnumDevices(*pprdiRasDevInfo,&dwLen,&dwCnt) returns %u, dwLen=%u, dwCnt=%u."), 
                 dwRes, dwLen, dwCnt);

        if (dwRes != ERROR_SUCCESS) 
        {
            CmFree(*pprdiRasDevInfo);
            *pprdiRasDevInfo = NULL;
            return (FALSE);
        }
    }
    else
    {
        CMASSERTMSG(FALSE, TEXT("GetRasModems -- CmMalloc failed to allocate memory for *pprdiRasDevInfo."));
        return (FALSE);
    }
    
    if (pdwCnt)
    {
        *pdwCnt = dwCnt;
    }

    return (TRUE);
}


//+----------------------------------------------------------------------------
//
// Function:  PickModem
//
// Synopsis:  
//
// Arguments: const pArgs, the pArgs->pIniProfile contains the modem name
//            OUT pszDeviceType, the device type if not NULL
//            OUT pszDeviceName, the device name if not NULL
//            OUT pfSameModem,   Whether the modem found is the same as 
//                               the one in profile
//
// Returns:   TRUE, is modem is found
//
// History:   fengsun Created Header    10/24/97
//
//+----------------------------------------------------------------------------
BOOL PickModem(IN const ArgsStruct *pArgs, OUT LPTSTR pszDeviceType, 
               OUT LPTSTR pszDeviceName, OUT BOOL* pfSameModem) 
{
    LPRASDEVINFO prdiModems;
    DWORD dwCnt;
    LPTSTR pszModem;
    DWORD dwIdx;
    BOOL bFound = FALSE;

    //
    // First, get a list of modems from RAS
    //
    
    if (!GetRasModems(&pArgs->rlsRasLink,&prdiModems,&dwCnt) || dwCnt == 0) 
    {
        return (FALSE);
    }

    if (pfSameModem)
    {
        *pfSameModem = FALSE;
    }

    //
    // Get the name of the current modem from the service profile and 
    // try to find a match against non-tunnel RAS devices in the list 
    //
    pszModem = pArgs->piniProfile->GPPS(c_pszCmSection, c_pszCmEntryDialDevice);

    if (*pszModem) 
    {
        CMTRACE1(TEXT("PickModem() - looking for match with %s"), pszModem);

        for (dwIdx=0; dwIdx < dwCnt; dwIdx++) 
        {               
            CMTRACE2(TEXT("PickModem() - examining device (%s) of type (%s)"), prdiModems[dwIdx].szDeviceName, prdiModems[dwIdx].szDeviceType);

            // 
            // we'll take only ISDN and modem devices
            //
            if (lstrcmpiU(prdiModems[dwIdx].szDeviceType, RASDT_Isdn) &&
                lstrcmpiU(prdiModems[dwIdx].szDeviceType, RASDT_Modem) &&
                lstrcmpiU(prdiModems[dwIdx].szDeviceType, RASDT_Atm))
            {
                continue;
            }

            // 
            // If we have a match, we're done here
            //

            if (lstrcmpiU(pszModem,prdiModems[dwIdx].szDeviceName) == 0) 
            {
                CMTRACE(TEXT("PickModem() - match found."));
                bFound = TRUE;
                if (pfSameModem)
                {
                    *pfSameModem = TRUE;
                }
                break;
            }
        }
    }

    if (FALSE == bFound)
    {
        //
        // No match, find the first non-tunnel device and use it by default.
        //

        CMTRACE(TEXT("PickModem() - enumerating devices for default match against type RASDT_Isdn, RASDT_Modem or RASDT_Atm")); 
        
        for (dwIdx=0; dwIdx < dwCnt; dwIdx++) 
        {
            CMTRACE2(TEXT("PickModem() - examining device (%s) of type (%s)"), prdiModems[dwIdx].szDeviceName, prdiModems[dwIdx].szDeviceType); 
            
            //
            // we'll take only ISDN and modem devices
            //

            if (!lstrcmpiU(prdiModems[dwIdx].szDeviceType, RASDT_Isdn) ||
                 !lstrcmpiU(prdiModems[dwIdx].szDeviceType, RASDT_Modem) ||
                 !lstrcmpiU(prdiModems[dwIdx].szDeviceType, RASDT_Atm)) 
            {
                CMTRACE2(TEXT("PickModem() - default device (%s) of type (%s) selected."), prdiModems[dwIdx].szDeviceName, prdiModems[dwIdx].szDeviceType);
                bFound = TRUE;
                break;
            }
        }
    }

    // 
    // If we have a match, fill device name and device type
    //

    if (bFound)
    {
        if (pszDeviceType) 
        {
            lstrcpyU(pszDeviceType,prdiModems[dwIdx].szDeviceType);
        }
        
        if (pszDeviceName) 
        {
            lstrcpyU(pszDeviceName,prdiModems[dwIdx].szDeviceName);
        }
    }

    CmFree(pszModem);
    CmFree(prdiModems);
    return (bFound);
}

//+----------------------------------------------------------------------------
//
// Function:  GetDeviceType
//
// Synopsis:  Get the deviceType for a chosen device name
//
// Arguments: pArgs - Pointer to ArgsStruct
//            pszDeviceType[OUT] - pointer to buffer where device 
//                                 type will be returned
//            uNumCharsInDeviceType [IN] - number of chars of memory available in pszDeviceType
//            pszDeviceName[IN] - device name
//
// Returns:   TRUE on success, FALSE otherwise
//
// History:   byao  Created  03/21/97
//-----------------------------------------------------------------------------
BOOL GetDeviceType(ArgsStruct *pArgs, LPTSTR pszDeviceType, UINT uNumCharsInDeviceType, LPTSTR pszDeviceName)
{
    LPRASDEVINFO prdiModems;
    DWORD dwCnt, dwIdx;

    if (!pszDeviceType)
    {
        return FALSE;
    }

    // first, get a list of modems from RAS
    if (!GetRasModems(&pArgs->rlsRasLink,&prdiModems,&dwCnt)) 
    {
        return (FALSE);
    }

    // choose the device that has the same name as pszDeviceName
    for (dwIdx=0;dwIdx<dwCnt;dwIdx++) 
    {
        if (lstrcmpiU(pszDeviceName,prdiModems[dwIdx].szDeviceName) == 0) 
        {
            lstrcpynU(pszDeviceType, prdiModems[dwIdx].szDeviceType, uNumCharsInDeviceType);
            break;
        }
    }

    CmFree(prdiModems);

    if (dwIdx == dwCnt)  // not found in the modem list -- strange things happened
    {
        return FALSE; 
    }

    return TRUE;
}

//+----------------------------------------------------------------------------
//
//      Function        PickTunnelDevice
//
//      Synopsis        pick a tunnel device used to dial out
//
//      Arguments       
//                              pszDeviceType   Tunnel device type. -- RASDT_Vpn
//                              pszDeviceName   Tunnel device name
//                              prdiModems              Pointer to a list of modems
//                              dwCnt                   Total number of modems available in the system
//
//      Returns         TRUE                    succeed
//                              FALSE                   otherwise
//
//      History         3/1/97          byao            Created
//
//-----------------------------------------------------------------------------
BOOL PickTunnelDevice(LPTSTR pszDeviceType, 
                                          LPTSTR pszDeviceName, 
                                          LPRASDEVINFO prdiModems, 
                                          DWORD dwCnt) 
{
    DWORD dwIdx;

    if (dwCnt == 0) 
    {
        return (FALSE);
    }
    
    for (dwIdx=0;dwIdx<dwCnt;dwIdx++) 
    {
        if (lstrcmpiU(pszDeviceType,prdiModems[dwIdx].szDeviceType) == 0) 
        {
            break;
        }
    }

    if (dwIdx == dwCnt) 
    {
        return (FALSE);
    }

    if (pszDeviceType) 
    {
        lstrcpyU(pszDeviceType,prdiModems[dwIdx].szDeviceType);
    }

    if (pszDeviceName) 
    {
        lstrcpyU(pszDeviceName,prdiModems[dwIdx].szDeviceName);
    }

        return (TRUE);
}

//+----------------------------------------------------------------------------
//
//      Function        PickTunnelDevice
//
//      Synopsis        pick a tunnel device used to dial out
//
//      Arguments       pArgs                   Pointer to ArgsStruct
//                              pszDeviceType   Tunnel device type. --  RASDT_Vpn
//                              pszDeviceName   Tunnel device name
//
//      Returns         TRUE - scripting has been installed
//
//      History         3/1/97          byao            Created
//
//-----------------------------------------------------------------------------
BOOL PickTunnelDevice(ArgsStruct *pArgs, 
                                          LPTSTR pszDeviceType, 
                                          LPTSTR pszDeviceName) 
{
        LPRASDEVINFO prdiModems;
        DWORD dwCnt;
        BOOL bRes;

        // first, get a list of modems from RAS
        if (!GetRasModems(&pArgs->rlsRasLink,&prdiModems,&dwCnt)) 
    {
                return (FALSE);
        }

        // then, pick up the one used for tunneling
        bRes = PickTunnelDevice(pszDeviceType,pszDeviceName,prdiModems,dwCnt);
        CmFree(prdiModems);
        return (bRes);
}

//+----------------------------------------------------------------------------
//
// Function:  CopyAutoDial
//
// Synopsis:  Sets the szAutodialDll and szAutodialFunc members of the 
//            specified RAS entry structure with our module name and
//            InetDialHandler repectively. Not on NT5.
//
// Arguments: LPRASENTRY preEntry - Ptr to the Ras entry structure.
//
// Returns:   Nothing
//
// History:   nickball    Created Header    03/16/98
//            nickball    Removed from NT5  11/17/98
//
//+----------------------------------------------------------------------------
void CopyAutoDial(LPRASENTRY preEntry) 
{
    MYDBGASSERT(preEntry);

    //
    // Don't set these on NT5, they are no longer used by IE and the 
    // InetDialHandler prototype differs from that used by RAS
    //

    if (OS_NT5 || NULL == preEntry)
    {
        return;
    }

    memset(preEntry->szAutodialDll,0,sizeof(preEntry->szAutodialDll));

    //
    // Set szAutodialDll with our Module name
    //

    GetModuleFileNameU(g_hInst, preEntry->szAutodialDll, sizeof(preEntry->szAutodialDll)/sizeof(TCHAR));    

    //
    // Set szAutodialFunc with the mangled form of InetDialHandler
    //

    memset(preEntry->szAutodialFunc,0,sizeof(preEntry->szAutodialFunc));
    lstrcpyU(preEntry->szAutodialFunc, c_pszInetDialHandler);
}

//+----------------------------------------------------------------------------
//
//      Function        MyRGEP
//
//      Synopsis        Call RasGetEntryProperties()
//                              
//      Arguments       
//
//      Returns         
//
//      Histroy         Revised to improve performance  08/7/97 fengsun
//-----------------------------------------------------------------------------
LPRASENTRY MyRGEP(LPCTSTR pszRasPbk, LPCTSTR pszEntryName, RasLinkageStruct *prlsRasLink) 
{
    LPRASENTRY preRasEntry;
    DWORD dwRes;

    if (!(preRasEntry = AllocateRasEntry()))
    {
        MYDBGASSERT(0);
        return NULL;
    }

    DWORD dwRasEntry = preRasEntry->dwSize;

    dwRes = prlsRasLink->pfnGetEntryProperties(pszRasPbk,
                                               pszEntryName,
                                               preRasEntry,
                                               &dwRasEntry,
                                               NULL,  // lpbDeviceInfo
                                               NULL); // lpdwDeviceInfoSize

    CMTRACE2(TEXT("MyRGEP() - dwRasEntry = %u : sizeof(*preRasEntry) = %u"), dwRasEntry, sizeof(*preRasEntry));
    
    if ((dwRes == ERROR_BUFFER_TOO_SMALL) && (dwRasEntry >= sizeof(*preRasEntry))) 
    {
        //
        // If the memory if not large enough, realloc one
        //
        CmFree(preRasEntry);

        preRasEntry = (LPRASENTRY) CmMalloc(dwRasEntry);

        if (NULL != preRasEntry)
        {
            //
            // dwSize has to be set to sizeof(RASENTRY)
            // because dwRasEntry contains the additional
            // bytes required for this connectoid (alternative
            // phone numbers, etc.
            //
            preRasEntry->dwSize = sizeof(RASENTRY); // Specifies version

            dwRes = prlsRasLink->pfnGetEntryProperties (pszRasPbk,
                                                        pszEntryName,
                                                        preRasEntry,
                                                        &dwRasEntry,
                                                        NULL,
                                                        NULL);
        }
        else
        {
            MYDBGASSERT(0);
            return NULL;
        }
    }

    if (dwRes != ERROR_SUCCESS) 
    {
        CMTRACE3(TEXT("MyRGEP(*pszRasPbk=%s, *pszEntryName=%s) RasGetEntryProperties returned %u"), pszRasPbk, pszEntryName, dwRes);
        CmFree(preRasEntry);
        preRasEntry = NULL;
    }

    SetLastError(dwRes);
    return (preRasEntry);
}

//+----------------------------------------------------------------------------
//
//  Function    IsConnectErrorFatal
//
//  Synopsis    Determine if an error is recoverable, (ie. we should re-dial). 
//
//  Arguments   DWORD dwErr             - The RAS error code
//              ArgsStruct* pArgs       - Ptr to global args struct
//
//  Returns     TRUE if error is fatal
//
//  Histroy     nickball    Created header  05/21/99     
//    
//-----------------------------------------------------------------------------
BOOL IsConnectErrorFatal(DWORD dwErr, ArgsStruct *pArgs)
{
    switch (dwErr)
    {
        //
        // The following cases are W9x ISDN error returns that actually mean
        // different things on WinNT.  Since we use the NT header files, we don't
        // have an include file that contains these errors.  We have to special
        // case these so that we recognize them as ISDN errors, and reconnect as
        // appropriate.
        //
        // The 9x errors are listed below along with the NT equivalents.
        //

    case 751:       // 9x.ERROR_BAD_DEST_ADDRESS    == NT.ERROR_INVALID_CALLBACK_NUMBER 
    case 752:       // 9x.ERROR_UNREACHABLE_DEST    == NT.ERROR_SCRIPT_SYNTAX
    case 753:       // 9x.ERROR_INCOMPATIBLE_DEST   == NT.ERROR_HANGUP_FAILED
    case 754:       // 9x.ERROR_NETWORK_CONGESTION  == NT.ERROR_BUNDLE_NOT_FOUND
    case 755:       // 9x.ERROR_CALL_BLOCKED        == NT.ERROR_CANNOT_DO_CUSTOMDIAL
    case 756:       // 9x.ERROR_NETWORK_TEMPFAILURE == NT.ERROR_DIAL_ALREADY_IN_PROGRESS
        if (OS_W9X)
        {
            //
            // On W9x, if you have an invalid ISDN number, the error codes
            // returned by Millennium RAS are different from the NT ones.
            // We have to special-case these by number so that we reconnect
            //
            CMTRACE1(TEXT("IsConnectErrorFatal : handled Win9x ISDN error %d"), dwErr);
            return FALSE;
        }
        break;

    case ERROR_PPP_TIMEOUT:             // Timed out waiting for a valid response from the remote PPP peer.%0
    case ERROR_PPP_REMOTE_TERMINATED:   // PPP terminated by remote machine.%0
    case ERROR_PPP_INVALID_PACKET:      // The PPP packet is invalid.%0
    case ERROR_PPP_NO_RESPONSE:         // Remote PPP peer is not responding
    case ERROR_SERVER_NOT_RESPONDING:
    case ERROR_LINE_BUSY:
    case ERROR_NO_CARRIER:
    case ERROR_REMOTE_DISCONNECTION:
    case ERROR_BAD_ADDRESS_SPECIFIED:
    case ERROR_AUTOMATIC_VPN_FAILED:    // New ras error for VPN
        return FALSE;
        break;
    case ERROR_NO_ANSWER: 
        {
            // 
            // For ISDN (Whistler bug#384223) we want to make sure CM displays the correct ras error (same as TAPI)
            // thus we have to treat this error as a fatal error.
            // This should return TRUE for the first time we are dialing and only dual-channel mode
            //
            if (0 == lstrcmpiU(pArgs->szDeviceType, RASDT_Isdn) && (pArgs->nMaxRedials == pArgs->nRedialCnt))
            {
                if (CM_ISDN_MODE_DUALCHANNEL_ONLY == pArgs->dwIsdnDialMode)
                {
                    return TRUE;
                }
            }

            return FALSE;
            break;
        }

        
    default:
        break;
    }

    return TRUE;
}


//+----------------------------------------------------------------------------
//
//  Function    IsRasError
//
//  Synopsis    Simple function to determine if an error falls in the RAS range
//
//  Arguments   DWORD dwErr - The error code
//
//  Returns     TRUE if error is within RAS range
//
//  Histroy     nickball    Created header  05/21/99     
//    
//-----------------------------------------------------------------------------
inline BOOL IsRasError(DWORD dwErr)
{
   return ((dwErr >= RASBASE) && (dwErr <= RASBASEEND));
}

//+----------------------------------------------------------------------------
//
//  Function    CheckConnectionError
//
//  Synopsis    Determine if a RAS error is recoverable. If not recoverable, 
//              retrieves the appropriate error message for display.
//
//  Arguments   DWORD dwErr             - The RAS error code
//              ArgsStruct* pArgs       - Ptr to global args struct
//              BOOL   fTunneling       - Flag indicating whether we're tunneling
//              LPTSTR *ppszRasErrMsg   - Pointer to pointer for message string
//
//  Returns     TRUE if error is fatal
//
//  Histroy     nickball    Created header  05/21/99     
//    
//-----------------------------------------------------------------------------
BOOL CheckConnectionError(HWND hwndDlg, 
    DWORD dwErr, 
    ArgsStruct *pArgs,
    BOOL    fTunneling,
    LPTSTR   *ppszRasErrMsg)
{
    DWORD dwIdMsg = 0;
    LPTSTR pszMsg = NULL;
    LPTSTR pszTmp = NULL;

    //
    // Examine the error more closely. Note: For W2K, we skip RAS 
    // errors and query RAS for a displayable error string below.
    //

    if ((!OS_NT5) || (!IsRasError(dwErr)))
    {  
        switch (dwErr) 
        {
            case ERROR_PPP_TIMEOUT:             // Timed out waiting for a valid response from the remote PPP peer.%0
            case ERROR_PPP_REMOTE_TERMINATED:   // PPP terminated by remote machine.%0
            case ERROR_PPP_INVALID_PACKET:      // The PPP packet is invalid.%0
            case ERROR_PPP_NO_RESPONSE:         // Remote PPP peer is not responding
            case ERROR_SERVER_NOT_RESPONDING:
                dwIdMsg = IDMSG_PPPPROBLEM;
                break;

            case ERROR_LINE_BUSY:
                if ((pArgs->nDialIdx+1 == MAX_PHONE_NUMBERS || 
                    !pArgs->aDialInfo[pArgs->nDialIdx+1].szDialablePhoneNumber[0]) &&
                    !pArgs->nRedialCnt)
                    dwIdMsg = IDMSG_LINEBUSY;
                else
                    dwIdMsg = IDMSG_LINEBUSYREDIAL;

                break;

            case ERROR_NO_ANSWER:
            case ERROR_NO_CARRIER:
                if ((pArgs->nDialIdx+1 == MAX_PHONE_NUMBERS || 
                    !pArgs->aDialInfo[pArgs->nDialIdx+1].szDialablePhoneNumber[0]) &&
                    !pArgs->nRedialCnt)
                    dwIdMsg = fTunneling ? IDMSG_TUNNEL_NOANSWER : IDMSG_NOANSWER ;
                else
                    dwIdMsg = fTunneling ? IDMSG_TUNNEL_NOANSWERREDIAL : IDMSG_NOANSWERREDIAL;
                break;

            case ERROR_REMOTE_DISCONNECTION:
                dwIdMsg = IDMSG_REMOTEDISCONNECTED;
                break;

            case ERROR_BAD_ADDRESS_SPECIFIED:
                dwIdMsg = IDMSG_TUNNEL_NOANSWERREDIAL;
                break;

            case ERROR_PPP_NO_PROTOCOLS_CONFIGURED: // No PPP control protocols configured.%0
                dwIdMsg = IDMSG_TCPIPPROBLEM;
                break;

            case ERROR_PORT_ALREADY_OPEN:
                dwIdMsg = fTunneling ? IDMSG_TUNNELINUSE : IDMSG_PORTINUSE ;
                break;

            case ERROR_FROM_DEVICE:
                dwIdMsg = IDMSG_DEVICEERROR;
                break;

            case ERROR_HARDWARE_FAILURE:
            case ERROR_PORT_OR_DEVICE: //11694
            case ERROR_DEVICE_NOT_READY:
                dwIdMsg = IDMSG_NOTRESPONDING;
                break;

            case ERROR_NO_DIALTONE:
                dwIdMsg = IDMSG_NODIALTONE;
                break;

            case ERROR_CANCELLED:
            case ERROR_USER_DISCONNECTION:
                dwIdMsg = IDMSG_CANCELED;                
                break;

            case ERROR_AUTHENTICATION_FAILURE:
            case ERROR_ACCESS_DENIED: // 13795 // WINDOWS ERROR
                dwIdMsg = IDMSG_BADPASSWORD;
                break;

            case ERROR_VOICE_ANSWER:
                dwIdMsg = IDMSG_VOICEANSWER;
                break;
           
            case ERROR_PORT_NOT_AVAILABLE:
                if (IsDialingTunnel(pArgs))
                {
                    dwIdMsg = IDMSG_TUNNELNOTAVAILABLE;
                }
                else
                {
                    dwIdMsg = IDMSG_PORTNOTAVAILABLE;
                }
                break;

            case ERROR_PORT_NOT_CONFIGURED:
                dwIdMsg = IDMSG_PORTNOTCONFIGURED;
                break;

            case ERROR_RESTRICTED_LOGON_HOURS:
                dwIdMsg = IDMSG_RESTRICTEDLOGONHOURS;
                break;
    
            case ERROR_ACCT_DISABLED:
            case ERROR_ACCT_EXPIRED:
                dwIdMsg = IDMSG_ACCTDISABLED;
                break;

            case ERROR_PASSWD_EXPIRED:
                dwIdMsg = IDMSG_PASSWDEXPIRED;
                break;
    
            case ERROR_NO_DIALIN_PERMISSION:
                dwIdMsg = IDMSG_NODIALINPERMISSION;
                break;

            case ERROR_PROTOCOL_NOT_CONFIGURED:
                dwIdMsg = IDMSG_PROTOCOL_NOT_CONFIGURED;
                break;

            case ERROR_INVALID_DATA: // WINDOWS ERROR

                //
                // The specific case in which we encountered DUN settings
                // that aren't supported on the current platform
                //

                CMTRACE(TEXT("CheckConnectionError - Unsupported DUN setting detected"));
                dwIdMsg = IDMSG_UNSUPPORTED_SETTING;
                break;
        
            case ERROR_BAD_PHONE_NUMBER: // TBD - drop through to default
            default: 
                break;
        }
    }

    if (0 == dwIdMsg)
    {
        //
        // If no message ID was picked up, then try to get one from RAS
        //

        if (pArgs->rlsRasLink.pfnGetErrorString) 
        {
            DWORD dwRes;
            DWORD dwFmtMsgId;

            pszTmp = (LPTSTR) CmMalloc(256 * sizeof(TCHAR)); // Docs say 256 chars is always enough.
            
            if (pszTmp)
            {
                dwRes = pArgs->rlsRasLink.pfnGetErrorString((UINT) dwErr, pszTmp, (DWORD) 256);
 
                if (ERROR_SUCCESS == dwRes)
                {
                    pszMsg = CmFmtMsg(g_hInst, IDMSG_RAS_ERROR, pszTmp, dwErr);
                }
            }

            CmFree(pszTmp);
        }
        
        if (NULL == pszMsg)
        {
            //
            // Still no message, try to get description from system (on NT)
            // Note: HRESULTS are displayed in Hex, Win errors are decimal.
            
            if (OS_NT)
            {                
                if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_IGNORE_INSERTS + FORMAT_MESSAGE_FROM_SYSTEM,
                                  NULL, dwErr, 0, (LPTSTR) &pszTmp, 1, NULL))
                {
                    if (pszTmp)
                    {
                        pszMsg = CmFmtMsg(g_hInst, (dwErr > 0x7FFFFFFF) ? IDMSG_SYS_ERROR_HEX : IDMSG_SYS_ERROR_DEC, pszTmp, dwErr);
                        LocalFree(pszTmp);
                    }
                    else
                    {
                        CMASSERTMSG(FALSE, TEXT("CheckConnectionError -- FormatMessage failed to allocate pszTmp."));
                    }
                }
            }
            
            if (NULL == pszMsg)
            {
                //
                // Still no message, go with default
                //
                
                pszMsg = CmFmtMsg(g_hInst, (dwErr > 0x7FFFFFFF) ? IDMSG_CM_ERROR_HEX : IDMSG_CM_ERROR_DEC, dwErr);       
            }
        }
    }

    //
    // Special check for tunneling to verify that we have a device name 
    //
    
    if (IsDialingTunnel(pArgs))
    {
        //
        // Check whether the tunnel device name is valid
        //
        if (!PickTunnelDevice(pArgs,pArgs->szTunnelDeviceType,pArgs->szTunnelDeviceName)) 
        {
            pArgs->szTunnelDeviceName[0]= TEXT('\0');
            pArgs->piniProfile->WPPS(c_pszCmSection, c_pszCmEntryTunnelDevice, TEXT(""));
                       
            pszMsg = CmLoadString(g_hInst, GetPPTPMsgId());

            dwIdMsg = 0;
        }

        pArgs->piniProfile->WPPS(c_pszCmSection, c_pszCmEntryTunnelDevice, pArgs->szTunnelDeviceName);
    }

    //
    // If we have a message ID format it for display
    //

    if (dwIdMsg) 
    {
        MYDBGASSERT(!pszMsg);
        pszMsg = CmFmtMsg(g_hInst,dwIdMsg);
    }

    if (pszMsg) 
    {
        if (!ppszRasErrMsg)
        {
            AppendStatusPane(hwndDlg,pszMsg);
            CmFree(pszMsg);
        }
        else
        {
            //
            // pass the msg to the caller.  the caller needs to free it.
            //
            *ppszRasErrMsg = pszMsg;
        }
    }

    BOOL bCancel = IsConnectErrorFatal(dwErr, pArgs);

    if (bCancel && dwErr != ERROR_CANCELLED && 
        dwErr != ERROR_AUTHENTICATION_FAILURE &&
        dwErr != ERROR_ACCESS_DENIED)
    {
        //
        // if we're canceling redial, then there might be something
        // seriously wrong.  We want to recheck our configs the next
        // time CM is run.
        //
        ClearComponentsChecked();
    }
    
    return (bCancel);
}

//+----------------------------------------------------------------------------
//
//  Function    GetRasConnectoidName
//
//  Synopsis    Construct a RAS connectoid name.
//
//              The connectoid name is "<long service name>-[Primary|Backup]".
//              or "<long service name>&Tunnel" for the case of tunnel entry.
//
//  Arguments   pArgs               Pointer to ArgsStruct
//              piniService[IN]     the service obj
//              fTunnelEntry[IN]    TRUE:  This connectoid is for tunneling
//                                  FALSE: otherwise
//
//  Returns     LPTSTR              The connectoid name.
//
//-----------------------------------------------------------------------------
LPTSTR GetRasConnectoidName(
    ArgsStruct  *pArgs, 
    CIni*       piniService, 
    BOOL        fTunnelEntry
)
{
    LPTSTR  pszConnectoid = GetServiceName(piniService);
    
    if (pszConnectoid)
    {       
        //
        // If tunneling 9X connectoid, append the Tunnel 
        // Suffix - e.g. "Tunnel (for advanced use only)"
        //

        if (OS_W9X && fTunnelEntry) 
        {
            LPTSTR pszSuffix = GetTunnelSuffix();       
   
            if (pszSuffix)
            {
                pszConnectoid = CmStrCatAlloc(&pszConnectoid, pszSuffix); 
            }
   
            CmFree(pszSuffix);
        }
    }   

    return pszConnectoid;
}

//+----------------------------------------------------------------------------
//
//      Function        CreateRASEntryStruct
//
//      Synopsis        Create a connectoid with the settings specified in the cms.
//              If a parameter does NOT exist in the cms file, the corresponding 
//              value is used.
//
//              The connectoid name is "<long service name>-[Primary|Backup]".
//              or "<long service name>&Tunnel" for the case of tunnel entry.
//
//      Arguments       pArgs                           Pointer to ArgsStruct
//                              pszDUN                  DUN name
//              piniService[IN]     the service file obj
//                              fTunnelEntry[IN]        TRUE:  This connectoid is for tunneling
//                                                                      FALSE: otherwise
//              pszRasPbk           the RAS phonebook in which the connectoid is located
//              ppbEapData[OUT]         Address of pointer to store EapData, allocated here.
//                              pdwEapSize[OUT]         Ptr to a DWORD to record the size of the data blob.
//
//      Returns         LPRASENTRY          The new RAS connectoid
//
//      History         5/12/97                         henryt          created
//                              5/23/97                         byao            Modified: added fSkipProfile flag
//                              6/9/97                          byao            Modified: use DUN= field when the 
//                                                                                              phone number has no DUN name associated
//                              7/28/97                         byao            Added change for #10459
//              4/13/97             nickball    Renamed, return LPRASENTRY
//-----------------------------------------------------------------------------
LPRASENTRY CreateRASEntryStruct(
    ArgsStruct  *pArgs,
    LPCTSTR     pszDUN, 
    CIni*       piniService, 
    BOOL        fTunnelEntry,
    LPTSTR      pszRasPbk,
    LPBYTE              *ppbEapData,
    LPDWORD         pdwEapSize
)
{
    LPTSTR      pszDunEntry = NULL;
    DWORD       dwErr;
    BOOL        bTmp;

    //
    // first we need to create a RAS entry in memory with default values
    //

    LPRASENTRY  preBuffer = AllocateRasEntry();

    if (!preBuffer)
    {
        return NULL;
    }

    MYDBGASSERT(preBuffer->dwSize >= sizeof(*preBuffer));

    //
    // Set up the preBuffer to defaults value
    //

    preBuffer->dwFramingProtocol = RASFP_Ppp;

    //
    // Allow only TCP/IP by default
    //
    preBuffer->dwfNetProtocols |= RASNP_Ip;

    //
    // Set default RASEO settings.  
    //

    if (!fTunnelEntry)
    {
        preBuffer->dwfOptions |= RASEO_UseCountryAndAreaCodes   |
                                 RASEO_IpHeaderCompression      |
                                 RASEO_RemoteDefaultGateway     |
                                 RASEO_SwCompression;
                                 //RASEO_SecureLocalFiles;      // NT 427042
                                 //RASEO_DisableLcpExtensions;  //13059 Olympus + 289461 NT 
        //
        //  We want to honor the HideTrayIcon flag.  If it is not NT5, then
        //  we always set this flag.  If it is NT5, then we should only set
        //  this flag if HideTrayIcon is false.
        //

        if (!OS_NT5 || !(pArgs->piniService->GPPI(c_pszCmSection, c_pszCmEntryHideTrayIcon)))
        {
            preBuffer->dwfOptions |= RASEO_ModemLights;
        }

        //
        //  In order for users to be able to specify device settings on Whistler,
        //  they have to do it from the control panel and we have to set the
        //  RASEO2_UseGlobalDeviceSettings flag in dwfOptions2.
        //
        if (OS_NT51)
        {
            ((LPRASENTRY_V501)preBuffer)->dwfOptions2 |= RASEO2_UseGlobalDeviceSettings;
        }

        //
        // We should have the devicename/devicetype by now.
        // (PickModem should be called)
        //
        
        MYDBGASSERT(pArgs->szDeviceType[0]);
        MYDBGASSERT(pArgs->szDeviceName[0]);

        lstrcpynU(preBuffer->szDeviceType, pArgs->szDeviceType, 
                    sizeof(preBuffer->szDeviceType)/sizeof(TCHAR));
        
        lstrcpynU(preBuffer->szDeviceName, pArgs->szDeviceName, 
                    sizeof(preBuffer->szDeviceName)/sizeof(TCHAR));
    }
    else
    {              
        preBuffer->dwfOptions = RASEO_IpHeaderCompression       |
                                RASEO_RemoteDefaultGateway      |
                                RASEO_NetworkLogon              |
                                RASEO_SwCompression;            
                                //RASEO_SecureLocalFiles        // NT 427042
                                //RASEO_DisableLcpExtensions    
        //
        //  Always set Modem lights on direct connection, unless HideTrayIcon
        //  flag is explicitly set in the .CMS. #262825, #262988
        //

        if (!(pArgs->piniService->GPPI(c_pszCmSection, c_pszCmEntryHideTrayIcon)))
        {           
            preBuffer->dwfOptions |= RASEO_ModemLights;
        }
   
        MYDBGASSERT(pArgs->szTunnelDeviceType[0]);
        MYDBGASSERT(pArgs->szTunnelDeviceName[0]);

        lstrcpynU(preBuffer->szDeviceType, pArgs->szTunnelDeviceType, 
                    sizeof(preBuffer->szDeviceType)/sizeof(TCHAR));
                
        lstrcpynU(preBuffer->szDeviceName, pArgs->szTunnelDeviceName, 
                    sizeof(preBuffer->szDeviceName)/sizeof(TCHAR));

        lstrcpyU(preBuffer->szLocalPhoneNumber, pArgs->GetTunnelAddress());
    }

    //
    //  Check to see if we need to tell RAS that this connection has Internet Connectivity or not
    //
    if (OS_NT51)
    {
        //
        //  Note that we use the top level service profile on purpose here (pArgs->pIniService directly)
        //  as this is a profile global setting.
        //
        if (pArgs->piniService->GPPB(c_pszCmSection, c_pszCmEntryInternetConnection,
                                     (BOOL) ((LPRASENTRY_V501)preBuffer)->dwfOptions2 & RASEO2_Internet)) 
        {
            ((LPRASENTRY_V501)preBuffer)->dwfOptions2 |= RASEO2_Internet;
        } 
        else 
        {
            ((LPRASENTRY_V501)preBuffer)->dwfOptions2 &= ~RASEO2_Internet;
        }
    }

    //
    // If we have a specific DUN name to use, then 
    // use it instead of the default DUN setting in the .CMS.
    //

    if (pszDUN && *pszDUN)
    {
        pszDunEntry = CmStrCpyAlloc(pszDUN);
    }
    else
    {
        pszDunEntry = GetDefaultDunSettingName(piniService, fTunnelEntry);
    }

    //
    // If we have a DUN setting name, read the settings from cms
    //

    if (pszDunEntry && *pszDunEntry)
    {
        dwErr = (DWORD)ReadDUNSettings(pArgs, piniService->GetFile(), pszDunEntry, preBuffer, ppbEapData ,pdwEapSize, fTunnelEntry);

        if (ERROR_SUCCESS != dwErr)
        {
            CMTRACE(TEXT("UpdateRASConnectoid: ReadDUNSettings failed"));
            CmFree(preBuffer);
            preBuffer = NULL;
            goto exit;
        }
    }

    //
    // Get autodial information, store in preBuffer
    //

    CopyAutoDial(preBuffer); 
        
    //
    // disable the RAS wizard on Win95
    //
    if (OS_W9X)
    {
        DisableWin95RasWizard();
    }

exit:
    if (pszDunEntry)
    {
        CmFree(pszDunEntry);
    }
      
    SetLastError(dwErr);

    return preBuffer;
}

//+----------------------------------------------------------------------------
//
//  Function    CreateRasPrivatePbk
//
//  Synopsis    Create the private RAS phone book and returns the full path.
//
//  Arguments   pArgs       Pointer to global Args struct
//
//  Returns     LPTSTR      The full path name of the newly created private pbk
//
//  History     ??/??/97    henryt      created
//
//              01/15/99    Jeffspr     Changed the GetTempFileName pattern,
//                                      as it was using more than the allowed/
//                                      used 3 chars, plus made the failure 
//                                      case use the same pattern (we will 
//                                      filter on this in the connection 
//                                      enumerator to ignore these entries).
//
//              05/21/99    nickball    Added allocation, removed input buf
//              04/10/00    quintinb    Removed GetTempFileName as we no longer
//                                      want this file to be temporary.  Changed
//                                      the function to create a file named _cmphone.pbk
//                                      in the profile directory.
//                                      Please see Whistler bug 15812 for details.
//              07/05/00    t-urama     Changed the path to the hidden pbk to point
//                                      to the RAS pbk.
//
//-----------------------------------------------------------------------------

LPTSTR CreateRasPrivatePbk(ArgsStruct  *pArgs)
{   
    //
    //  No private PBK on win9x, everything is in the registry.
    //
    if (OS_W9X)
    {
        return NULL;
    }

    if (NULL == pArgs)
    {
        MYDBGASSERT(pArgs);
        return NULL;
    }

    LPTSTR pszHiddenPbkPath = NULL;
    LPCTSTR pszCmp = pArgs->piniProfile->GetFile();

    //
    // This version of the function uses the function GetPathToPbk in connect.cpp to find the path
    // to the phone book. The hidden phone book also has to be created in the same directory.
    //
    if (pszCmp)
    {
        LPTSTR pszRasPbkDir = GetPathToPbk(pszCmp, pArgs);
        MYDBGASSERT(pszRasPbkDir);

        if (pszRasPbkDir)
        {
            pszHiddenPbkPath = (LPTSTR) CmMalloc((lstrlen(pszRasPbkDir) + lstrlen(CM_PBK_FILTER_PREFIX) + 7) * sizeof(TCHAR));

            if (pszHiddenPbkPath)
            {
                wsprintfU(pszHiddenPbkPath, TEXT("%s\\%sphone"), pszRasPbkDir, CM_PBK_FILTER_PREFIX);
                MYDBGASSERT(pszHiddenPbkPath);
                
                HANDLE hFile = INVALID_HANDLE_VALUE;
                SECURITY_ATTRIBUTES sa = {0};
                PSECURITY_ATTRIBUTES pSA = NULL;
                PSECURITY_DESCRIPTOR pSd = NULL;

                if (OS_NT5 && pArgs->fAllUser)
                {
                    //
                    // Be sure to create it with a security descriptor that 
                    // allows it to be read by any authenticated user.  If we don't it may 
                    // prevent other users from being able to read it. We didn't want to 
                    // change the old behavior downlevel so this fix is just for Whistler+.
                    //
                    DWORD dwErr = AllocateSecurityDescriptorAllowAccessToWorld(&pSd);
                    if ((ERROR_SUCCESS == dwErr) && pSd)
                    {
                        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
                        sa.lpSecurityDescriptor = pSd;
                        sa.bInheritHandle = TRUE;

                        pSA = &sa;
                    }
                }                
               
                hFile = CreateFileU(pszHiddenPbkPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
                                           pSA, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);


                CmFree(pSd);

                if (hFile == INVALID_HANDLE_VALUE)
                {
                    DWORD dwLastError = GetLastError();
                    MYDBGASSERT(hFile != INVALID_HANDLE_VALUE);
                    CMTRACE1(TEXT("CreateRasPrivatePbk - CreateFileU failed. GetLastError = %d"), dwLastError);
                }
                CloseHandle(hFile);
            }
            else
            {
                CMASSERTMSG(FALSE, TEXT("CreateRasPrivatePbk -- CmMalloc returned NULL for pszHiddenPbkPath"));
            }

            CmFree(pszRasPbkDir);
        }
    }
    
    return pszHiddenPbkPath;
}



//+----------------------------------------------------------------------------
//
//      Function        GetPathToPbk
//
//      Synopsis        This function is a helper function called by 
//                      CheckAccessToCmpAndPbk in connect.cpp and by 
//                      CreateRasPrivatePbk. It returns the path to the RAS
//                      phonebook.
//
//      Arguments       LPTSTR pszCmp       - The path to the cmp file
//                      LPTSTR pszRasPbk    - The string to store the result
//                      ArgsStruct *pArgs   - pArgs
//
//      Returns         NONE
//
//      History         07/05/00                        t-urama         created
//----------------------------------------------------------------------------- 
LPTSTR GetPathToPbk(LPCTSTR pszCmp, ArgsStruct *pArgs)
{
    MYDBGASSERT(pArgs); 
    if (NULL == pArgs)
    {
        return NULL;
    }

    MYDBGASSERT(pszCmp); 
    if (NULL == pszCmp)
    {
        return NULL;
    }

    LPTSTR pszRasPbk = NULL;

    //
    //  pszRasPbk could be NULL if we are on NT4 or we are using the
    //  all user default phonebook.
    //
    if (NULL == pArgs->pszRasPbk)
    {
        if (OS_NT4)
        {
            DWORD dwSize = (MAX_PATH + 1);
            DWORD dwRet;
            BOOL bExitLoop = TRUE;

            do
            {
                pszRasPbk = (LPTSTR)CmMalloc(dwSize*sizeof(TCHAR));

                if (pszRasPbk)
                {
                    dwRet = GetSystemDirectoryU(pszRasPbk, dwSize);
                    if (dwRet)
                    {
                        if (dwRet > dwSize)
                        {
                            dwSize = dwRet + 1;
                            bExitLoop = FALSE;  //  we didn't get all of the string, try again
                            CmFree(pszRasPbk);
                        }
                        else
                        {
                            bExitLoop = TRUE;
                            CmStrCatAlloc(&pszRasPbk, c_pszRasDirRas);
                        }
                    }
                    else
                    {
                        CmFree(pszRasPbk);
                        pszRasPbk = NULL;
                    }
                }
                else
                {
                    CMASSERTMSG(FALSE, TEXT("GetPathToPbk -- CmMalloc failed!"));
                    return NULL;
                }
            } while (!bExitLoop);
        }
        else
        {
            pszRasPbk = CmStrCpyAlloc(pszCmp);

            if (pszRasPbk)
            {
                LPTSTR pszSlash = CmStrrchr(pszRasPbk, TEXT('\\'));
                if (pszSlash)
                {
                    *pszSlash = TEXT('\0'); // remove <shortservicename>.cmp

                    pszSlash = CmStrrchr(pszRasPbk, TEXT('\\'));

                    if (pszSlash)
                    {
                        *pszSlash = TEXT('\0');

                        CmStrCatAlloc(&pszRasPbk, TEXT("\\"));
                        CmStrCatAlloc(&pszRasPbk, c_pszPbk);
                    }
                    else
                    {
                        CMASSERTMSG(FALSE, TEXT("GetPathToPbk -- unable to convert cmp path to pbk path."));
                        CmFree(pszRasPbk);
                        pszRasPbk = NULL;
                    }

                
                }
                 
                else
                {
                    CMASSERTMSG(FALSE, TEXT("GetPathToPbk -- unable to convert cmp path to pbk path!"));
                    CmFree(pszRasPbk);
                    pszRasPbk = NULL;
                }
            }
        }
    }
    else
    {
        pszRasPbk = CmStrCpyAlloc(pArgs->pszRasPbk);
        LPTSTR pszSlash = CmStrrchr(pszRasPbk, TEXT('\\'));
        if (pszSlash)
        {
            *pszSlash = TEXT('\0'); // remove the RAS phonebook name
        }   
        else
        {
            CMASSERTMSG(FALSE, TEXT("GetPathToPbk -- unable to convert RAS pbk name to pbk path!"));
            CmFree(pszRasPbk);
            pszRasPbk = NULL;
        }
    }

    return pszRasPbk;
}

//+----------------------------------------------------------------------------
//
//      Function        DisableWin95RasWizard
//
//      Synopsis        This function disable the Win95 Dial-up Networking wizard
//              by writing a dword reg value 0x00000080 in the registry.
//
//      Arguments       NONE
//
//      Returns         NONE
//
//      History         7/1/97                          henryt          created
//-----------------------------------------------------------------------------
void DisableWin95RasWizard(
    void)
{
    HKEY    hkReg = NULL;
    LONG    lRes;
    DWORD   dwSize;
    DWORD   dwType;
    DWORD   dwValue;

    lRes = RegOpenKeyExA(HKEY_CURRENT_USER, c_pszRegRemoteAccess, 0,
                         KEY_QUERY_VALUE|KEY_SET_VALUE, &hkReg);

    if (ERROR_SUCCESS != lRes)
    {
        CMTRACE1(TEXT("DisableWin95RasWizard() RegOpenKeyEx() failed, GLE=%u."), lRes);
        goto exit;
    }
        
    //
    // see if we already have a value there.
    //
    dwSize = sizeof(DWORD);
    lRes = RegQueryValueExA(hkReg, 
                            c_pszRegWizard, 
                            NULL, 
                            &dwType,
                            (LPBYTE)&dwValue,
                            &dwSize);

    if (lRes == ERROR_SUCCESS   &&
        dwSize == sizeof(DWORD) &&
        dwType == REG_BINARY    &&
        dwValue == ICM_RAS_REG_WIZARD_VALUE) 
    {
        CMTRACE(TEXT("DisableWin95RasWizard() RegQueryValueEx() - found correct value."));
        goto exit;
    }
        
    //
    // well, the value is not in reg yet.  we need to create the value.
    //
    dwValue = ICM_RAS_REG_WIZARD_VALUE;
    lRes = RegSetValueExA(hkReg, 
                          c_pszRegWizard,
                          0, 
                          REG_BINARY, 
                          (LPBYTE)&dwValue, 
                          sizeof(dwValue));
#ifdef DEBUG
    if (ERROR_SUCCESS != lRes)
    {
        CMTRACE1(TEXT("DisableWin95RasWizard() RegSetValueEx() failed, GLE=%u."), lRes);
    }
#endif    
exit:

    if (hkReg)
    {
        lRes = RegCloseKey(hkReg);

#ifdef DEBUG
        if (ERROR_SUCCESS != lRes)
        {
            CMTRACE1(TEXT("DisableWin95RasWizard() RegCloseKey() failed, GLE=%u."), lRes);
        }
#endif
    }
    return;
}



//+----------------------------------------------------------------------------
//
//  Function    SetIsdnDualChannelEntries
//
//  Synopsis    As what the func name says.  We prepare the RASENTRY and
//              RASSUBENTRY properly.  We don't actually make RAS calls to
//              save the entries. We'll leave it to the caller(so that the
//              can make other changes to the structs for other reasons and 
//              commit the changes in 1 or 2 RAS calls).
//
//  Arguments   pArgs [IN]          Pointer to ArgsStruct
//              pRasEntry [IN/OUT]  rasentry to be filled
//              ppRasSubEntry [OUT] pointer to be filled with the subentry array
//                                  The buffer is allocated in this function.
//              pdwSubEntryCount    Number of subentries allocated.
//
//  Returns     BOOL                TRUE = success, FALSE = failure.
//
//-----------------------------------------------------------------------------
BOOL SetIsdnDualChannelEntries(ArgsStruct *pArgs, LPRASENTRY pRasEntry,
                                      LPRASSUBENTRY *ppRasSubEntry, PDWORD pdwSubEntryCount)
{
    //
    //  Lets check the input parameters
    //
    MYDBGASSERT(pArgs);
    MYDBGASSERT(pRasEntry);
    MYDBGASSERT(ppRasSubEntry);
    MYDBGASSERT(pdwSubEntryCount);
    if ((NULL == pArgs) || (NULL == pRasEntry) || (NULL == ppRasSubEntry) ||
        (NULL == pdwSubEntryCount))
    {
        return FALSE;
    }
    
    //
    //  Since we don't support BAP if they called this function they must have wanted
    //  to do DualChannel ISDN.  If the dial mode isn't set for dual channel, we will
    //  assert an continue.  Better to connect the user in dual channel mode then not
    //  at all if they have a misconfigured profile.
    //
    MYDBGASSERT(pArgs->dwIsdnDialMode != CM_ISDN_MODE_SINGLECHANNEL);

    //
    //  Check the size of the passed in RasEntry struct.  If it isn't at least
    //  a 4.01 size struct, then return.
    //
    MYDBGASSERT(pRasEntry->dwSize >= sizeof(LPRASENTRY_V401));
    if (sizeof(LPRASENTRY_V401) > pRasEntry->dwSize)
    {
        return FALSE;
    }

    LPRASENTRY_V401 pRasEntry401 = (LPRASENTRY_V401)pRasEntry;

    //
    // set isdn dial mode to dial both channels
    //
    pRasEntry401->dwDialMode = RASEDM_DialAll;
    CMTRACE(TEXT("ISDN Dual Channel Mode On"));

    if (OS_NT)
    {
       *pdwSubEntryCount = 2;
    }
    else if (OS_MIL)
    {
        // 112351: 9x only requires one sub entry.  We'll keep the device name the same.
        // In this case, Win9x will work as follows:
        //  for the 1st channel, the device name provided works fine.
        //  for the 2nd channel, 9x sees the device is in use and looks for the
        //  the closest match (which is the 2nd channel).
        //
    
       *pdwSubEntryCount = 1;
    }
    else
    {
        CMASSERTMSG(FALSE, TEXT("SetIsdnDualChannelEntries -- Function called on a platform other than NT or Millennium."));
        return FALSE;
    }

    //
    //  Allocate the sub entries
    //
    *ppRasSubEntry = (LPRASSUBENTRY)CmMalloc((*pdwSubEntryCount)*(sizeof(RASSUBENTRY)));

    if (NULL == *ppRasSubEntry)
    {
        CMASSERTMSG(FALSE, TEXT("SetIsdnDualChannelEntries -- CmMalloc failed to alloc ppRasSubEntry."));
        return FALSE;
    }

    //
    //  Fill in the sub entries with the device and phonenumber information
    //
    for (DWORD dwIndex=0; dwIndex < (*pdwSubEntryCount); dwIndex++)
    {
        (*ppRasSubEntry)[dwIndex].dwSize = sizeof(RASSUBENTRY);
        lstrcpyU((*ppRasSubEntry)[dwIndex].szDeviceType, pArgs->szDeviceType);
        lstrcpyU((*ppRasSubEntry)[dwIndex].szDeviceName, pArgs->szDeviceName);
        lstrcpyU((*ppRasSubEntry)[dwIndex].szLocalPhoneNumber, pRasEntry401->szLocalPhoneNumber);
    }
    
    return TRUE;
}

//
// Keep in case we ever want to support BAP
//
/*
BOOL SetIsdnDualChannelEntries(
    ArgsStruct              *pArgs,
    LPRASENTRY              pre,
    LPRASSUBENTRY           *prgrse,
    PDWORD                  pdwSubEntryCount
)
{
    LPRASENTRY_V401 pre401;

    MYDBGASSERT(pArgs->dwIsdnDialMode != CM_ISDN_MODE_SINGLECHANNEL);

    MYDBGASSERT(pre->dwSize >= sizeof(LPRASENTRY_V401));
    pre401 = (LPRASENTRY_V401)pre;

    //
    // set isdn dial mode
    //

    if (pArgs->dwIsdnDialMode == CM_ISDN_MODE_DIALALL)
    {
        //
        // dial both channels
        //
        pre401->dwDialMode = RASEDM_DialAll;
        CMTRACE(TEXT("ISDN Dual Channel Mode On"));
    }
    else
    {
        //
        // dial 2nd channel on demand
        //

        //
        // First get the 4 thresholds
        //
        if (!pArgs->dwDialExtraPercent)
        {
            pArgs->dwDialExtraPercent = pArgs->piniService->GPPI(c_pszCmSection, 
                                                                 c_pszCmEntryDialExtraPercent, 
                                                                 DEFAULT_DIALEXTRAPERCENT);
            if (pArgs->dwDialExtraPercent < 0 ||
                pArgs->dwDialExtraPercent > 100)
            {
                pArgs->dwDialExtraPercent = DEFAULT_DIALEXTRAPERCENT;
            }
        }
        
        if (!pArgs->dwDialExtraSampleSeconds)
        {
            pArgs->dwDialExtraSampleSeconds = pArgs->piniService->GPPI(c_pszCmSection, 
                                                                       c_pszCmEntryDialExtraSampleSeconds, 
                                                                       DEFAULT_DIALEXTRASAMPLESECONDS);
            if (pArgs->dwDialExtraSampleSeconds < 0)
            {
                pArgs->dwDialExtraSampleSeconds = DEFAULT_DIALEXTRASAMPLESECONDS;
            }
        }
    
        if (!pArgs->dwHangUpExtraPercent)
        {
            pArgs->dwHangUpExtraPercent = pArgs->piniService->GPPI(c_pszCmSection, 
                                                                   c_pszCmEntryHangUpExtraPercent, 
                                                                   DEFAULT_HANGUPEXTRAPERCENT);
            if (pArgs->dwHangUpExtraPercent < 0 ||
                pArgs->dwHangUpExtraPercent > 100)
            {
                pArgs->dwHangUpExtraPercent = DEFAULT_HANGUPEXTRAPERCENT;
            }
        }
        
        if (!pArgs->dwHangUpExtraSampleSeconds)
        {
            pArgs->dwHangUpExtraSampleSeconds = pArgs->piniService->GPPI(c_pszCmSection, 
                                                                         c_pszCmEntryHangUpExtraSampleSeconds, 
                                                                         DEFAULT_HANGUPEXTRASAMPLESECONDS);
            if (pArgs->dwHangUpExtraSampleSeconds < 0)
            {
                pArgs->dwHangUpExtraSampleSeconds = DEFAULT_HANGUPEXTRASAMPLESECONDS;
            }
        }

        //
        // set multilink info
        //
        pre401->dwDialMode = RASEDM_DialAsNeeded;

        pre401->dwDialExtraPercent          = pArgs->dwDialExtraPercent;
        pre401->dwDialExtraSampleSeconds    = pArgs->dwDialExtraSampleSeconds;
        pre401->dwHangUpExtraPercent        = pArgs->dwHangUpExtraPercent;
        pre401->dwHangUpExtraSampleSeconds  = pArgs->dwHangUpExtraSampleSeconds;

        CMTRACE2(TEXT("ISDN 2nd Channel Dial On Demand: dial extra %u%%, dial extra %u sample secs"),
                 pre401->dwDialExtraPercent, pre401->dwDialExtraSampleSeconds);

        CMTRACE2(TEXT("\t\thangup extra %u%%, hangup extra %u sample secs"),
                 pre401->dwHangUpExtraPercent, pre401->dwHangUpExtraSampleSeconds);
    }


    if (OS_NT)
    {
        if (!(*prgrse = (LPRASSUBENTRY)CmMalloc(2*sizeof(RASSUBENTRY))))
        {
            CMTRACE(TEXT("SetIsdnDualChannelEntries failed to alloc a ras sub entry"));
            return FALSE;
        }
        
        ZeroMemory((PVOID)*prgrse, 2*sizeof(RASSUBENTRY));

        //
        // first channel
        //
        (*prgrse)[0].dwSize = sizeof(RASSUBENTRY);
        lstrcpyU((*prgrse)[0].szDeviceType, pArgs->szDeviceType);
        lstrcpyU((*prgrse)[0].szDeviceName, pArgs->szDeviceName);
        lstrcpyU((*prgrse)[0].szLocalPhoneNumber, pre401->szLocalPhoneNumber);

        //
        // the 2nd channel is identical
        //
        CopyMemory((PVOID)(*prgrse + 1), (PVOID)*prgrse, sizeof(RASSUBENTRY));

        *pdwSubEntryCount = 2;
    }
    else
    {
        MYDBGASSERT(OS_MIL);

        CMTRACE(TEXT("doing the Millennium subentry stuff"));

        // 112351: 9x only requires one sub entry.  We'll keep the device name the same.
        // In this case, Win9x will work as follows:
        //  for the 1st channel, the device name provided works fine.
        //  for the 2nd channel, 9x sees the device is in use and looks for the
        //  the closest match (which is the 2nd channel).
        //

        if (!(*prgrse = (LPRASSUBENTRY)CmMalloc(1*sizeof(RASSUBENTRY))))
        {
            CMTRACE(TEXT("SetIsdnDualChannelEntries failed to alloc a ras sub entry"));
            return FALSE;
        }

        ZeroMemory((PVOID)*prgrse, 1*sizeof(RASSUBENTRY));

        //
        // 2nd channel
        //
        (*prgrse)[0].dwSize = sizeof(RASSUBENTRY);
        lstrcpyU((*prgrse)[0].szDeviceType, pArgs->szDeviceType);
        lstrcpyU((*prgrse)[0].szDeviceName, pArgs->szDeviceName);
        lstrcpyU((*prgrse)[0].szLocalPhoneNumber, pre401->szLocalPhoneNumber);

        *pdwSubEntryCount = 1;
    }
    return TRUE;
}
*/


//+----------------------------------------------------------------------------
//
//      Function        SetNtIdleDisconnectInRasEntry
//
//      Synopsis        As what the func name says.  We prepare the RASENTRY and
//              RASSUBENTRY properly.  We don't actually make RAS calls to
//              save the entries. We'll leave it to the caller(so that the
//              can make other changes to the structs for other reasons and 
//              commit the changes in 1 or 2 RAS calls).
//
//      Arguments       pArgs [IN]              Pointer to ArgsStruct
//              pre   [OUT]         Pointer to RASENTRY with the correct size
//
//      Returns         BOOL                TRUE = success, FALSE = failure.
//
//-----------------------------------------------------------------------------
BOOL SetNtIdleDisconnectInRasEntry(
    ArgsStruct      *pArgs,
    LPRASENTRY      pre
)
{
    if (!OS_NT4)
    {
        return FALSE;
    }
    
    if ((NULL == pArgs) || (NULL == pre) || (pre->dwSize < sizeof(LPRASENTRY_V401)))
    {
        CMASSERTMSG(FALSE, TEXT("SetNtIdleDisconnectInRasEntry -- Invalid parameter"));
        return FALSE;
    }

    //
    // If idle-disconnect is enabled, use the options value otherwise 
    // pArgs->dwIdleTimeout is in minutes.  Note that 0xFFFFFFFF means
    // no idle disconnect to RAS but 0 is the value we use to mean never
    // idle disconnect in the CMS.
    //

    DWORD dwIdle = (pArgs->dwIdleTimeout * 60);

    if (0 == dwIdle)
    {
        dwIdle = (DWORD)-1;
    }

    ((LPRASENTRY_V401 )pre)->dwIdleDisconnectSeconds = dwIdle;

    CMTRACE1(TEXT("SetNtIdleDisconnect: current idle Timeout is %u seconds."), dwIdle);

    return TRUE;
}

//+----------------------------------------------------------------------------
//
// Function:   DisableSystemIdleDisconnect
//
// Synopsis:   This function sets the idle timeout value of a RAS connection to
//             be disabled.
//
// Arguments:  LPRASENTRY pre - pointer to a RASENTRY to disable idle disconnect for
//
// Returns:    BOOL TRUE = success, FALSE = failure.
//
//-----------------------------------------------------------------------------
BOOL DisableSystemIdleDisconnect(LPRASENTRY pre)
{
    if ((NULL == pre) || (pre->dwSize < sizeof(LPRASENTRY_V401)))
    {
        CMASSERTMSG(FALSE, TEXT("DisableSystemIdleDisconnect -- Invalid parameter"));
        return FALSE;
    }

    //
    // Set the idle time to 0xFFFFFFFF which means no idle disconnect to RAS
    //

    ((LPRASENTRY_V401 )pre)->dwIdleDisconnectSeconds = (DWORD)-1;

    CMTRACE(TEXT("DisableSystemIdleDisconnect -- System Idle disconnect disabled"));

    return TRUE;
}


//+----------------------------------------------------------------------------
//
//      Function        RasDialFunc2
//
//      Synopsis        A RAS callback type 2 for RasDial.
//
//      Arguments       
//      ULONG_PTR dwCallbackId,// user-defined value specified in RasDial 
//      DWORD dwSubEntry,      // subentry index in multilink connection
//      HRASCONN hrasconn,     // handle to RAS connection
//      UINT unMsg,            // type of event that has occurred
//      RASCONNSTATE rascs,    // connection state about to be entered
//      DWORD dwError,         // error that may have occurred
//      DWORD dwExtendedError  // extended error information for some errors
//
//      Returns         LPRASENTRY - pointer to the RASENTRY structure allocated
//
//-----------------------------------------------------------------------------
DWORD WINAPI RasDialFunc2(
    ULONG_PTR dwCallbackId,     // user-defined value specified in RasDial 
    DWORD dwSubEntry,           // subentry index in multilink connection
    HRASCONN hrasconn,          // handle to RAS connection
    UINT unMsg,                 // type of event that has occurred
    RASCONNSTATE rascs,         // connection state about to be entered
    DWORD dwError,              // error that may have occurred
    DWORD dwExtendedError       // extended error information for some errors
)
{   
    CMTRACE2(TEXT("RasDialFunc2():  dwSubentry=%u. dwErr=0x%x"), dwSubEntry, dwError);
    CMTRACE2(TEXT("RasDialFunc2():  dwExtendedErr=0x%x, rascs=%u"), dwExtendedError, rascs);

    MYDBGASSERT(dwCallbackId);

    if (dwCallbackId)
    {
        ArgsStruct *pArgs = (ArgsStruct *) dwCallbackId;        
        pArgs->dwRasSubEntry = dwSubEntry;

        //CMTRACE1(TEXT("RasDialFunc2():  pArgs->lInConnectOrCancel=%d"),pArgs->lInConnectOrCancel);
        //CMASSERTMSG((NOT_IN_CONNECT_OR_CANCEL == pArgs->lInConnectOrCancel),
        //            TEXT("RasDialFunc2 - RasDial mutex is NOT NULL..."));

        SendMessage(pArgs->hwndMainDlg, pArgs->uMsgId, rascs, dwError);
    }

    return 1;
}

//+----------------------------------------------------------------------------
//
// Function:  SetRasDialExtensions
//
// Synopsis:  Encapsulates initialization and configuration of the 
//            RasDialExtensions that we use on NT.
//
// Arguments: pArgs - Ptr to global args struct
//            fEnablePausedStates - Use Paused states or not
//            fEnableCustomScripting - whether to use custom scripting or not
//
// Returns:   DWORD - Error code
//
// History:   nickball      Created    7/22/99
//
//+----------------------------------------------------------------------------
DWORD SetRasDialExtensions(ArgsStruct* pArgs, BOOL fEnablePausedStates, BOOL fEnableCustomScripting)
{
    DWORD dwRes = ERROR_SUCCESS;

    MYDBGASSERT(pArgs);
    
    if (NULL == pArgs)
    {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // If not already allocated, we need a RasDialExtensions struct
    //

    if (!pArgs->pRasDialExtensions)
    {
        pArgs->pRasDialExtensions = AllocateAndInitRasDialExtensions();

        if (!pArgs->pRasDialExtensions)
        {
            dwRes = ERROR_NOT_ENOUGH_MEMORY;
        }
    }
    else
    {                    
        dwRes = InitRasDialExtensions(pArgs->pRasDialExtensions);
    }

    if (ERROR_SUCCESS != dwRes)
    {
        goto SetRasDialExtensionsExit;
    }

    //
    // Turn on PauseStates for NT
    //

    if (fEnablePausedStates)
    {
        pArgs->pRasDialExtensions->dwfOptions |= RDEOPT_PausedStates; 
    }

    //
    //  Turn on custom scripting if we are running on Whistler+ and the caller
    //  asked for it.
    //
    if (fEnableCustomScripting && OS_NT51)
    {
        pArgs->pRasDialExtensions->dwfOptions |= RDEOPT_UseCustomScripting;
    }

    //
    // RDEOPT_NoUser is required for the WinLogon credential case, 
    // which we identify by the presence of either lpEapLogonInfo 
    // or lpRasNoUser.  Note that the if statement below is somewhat redundant
    // since we should have CM_LOGON_TYPE_WINLOGON set if we get either a NoUser
    // struct or an EapLogonInfo struct.  However, wanted to point out that on Win2k
    // one of the first two will always be true and on Whistler the first two may be
    // false but the third true (RAS now sends a flag to tell us when we are at WinLogon on 
    // whistler as there are Ctrl-Alt-Del with SmartCard cases where it sends neither struct).
    //

    if (pArgs->lpEapLogonInfo || pArgs->lpRasNoUser || (CM_LOGON_TYPE_WINLOGON == pArgs->dwWinLogonType))
    {
        pArgs->pRasDialExtensions->dwfOptions |= RDEOPT_NoUser;
    }

    //
    // If the modem speaker is turned off, makes sure that we
    // disable it explicitly in RAS, otherwise it will use 
    // its default and turn the speaker on. These settings 
    // should be ignored by RAS in the tunnel case.
    //

    if (pArgs->tlsTapiLink.bModemSpeakerOff)
    {
        pArgs->pRasDialExtensions->dwfOptions |= RDEOPT_IgnoreModemSpeaker;
        pArgs->pRasDialExtensions->dwfOptions &= ~RDEOPT_SetModemSpeaker;
    }

SetRasDialExtensionsExit:

    return dwRes;
}

//+----------------------------------------------------------------------------
//
// Function:  InitRasDialExtensions
//
// Synopsis:  Flushes a previously allocated RasDialExtensions buffer and sets
//            size, options for re-use.
//
// Arguments: LPRASDIALEXTENSIONS - Ptr to allocated struct with size set.
//
// Returns:   DWORD - Error code
//
// History:   nickball      Created    5/22/99
//
//+----------------------------------------------------------------------------
DWORD InitRasDialExtensions(LPRASDIALEXTENSIONS lpRasDialExtensions)
{   
    MYDBGASSERT(lpRasDialExtensions);

    if (NULL == lpRasDialExtensions)
    {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // First, we determine the size
    //

    DWORD dwSize = OS_NT5 ? sizeof(RASDIALEXTENSIONS_V500) : sizeof(RASDIALEXTENSIONS);

    //
    // Flush buffer and reset size.
    //

    ZeroMemory(lpRasDialExtensions, dwSize);

    lpRasDialExtensions->dwSize = dwSize;

    //
    // Set customdial if needed
    //

    if (dwSize == sizeof(RASDIALEXTENSIONS_V500))
    {  
        // 
        // Set the CustomDial flag for NT5. We don't set this on NT4 
        // and 9X as a precaution because the falg isn't defined.
        //

        lpRasDialExtensions->dwfOptions |= RDEOPT_CustomDial;
    }

    CMTRACE1(TEXT("InitRasDialExtensions() - dwSize is %u"), dwSize);

    return ERROR_SUCCESS;
}

//+----------------------------------------------------------------------------
//
// Function:  AllocateAndInitRasDialExtensions
//
// Synopsis:  Encapsulates the allocation of a RASEXTENSION based upon the OS
//
// Arguments: None
//
// Returns:   LPRASDIALEXTENSIONS - Ptr to allocated struct with size set.
//
// History:   nickball      Created    5/13/99
//
//+----------------------------------------------------------------------------
LPRASDIALEXTENSIONS AllocateAndInitRasDialExtensions()
{
    //
    // Allocate struct and pre-fill as appropriate
    //

    LPRASDIALEXTENSIONS prdeNew = (LPRASDIALEXTENSIONS)CmMalloc(OS_NT5 ? 
        sizeof(RASDIALEXTENSIONS_V500) : sizeof(RASDIALEXTENSIONS));

    if (!prdeNew)
    {
        CMTRACE(TEXT("AllocateAndInitRasDialExtensions: failed to alloc RasDialExtension buffer"));
        return NULL;
    }
    
    InitRasDialExtensions(prdeNew);

    return prdeNew;
}

//+----------------------------------------------------------------------------
//
// Function:  InitRasDialParams
//
// Synopsis:  Flushes a previously allocated RasDialParams buffer and sets
//            size, options for re-use.
//
// Arguments: LPRASDIALPARAMS - Ptr to allocated struct with size set.
//
// Returns:   DWORD - Error code
//
// History:   nickball      Created    5/22/99
//
//+----------------------------------------------------------------------------
DWORD InitRasDialParams(LPRASDIALPARAMS lpRasDialParams)
{   
    MYDBGASSERT(lpRasDialParams);

    if (NULL == lpRasDialParams)
    {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // First, we determine the size
    //

    DWORD dwSize = OS_NT ? sizeof(RASDIALPARAMS_V401) : sizeof(RASDIALPARAMS);

    //
    // Flush buffer and reset size.
    //

    ZeroMemory(lpRasDialParams, dwSize);

    lpRasDialParams->dwSize = dwSize;

    CMTRACE1(TEXT("InitRasDialParams() - dwSize is %u"), dwSize);

    return ERROR_SUCCESS;
}

//+----------------------------------------------------------------------------
//
// Function:  AllocateAndInitRasDialParams
//
// Synopsis:  Encapsulates the allocation of a RASDIALPARAMS based upon the OS
//
// Arguments: None
//
// Returns:   LPRASDIALPARAMS - Ptr to allocated struct with size set.
//
// History:   nickball      Created    5/22/99
//
//+----------------------------------------------------------------------------
LPRASDIALPARAMS AllocateAndInitRasDialParams()
{
    //
    // Allocate struct and pre-fill as appropriate
    //

    LPRASDIALPARAMS prdpNew = (LPRASDIALPARAMS)CmMalloc(OS_NT ? 
        sizeof(RASDIALPARAMS_V401) : sizeof(RASDIALPARAMS));

    if (!prdpNew)
    {
        CMTRACE(TEXT("AllocateRasDialParams: failed to alloc RasDialParams buffer"));
        return NULL;
    }
    
    InitRasDialParams(prdpNew);

    return prdpNew;
}

//+----------------------------------------------------------------------------
//
// Function:  AllocateRasEntry
//
// Synopsis:  Encapsulates the allocation of a RASENTRY struct based upon the OS
//
// Arguments: None
//
// Returns:   LPRASENTRY - Ptr to allocated struct with size set.
//
// History:   nickball  Created Header    5/13/99
//
//+----------------------------------------------------------------------------
LPRASENTRY AllocateRasEntry()
{
    static DWORD    s_dwRasEntrySize = -1;

    //
    // first, we determine the size
    //
    if (s_dwRasEntrySize == -1)
    {                
        if (OS_NT51)
        {
            //
            // Whistler
            //
            s_dwRasEntrySize = sizeof(RASENTRY_V501);
        }
        else if (OS_W2K)
        {
            //
            // nt5
            //
            s_dwRasEntrySize = sizeof(RASENTRY_V500);        
        }
        else if (OS_MIL || OS_NT4)
        {
            //
            // Millennium uses the NT4 structure
            //
            s_dwRasEntrySize = sizeof(RASENTRY_V401);
        }
        else
        {
            //
            // win9x
            //
            s_dwRasEntrySize = sizeof(RASENTRY);
        }
    }

    //
    // add 512 bytes since a rasentry can contain alternate phone #'s
    // See RASENTRY.dwAlternateOffset 
    //
    LPRASENTRY preNew = (LPRASENTRY)CmMalloc(s_dwRasEntrySize+512);

    if (!preNew)
    {
        CMTRACE(TEXT("AllocateRasEntry: failed to alloc rasentry buffer"));
        return NULL;
    }
    
    preNew->dwSize = s_dwRasEntrySize;
    if (s_dwRasEntrySize >= sizeof(RASENTRY_V500))
    {
        ((LPRASENTRY_V500)preNew)->dwType = RASET_Internet;
   
        //
        // For NT5, set szCustomDialDll with our Module name. This ensures that our
        // custom DialDlg, DialEntry, and Hangup routines will be called by RAS for
        // operations on our connectoid. We don't want to tie our path to anything 
        // machine specific, so we'll use the %windir% environment string. 
        // 

        lstrcpyU(((LPRASENTRY_V500)preNew)->szCustomDialDll, c_pszCmDialPath);
    }

    CMTRACE1(TEXT("AllocateRasEntry() - s_dwRasEntrySize is %u"), s_dwRasEntrySize);

    return preNew;
}

#if 0
/*

//+----------------------------------------------------------------------------
//
// Function:  GetRasSystemPhoneBookPath
//
// Synopsis:  Builds the conventional path to the RAS system phonebook
//
// Arguments: None
//
// Returns:   LPTSTR - The phonebook path 
//
// History:   nickball    Created    8/14/98
//
//+----------------------------------------------------------------------------
LPTSTR GetRasSystemPhoneBookPath()
{
    MYDBGASSERT(OS_NT);
    
    TCHAR szTemp[MAX_PATH+1];

    GetSystemDirectoryU(szTemp,sizeof(szTemp));
    lstrcatU(szTemp, c_pszRasDirRas);
    lstrcatU(szTemp, c_pszRasPhonePbk);
    
    return CmStrCpyAlloc(szTemp);
}

//+---------------------------------------------------------------------------
//
//      Function:       InitDefaultRasPhoneBook
//
//      Synopsis:       Special case Helper function ensures that there is a default 
//                              ras phonebook when running on NT. We simply attempt to create 
//                              the file which fails if the file already exists, or creates 
//                              an empty file if it does not.
//
//      Arguments:      None
//
//      Returns:        Nothing
//
//      History:        a-nichb -       4/30/97         Created
//                      VetriV          5/21/97         Changed code to call GetOSVersion()
//                                                      instead of using pArgs->dwPlatformID
//                                                      for bug #4700    
//                      nickball        ??/??/98        Removed as we no longer call RasValidateEntry
//                                                      which introduced the requirement of having at
//                                                      least an empty phonebook for the API to work.
//
//----------------------------------------------------------------------------
void InitDefaultRasPhoneBook()
{               
    //
    // NT only. Create empty system phonebook if none exists
    //

    if (OS_NT) 
    {       
        LPTSTR pszSystemPbk = GetRasSystemPhoneBookPath();

        if (pszSystemPbk)
        {
            //
            // Try to create the phonebook, fails if file already exists
            //
            
            HANDLE hInf = CreateFileU(pszSystemPbk,
                                      GENERIC_WRITE|GENERIC_READ,
                                      0,
                                      NULL,
                                      CREATE_NEW,
                                      FILE_ATTRIBUTE_NORMAL,
                                      NULL);

            if (hInf != INVALID_HANDLE_VALUE)
            {
                CloseHandle(hInf);
            }
        }
        CmFree(pszSystemPbk);
    }
}
*/
#endif

//+----------------------------------------------------------------------------
//
// Function:  GetRasPbkFromNT5ProfilePath
//
// Synopsis:  Helper function to manufacture a RAS phonebook path from
//            a .CMP file path on NT5
//
// Arguments: LPCTSTR pszProfile - The full path to a profile .CMP file.
//
// Returns:   LPTSTR - The new phonebook path. NULL on failure
//
// History:   nickball    Created    8/13/98
//
//+----------------------------------------------------------------------------
LPTSTR GetRasPbkFromNT5ProfilePath(LPCTSTR pszProfile)
{
    MYDBGASSERT(OS_NT5);
    MYDBGASSERT(pszProfile);

    if (NULL == pszProfile)
    {
        return NULL;
    }

    //
    // We will deduce the phonebook path from our current profile location.
    //

    LPTSTR pszRasPhonePath = (LPTSTR) CmMalloc(MAX_PATH + 1);    
    MYDBGASSERT(pszRasPhonePath);

    if (pszRasPhonePath)
    {
        //
        // Strip .CMP file name and parent directory
        //
        
        LPTSTR pszDir = CmStripFileName(pszProfile, FALSE);
        MYDBGASSERT(pszDir);
        
        if (pszDir)
        {
            LPTSTR pszTmp = CmStrrchr(pszDir, TEXT('\\'));                   
            MYDBGASSERT(pszTmp);

            if (pszTmp)
            {

                *pszTmp = 0;
                
                //
                // Append \\pbk\\rasphone.pbk
                //
                
                lstrcpyU(pszRasPhonePath, pszDir);
                lstrcatU(pszRasPhonePath, TEXT("\\"));
                lstrcatU(pszRasPhonePath, c_pszPbk);
                lstrcatU(pszRasPhonePath, c_pszRasPhonePbk);               
            }
            
            CmFree(pszDir);
        }           
        else
        {
            CmFree(pszRasPhonePath);
        }
    }                            

    return pszRasPhonePath;
}

#define MAX_BLOB_CHARS_PER_LINE 128

//+----------------------------------------------------------------------------
//
// Function:  ReadDunSettingsEapData
//
// Synopsis:  Retrieves DUN setting for EAP config (opaque blob) data. The 
//            entry may span several lines and contain several EAP data blocks.
//
// Arguments: CIni *pIni - Ptr to ini object to be used.
//            LPBYTE* ppbEapData - Address of pointer to store EapData, allocated here.
//            LPDWORD pdwEapSize - Ptr to a DWORD to record the size of the data blob.
//            DWORD dwCustomAuthKey - The EAP type that we are interested in.
//
// Returns:   TRUE on success
//
// Note:      CM expects blob data to be provided in numbered entries such as:
//                    CustomAuthData0=, CustomAuthData1=, CustomAuthData2=, etc.
//
// History:   nickball    Created                                       08/24/98
//            nickball    Handle multiple EAP data blocks in blob.      09/11/99
//
//+----------------------------------------------------------------------------
BOOL ReadDunSettingsEapData(CIni *pIni, 
        LPBYTE* ppbEapData,
        LPDWORD pdwEapSize,
        const DWORD dwCustomAuthKey)
{
    CHAR *pchBuf = NULL;
    CHAR szTmp[MAX_BLOB_CHARS_PER_LINE + 2]; 
    CHAR szEntry[128];
    int nLine = -1;
    int nRead = -1; 
    int nTotal = 0;

    LPBYTE pbEapBytes = NULL;

    MYDBGASSERT(pIni);
    MYDBGASSERT(ppbEapData);
    MYDBGASSERT(pdwEapSize);

    if (NULL == pIni || NULL == ppbEapData || NULL == pdwEapSize) 
    {
        return FALSE;
    }

    //
    // First get the section (it should include &) then the entry.
    //

    BOOL bRet = FALSE;
    LPWSTR pszLoadSection = pIni->LoadSection(c_pszCmSectionDunServer);         
    LPSTR pszSection = WzToSzWithAlloc(pszLoadSection);       
    LPSTR pszFile = WzToSzWithAlloc(pIni->GetFile());

    if (!pszLoadSection || !pszSection || !pszFile)
    {
        bRet = FALSE;
        goto exit;
    }

    // 
    // Read numbered entries until there are no more. 
    // Note: RAS blob doesn't exceed 64 chars, but can wrap over multiple lines
    //

    while (nRead)
    {
        //
        // Read CustomAuthDataX where X is the number of entries
        // 

        nLine++;
        wsprintfA(szEntry, "%s%d", c_pszCmEntryDunServerCustomAuthData, nLine);

        nRead = GetPrivateProfileStringA(pszSection, szEntry, "", szTmp, sizeof(szTmp), pszFile);

        if (nRead)
        {               
            //
            // If line exceeded 64 chars, it is considered corrupt
            // 

            if (MAX_BLOB_CHARS_PER_LINE < nRead)
            {                               
                nTotal = 0;
                break;
            }

            //
            // Update our local master buffer with the latest fragment
            //

            if (nLine)
            {
                pchBuf = CmStrCatAllocA(&pchBuf, szTmp);
            }
            else
            {
                pchBuf = CmStrCpyAllocA(szTmp);
            }

            if (!pchBuf)
            {
                bRet = FALSE;
                goto exit;
            }

            nTotal += nRead;
        }
    }

    //
    // At this point we should have the entire entry in pchBuf in HEX format
    // Convert the buffer to byte format and store in supplied EAP buffer.
    //

    if (nTotal && !(nTotal & 1))
    {
        nTotal /= 2; // Only need half the hex char size

        pbEapBytes = (BYTE *) CmMalloc(nTotal + 1);

        if (!pbEapBytes)
        {
            goto exit;
        }

        CHAR *pch = pchBuf;
        BYTE *pb = pbEapBytes;

        while (*pch != '\0')
        {
                *pb = HexValue( *pch++ ) * 16;
                *pb += HexValue( *pch++ );
                ++pb;
        }

        //
        // Now we have the bytes, locate and extract the data block that we
        // are after. Note: Multiple blocks are arrayed using the following 
        // header:
        //
        //  typedef struct _EAP_CUSTOM_DATA
        //  {
        //      DWORD dwSignature;
        //      DWORD dwCustomAuthKey;
        //      DWORD dwSize;
        //      BYTE  abdata[1];
        //  } EAP_CUSTOM_DATA;
        //

        EAP_CUSTOM_DATA *pCustomData = (EAP_CUSTOM_DATA *) pbEapBytes;

        while (((LPBYTE) pCustomData - pbEapBytes) < nTotal)
        {
            if (pCustomData->dwCustomAuthKey == dwCustomAuthKey)
            {
                //
                // Bingo! We have a match, first make sure that the indicated 
                // size isn't pointing out into space, then make a copy and 
                // run for the hills.
                //

                if (((LPBYTE) pCustomData - pbEapBytes) + sizeof(EAP_CUSTOM_DATA) + pCustomData->dwSize > (DWORD) nTotal)
                {
                    MYDBGASSERT(FALSE);
                    goto exit;
                }

                *ppbEapData = (BYTE *) CmMalloc(pCustomData->dwSize);        

                if (*ppbEapData)
                {   
                    CopyMemory(*ppbEapData, pCustomData->abdata, pCustomData->dwSize);                    

                    *pdwEapSize = pCustomData->dwSize;                                                     
                    bRet = TRUE;
                    goto exit;                                
                }
            }       

            //
            // Locate the next data block
            //

            pCustomData = (EAP_CUSTOM_DATA *) ((LPBYTE) pCustomData + sizeof(EAP_CUSTOM_DATA) + pCustomData->dwSize); 
        }
    }

exit:

    CmFree(pchBuf);
    CmFree(pszLoadSection);
    CmFree(pszSection);
    CmFree(pszFile);
    CmFree(pbEapBytes);

    return bRet;
}

//+----------------------------------------------------------------------------
//
// Function:  ReadDUNSettings
//
// Synopsis:  Reads the DUN settings for the specified DUN name and .CMS file 
//            into a RASENTRY structure. Because some settings are not supported 
//                        on downlevel platforms, this function will potentially display an
//                        error message to the user.
//
// Arguments: ArgsStruct *pArgs  - Ptr to global args struct.
//            LPCSTR pszFile     - Full path to the .CMS file.
//            LPCTSTR pszDunName - The DUN name for the settings.
//            LPVOID pvBuffer    - Ptr to RASENTRY buffer.
//            LPBYTE* ppbEapData - Address of pointer to store EapData
//            LPDWORD pdwEapSize - Ptr to a DWORD to record the size of the data blob.
//            BOOL  fTunnel      - are we reading tunnel settings?
//
// Returns:   ERROR_SUCCESS on success. Use GetLastError for failure details.
//
// Note:      This was formerly the PhoneBookReadDun API in CMPBK.DLL
//
// History:   nickball    8/22/98   Created Header    
//            nickball    02/03/99  Added pArgs :( in order to have access to the 
//                                  the top-level service for path conversion.
//
//+----------------------------------------------------------------------------
LRESULT ReadDUNSettings(ArgsStruct *pArgs,
        LPCTSTR pszFile, 
        LPCTSTR pszDunName, 
        LPVOID pvBuffer, 
        LPBYTE* ppbEapData, 
        LPDWORD pdwEapSize,
        BOOL    fTunnel) 
{       
    MYDBGASSERT(pszFile);
    MYDBGASSERT(pszDunName);
    MYDBGASSERT(pvBuffer);

    if (NULL == pszFile || NULL == pszDunName || NULL == pvBuffer)
    {
        return (ERROR_INVALID_PARAMETER);
    }

    CMTRACE1(TEXT("ReadDUNSettings -- using DUN setting: %s"), pszDunName);
    
    RASENTRYW *preRas = (RASENTRYW *) pvBuffer;

    //
    // Setup INI object. Prepend pszDunName with "&" for section.
    //

    CIni iniFile(g_hInst, pszFile);
    
    LPTSTR pszSection = CmStrCpyAlloc(TEXT("&"));
    pszSection = CmStrCatAlloc(&pszSection, pszDunName);
    iniFile.SetSection(pszSection);
    CmFree(pszSection);

    //
    // Get and apply the Phone section entries
    //

    if (iniFile.GPPB(c_pszCmSectionDunPhone, c_pszCmEntryDunPhoneDialAsIs)) 
    {
        preRas->dwfOptions &= ~RASEO_UseCountryAndAreaCodes;;
    }
    
    CopyGPPS(&iniFile, c_pszCmSectionDunPhone, c_pszCmEntryDunPhonePhoneNumber, preRas->szLocalPhoneNumber, sizeof(preRas->szLocalPhoneNumber)/sizeof(TCHAR));
    CopyGPPS(&iniFile,c_pszCmSectionDunPhone, c_pszCmEntryDunPhoneAreaCode, preRas->szAreaCode, sizeof(preRas->szAreaCode)/sizeof(TCHAR));
    preRas->dwCountryCode = iniFile.GPPI(c_pszCmSectionDunPhone, c_pszCmEntryDunPhoneCountryCode, preRas->dwCountryCode);
    preRas->dwCountryID = iniFile.GPPI(c_pszCmSectionDunPhone, c_pszCmEntryDunPhoneCountryId, preRas->dwCountryID);
    
    //
    // Get and apply the Device section entries
    //

    CopyGPPS(&iniFile,c_pszCmSectionDunDevice, c_pszCmEntryDunDeviceType, preRas->szDeviceType, sizeof(preRas->szDeviceType)/sizeof(TCHAR));
    CopyGPPS(&iniFile,c_pszCmSectionDunDevice, c_pszCmEntryDunDeviceName, preRas->szDeviceName, sizeof(preRas->szDeviceName)/sizeof(TCHAR));
    
    //
    // Get and apply the Server section entries
    //

    LPTSTR pszTmp = iniFile.GPPS(c_pszCmSectionDunServer, c_pszCmEntryDunServerType);
    if (*pszTmp) 
    {
        if (0 == lstrcmpiU(pszTmp, c_pszDunPpp)) 
        {
            preRas->dwFramingProtocol = RASFP_Ppp;
        } 
        else if (0 == lstrcmpiU(pszTmp, c_pszDunCslip)) 
        {
            preRas->dwFramingProtocol = RASFP_Slip;
            preRas->dwfOptions |= RASEO_IpHeaderCompression;
        } 
        else if (0 == lstrcmpiU(pszTmp, c_pszDunSlip)) 
        {
            preRas->dwFramingProtocol = RASFP_Slip;
            if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunTcpIpIpHeaderCompress,
                             (BOOL) preRas->dwfOptions & RASEO_IpHeaderCompression)) 
            {
                preRas->dwfOptions |= RASEO_IpHeaderCompression;
            } 
            else 
            {
                preRas->dwfOptions &= ~RASEO_IpHeaderCompression;
            }
        }
    }
    CmFree(pszTmp);
    
    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerSwCompress,
                                    (BOOL) preRas->dwfOptions & RASEO_SwCompression)) 
    {
        preRas->dwfOptions |= RASEO_SwCompression;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_SwCompression;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerDisableLcp,
                                    (BOOL) preRas->dwfOptions & RASEO_DisableLcpExtensions)) 
    {
        preRas->dwfOptions |= RASEO_DisableLcpExtensions;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_DisableLcpExtensions;
    }
    
    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerNetworkLogon,
                                    (BOOL) preRas->dwfOptions & RASEO_NetworkLogon)) 
    {
        preRas->dwfOptions |= RASEO_NetworkLogon;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_NetworkLogon;
    }
        
    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerNegotiateTcpIp,
                                    (BOOL) preRas->dwfNetProtocols & RASNP_Ip)) 
    {
        preRas->dwfNetProtocols |= RASNP_Ip;
    } 
    else 
    {
        preRas->dwfNetProtocols &= ~RASNP_Ip;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerNegotiateIpx,
                                    (BOOL) preRas->dwfNetProtocols & RASNP_Ipx)) 
    {
        preRas->dwfNetProtocols |= RASNP_Ipx;
    } 
    else 
    {
        preRas->dwfNetProtocols &= ~RASNP_Ipx;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerNegotiateNetBeui, preRas->dwfNetProtocols&RASNP_NetBEUI)) 
    {
        preRas->dwfNetProtocols |= RASNP_NetBEUI;
    } 
    else 
    {
        preRas->dwfNetProtocols &= ~RASNP_NetBEUI;
    }

    //
    // Get the NT5 specific DUN settings. We will error out if we're running 
    // downlevel when these settings are configured and the EnforceCustomSecurity
    // flag has been set.
    //
    // Note: c_pszCmEntryDunServerEnforceCustomSecurity is a DUN setting and is FALSE by default
    //

    BOOL bEnforceCustomSecurity = iniFile.GPPI(c_pszCmSectionDunServer, c_pszCmEntryDunServerEnforceCustomSecurity, FALSE);

    //
    // Is EAP required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireEap,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireEAP)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequireEAP;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireEAP;
    }

    //
    // PAP required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequirePap,
                                    (BOOL) preRas->dwfOptions & RASEO_RequirePAP)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequirePAP;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequirePAP;
    }

    //
    // SPAP required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireSpap,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireSPAP)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequireSPAP;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireSPAP;
    }

    //
    // CHAP required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireChap,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireCHAP)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequireCHAP;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireCHAP;
    }

    //
    // MSCHAP required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireMsChap,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireMsCHAP)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequireMsCHAP;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireMsCHAP;
    }

    //
    // MSCHAP2 required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireMsChap2,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireMsCHAP2)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequireMsCHAP2;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireMsCHAP2;
    }

    //
    // W95 MSCHAP required
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireW95MsChap,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireW95MSCHAP)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_RequireW95MSCHAP;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireW95MSCHAP;
    }

    //
    // Custom Security configuration
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerCustomSecurity,
                                    (BOOL) preRas->dwfOptions & RASEO_Custom)) 
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {               
            return (ERROR_INVALID_DATA);
        }

        preRas->dwfOptions |= RASEO_Custom;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_Custom;
    }

    //
    // Now get the legacy security settings if we don't already have
    // settings specificed from above.  By checking for the Win2k specific
    // settings first we allow Admins to specify both so that legacy platforms
    // can have settings but Win2k can use the more granular settings.
    // If we didn't do this the legacy flags could water down the security on Win2k ...
    //
    const DWORD dwWin2kSecuritySettings = RASEO_RequireEAP | RASEO_RequirePAP | RASEO_RequireSPAP | 
                                          RASEO_RequireCHAP | RASEO_RequireMsCHAP | RASEO_RequireMsCHAP2 | RASEO_RequireW95MSCHAP;

    if (0 == (preRas->dwfOptions & dwWin2kSecuritySettings) || !OS_NT5)
    {
        //
        // Security settings
        //
        if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerPwEncrypt,
                                            (BOOL) preRas->dwfOptions & RASEO_RequireEncryptedPw)) 
        {
            preRas->dwfOptions |= RASEO_RequireEncryptedPw;
        } 
        else 
        {
            preRas->dwfOptions &= ~RASEO_RequireEncryptedPw;
        }

        //
        // MS-CHAP
        //

        if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerPwEncryptMs,
                                        (BOOL) preRas->dwfOptions & RASEO_RequireMsEncryptedPw)) 
        {
            preRas->dwfOptions |= RASEO_RequireMsEncryptedPw;
        } 
        else 
        {
            preRas->dwfOptions &= ~RASEO_RequireMsEncryptedPw;
        }
    }
    else
    {
        CMASSERTMSG((preRas->dwfOptions & RASEO_Custom), TEXT("ReadDUNSettings -- Win2k+ security setting configured but RASEO_Custom not specified."));
    }

    //
    // Encrypt Data (legacy setting, same as ET_Require from above)
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerDataEncrypt,
                                    (BOOL) preRas->dwfOptions & RASEO_RequireDataEncryption)) 
    {
        preRas->dwfOptions |= RASEO_RequireDataEncryption;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RequireDataEncryption;
    }    

    //
    // Encryption type, just a straight int read. (win2k+ setting)
    //
    
    int nTmp = iniFile.GPPI(c_pszCmSectionDunServer, c_pszCmEntryDunServerEncryptionType, -1);

    if (OS_NT5)
    {
        //
        // We need to set Tunnel encryption type to ET_Require because that's what the ConnFolder does.
        // We also set it ET_Require if the user specified RASEO_RequireDataEncryption as a legacy setting
        // but didn't specify a specific win2k setting.
        //
        if (-1 == nTmp)
        {
            if (fTunnel || (preRas->dwfOptions & RASEO_RequireDataEncryption))
            {
                nTmp = ET_Require;
            }
            else
            {
                nTmp = ET_Optional;       
            }
        }
        ((LPRASENTRY_V500)preRas)->dwEncryptionType = (DWORD) nTmp;
    }
    else
    {
        if (-1 != nTmp && bEnforceCustomSecurity)
        {
            return (ERROR_INVALID_DATA);
        }
    }  
   
    //
    // Get the EAP type ID (CustomAuthKey) - The data is stored in the RAS 
    // pbk via a specific API, just before dialing - SetCustomAuthData().
    //

    nTmp = iniFile.GPPI(c_pszCmSectionDunServer, c_pszCmEntryDunServerCustomAuthKey, -1);

    //
    // If a type ID for EAP is specified, see if there is any config data
    //

    if (-1 != nTmp) 
    {                       
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {
            return (ERROR_INVALID_DATA);
        }

        //
        // We have an ID and its NT5, read the EAP config data
        //

        ((LPRASENTRY_V500)preRas)->dwCustomAuthKey = nTmp;              

        ReadDunSettingsEapData(&iniFile, ppbEapData, pdwEapSize, nTmp);         
    }

    //
    // Get and apply the Networking section entries. 
    //

    nTmp = iniFile.GPPI(c_pszCmSectionDunNetworking, c_pszCmEntryDunNetworkingVpnStrategy, -1);

    if (-1 != nTmp)
    {
        if (!(OS_NT5) && bEnforceCustomSecurity)
        {
            return (ERROR_INVALID_DATA);
        }

        ((LPRASENTRY_V500)preRas)->dwVpnStrategy = nTmp;
    }

    //
    //  See if the profile calls for using a Pre-Shared Key for L2TP.  Note that we currently don't
    //  provide a mechanism to set the Pre-Shared Key through RasSetCredentials but that could easily
    //  be done through a custom action or a post install action.
    //
    if (OS_NT51)
    {
        if (iniFile.GPPB(c_pszCmSectionDunNetworking, c_pszCmEntryDunNetworkingUsePreSharedKey,
                         (BOOL) ((LPRASENTRY_V501)preRas)->dwfOptions2 & RASEO2_UsePreSharedKey)) 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 |= RASEO2_UsePreSharedKey;
        } 
        else 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 &= ~RASEO2_UsePreSharedKey;
        }
    }

    //
    // File and Print sharing.  Note that on systems up to Win2k we only have the traditional RASEO_SecureLocalFiles.
    // However, Win2k gave this flag two purposes (enable/disable NetBt and enable/disable file and print sharing).
    // In Whistler two separate flags were developed to allow greater granularity.  To give legacy profiles the behavior
    // they expect while disabling file and print sharing as the default the logic gets a little complicated.  Basically
    // the new flag overrides the legacy flag and defaults to 1.  If the new flag isn't specified then we use the value
    // of the legacy flag if it is specified.  If neither is specified we set it to 1.  On platforms previous to Whistler
    // the old flag is the only thing we have and it defaults to 0.
    //

    int nLegacySecureLocalFiles = iniFile.GPPI(c_pszCmSectionDunServer, c_pszCmEntryDunServerSecureLocalFiles, -1);
    int nSecureFileAndPrint = iniFile.GPPI(c_pszCmSectionDunServer, c_pszCmEntryDunNetworkingSecureFileAndPrint, -1);

    if (-1 == nSecureFileAndPrint)
    {
        nSecureFileAndPrint = nLegacySecureLocalFiles ? 1 : 0;
    }

    if (-1 == nLegacySecureLocalFiles)
    {
        nLegacySecureLocalFiles = 0;
    }

    if (OS_NT51)
    {
        //
        // Set the 501/Options2 style File and Print sharing flag
        //

        if (nSecureFileAndPrint) 
        {
            if (!(OS_NT5) && bEnforceCustomSecurity)
            {               
                return (ERROR_INVALID_DATA);
            }

            ((LPRASENTRY_V501)preRas)->dwfOptions2 |= RASEO2_SecureFileAndPrint;
        } 
        else 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 &= ~RASEO2_SecureFileAndPrint;
        }
    }
    else
    {
        if (nLegacySecureLocalFiles) 
        {
            preRas->dwfOptions |= RASEO_SecureLocalFiles;
        }
        else
        {
            preRas->dwfOptions &= ~RASEO_SecureLocalFiles;    
        }    
    }

    //
    // Pick up Whistler specific DUN settings
    //
    
    if (OS_NT51)    
    {
        //
        // Get the 501/Options2 style MSNet binding flag
        //

        if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunNetworkingSecureClientForMSNet,
                                        (BOOL) ((LPRASENTRY_V501)preRas)->dwfOptions2 & RASEO2_SecureClientForMSNet)) 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 |= RASEO2_SecureClientForMSNet;
        } 
        else 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 &= ~RASEO2_SecureClientForMSNet;
        }

        //
        // Get the 501/Options2 style Multilink Negotiation flag
        //

        if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunNetworkingDontNegotiateMultilink,
                                        (BOOL) ((LPRASENTRY_V501)preRas)->dwfOptions2 & RASEO2_DontNegotiateMultilink)) 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 |= RASEO2_DontNegotiateMultilink;
        } 
        else 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 &= ~RASEO2_DontNegotiateMultilink;
        }

        //
        // Get the 501/Options2 style DontUseRasCredentials flag
        //

        if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunNetworkingDontUseRasCredentials,
                                        (BOOL) ((LPRASENTRY_V501)preRas)->dwfOptions2 & RASEO2_DontUseRasCredentials)) 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 |= RASEO2_DontUseRasCredentials;
        } 
        else 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 &= ~RASEO2_DontUseRasCredentials;
        }

        //
        //  Get the RASEO_CustomScript flag value.  Note that this flag existed on Win2k but wasn't
        //  available for RasDial only RasDialDlg.  On Whistler+ it is available to RasDial as well.
        //  Note that we also have to set the RDEOPT_UseCustomScripting flag in the RASDIALEXTENSIONS
        //  for this to work.
        //
        if (iniFile.GPPB(c_pszCmSectionDunScripting, c_pszCmEntryDunScriptingUseRasCustomScriptDll,
                                        (BOOL) (preRas->dwfOptions & RASEO_CustomScript))) 
        {
            preRas->dwfOptions |= RASEO_CustomScript;
        } 
        else 
        {
            preRas->dwfOptions &= ~RASEO_CustomScript;
        }

        if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerDisableNbtOverIP,
                         (BOOL) (((LPRASENTRY_V501)preRas)->dwfOptions2 & RASEO2_DisableNbtOverIP)))
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 |= RASEO2_DisableNbtOverIP;
        } 
        else 
        {
            ((LPRASENTRY_V501)preRas)->dwfOptions2 &= ~RASEO2_DisableNbtOverIP;
        }
    }

    //
    // Get and apply the TCP/IP section entries
    //

    if (iniFile.GPPB(c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpSpecifyIpAddress,
                                        (BOOL) preRas->dwfOptions & RASEO_SpecificIpAddr)) 
    {
        preRas->dwfOptions |= RASEO_SpecificIpAddr;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_SpecificIpAddr;
    }
    
    Ip_GPPS(&iniFile, c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpIpAddress, &preRas->ipaddr);
    
    if (iniFile.GPPB(c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpSpecifyServerAddress,
                                    (BOOL) preRas->dwfOptions & RASEO_SpecificNameServers)) 
    {
        preRas->dwfOptions |= RASEO_SpecificNameServers;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_SpecificNameServers;
    }

    if (iniFile.GPPB(c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpIpHeaderCompress,
                                    (BOOL) preRas->dwfOptions & RASEO_IpHeaderCompression)) 
    {
        preRas->dwfOptions |= RASEO_IpHeaderCompression;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_IpHeaderCompression;
    }

    Ip_GPPS(&iniFile, c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpDnsAddress, &preRas->ipaddrDns);
    Ip_GPPS(&iniFile, c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpDnsAltAddress, &preRas->ipaddrDnsAlt);
    Ip_GPPS(&iniFile, c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpWinsAddress, &preRas->ipaddrWins);
    Ip_GPPS(&iniFile, c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpWinsAltAddress, &preRas->ipaddrWinsAlt);
    
    if (iniFile.GPPB(c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpGatewayOnRemote,
                                    (BOOL) preRas->dwfOptions & RASEO_RemoteDefaultGateway)) 
    {
        preRas->dwfOptions |= RASEO_RemoteDefaultGateway;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_RemoteDefaultGateway;
    }

    if (OS_NT51)
    {
        //
        //  If the caller specified a DNS suffix then lets read it and add it to the RAS entry
        //
        CopyGPPS(&iniFile, c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpDnsSuffix, ((LPRASENTRY_V501)preRas)->szDnsSuffix, sizeof(((LPRASENTRY_V501)preRas)->szDnsSuffix)/sizeof(TCHAR));
    }

    //
    //  Set the TCP Window size -- the NTT DoCoMo fix for Whistler.  The Win2k version of this fix
    //  must be written through a private RAS API that must be called after the phonebook entry 
    //  exists ie. after we call RasSetEntryProperties ... otherwise it won't work on the first
    //  dial.
    //
    if (OS_NT51)
    {
        ((LPRASENTRY_V501)preRas)->dwTcpWindowSize = iniFile.GPPI(c_pszCmSectionDunTcpIp, c_pszCmEntryDunTcpIpTcpWindowSize, 0);
    }

    //
    // Get and apply the Scripting section entries
    //

    TCHAR szScript[MAX_PATH + 1] = TEXT("");
    CopyGPPS(&iniFile,c_pszCmSectionDunScripting, c_pszCmEntryDunScriptingName, szScript, sizeof(szScript)/sizeof(TCHAR));

    //
    // The script path from our cms file is a relative path. We need to convert
    // it to a full path, but make sure that we use the top-level service for
    // the conversion because it is used to derive the short-service name for
    // the directory.  Note that tunnel dun settings cannot have a script.
    //        

    if (szScript[0] && !fTunnel) 
    {
        CMTRACE1(TEXT("ReadDunSettings() - Converting script path %s to full path"), szScript);
        pszTmp = CmConvertRelativePath(pArgs->piniService->GetFile(), szScript);

        MYDBGASSERT(pszTmp);

        if (pszTmp && *pszTmp)
        {           
            lstrcpyU(preRas->szScript, pszTmp);
            CMTRACE1(TEXT("ReadDunSettings() - Script file is %s"), preRas->szScript);
        }

        CmFree(pszTmp);
    }   
    else
    {
        //
        // The cms didn't specify a script ==> no script
        //
        preRas->szScript[0] = TEXT('\0');
    }

    //
    //  If this is Whistler+ then we may need to invoke a terminal window
    //
    if (OS_NT51 && !fTunnel && iniFile.GPPB(c_pszCmSectionDunScripting, c_pszCmEntryDunScriptingUseTerminalWindow,
                                            (BOOL) preRas->dwfOptions & RASEO_TerminalAfterDial)) 
    {
        preRas->dwfOptions |= RASEO_TerminalAfterDial;
    } 
    else 
    {
        preRas->dwfOptions &= ~RASEO_TerminalAfterDial;
    }

    return (ERROR_SUCCESS);
}


//+----------------------------------------------------------------------------
//
// Function:  ValidateDialupDunSettings
//
// Synopsis:  Verifies the DUN settings that the specified .CMS and DUN name are
//            supported on the current platform. If we are running on a downlevel
//            OS and we encounter any NT specific security settings we error out.
//
// Arguments: LPCTSTR pszCmsFile     - The phone # specific .CMS file name.
//            LPCTSTR pszDunName     - The DUN name, if any for the settings.
//            LPCTSTR pszTopLevelCms - The top-level CMS file name.
//
// Returns:   BOOL - TRUE on success.
//
// History:   nickball    Created               8/26/98
//
//+----------------------------------------------------------------------------
BOOL ValidateDialupDunSettings(LPCTSTR pszCmsFile, LPCTSTR pszDunName, LPCTSTR pszTopLevelCms)
{
    MYDBGASSERT(pszCmsFile);
    MYDBGASSERT(*pszCmsFile);
    MYDBGASSERT(pszDunName);

    if (NULL == pszCmsFile || (!*pszCmsFile) || NULL == pszDunName)
    {
        return FALSE;
    }

    //
    // On NT5 we currently support all settings, so succeed automatically
    //

    if (OS_NT5)
    {
        return TRUE;
    }

    //
    // Determine the DUN name that we are looking for. In the tunnel case we
    // always read it from the .CMS. For dial-up, we'll use the specified DUN
    // name, and revert to the .CMS if blank. 
    //

    CIni iniFile(g_hInst, pszCmsFile);

    //
    // Now determine the DUN name to be used when looking up settings.
    //

    LPTSTR pszEntryName;

    //
    // If we have a specific DUN name to use, and we're not tunneling 
    // use it instead of the default DUN setting in the .CMS.
    //

    if (pszDunName && *pszDunName)
    {
        pszEntryName = CmStrCpyAlloc(pszDunName);
    }
    else
    {
        pszEntryName = GetDefaultDunSettingName(&iniFile, FALSE); // FALSE == not tunnel
    }

    //
    // If no DUN name is specified, then pass validation automatically
    //

    if (!pszEntryName || (!*pszEntryName))
    {
        CmFree(pszEntryName);
        CMTRACE1(TEXT("ValidateDunSettings() - No DUN name found in %s"), pszCmsFile);
        return TRUE;
    }
    
    //
    // Include the entryname in the section headers
    //

    LPTSTR pszSection = CmStrCpyAlloc(TEXT("&"));
    pszSection = CmStrCatAlloc(&pszSection, pszEntryName);
    iniFile.SetSection(pszSection);

    CmFree(pszSection);
    CmFree(pszEntryName);

    //
    // Check to see if the admin wants us to check the custom security settings
    // against the platform. By default, we do not enforce this check.
    // 
    //

    if (FALSE == iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerEnforceCustomSecurity))
    {
        return TRUE;
    }

    //
    // Now check the actual settings if we're still here.
    //

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireEap))                             
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequirePap))
    {
        goto ValidateDunSettingsExit;
    }
            
    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireSpap))
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireChap))
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireMsChap))
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireMsChap2))
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerRequireW95MsChap))
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerCustomSecurity))
    {
        goto ValidateDunSettingsExit;
    }

    if (iniFile.GPPB(c_pszCmSectionDunServer, c_pszCmEntryDunServerEncryptionType))                             
    {
        goto ValidateDunSettingsExit;
    }

    if (-1 != iniFile.GPPI(c_pszCmSectionDunServer, c_pszCmEntryDunServerCustomAuthKey, -1))  
    {
        goto ValidateDunSettingsExit;
    }
            
    if (-1 != iniFile.GPPI(c_pszCmSectionDunNetworking, c_pszCmEntryDunNetworkingVpnStrategy, -1))
    {
        goto ValidateDunSettingsExit;
    }
    
    return TRUE;

ValidateDunSettingsExit:

    //
    // Get the top-level service name
    //

    CIni iniTopLevelCms(g_hInst, pszTopLevelCms);

    LPTSTR pszTitle = GetServiceName(&iniTopLevelCms);
    LPTSTR pszTmp = CmFmtMsg(g_hInst,IDMSG_UNSUPPORTED_SETTING_NUM);       

    MessageBoxEx(NULL, pszTmp, pszTitle, MB_OK|MB_ICONSTOP, LANG_USER_DEFAULT);//13309
    
    CmFree(pszTmp);                 
    CmFree(pszTitle);

    CMTRACE1(TEXT("ValidateDunSettings() - Unsupported setting detected in %s"), pszCmsFile);
    return FALSE;
}

//+----------------------------------------------------------------------------
//
// Function:  InvokeTerminalWindow
//
// Synopsis:  Allows CM to invoke a terminal window on Whistler or later versions
//            of Win2k but calling a private RAS API in RasDlg.dll.
//
// Arguments: LPCWSTR pszPhoneBook - full path to the phonebook file
//            LPCWSTR pszEntry - entry name to invoke the terminal window for
//            RASDIALPARAMS *pRasDialParams - RasDialParams for the connection
//                                            to invoke the terminal window for
//            HWND hWnd - window handle of the parent dialog
//            HRASCONN hRasconn - handle to the ras connection
//
// Returns:   Windows error message
//
// History:   quintinb    Created               07/11/00
//
//+----------------------------------------------------------------------------
DWORD InvokeTerminalWindow(LPCWSTR pszPhoneBook, LPCWSTR pszEntry, RASDIALPARAMS *pRasDialParams, HWND hWnd, HRASCONN hRasconn)
{
    //
    //  Validate the input parameters.  Note that pszPhoneBook can be NULL but if it is non-NULL then we cannot have
    //  an empty string.
    //
    MYDBGASSERT(OS_NT51);
    if (((NULL != pszPhoneBook) && (L'\0' == pszPhoneBook[0])) || 
        (NULL == pszEntry) || (L'\0' == pszEntry[0]) || (NULL == pRasDialParams) || 
        (NULL == hWnd) || (NULL == hRasconn))
    {
        CMASSERTMSG(FALSE, TEXT("InvokeTerminalWindow - Invalid parameter passed."));
        return ERROR_INVALID_PARAMETER; 
    }

    DWORD dwReturn;
    typedef DWORD (WINAPI *pfnDwTerminalDlgSpec)(LPCWSTR, LPCWSTR, RASDIALPARAMS *, HWND, HRASCONN);

    //
    //  First call loadlibrary on rasdlg.dll
    //

    HMODULE hRasDlg = LoadLibraryExU(TEXT("rasdlg.dll"), NULL, 0);

    if (hRasDlg)
    {
        pfnDwTerminalDlgSpec pfnDwTerminalDlg = (pfnDwTerminalDlgSpec)GetProcAddress(hRasDlg, "DwTerminalDlg");

        if (pfnDwTerminalDlg)
        {
            dwReturn = pfnDwTerminalDlg(pszPhoneBook, pszEntry, pRasDialParams, hWnd, hRasconn);
        }
        else
        {
            dwReturn = ERROR_PROC_NOT_FOUND;
        }
        
        FreeLibrary(hRasDlg);
    }
    else
    {
        dwReturn = ERROR_MOD_NOT_FOUND;
    }

    return dwReturn;
}

//+----------------------------------------------------------------------------
//
// Function:  OnPauseRasDial
//
// Synopsis:  Message handler for RasDial pause states. In the pause state, RAS
//            is suspended, waiting for us to restart it by calling RasDial after
//            performing the appropriate interface with the user.
//
// Arguments: HWND hwndDlg      - Window handle of main dialog
//            ArgsStruct *pArgs - Ptr to global args struct  
//            WPARAM wParam     - wParam being handled  
//            LPARAM lParam     - lParam being handled
//
// Returns:   Windows error message
//
// History:   nickball    Created               05/19/99
//
//+----------------------------------------------------------------------------

DWORD OnPauseRasDial(HWND hwndDlg, ArgsStruct *pArgs, WPARAM wParam, LPARAM lParam)        
{                      
    CMTRACE2(TEXT("OnPauseRasDial - wParam is %u and lParam is %u."), wParam, lParam);    

    MYDBGASSERT(pArgs);
    if (NULL == pArgs)
    {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Get connection handle and re-dial
    //
    
    HRASCONN hRasConn;
    DWORD dwRes = ERROR_SUCCESS;    
    LPTSTR pszRasPbk = pArgs->pszRasPbk;
    
    //
    // Determine the appropriate connection handle and phonebook
    // Note: Make an explicit copy or we'll wind up re-dialing
    // if the connection drops while the pause UI is invoked.
    //

    if (IsDialingTunnel(pArgs))
    {
        hRasConn = pArgs->hrcTunnelConn;
    }
    else
    {
        hRasConn = pArgs->hrcRasConn;

        if (pArgs->pszRasHiddenPbk)
        {
            pszRasPbk = pArgs->pszRasHiddenPbk;
        }
    }

    //
    // Handle the pause
    //

    switch (wParam)
    {
        case (RASCS_PAUSED + 4):  // 4100 - RASCS_InvokeEapUI )

            //
            // If UNATTENDED, just bail out immediately.
            //
    
            if (pArgs->dwFlags & FL_UNATTENDED)
            {
                dwRes = ERROR_INTERACTIVE_MODE;
                goto OnPauseRasDialExit;
            }

            //
            // If EAP triggered the pause, invoke the EAP UI
            //
    
            dwRes = pArgs->rlsRasLink.pfnInvokeEapUI(hRasConn, pArgs->dwRasSubEntry, pArgs->pRasDialExtensions, hwndDlg);
      
            CMTRACE1(TEXT("OnPauseRasDial() - InvokeEapUI() returns %u."), dwRes);           
            break;

        case RASCS_PasswordExpired: // Domain password has expired
        {
            //
            // If UNATTENDED, just bail out immediately.
            //
    
            if (pArgs->dwFlags & FL_UNATTENDED)
            {
                dwRes = ERROR_INTERACTIVE_MODE;
                goto OnPauseRasDialExit;
            }

            CChangePasswordDlg  NewPasswordDlg(pArgs);
            
            if (IDOK != NewPasswordDlg.DoDialogBox(g_hInst, IDD_CHANGEPASSWORD, pArgs->hwndMainDlg))
            {
                if (pArgs->dwExitCode)
                {
                    dwRes = pArgs->dwExitCode;
                }
                else
                {
                    dwRes = ERROR_CANCELLED;
                }
            }
            
            CMTRACE1(TEXT("OnPauseRasDial() - Password Expired"), dwRes);
            
            break;
        }

        case RASCS_CallbackSetByCaller: // Server wants to call us back
        {           
            //
            // Preset dial params and call dialog to retrieve number from user
            // 
            
            LPTSTR pszTmp = pArgs->piniProfile->GPPS(c_pszCmSection, c_pszCmEntryCallbackNumber);   
            lstrcpyU(pArgs->pRasDialParams->szCallbackNumber, pszTmp);
            CmFree(pszTmp);
       
            //
            // If we're running unattended, skip the dialog phase. The 
            // presumption is that there is no user there to receive it.
            //

            BOOL bPromptUser = !(pArgs->dwFlags & FL_UNATTENDED);
                               
            if (bPromptUser)    
            {
                //
                // The above also applies in the case of DialAutomatically
                // if we have a phone number, then there is no need to prompt.
                //
                
                if (pArgs->fDialAutomatically && TEXT('\0') != pArgs->pRasDialParams->szCallbackNumber[0])
                {
                    bPromptUser = FALSE;
                }
            }

            if (bPromptUser)
            {
                CCallbackNumberDlg CallbackNumberDialog(pArgs);                       
                
                if (IDOK != CallbackNumberDialog.DoDialogBox(g_hInst, IDD_CALLBACK_NUMBER, pArgs->hwndMainDlg))
                {
                    //
                    // If the user canceled, clear the number so that RAS wont attempt callback
                    //

                    lstrcpyU(pArgs->pRasDialParams->szCallbackNumber, TEXT(""));
                }
            }
           
            dwRes = ERROR_SUCCESS;          
            CMTRACE1(TEXT("OnPauseRasDial() - CallbackSetByCaller returns %u"), dwRes);
            
            break;
        }

        case RASCS_RetryAuthentication: // Credentials aren't correct
        {
            //
            // If UNATTENDED, just bail out immediately.
            //
    
            if (pArgs->dwFlags & FL_UNATTENDED)
            {
                dwRes = ERROR_INTERACTIVE_MODE;
                goto OnPauseRasDialExit;
            }

            //
            // Creds didn't work, prompt user for new ones.
            //

            CRetryAuthenticationDlg RetryAuthenticationDialog(pArgs); 

            if (IDOK != RetryAuthenticationDialog.DoDialogBox(g_hInst, 
                                                               RetryAuthenticationDialog.GetDlgTemplate(),
                                                               pArgs->hwndMainDlg))         
            {
                //
                // User canceled, or the call was dropped elsewhere. Use 
                // existing error code or designate authentication failure.
                //

                if (pArgs->dwExitCode)
                {
                    dwRes = pArgs->dwExitCode;
                }
                else
                {
                    dwRes = ERROR_AUTHENTICATION_FAILURE;
                }
            }
            
            CMTRACE1(TEXT("OnPauseRasDial() - RetryAuthentication"), dwRes);            
            break;      
        }
        
        case RASCS_Interactive:         // Terminal/script pause state
            if (OS_NT51)
            {
                if (pArgs->dwFlags & FL_UNATTENDED)
                {
                    dwRes = ERROR_INTERACTIVE_MODE;
                    goto OnPauseRasDialExit;
                }

                dwRes = InvokeTerminalWindow(pszRasPbk, pArgs->szServiceName, pArgs->pRasDialParams, pArgs->hwndMainDlg, hRasConn);
                break;
            } // else fail through to default and error out.

        //
        // We got a pause state that we don't handle, error out.
        //
        default:
            dwRes = ERROR_INTERACTIVE_MODE;
            CMASSERTMSG(FALSE, TEXT("OnPauseRasDial() - Error, unsupported RAS pause state encountered."));                                
            break;
    }

    //
    // On success, call RasDial to resume connection
    //

    if (ERROR_SUCCESS == dwRes)
    {
        //
        // Decode active password, re-call RasDial, then re-encode
        //
        
        CmDecodePassword(pArgs->pRasDialParams->szPassword); 

        CMASSERTMSG((NOT_IN_CONNECT_OR_CANCEL == pArgs->lInConnectOrCancel),
                    TEXT("OnPauseRasDial - RasDial mutex is NOT NULL..."));

        dwRes = pArgs->rlsRasLink.pfnDial(pArgs->pRasDialExtensions, 
                                          pszRasPbk, 
                                          pArgs->pRasDialParams, 
                                          GetRasCallBackType(), 
                                          GetRasCallBack(pArgs), 
                                          &hRasConn);

        CmEncodePassword(pArgs->pRasDialParams->szPassword); 
 
        CMTRACE1(TEXT("OnPauseRasDial() - RasDial() returns %u."), dwRes);           

        //
        // Reset timers, the current action starts now.
        //

        pArgs->dwStateStartTime = GetTickCount();
        pArgs->nLastSecondsDisplay = (UINT) -1;
    }

OnPauseRasDialExit:

    if (ERROR_SUCCESS != dwRes)
    {
        OnRasErrorMessage(hwndDlg, pArgs, dwRes);
    }

    return dwRes;
}

//+----------------------------------------------------------------------------
//
// Function:  GetRasCallBackType
//
// Synopsis:  Simple function to return the Callback type that we use for RasDial
//            depending upon the OS.
//
// Arguments: None
//
// Returns:   DWORD - The callback type
//
// History:   nickball    Created       05/22/99
//
//+----------------------------------------------------------------------------

DWORD GetRasCallBackType()
{
    if (OS_NT5) 
    {
        return 2;
    }
    else
    {
        return -1;
    }
}

//+----------------------------------------------------------------------------
//
// Function:  GetRasCallBack
//
// Synopsis:  Simple function to return the Callback that we use for RasDial
//            depending upon the OS.
//
// Arguments: ArgsStruct *pArgs - Ptr to global args struct  
//
// Returns:   LPVOID - The callback
//
// History:   nickball    Created       05/22/99
//
//+----------------------------------------------------------------------------

LPVOID GetRasCallBack(ArgsStruct* pArgs)
{
    MYDBGASSERT(pArgs);

    if (NULL == pArgs)
    {
        return NULL;
    }

    //
    // Now set return the callback func or hwnd according to OS.
    //

    if (OS_NT5)
    {       
        //
        // Set Callback data in RasDialParams
        //

        if (pArgs->pRasDialParams->dwSize == sizeof(RASDIALPARAMS_V401))
        {          
            ((LPRASDIALPARAMS_V401)pArgs->pRasDialParams)->dwCallbackId = (ULONG_PTR) pArgs;
        }      

       return (LPVOID) RasDialFunc2;
    }
    else
    {
        MYDBGASSERT(pArgs->hwndMainDlg);
        return (LPVOID) pArgs->hwndMainDlg;
    }
}


//+----------------------------------------------------------------------------
//
// Function:  AllocateSecurityDescriptorAllowAccessToWorld
//
// Synopsis:  This function allocates a security descriptor for all users.
//            This function was taken directly from RAS when they create their
//            phonebook. This has to be before GetPhoneBookPath otherwise it 
//            causes compile errors in other components since we don't have a
//            function prototype anywhere and cmcfg just includes this (getpbk.cpp)
//            file. This function is also in common\source\getpbk.cpp
//
// Arguments: PSECURITY_DESCRIPTOR *ppSd - Pointer to a pointer to the SD struct
//
// Returns:   DWORD - returns ERROR_SUCCESS if successfull
//
// History:   06/27/2001    tomkel  Taken from RAS ui\common\pbk\file.c
//
//+----------------------------------------------------------------------------
#define SIZE_ALIGNED_FOR_TYPE(_size, _type) \
    (((_size) + sizeof(_type)-1) & ~(sizeof(_type)-1))

DWORD AllocateSecurityDescriptorAllowAccessToWorld(PSECURITY_DESCRIPTOR *ppSd)
{
    PSECURITY_DESCRIPTOR    pSd;
    PSID                    pSid;
    PACL                    pDacl;
    DWORD                   dwErr = ERROR_SUCCESS;
    DWORD                   dwAlignSdSize;
    DWORD                   dwAlignDaclSize;
    DWORD                   dwSidSize;
    PVOID                   pvBuffer;
    DWORD                   dwAcls = 0;

    // Here is the buffer we are building.
    //
    //   |<- a ->|<- b ->|<- c ->|
    //   +-------+--------+------+
    //   |      p|      p|       |
    //   | SD   a| DACL a| SID   |
    //   |      d|      d|       |
    //   +-------+-------+-------+
    //   ^       ^       ^
    //   |       |       |
    //   |       |       +--pSid
    //   |       |
    //   |       +--pDacl
    //   |
    //   +--pSd (this is returned via *ppSd)
    //
    //   pad is so that pDacl and pSid are aligned properly.
    //
    //   a = dwAlignSdSize
    //   b = dwAlignDaclSize
    //   c = dwSidSize
    //

    if (NULL == ppSd)
    {
        return ERROR_INVALID_PARAMETER;
    }

    // Initialize output parameter.
    //
    *ppSd = NULL;

    // Compute the size of the SID.  The SID is the well-known SID for World
    // (S-1-1-0).
    //
    dwSidSize = GetSidLengthRequired(1);

    // Compute the size of the DACL.  It has an inherent copy of SID within
    // it so add enough room for it.  It also must sized properly so that
    // a pointer to a SID structure can come after it.  Hence, we use
    // SIZE_ALIGNED_FOR_TYPE.
    //
    dwAlignDaclSize = SIZE_ALIGNED_FOR_TYPE(
                        sizeof(ACCESS_ALLOWED_ACE) + sizeof(ACL) + dwSidSize,
                        PSID);

    // Compute the size of the SD.  It must be sized propertly so that a
    // pointer to a DACL structure can come after it.  Hence, we use
    // SIZE_ALIGNED_FOR_TYPE.
    //
    dwAlignSdSize   = SIZE_ALIGNED_FOR_TYPE(
                        sizeof(SECURITY_DESCRIPTOR),
                        PACL);

    // Allocate the buffer big enough for all.
    //
    dwErr = ERROR_OUTOFMEMORY;
    pvBuffer = CmMalloc(dwSidSize + dwAlignDaclSize + dwAlignSdSize);
    if (pvBuffer)
    {
        SID_IDENTIFIER_AUTHORITY SidIdentifierWorldAuth
                                    = SECURITY_WORLD_SID_AUTHORITY;
        PULONG  pSubAuthority;

        dwErr = NOERROR;

        // Setup the pointers into the buffer.
        //
        pSd   = pvBuffer;
        pDacl = (PACL)((PBYTE)pvBuffer + dwAlignSdSize);
        pSid  = (PSID)((PBYTE)pDacl + dwAlignDaclSize);

        // Initialize pSid as S-1-1-0.
        //
        if (!InitializeSid(
                pSid,
                &SidIdentifierWorldAuth,
                1))  // 1 sub-authority
        {
            dwErr = GetLastError();
            goto finish;
        }

        pSubAuthority = GetSidSubAuthority(pSid, 0);
        *pSubAuthority = SECURITY_WORLD_RID;

        // Initialize pDacl.
        //
        if (!InitializeAcl(
                pDacl,
                dwAlignDaclSize,
                ACL_REVISION))
        {
            dwErr = GetLastError();
            goto finish;
        }

        dwAcls = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;

        dwAcls &= ~(WRITE_DAC | WRITE_OWNER);
        
        if(!AddAccessAllowedAce(
                pDacl,
                ACL_REVISION,
                dwAcls,
                pSid))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Initialize pSd.
        //
        if (!InitializeSecurityDescriptor(
                pSd,
                SECURITY_DESCRIPTOR_REVISION))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Set pSd to use pDacl.
        //
        if (!SetSecurityDescriptorDacl(
                pSd,
                TRUE,
                pDacl,
                FALSE))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Set the owner for pSd.
        //
        if (!SetSecurityDescriptorOwner(
                pSd,
                NULL,
                TRUE))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Set the group for pSd.
        //
        if (!SetSecurityDescriptorGroup(
                pSd,
                NULL,
                FALSE))
        {
            dwErr = GetLastError();
            goto finish;
        }

finish:
        if (!dwErr)
        {
            *ppSd = pSd;
        }
        else
        {
            CmFree(pvBuffer);
        }
    }

    return dwErr;
}