/* Copyright (c) 1995, Microsoft Corporation, all rights reserved
**
** ifw.c
** Remote Access Common Dialog APIs
** Add Interface wizard
**
** 02/11/97 Abolade Gbadegesin (based on entryw.c, by Steve Cobb).
*/

#include "rasdlgp.h"
#include "entry.h"

/* Page definitions.
*/
#define AI_InPage    0
#define AI_SsPage    1
#define AI_RnPage    2
#define AI_RpPage    3
#define AI_RaPage    4
#define AI_NsPage    5
#define AI_RcPage    6
#define AI_DoPage    7
#define AI_DiPage    8
#define AI_RfPage    9
#define AI_PageCount 10


/*----------------------------------------------------------------------------
** Local datatypes (alphabetically)
**----------------------------------------------------------------------------
*/

/* Add Interface wizard context block.  All property pages refer to the single
** context block associated with the sheet.
*/
#define AIINFO struct tagAIINFO
AIINFO
{
    /* Common input arguments.
    */
    EINFO* pArgs;

    /* Wizard and page handles.  'hwndFirstPage' is the handle of the first
    ** property page initialized.  This is the page that allocates and frees
    ** the context block.
    */
    HWND hwndDlg;
    HWND hwndFirstPage;
    HWND hwndIn;
    HWND hwndSs;
    HWND hwndRn;
    HWND hwndRp;
    HWND hwndRc;
    HWND hwndRa;
    HWND hwndNs;
    HWND hwndDo;
    HWND hwndDi;
    HWND hwndRf;

    /* Interface Name page.
    */
    HWND hwndEbInterfaceName;

    /* Modem/Adapter page.
    */
    HWND hwndLv;

    /* Phone number page.
    */
    HWND hwndStNumber;
    HWND hwndEbNumber;
    HWND hwndPbAlternates;

    /* Login script page.
    */
    HWND hwndRbNone;
    HWND hwndRbScript;
    HWND hwndLbScripts;
    HWND hwndPbEdit;
    HWND hwndPbRefresh;

    /* IP address page.
    */
    HWND hwndCcIp;

    /* Name server page.
    */
    HWND hwndCcDns;
    HWND hwndCcWins;

    /* Dial-out credentials page.
    */
    HWND hwndDoEbUserName;
    HWND hwndDoEbDomain;
    HWND hwndDoEbPassword;
    HWND hwndDoEbConfirm;

    /* Dial-in credentials page.
    */
    HWND hwndDiEbUserName;
    HWND hwndDiEbDomain;
    HWND hwndDiEbPassword;
    HWND hwndDiEbConfirm;

    /* The phone number stash.  This allows user to change the port to another
    ** link without losing the phone number he typed.  Initialized to empty in
    ** AiInit and saved to entry in AiFinish.
    */
    DTLLIST* pListPhoneNumbers;
    BOOL     fPromoteHuntNumbers;

    /* Checkbox options chosen by user.
    */
    BOOL fIp;
    BOOL fIpx;
    BOOL fClearPwOk;
    BOOL fNotNt;

    /* Set true when there is only one meaningful choice of device.
    */
    BOOL fSkipMa;

    /* Set true if the selected device is a modem or null modem.
    */
    BOOL fModem;

    /* The NP_* mask of protocols configured for RAS.
    */
    DWORD dwfConfiguredProtocols;

    /* Set true if IP is configured for RAS.
    */
    BOOL fIpConfigured;
    BOOL fIpxConfigured;
};


/*----------------------------------------------------------------------------
** Local prototypes (alphabetically)
**----------------------------------------------------------------------------
*/

int CALLBACK
AiCallbackFunc(
    IN HWND   hwndDlg,
    IN UINT   unMsg,
    IN LPARAM lparam );

VOID
AiCancel(
    IN HWND hwndPage );

AIINFO*
AiContext(
    IN HWND hwndPage );

VOID
AiExit(
    IN AIINFO* pInfo,
    IN DWORD   dwError );

VOID
AiExitInit(
    IN HWND hwndDlg );

BOOL
AiFinish(
    IN HWND hwndPage );

AIINFO*
AiInit(
    IN HWND   hwndFirstPage,
    IN EINFO* pArgs );

VOID
AiTerm(
    IN HWND hwndPage );

INT_PTR CALLBACK
DiDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
DiInit(
    IN HWND   hwndPage );

BOOL
DiKillActive(
    IN AIINFO* pInfo );

BOOL
DiSetActive(
    IN AIINFO* pInfo );

INT_PTR CALLBACK
DoDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
DoInit(
    IN HWND   hwndPage );

BOOL
DoKillActive(
    IN AIINFO* pInfo );

BOOL
DoSetActive(
    IN AIINFO* pInfo );

BOOL
InCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl );

INT_PTR CALLBACK
InDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
InInit(
    IN     HWND   hwndPage,
    IN OUT EINFO* pArgs );

BOOL
InKillActive(
    IN AIINFO* pInfo );

BOOL
InSetActive(
    IN AIINFO* pInfo );

INT_PTR CALLBACK
NsDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
NsInit(
    IN HWND   hwndPage );

BOOL
NsKillActive(
    IN AIINFO* pInfo );

BOOL
NsSetActive(
    IN AIINFO* pInfo );

INT_PTR CALLBACK
RaDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
RaInit(
    IN HWND hwndPage );

BOOL
RaKillActive(
    IN AIINFO* pInfo );

BOOL
RaSetActive(
    IN AIINFO* pInfo );

BOOL
RcCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl );

INT_PTR CALLBACK
RcDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
RcInit(
    IN HWND hwndPage );

BOOL
RcKillActive(
    IN AIINFO* pInfo );

BOOL
RcSetActive(
    IN AIINFO* pInfo );

BOOL
RfCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl );

INT_PTR CALLBACK
RfDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
RfInit(
    IN HWND hwndPage );

BOOL
RfSetActive(
    IN AIINFO* pInfo );

INT_PTR CALLBACK
RnDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
RnInit(
    IN HWND hwndPage );

LVXDRAWINFO*
RnLvCallback(
    IN HWND  hwndLv,
    IN DWORD dwItem );

VOID
RnLvItemChanged(
    IN AIINFO* pInfo );

BOOL
RnSetActive(
    IN AIINFO* pInfo );

VOID
RpAlternates(
    IN AIINFO* pInfo );

BOOL
RpCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl );

INT_PTR CALLBACK
RpDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
RpInit(
    IN HWND hwndPage );

BOOL
RpKillActive(
    IN AIINFO* pInfo );

VOID
RpPhoneNumberToStash(
    IN AIINFO* pInfo );

BOOL
RpSetActive(
    IN AIINFO* pInfo );

BOOL
SsCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl );

INT_PTR CALLBACK
SsDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam );

BOOL
SsInit(
    IN HWND hwndPage );

BOOL
SsKillActive(
    IN AIINFO* pInfo );

BOOL
SsSetActive(
    IN AIINFO* pInfo );


/*----------------------------------------------------------------------------
** Add Interface wizard entry point
**----------------------------------------------------------------------------
*/

VOID
AiWizard(
    IN OUT EINFO* pEinfo )

    /* Runs the Phonebook entry property sheet.  'PEinfo' is an input block
    ** with only caller's API arguments filled in.
    */
{
    DWORD           dwErr;
    PROPSHEETHEADER header;
    PROPSHEETPAGE   apage[ AI_PageCount ];
    PROPSHEETPAGE*  ppage;

    TRACE("AiWizard");

    ZeroMemory( &header, sizeof(header) );

    header.dwSize = sizeof(PROPSHEETHEADER);
    header.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD | PSH_USECALLBACK;
    header.hwndParent = pEinfo->pApiArgs->hwndOwner;
    header.hInstance = g_hinstDll;
    header.nPages = AI_PageCount;
    header.ppsp = apage;
    header.pfnCallback = AiCallbackFunc;

    ZeroMemory( apage, sizeof(apage) );

    ppage = &apage[ AI_InPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_IN_InterfaceName );
    ppage->pfnDlgProc = InDlgProc;
    ppage->lParam = (LPARAM )pEinfo;

    ppage = &apage[ AI_SsPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_SS_ServerSettings );
    ppage->pfnDlgProc = SsDlgProc;

    ppage = &apage[ AI_RnPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_RN_RouterModemAdapter );
    ppage->pfnDlgProc = RnDlgProc;

    ppage = &apage[ AI_RpPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_RP_RouterPhoneNumber );
    ppage->pfnDlgProc = RpDlgProc;

    ppage = &apage[ AI_RaPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_RA_RouterIpAddress );
    ppage->pfnDlgProc = RaDlgProc;

    ppage = &apage[ AI_NsPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_NS_RouterNameServers );
    ppage->pfnDlgProc = NsDlgProc;

    ppage = &apage[ AI_RcPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_RC_RouterScripting );
    ppage->pfnDlgProc = RcDlgProc;

    ppage = &apage[ AI_DoPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_DO_RouterDialOut );
    ppage->pfnDlgProc = DoDlgProc;

    ppage = &apage[ AI_DiPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_DI_RouterDialIn );
    ppage->pfnDlgProc = DiDlgProc;

    ppage = &apage[ AI_RfPage ];
    ppage->dwSize = sizeof(PROPSHEETPAGE);
    ppage->hInstance = g_hinstDll;
    ppage->pszTemplate = MAKEINTRESOURCE( PID_RF_RouterFinish );
    ppage->pfnDlgProc = RfDlgProc;

    if (PropertySheet( &header ) == -1)
    {
        TRACE("PropertySheet failed");
        ErrorDlg( pEinfo->pApiArgs->hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN,
            NULL );
    }
}


/*----------------------------------------------------------------------------
** Add Interface wizard
** Listed alphabetically
**----------------------------------------------------------------------------
*/

int CALLBACK
AiCallbackFunc(
    IN HWND   hwndDlg,
    IN UINT   unMsg,
    IN LPARAM lparam )

    /* A standard Win32 commctrl PropSheetProc.  See MSDN documentation.
    **
    ** Returns 0 always.
    */
{
    TRACE2("AiCallbackFunc(m=%d,l=%08x)",unMsg,lparam);

    if (unMsg == PSCB_PRECREATE)
    {
        DLGTEMPLATE* pDlg = (DLGTEMPLATE* )lparam;
        pDlg->style &= ~(DS_CONTEXTHELP);
    }

    return 0;
}


VOID
AiCancel(
    IN HWND hwndPage )

    /* Cancel was pressed.  'HwndPage' is the handle of a wizard page.
    */
{
    TRACE("AiCancel");
}


AIINFO*
AiContext(
    IN HWND hwndPage )

    /* Retrieve the property sheet context from a wizard page handle.
    */
{
    return (AIINFO* )GetProp( GetParent( hwndPage ), g_contextId );
}


VOID
AiExit(
    IN AIINFO* pInfo,
    IN DWORD   dwError )

    /* Forces an exit from the dialog, reporting 'dwError' to the caller.
    ** 'PInfo' is the dialog context.
    **
    ** Note: This cannot be called during initialization of the first page.
    **       See AiExitInit.
    */
{
    TRACE("AiExit");

    pInfo->pArgs->pApiArgs->dwError = dwError;
    PropSheet_PressButton( pInfo->hwndDlg, PSBTN_CANCEL );
}


VOID
AiExitInit(
    IN HWND hwndDlg )

    /* Utility to report errors within AiInit and other first page
    ** initialization.  'HwndDlg' is the dialog window.
    */
{
    SetOffDesktop( hwndDlg, SOD_MoveOff, NULL );
    SetOffDesktop( hwndDlg, SOD_Free, NULL );
    PostMessage( hwndDlg, WM_COMMAND,
        MAKEWPARAM( IDCANCEL , BN_CLICKED ),
        (LPARAM )GetDlgItem( hwndDlg, IDCANCEL ) );
}


BOOL
AiFinish(
    IN HWND hwndPage )

    /* Saves the contents of the wizard.  'HwndPage is the handle of a
    ** property page.  Pops up any errors that occur.  'FPropertySheet'
    ** indicates the user chose to edit the property sheet directly.
    **
    ** Returns true is page can be dismissed, false otherwise.
    */
{
    const TCHAR* pszIp0 = TEXT("0.0.0.0");

    AIINFO*  pInfo;
    PBENTRY* pEntry;

    TRACE("AiFinish");

    pInfo = AiContext( hwndPage );
    ASSERT(pInfo);
    pEntry = pInfo->pArgs->pEntry;
    ASSERT(pEntry);

    /* Attach the stashed phone number information to the final link(s).
    */
    EuPhoneNumberStashToEntry( pInfo->pArgs, pInfo->pListPhoneNumbers,
        pInfo->fPromoteHuntNumbers, TRUE );

    /* Update some settings based on user selections.
    */
    if (pInfo->fNotNt)
    {
        pEntry->fLcpExtensions = FALSE;
        pEntry->fSwCompression = FALSE;
    }

    if (!pInfo->fClearPwOk)
        pEntry->dwAuthRestrictions = AR_AuthEncrypted;

    if (!pInfo->fIp)
        pEntry->dwfExcludedProtocols |= NP_Ip;

    if (!pInfo->fIpx)
        pEntry->dwfExcludedProtocols |= NP_Ipx;

    if (pEntry->pszIpAddress
        && lstrcmp( pEntry->pszIpAddress, pszIp0 ) != 0)
    {
        pEntry->dwIpAddressSource = ASRC_RequireSpecific;
    }

    if ((pEntry->pszIpDnsAddress
             && lstrcmp( pEntry->pszIpDnsAddress, pszIp0 ) != 0)
        || (pEntry->pszIpWinsAddress
             && lstrcmp( pEntry->pszIpWinsAddress, pszIp0 ) != 0))
    {
        pEntry->dwIpNameSource = ASRC_RequireSpecific;
    }

    /* It's a valid new entry and caller has not chosen to edit properties
    ** directly, so mark the entry for commitment.
    */
    if (!pInfo->pArgs->fChainPropertySheet)
        pInfo->pArgs->fCommit = TRUE;

    return TRUE;
}


AIINFO*
AiInit(
    IN HWND   hwndFirstPage,
    IN EINFO* pArgs )

    /* Wizard level initialization.  'HwndPage' is the handle of the first
    ** page.  'PArgs' is the common entry input argument block.
    **
    ** Returns address of the context block if successful, NULL otherwise.  If
    ** NULL is returned, an appropriate message has been displayed, and the
    ** wizard has been cancelled.
    */
{
    DWORD   dwErr;
    DWORD   dwOp;
    AIINFO* pInfo;
    HWND    hwndDlg;

    TRACE("AiInit");

    hwndDlg = GetParent( hwndFirstPage );

    /* Allocate the context information block.  Initialize it enough so that
    ** it can be destroyed properly, and associate the context with the
    ** window.
    */
    {
        pInfo = Malloc( sizeof(*pInfo) );
        if (!pInfo)
        {
            TRACE("Context NOT allocated");
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            pArgs->pApiArgs->dwError = ERROR_NOT_ENOUGH_MEMORY;
            AiExitInit( hwndDlg );
            return NULL;
        }

        ZeroMemory( pInfo, sizeof(AIINFO) );
        pInfo->pArgs = pArgs;
        pInfo->hwndDlg = hwndDlg;
        pInfo->hwndFirstPage = hwndFirstPage;

        if (!SetProp( hwndDlg, g_contextId, pInfo ))
        {
            TRACE("Context NOT set");
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
            pArgs->pApiArgs->dwError = ERROR_UNKNOWN;
            Free( pInfo );
            AiExitInit( hwndDlg );
            return NULL;
        }

        TRACE("Context set");
    }

    /* Position the dialog per caller's instructions.
    */
    PositionDlg( hwndDlg,
        pArgs->pApiArgs->dwFlags & RASDDFLAG_PositionDlg,
        pArgs->pApiArgs->xDlg, pArgs->pApiArgs->yDlg );

    /* Mess with the title bar gadgets.
    */
    TweakTitleBar( hwndDlg );

#if 0
    /* Load MPRAPI DLL entrypoints which starts RASMAN, if necessary.
    */
    dwErr = LoadMpradminDll( );
    if (dwErr != 0)
    {
        ErrorDlg( hwndDlg, SID_OP_LoadRas, dwErr, NULL );
        pArgs->pApiArgs->dwError = dwErr;
        AiExitInit( hwndDlg );
        return NULL;
    }
#endif

    /* Load the common entry information.
    ** Note that EuInit assumes that EuInit0 has previously been called.
    */
    dwErr = EuInit( pArgs, &dwOp );
    if (dwErr != 0)
    {
        ErrorDlg( hwndDlg, dwOp, dwErr, NULL );
        pArgs->pApiArgs->dwError = dwErr;
        AiExitInit( hwndDlg );
        return NULL;
    }

    /* Initialize these meta-flags that are not actually stored.
    */
    pInfo->fNotNt = FALSE;
    pInfo->fSkipMa = FALSE;
    pInfo->fModem = FALSE;
    pInfo->pArgs->fPadSelected = FALSE;
    pInfo->dwfConfiguredProtocols = g_pGetInstalledProtocols();
    pInfo->fIpConfigured = (pInfo->dwfConfiguredProtocols & NP_Ip);
    pInfo->fIpxConfigured = (pInfo->dwfConfiguredProtocols & NP_Ipx);

    /* Initialize the phone number stash to from the entry, i.e. set it to
    ** empty default.  The stash list is edited rather than the list in the
    ** entry so user can change active links without losing the phone number
    ** he entered.
    */
    EuPhoneNumberStashFromEntry( pArgs, &pInfo->pListPhoneNumbers,
        &pInfo->fPromoteHuntNumbers );

    return pInfo;
}


VOID
AiTerm(
    IN HWND hwndPage )

    /* Wizard level termination.  Releases the context block.  'HwndPage' is
    ** the handle of a property page.
    */
{
    AIINFO* pInfo;

    TRACE("AiTerm");

    pInfo = AiContext( hwndPage );
    if (pInfo)
    {
        DtlDestroyList( pInfo->pListPhoneNumbers, DestroyPszNode );

        Free( pInfo );
        TRACE("Context freed");
    }

    RemoveProp( GetParent( hwndPage ), g_contextId );
}



/*----------------------------------------------------------------------------
** Dial-In Credentials property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
DiDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Dial-in Credentials page of the wizard.
    ** Parameters and return values are as described for standard windows
    ** 'DialogProc's.
    */
{

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return
                DiInit( hwnd );
        }

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("DiSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = DiSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("DiKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = DiKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }
    }

    return FALSE;
}



BOOL
DiInit(
    IN     HWND   hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.  'PArgs' is the arguments from the PropertySheet caller.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("DiInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndDi = hwndPage;
    pInfo->hwndDiEbUserName = GetDlgItem( hwndPage, CID_DI_EB_UserName );
    Edit_LimitText( pInfo->hwndDiEbUserName, UNLEN );
    pInfo->hwndDiEbDomain = GetDlgItem( hwndPage, CID_DI_EB_Domain );
    Edit_LimitText( pInfo->hwndDiEbDomain, DNLEN );
    pInfo->hwndDiEbPassword = GetDlgItem( hwndPage, CID_DI_EB_Password );
    Edit_LimitText( pInfo->hwndDiEbPassword, PWLEN );
    pInfo->hwndDiEbConfirm = GetDlgItem( hwndPage, CID_DI_EB_Confirm );
    Edit_LimitText( pInfo->hwndDiEbConfirm, PWLEN );

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}




BOOL
DiKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    TCHAR* psz;

    psz = GetText(pInfo->hwndDiEbPassword);
    if (psz)
    {
        TCHAR* psz2;

        psz2 = GetText(pInfo->hwndDiEbConfirm);
        if (psz2)
        {

            if (lstrcmp(psz, psz2))
            {
                Free(psz);
                Free(psz2);
                MsgDlg(pInfo->hwndDlg, SID_PasswordMismatch, NULL);
                SetFocus(pInfo->hwndDiEbPassword);
                return TRUE;
            }

            Free(psz2);
            Free0(pInfo->pArgs->pszRouterDialInPassword);
            pInfo->pArgs->pszRouterDialInPassword = psz;
        }            
        else
        {
            Free(psz);
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
DiSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    /* The dialog is only displayed if the user is adding a dial-in account.
    */
    if (!pInfo->pArgs->fAddUser)
        return FALSE;

    /* Display the interface name in the disabled edit-box
    */
    SetWindowText(
        pInfo->hwndDiEbUserName, pInfo->pArgs->pEntry->pszEntryName );

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}




/*----------------------------------------------------------------------------
** Dial-Out Credentials property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
DoDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Dial-Out Credentials page of the wizard.
    ** Parameters and return values are as described for standard windows
    ** 'DialogProc's.
    */
{

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return
                DoInit( hwnd );
        }

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("DoSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = DoSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("DoKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = DoKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }
    }

    return FALSE;
}



BOOL
DoInit(
    IN     HWND   hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.  'PArgs' is the arguments from the PropertySheet caller.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("DoInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndDo = hwndPage;
    pInfo->hwndDoEbUserName = GetDlgItem( hwndPage, CID_DO_EB_UserName );
    Edit_LimitText( pInfo->hwndDoEbUserName, UNLEN );
    pInfo->hwndDoEbDomain = GetDlgItem( hwndPage, CID_DO_EB_Domain );
    Edit_LimitText( pInfo->hwndDoEbDomain, DNLEN );
    pInfo->hwndDoEbPassword = GetDlgItem( hwndPage, CID_DO_EB_Password );
    Edit_LimitText( pInfo->hwndDoEbPassword, PWLEN );
    pInfo->hwndDoEbConfirm = GetDlgItem( hwndPage, CID_DO_EB_Confirm );
    Edit_LimitText( pInfo->hwndDoEbConfirm, PWLEN );

    /* Use the target router name as the default "User name",
    */
    if (pInfo->pArgs->pszRouter)
    {
        if (pInfo->pArgs->pszRouter[0] == TEXT('\\') &&
            pInfo->pArgs->pszRouter[1] == TEXT('\\'))
            SetWindowText(pInfo->hwndDoEbUserName, pInfo->pArgs->pszRouter+2);
        else
            SetWindowText(pInfo->hwndDoEbUserName, pInfo->pArgs->pszRouter);
    }

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}




BOOL
DoKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    TCHAR* psz;

    psz = GetText(pInfo->hwndDoEbUserName);
    if (psz)
    {
        if (!lstrlen(psz))
        {
            Free(psz);
            MsgDlg(pInfo->hwndDlg, SID_DialOutUserName, NULL);
            SetFocus(pInfo->hwndDoEbUserName);
            return TRUE;
        }

        Free0(pInfo->pArgs->pszRouterUserName);
        pInfo->pArgs->pszRouterUserName = psz;
    }

    psz = GetText(pInfo->hwndDoEbDomain);
    if (psz)
    {
        Free0(pInfo->pArgs->pszRouterDomain);
        pInfo->pArgs->pszRouterDomain = psz;
    }

    psz = GetText(pInfo->hwndDoEbPassword);
    if (psz)
    {
        TCHAR* psz2;

        psz2 = GetText(pInfo->hwndDoEbConfirm);

        if (psz2 == NULL)
        {
            Free(psz);
            return TRUE;
        }

        if (lstrcmp(psz, psz2))
        {
            Free(psz);
            Free(psz2);
            MsgDlg(pInfo->hwndDlg, SID_PasswordMismatch, NULL);
            SetFocus(pInfo->hwndDoEbPassword);
            return TRUE;
        }

        Free(psz2);
        Free0(pInfo->pArgs->pszRouterPassword);
        pInfo->pArgs->pszRouterPassword = psz;
    }

    return FALSE;
}


BOOL
DoSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    TCHAR* psz;

    /* Fill in the interface name in the explanatory text.
    */
    psz = PszFromId( g_hinstDll, SID_RouterDialOut );
    if (psz)
    {
        MSGARGS msgargs;

        ZeroMemory( &msgargs, sizeof(msgargs) );
        msgargs.apszArgs[ 0 ] = pInfo->pArgs->pEntry->pszEntryName;
        msgargs.fStringOutput = TRUE;
        msgargs.pszString = psz;

        MsgDlgUtil( NULL, 0, &msgargs, g_hinstDll, 0 );

        if (msgargs.pszOutput)
        {
            SetDlgItemText( pInfo->hwndDo, CID_DO_ST_Explain,
                msgargs.pszOutput );
            Free( msgargs.pszOutput );
        }

        Free( psz );
    }

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}



/*----------------------------------------------------------------------------
** Interface Name property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
InDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Interface Name page of the wizard.
    ** Parameters and return values are as described for standard windows
    ** 'DialogProc's.
    */
{
#if 0
    TRACE4("InDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return
                InInit( hwnd, (EINFO* )(((PROPSHEETPAGE* )lparam)->lParam) );
        }

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_RESET:
                {
                    TRACE("InRESET");
                    AiCancel( hwnd );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, FALSE );
                    return TRUE;
                }

                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("InSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = InSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("InKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = InKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }

                case PSN_WIZFINISH:
                {
                    AIINFO* pInfo;

                    TRACE("InWIZFINISH");

                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);

                    /* You'd think pressing Finish would trigger a KILLACTIVE
                    ** event, but it doesn't, so we do it ourselves.
                    */
                    InKillActive( pInfo );

                    /* Set "no wizard" user preference, per user's check.
                    */
                    pInfo->pArgs->pUser->fNewEntryWizard = FALSE;
                    pInfo->pArgs->pUser->fDirty = TRUE;
                    SetUserPreferences(
                        pInfo->pArgs->pUser,
                        pInfo->pArgs->fNoUser ? UPM_Logon : UPM_Normal );

                    pInfo->pArgs->fPadSelected = FALSE;
                    pInfo->pArgs->fChainPropertySheet = TRUE;
                    AiFinish( hwnd );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, 0 );
                    return TRUE;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            AIINFO* pInfo = AiContext( hwnd );
            ASSERT(pInfo);

            return InCommand(
                pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }

        case WM_DESTROY:
        {
            AiTerm( hwnd );
            break;
        }
    }

    return FALSE;
}


BOOL
InCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl )

    /* Called on WM_COMMAND.  'PInfo' is the dialog context.  'WNotification'
    ** is the notification code of the command.  'wId' is the control/menu
    ** identifier of the command.  'HwndCtrl' is the control window handle of
    ** the command.
    **
    ** Returns true if processed message, false otherwise.
    */
{
    TRACE2("InCommand(n=%d,i=%d)",
        (DWORD)wNotification,(DWORD)wId);

    switch (wId)
    {
        case CID_IN_CB_SkipWizard:
        {
            if (IsDlgButtonChecked( pInfo->hwndIn, CID_IN_CB_SkipWizard ))
                PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_FINISH );
            else
                PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_NEXT );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
InInit(
    IN     HWND   hwndPage,
    IN OUT EINFO* pArgs )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.  'PArgs' is the arguments from the PropertySheet caller.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    DWORD    dwErr;
    AIINFO*  pInfo;
    PBENTRY* pEntry;

    TRACE("InInit");

    /* We're first page, so initialize the wizard.
    */
    pInfo = AiInit( hwndPage, pArgs );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndIn = hwndPage;
    pInfo->hwndEbInterfaceName =
        GetDlgItem( hwndPage, CID_IN_EB_InterfaceName );
    ASSERT(pInfo->hwndEbInterfaceName);

    /* Initialize the entry name field.
    */
    pEntry = pInfo->pArgs->pEntry;
    if (!pEntry->pszEntryName)
    {
        /* No entry name, so think up a default.
        */
        dwErr = GetDefaultEntryName(
            pInfo->pArgs->pFile->pdtllistEntries,
            pInfo->pArgs->fRouter,
            &pEntry->pszEntryName );
        if (dwErr != 0)
        {
            ErrorDlg( pInfo->hwndDlg, SID_OP_LoadPage, dwErr, NULL );
            AiExit( pInfo, dwErr );
            return TRUE;
        }
    }

    Edit_LimitText( pInfo->hwndEbInterfaceName, RAS_MaxEntryName );
    SetWindowText( pInfo->hwndEbInterfaceName, pEntry->pszEntryName );

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return TRUE;
}


BOOL
InKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    TCHAR* psz;

    psz = GetText( pInfo->hwndEbInterfaceName );
    if (psz)
    {
        /* Update the entry name from the editbox.
        */
        Free0( pInfo->pArgs->pEntry->pszEntryName );
        pInfo->pArgs->pEntry->pszEntryName = psz;

        /* Validate the entry name.
        */
        if (!EuValidateName( pInfo->hwndDlg, pInfo->pArgs ))
        {
            SetFocus( pInfo->hwndEbInterfaceName );
            Edit_SetSel( pInfo->hwndEbInterfaceName, 0, -1 );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
InSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_NEXT );
    return TRUE;
}



/*----------------------------------------------------------------------------
** Name Servers property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
NsDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Name Servers page of the wizard.
    ** Parameters and return values are as described for standard windows
    ** 'DialogProc's.
    */
{

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return
                NsInit( hwnd );
        }

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("NsSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = NsSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("NsKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = NsKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }
    }

    return FALSE;
}



BOOL
NsInit(
    IN     HWND   hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.  'PArgs' is the arguments from the PropertySheet caller.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("NsInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndNs = hwndPage;
    pInfo->hwndCcDns = GetDlgItem( hwndPage, CID_NS_CC_Dns );
    ASSERT(pInfo->hwndCcDns);
    pInfo->hwndCcWins = GetDlgItem( hwndPage, CID_NS_CC_Wins );
    ASSERT(pInfo->hwndCcWins);

    /* Set the IP address fields.
    */
    SetWindowText( pInfo->hwndCcDns, pInfo->pArgs->pEntry->pszIpDnsAddress );
    SetWindowText( pInfo->hwndCcWins, pInfo->pArgs->pEntry->pszIpWinsAddress );

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}




BOOL
NsKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    TCHAR*      psz;
    PBENTRY*    pEntry = pInfo->pArgs->pEntry;

    psz = GetText( pInfo->hwndCcDns );
    if (psz)
    {
        Free0( pEntry->pszIpDnsAddress );
        pEntry->pszIpDnsAddress = psz;
    }

    psz = GetText( pInfo->hwndCcWins );
    if (psz)
    {
        Free0( pEntry->pszIpWinsAddress );
        pEntry->pszIpWinsAddress = psz;
    }

    return FALSE;
}


BOOL
NsSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    PBENTRY* pEntry;

    pEntry = pInfo->pArgs->pEntry;

    if (!pInfo->fNotNt || !pInfo->fIpConfigured)
    {
        return FALSE;
    }

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}




/*----------------------------------------------------------------------------
** IP Address property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
RaDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the IP Address page of the wizard.  Parameters
    ** and return value are as described for standard windows 'DialogProc's.
    */
{
#if 0
    TRACE4("RaDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    switch (unMsg)
    {
        case WM_INITDIALOG:
            return RaInit( hwnd );

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("RaSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = RaSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("RaKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = RaKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }
    }

    return FALSE;
}


BOOL
RaInit(
    IN HWND hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("RaInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndRa = hwndPage;
    pInfo->hwndCcIp = GetDlgItem( hwndPage, CID_RA_CC_Ip );
    ASSERT(pInfo->hwndCcIp);

    /* Set the IP address field to '0.0.0.0'.
    */
    SetWindowText( pInfo->hwndCcIp, pInfo->pArgs->pEntry->pszIpAddress );

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}


BOOL
RaKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    TCHAR* psz;

    psz = GetText( pInfo->hwndCcIp );
    if (psz)
    {
        PBENTRY* pEntry = pInfo->pArgs->pEntry;

        Free0( pEntry->pszIpAddress );
        pEntry->pszIpAddress = psz;
    }

    return FALSE;
}


BOOL
RaSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    PBENTRY* pEntry;

    pEntry = pInfo->pArgs->pEntry;

    if (!pInfo->fNotNt || !pInfo->fIpConfigured)
    {
        return FALSE;
    }

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}



/*----------------------------------------------------------------------------
** Logon Script property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
RcDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Logon Script page of the wizard.
    ** Parameters and return value are as described for standard windows
    ** 'DialogProc's.
    */
{
#if 0
    TRACE4("RcDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    switch (unMsg)
    {
        case WM_INITDIALOG:
            return RcInit( hwnd );

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("RcSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = RcSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("RcKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = RcKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            AIINFO* pInfo = AiContext( hwnd );
            ASSERT(pInfo);

            return RcCommand(
                pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
RcCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl )

    /* Called on WM_COMMAND.  'PInfo' is the dialog context.  'WNotification'
    ** is the notification code of the command.  'wId' is the control/menu
    ** identifier of the command.  'HwndCtrl' is the control window handle of
    ** the command.
    **
    ** Returns true if processed message, false otherwise.
    */
{
    TRACE2("RcCommand(n=%d,i=%d)",
        (DWORD)wNotification,(DWORD)wId);

    switch (wId)
    {
        case CID_RC_PB_Refresh:
        {
            INT    iSel;
            TCHAR* pszSel;

            iSel = ComboBox_GetCurSel( pInfo->hwndLbScripts );
            if (iSel > 0)
                pszSel = ComboBox_GetPsz( pInfo->hwndLbScripts, iSel );
            else
                pszSel = NULL;

            EuFillDoubleScriptsList(
                pInfo->pArgs, pInfo->hwndLbScripts, pszSel );
            Free0( pszSel );
            return TRUE;
        }

        case CID_RC_PB_Edit:
        {
            TCHAR* psz;

            psz = GetText( pInfo->hwndLbScripts );
            if (psz)
            {
                if (FileExists( psz ))
                    EuEditScpScript( pInfo->hwndDlg, psz );
                else
                    EuEditSwitchInf( pInfo->hwndDlg );

                Free( psz );
            }

            return TRUE;
        }

        case CID_RC_RB_None:
        case CID_RC_RB_Script:
        {
            /* Scripts listbox is gray unless script mode is selected.
            */
            if (wNotification == BN_CLICKED)
            {
                EnableWindow( pInfo->hwndLbScripts,
                    (wId == CID_RC_RB_Script) );
            }
            break;
        }
    }

    return FALSE;
}


BOOL
RcInit(
    IN HWND hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("RcInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndRc = hwndPage;
    pInfo->hwndRbNone = GetDlgItem( hwndPage, CID_RC_RB_None );
    ASSERT(pInfo->hwndRbNone);
    pInfo->hwndRbScript = GetDlgItem( hwndPage, CID_RC_RB_Script );
    ASSERT(pInfo->hwndRbScript);
    pInfo->hwndLbScripts = GetDlgItem( hwndPage, CID_RC_LB_Scripts );
    ASSERT(pInfo->hwndLbScripts);
    pInfo->hwndPbEdit = GetDlgItem( hwndPage, CID_RC_PB_Edit );
    ASSERT(pInfo->hwndPbEdit);
    pInfo->hwndPbRefresh = GetDlgItem( hwndPage, CID_RC_PB_Refresh );
    ASSERT(pInfo->hwndPbRefresh);

    /* Fill the script list and select "(none)".
    */
    EuFillDoubleScriptsList( pInfo->pArgs, pInfo->hwndLbScripts, NULL );

    pInfo->pArgs->pEntry->dwScriptModeAfter = SM_None;

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}


BOOL
RcKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    INT      iSel;
    PBENTRY* pEntry;

    pEntry = pInfo->pArgs->pEntry;

    if (IsDlgButtonChecked( pInfo->hwndRc, CID_RC_RB_None ))
        pEntry->dwScriptModeAfter = SM_None;
    else
        pEntry->dwScriptModeAfter = SM_Script;

    iSel = ComboBox_GetCurSel( pInfo->hwndLbScripts );
    Free0( pEntry->pszScriptAfter );
    if (iSel > 0)
        pEntry->pszScriptAfter = ComboBox_GetPsz( pInfo->hwndLbScripts, iSel );
    else
        pEntry->pszScriptAfter = NULL;

    /* Silently fix-up "no script specified" error.
    */
    if (pEntry->dwScriptModeAfter == SM_Script && !pEntry->pszScriptAfter)
        pEntry->dwScriptModeAfter = SM_None;

    return FALSE;
}


BOOL
RcSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    HWND hwndRb;

    if (!pInfo->fNotNt || !pInfo->fModem)
        return FALSE;

    /* Select the correct mode.
    */
    {
        HWND  hwndRb;
        DWORD dwScriptMode;

        dwScriptMode = pInfo->pArgs->pEntry->dwScriptModeAfter;
        if (dwScriptMode == SM_Script)
            hwndRb = pInfo->hwndRbScript;
        else
        {
            ASSERT(dwScriptMode==SM_None);
            hwndRb = pInfo->hwndRbNone;
        }

        SendMessage( hwndRb, BM_CLICK, 0, 0 );
    }

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}



/*----------------------------------------------------------------------------
** Finish property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
RfDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Finish page of the wizard.
    ** Parameters and return value are as described for standard windows
    ** 'DialogProc's.
    */
{
#if 0
    TRACE4("RfDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    switch (unMsg)
    {
        case WM_INITDIALOG:
            return RfInit( hwnd );

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("RfSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = RfSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_WIZFINISH:
                {
                    TRACE("RfWIZFINISH");
                    AiFinish( hwnd );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, 0 );
                    return TRUE;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            AIINFO* pInfo = AiContext( hwnd );
            ASSERT(pInfo);

            return RfCommand(
                pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
RfCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl )

    /* Called on WM_COMMAND.  'PInfo' is the dialog context.  'WNotification'
    ** is the notification code of the command.  'wId' is the control/menu
    ** identifier of the command.  'HwndCtrl' is the control window handle of
    ** the command.
    **
    ** Returns true if processed message, false otherwise.
    */
{
    TRACE2("RfCommand(n=%d,i=%d)",
        (DWORD)wNotification,(DWORD)wId);

#if 0
    switch (wId)
    {
        case CID_RF_PB_Properties:
        {
            pInfo->pArgs->fChainPropertySheet = TRUE;
            PropSheet_PressButton( pInfo->hwndDlg, PSBTN_FINISH );
            return TRUE;
        }
    }
#endif

    return FALSE;
}


BOOL
RfInit(
    IN HWND hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("RfInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndRf = hwndPage;

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

#if 0 // Disables AP page display

    SetOffDesktop( pInfo->hwndDlg, SOD_MoveOff, NULL );
    SetOffDesktop( pInfo->hwndDlg, SOD_Free, NULL );
    PostMessage( pInfo->hwndDlg, PSM_PRESSBUTTON, PSBTN_FINISH, 0 );

#endif

    return FALSE;
}


BOOL
RfSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    TCHAR* psz;

    /* Fill in the entry name.
    */
    psz = PszFromId( g_hinstDll, SID_FinishWizard );
    if (psz)
    {
        MSGARGS msgargs;

        ZeroMemory( &msgargs, sizeof(msgargs) );
        msgargs.apszArgs[ 0 ] = pInfo->pArgs->pEntry->pszEntryName;
        msgargs.fStringOutput = TRUE;
        msgargs.pszString = psz;

        MsgDlgUtil( NULL, 0, &msgargs, g_hinstDll, 0 );

        if (msgargs.pszOutput)
        {
            SetDlgItemText( pInfo->hwndRf, CID_RF_ST_Interface,
                msgargs.pszOutput );
            Free( msgargs.pszOutput );
        }

        Free( psz );
    }

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_FINISH );
    return TRUE;
}


/*----------------------------------------------------------------------------
** Modem/Adapter property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
RnDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Modem/Adapter page of the wizard.
    ** Parameters and return value are as described for standard windows
    ** 'DialogProc's.
    */
{
#if 0
    TRACE4("RnDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    if (ListView_OwnerHandler(
            hwnd, unMsg, wparam, lparam, RnLvCallback ))
    {
        return TRUE;
    }

    switch (unMsg)
    {
        case WM_INITDIALOG:
            return RnInit( hwnd );

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("RnSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = RnSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case LVN_ITEMCHANGED:
                {
                    AIINFO* pInfo;

                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    RnLvItemChanged( pInfo );
                    return TRUE;
                }
            }
            break;
        }
    }

    return FALSE;
}


BOOL
RnInit(
    IN HWND hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    DWORD   dwErr;
    AIINFO* pInfo;

    TRACE("RnInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndRn = hwndPage;
    pInfo->hwndLv = GetDlgItem( hwndPage, CID_RN_LV_Devices );
    ASSERT(pInfo->hwndLv);

    ListView_DeleteAllItems( pInfo->hwndLv );

    /* Add the modem and adapter images.
    */
    ListView_SetDeviceImageList( pInfo->hwndLv, g_hinstDll );

    /* Fill the list of devices and select the first item.
    */
    {
        TCHAR*   psz;
        DTLNODE* pNode;
        DWORD    cMultilinkableIsdn;
        INT      iItem;

        iItem = 1;
        cMultilinkableIsdn = 0;
        for (pNode = DtlGetFirstNode( pInfo->pArgs->pEntry->pdtllistLinks );
             pNode;
             pNode = DtlGetNextNode( pNode ))
        {
            PBLINK* pLink;

            pLink = (PBLINK* )DtlGetData( pNode );
            ASSERT(pLink);

            if (pLink->pbport.pbdevicetype == PBDT_Isdn
                && !pLink->fProprietaryIsdn)
            {
                ++cMultilinkableIsdn;
            }

            psz = DisplayPszFromDeviceAndPort(
                pLink->pbport.pszDevice, pLink->pbport.pszPort );
            if (psz)
            {
                PBLINK* pLink;
                LV_ITEM item;

                pLink = (PBLINK* )DtlGetData( pNode );

                ZeroMemory( &item, sizeof(item) );
                item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
                item.iItem = iItem++;
                item.pszText = psz;

                item.iImage =
                    (pLink->pbport.pbdevicetype == PBDT_Modem)
                        ? DI_Modem : DI_Adapter;

                item.lParam = (LPARAM )pNode;

                ListView_InsertItem( pInfo->hwndLv, &item );
                Free( psz );
            }
        }

        if (cMultilinkableIsdn > 1)
        {
            psz = PszFromId( g_hinstDll, SID_IsdnAdapter );
            if (psz)
            {
                LONG    lStyle;
                LV_ITEM item;

                /* Turn off sorting so the special ISDN-multilink item appears
                ** at the top of the list.
                */
                lStyle = GetWindowLong( pInfo->hwndLv, GWL_STYLE );
                SetWindowLong( pInfo->hwndLv, GWL_STYLE,
                    (lStyle & ~(LVS_SORTASCENDING)) );

                ZeroMemory( &item, sizeof(item) );
                item.mask = LVIF_TEXT + LVIF_IMAGE + LVIF_PARAM;
                item.iItem = 0;
                item.pszText = psz;
                item.iImage = DI_Adapter;
                item.lParam = (LPARAM )NULL;

                ListView_InsertItem( pInfo->hwndLv, &item );
                Free( psz );
            }
        }

        /* Select the first item.
        */
        ListView_SetItemState( pInfo->hwndLv, 0, LVIS_SELECTED, LVIS_SELECTED );

        /* Add a single column exactly wide enough to fully display the
        ** widest member of the list.
        */
        {
            LV_COLUMN col;

            ZeroMemory( &col, sizeof(col) );
            col.mask = LVCF_FMT;
            col.fmt = LVCFMT_LEFT;
            ListView_InsertColumn( pInfo->hwndLv, 0, &col );
            ListView_SetColumnWidth(
                pInfo->hwndLv, 0, LVSCW_AUTOSIZE_USEHEADER );
        }
    }

    /* Don't bother with this page if there's only one device, not counting
    ** the bogus "uninstalled" standard modem that's added by EuInit so
    ** entries can be edited when there are no ports.
    */
    if (!pInfo->pArgs->fNoPortsConfigured
        && ListView_GetItemCount( pInfo->hwndLv ) == 1)
    {
        pInfo->fSkipMa = TRUE;
    }

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}


LVXDRAWINFO*
RnLvCallback(
    IN HWND  hwndLv,
    IN DWORD dwItem )

    /* Enhanced list view callback to report drawing information.  'HwndLv' is
    ** the handle of the list view control.  'DwItem' is the index of the item
    ** being drawn.
    **
    ** Returns the address of the column information.
    */
{
    /* Use "wide selection bar" feature and the other recommended options.
    **
    ** Fields are 'nCols', 'dxIndent', 'dwFlags', 'adwFlags[]'.
    */
    static LVXDRAWINFO info =
        { 1, 0, LVXDI_DxFill, { 0, 0 } };

    return &info;
}


VOID
RnLvItemChanged(
    IN AIINFO* pInfo )

    /* Called when the combobox selection changes.  'PInfo' is the wizard
    ** context.
    */
{
    INT      iSel;
    DTLNODE* pNode;
    DTLLIST* pList;

    TRACE("RnLvItemChanged");

    pList = pInfo->pArgs->pEntry->pdtllistLinks;
    ASSERT(pList);
    pNode = (DTLNODE* )ListView_GetSelectedParamPtr( pInfo->hwndLv );

    if (pNode)
    {
        PBLINK* pLink;

        /* Single device selected.  Enable it, move it to the head of the list
        ** of links, and disable all the other links.
        */
        pLink = (PBLINK* )DtlGetData( pNode );
        pLink->fEnabled = TRUE;

        pInfo->fModem =
            (pLink->pbport.pbdevicetype == PBDT_Modem
             || pLink->pbport.pbdevicetype == PBDT_Null);

        /* If the device selected is an X25 PAD, we will drop the user into
        ** the phonebook entry-dialog after this wizard, so that the X25
        ** address can be entered there.
        */
        pInfo->pArgs->fPadSelected = (pLink->pbport.pbdevicetype == PBDT_Pad);

        DtlRemoveNode( pList, pNode );
        DtlAddNodeFirst( pList, pNode );

        for (pNode = DtlGetNextNode( pNode );
             pNode;
             pNode = DtlGetNextNode( pNode ))
        {
            PBLINK* pLink = (PBLINK* )DtlGetData( pNode );
            ASSERT(pLink);
            pLink->fEnabled = FALSE;
        }
    }
    else
    {
        DTLNODE* pNextNode;
        DTLNODE* pAfterNode;

        pInfo->fModem = FALSE;

        /* ISDN multi-link selected.  Enable the ISDN multi-link nodes, move
        ** them to the head of the list, and disable all the other links.
        */
        pAfterNode = NULL;
        for (pNode = DtlGetFirstNode( pList );
             pNode;
             pNode = pNextNode)
        {
            PBLINK* pLink = (PBLINK* )DtlGetData( pNode );
            ASSERT(pLink);

            pNextNode = DtlGetNextNode( pNode );

            if (pLink->pbport.pbdevicetype == PBDT_Isdn
                && !pLink->fProprietaryIsdn)
            {
                pLink->fEnabled = TRUE;

                DtlRemoveNode( pList, pNode );
                if (pAfterNode)
                    DtlAddNodeAfter( pList, pAfterNode, pNode );
                else
                    DtlAddNodeFirst( pList, pNode );
                pAfterNode = pNode;
            }
            else
            {
                pLink->fEnabled = FALSE;
            }
        }
    }
}


BOOL
RnSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    INT cDevices;

    if (pInfo->fSkipMa)
        return FALSE;

    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}


/*----------------------------------------------------------------------------
** Phone Number property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
RpDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the Phone Number page of the wizard.
    ** Parameters and return value are as described for standard windows
    ** 'DialogProc's.
    */
{
#if 0
    TRACE4("RpDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    switch (unMsg)
    {
        case WM_INITDIALOG:
            return RpInit( hwnd );

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("RpSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = RpSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("RpKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = RpKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            AIINFO* pInfo = AiContext( hwnd );
            ASSERT(pInfo);

            return RpCommand(
                pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


VOID
RpAlternates(
    IN AIINFO* pInfo )

    /* Popup the Alternate Phone Numbers dialog.  'PInfo' is the property
    ** sheet context.
    */
{
    RpPhoneNumberToStash( pInfo );

    if (PhoneNumberDlg(
            pInfo->hwndRp,
            pInfo->pArgs->fRouter,
            pInfo->pListPhoneNumbers,
            &pInfo->fPromoteHuntNumbers ))
    {
        TCHAR* pszPhoneNumber;

        pszPhoneNumber = FirstPszFromList( pInfo->pListPhoneNumbers );
        SetWindowText( pInfo->hwndEbNumber, pszPhoneNumber );
    }
}


BOOL
RpCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl )

    /* Called on WM_COMMAND.  'PInfo' is the dialog context.  'WNotification'
    ** is the notification code of the command.  'wId' is the control/menu
    ** identifier of the command.  'HwndCtrl' is the control window handle of
    ** the command.
    **
    ** Returns true if processed message, false otherwise.
    */
{
    TRACE2("RpCommand(n=%d,i=%d)",
        (DWORD)wNotification,(DWORD)wId);

    switch (wId)
    {
        case CID_RP_PB_Alternates:
            RpAlternates( pInfo );
            return TRUE;
    }

    return FALSE;
}


BOOL
RpInit(
    IN HWND hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("RpInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndRp = hwndPage;
    pInfo->hwndStNumber = GetDlgItem( hwndPage, CID_RP_ST_Number );
    ASSERT(pInfo->hwndStNumber);
    pInfo->hwndEbNumber = GetDlgItem( hwndPage, CID_RP_EB_Number );
    ASSERT(pInfo->hwndEbNumber);
    pInfo->hwndPbAlternates = GetDlgItem( hwndPage, CID_RP_PB_Alternates );
    ASSERT(pInfo->hwndPbAlternates);

    /* Fill the phone number field from the stash created earlier.
    */
    Edit_LimitText( pInfo->hwndEbNumber, RAS_MaxPhoneNumber );
    SetWindowText( pInfo->hwndEbNumber,
        FirstPszFromList( pInfo->pListPhoneNumbers ) );

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}


BOOL
RpKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    /* Update the stashed phone number from the editbox.
    */
    RpPhoneNumberToStash( pInfo );

    return FALSE;
}


VOID
RpPhoneNumberToStash(
    IN AIINFO* pInfo )

    /* Replace the first phone number in the stashed list with the contents of
    ** the phone number field.  'pInfo' is the property sheet context.
    */
{
    DWORD  dwErr;
    TCHAR* pszPhoneNumber;

    TRACE("RpPhoneNumberToStash");

    pszPhoneNumber = GetText( pInfo->hwndEbNumber );
    if (pszPhoneNumber)
    {
        dwErr = FirstPszToList( pInfo->pListPhoneNumbers, pszPhoneNumber );
        Free( pszPhoneNumber );
    }
    else
        dwErr = ERROR_NOT_ENOUGH_MEMORY;

    if (dwErr != 0)
    {
        ErrorDlg( pInfo->hwndDlg, SID_OP_RetrievingData, dwErr, NULL );
        AiExit( pInfo, dwErr );
    }
}


BOOL
RpSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}



/*----------------------------------------------------------------------------
** Server settings property page
** Listed alphabetically following dialog proc
**----------------------------------------------------------------------------
*/

INT_PTR CALLBACK
SsDlgProc(
    IN HWND   hwnd,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* DialogProc callback for the 5 checkboxes page of the wizard.
    ** Parameters and return value are as described for standard windows
    ** 'DialogProc's.
    */
{
#if 0
    TRACE4("SsDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
        (DWORD)hwnd,(DWORD)unMsg,(DWORD)wparam,(DWORD)lparam);
#endif

    switch (unMsg)
    {
        case WM_INITDIALOG:
            return SsInit( hwnd );

        case WM_NOTIFY:
        {
            switch (((NMHDR* )lparam)->code)
            {
                case PSN_SETACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fDisplay;

                    TRACE("SsSETACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fDisplay = SsSetActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, (fDisplay) ? 0 : -1 );
                    return TRUE;
                }

                case PSN_KILLACTIVE:
                {
                    AIINFO* pInfo;
                    BOOL    fInvalid;

                    TRACE("SsKILLACTIVE");
                    pInfo = AiContext( hwnd );
                    ASSERT(pInfo);
                    fInvalid = SsKillActive( pInfo );
                    SetWindowLong( hwnd, DWLP_MSGRESULT, fInvalid );
                    return TRUE;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            AIINFO* pInfo = AiContext( hwnd );
            ASSERT(pInfo);

            return SsCommand(
                pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}



BOOL
SsCommand(
    IN AIINFO* pInfo,
    IN WORD    wNotification,
    IN WORD    wId,
    IN HWND    hwndCtrl )

    /* Called on WM_COMMAND.  'PInfo' is the dialog context.  'WNotification'
    ** is the notification code of the command.  'wId' is the control/menu
    ** identifier of the command.  'HwndCtrl' is the control window handle of
    ** the command.
    **
    ** Returns true if processed message, false otherwise.
    */
{
    TRACE2("SsCommand(n=%d,i=%d)",
        (DWORD)wNotification,(DWORD)wId);

    switch (wId)
    {
        case CID_SS_CB_AddUser:

            /* Toggle the state of the "Authenticate remote..." checkbox,
            ** which is enabled when the "Add user ..." checkbox is enabled.
            */
            if (wNotification == BN_CLICKED)
            {
                if (pInfo->pArgs->fAddUser = !pInfo->pArgs->fAddUser)
                {
                    EnableWindow(
                        GetDlgItem(pInfo->hwndSs, CID_SS_CB_AuthRemote), TRUE);
                }
                else
                {
                    CheckDlgButton(
                        pInfo->hwndSs, CID_SS_CB_AuthRemote, BST_UNCHECKED );
                    EnableWindow(
                        GetDlgItem(pInfo->hwndSs, CID_SS_CB_AuthRemote), FALSE);
                }
            }
            return TRUE;
    }

    return FALSE;
}


BOOL
SsInit(
    IN HWND hwndPage )

    /* Called on WM_INITDIALOG.  'hwndPage' is the handle of the property
    ** page.
    **
    ** Return false if focus was set, true otherwise.
    */
{
    AIINFO* pInfo;

    TRACE("SsInit");

    pInfo = AiContext( hwndPage );
    if (!pInfo)
        return TRUE;

    /* Initialize page-specific context information.
    */
    pInfo->hwndSs = hwndPage;

    if (pInfo->fIpConfigured)
        CheckDlgButton( hwndPage, CID_SS_CB_RouteIp, BST_CHECKED );
    if (pInfo->fIpxConfigured)
        CheckDlgButton( hwndPage, CID_SS_CB_RouteIpx, BST_CHECKED );

    /* The 'Authenticate remote router when dialing out' checkbox
    ** only applies when the user elects to add a user for dial-in.
    */

    if (!pInfo->pArgs->fAddUser)
    {
        CheckDlgButton( hwndPage, CID_SS_CB_AddUser, BST_UNCHECKED );
        EnableWindow( GetDlgItem(hwndPage, CID_SS_CB_AuthRemote), FALSE );
    }
    else
    {
        CheckDlgButton( hwndPage, CID_SS_CB_AddUser, BST_CHECKED );
        CheckDlgButton( hwndPage, CID_SS_CB_AuthRemote,
            pInfo->pArgs->pEntry->fAuthenticateServer );
    }

    /* Create and display the wizard bitmap.
    */
    CreateWizardBitmap( hwndPage, TRUE );

    return FALSE;
}


BOOL
SsKillActive(
    IN AIINFO* pInfo )

    /* Called when PSN_KILLACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true if the page is invalid, false is it can be dismissed.
    */
{
    pInfo->fIp =
        IsDlgButtonChecked( pInfo->hwndSs, CID_SS_CB_RouteIp );
    pInfo->fIpx =
        IsDlgButtonChecked( pInfo->hwndSs, CID_SS_CB_RouteIpx );
    pInfo->pArgs->fAddUser = 
        IsDlgButtonChecked( pInfo->hwndSs, CID_SS_CB_AddUser );
    pInfo->pArgs->pEntry->fAuthenticateServer = !pInfo->pArgs->fAddUser ?FALSE:
        IsDlgButtonChecked( pInfo->hwndSs, CID_SS_CB_AuthRemote );
    pInfo->fClearPwOk =
        IsDlgButtonChecked( pInfo->hwndSs, CID_SS_CB_PlainPw );
    pInfo->fNotNt =
        IsDlgButtonChecked( pInfo->hwndSs, CID_SS_CB_NotNt );

    if (pInfo->fIp && !pInfo->fIpConfigured)
    {
        MsgDlg( pInfo->hwndDlg, SID_ConfigureIp, NULL );
        SetFocus( GetDlgItem( pInfo->hwndSs, CID_SS_CB_RouteIp) );
        return TRUE;
    }
    if (pInfo->fIpx && !pInfo->fIpxConfigured)
    {
        MsgDlg( pInfo->hwndDlg, SID_ConfigureIpx, NULL );
        SetFocus( GetDlgItem( pInfo->hwndSs, CID_SS_CB_RouteIpx) );
        return TRUE;
    }

    return FALSE;
}


BOOL
SsSetActive(
    IN AIINFO* pInfo )

    /* Called when PSN_SETACTIVE is received.  'PInfo' is the wizard context.
    **
    ** Returns true to display the page, false to skip it.
    */
{
    PropSheet_SetWizButtons( pInfo->hwndDlg, PSWIZB_BACK | PSWIZB_NEXT );
    return TRUE;
}