// Copyright (c) 1995, Microsoft Corporation, all rights reserved
//
// dial.c
// Remote Access Common Dialog APIs
// RasDialDlg APIs
//
// 11/19/95 Steve Cobb


#include "rasdlgp.h"
#include "raseapif.h"
#include "inetcfgp.h"
#include "netconp.h"

// Posted message codes for tasks that should not or cannot occur in the
// RasDial callback.
//
#define WM_RASEVENT       0xCCCC
#define WM_RASERROR       0xCCCD
#define WM_RASDIAL        0xCCCE
#define WM_RASBUNDLEERROR 0xCCCF

// Dialer dialog mode bits
//
#define DR_U 0x00000001 // Username and password present
#define DR_D 0x00000002 // Domain present
#define DR_N 0x00000004 // Phone number present
#define DR_L 0x00000008 // Location controls present
#define DR_I 0x00000010 // Eap identity dialog

// Internal constants used by DrXxx routines to implement the "manual edit"
// combo-box.
//
#define DR_WM_SETTEXT 0xCCC0
#define DR_BOGUSWIDTH 19591

#define EAP_RASTLS      13


//----------------------------------------------------------------------------
// Help maps
//----------------------------------------------------------------------------

static DWORD g_adwDrHelp[] =
{
    CID_DR_BM_Useless,      HID_DR_BM_Useless,
    CID_DR_ST_User,         HID_DR_EB_User,
    CID_DR_EB_User,         HID_DR_EB_User,
    CID_DR_ST_Password,     HID_DR_EB_Password,
    CID_DR_EB_Password,     HID_DR_EB_Password,
    CID_DR_ST_Domain,       HID_DR_EB_Domain,
    CID_DR_EB_Domain,       HID_DR_EB_Domain,
    CID_DR_CB_SavePassword, HID_DR_CB_SavePassword,
    CID_DR_ST_Numbers,      HID_DR_CLB_Numbers,
    CID_DR_CLB_Numbers,     HID_DR_CLB_Numbers,
    CID_DR_ST_Locations,    HID_DR_LB_Locations,
    CID_DR_LB_Locations,    HID_DR_LB_Locations,
    CID_DR_PB_Rules,        HID_DR_PB_Rules,
    CID_DR_PB_Properties,   HID_DR_PB_Properties,
    CID_DR_PB_DialConnect,  HID_DR_PB_DialConnect,
    CID_DR_PB_Cancel,       HID_DR_PB_Cancel,
    CID_DR_PB_Help,         HID_DR_PB_Help,
    CID_DR_RB_SaveForMe,    HID_DR_RB_SaveForMe,
    CID_DR_RB_SaveForEveryone, HID_DR_RB_SaveForEveryone,
    0, 0
};

static DWORD g_adwCpHelp[] =
{
    CID_CP_ST_Explain,         HID_CP_ST_Explain,
    CID_CP_ST_OldPassword,     HID_CP_EB_OldPassword,
    CID_CP_EB_OldPassword,     HID_CP_EB_OldPassword,
    CID_CP_ST_Password,        HID_CP_EB_Password,
    CID_CP_EB_Password,        HID_CP_EB_Password,
    CID_CP_ST_ConfirmPassword, HID_CP_EB_ConfirmPassword,
    CID_CP_EB_ConfirmPassword, HID_CP_EB_ConfirmPassword,
    0, 0
};

static DWORD g_adwDcHelp[] =
{
    CID_DC_ST_Explain, HID_DC_ST_Explain,
    CID_DC_ST_Number,  HID_DC_EB_Number,
    CID_DC_EB_Number,  HID_DC_EB_Number,
    0, 0
};

static DWORD g_adwDeHelp[] =
{
    CID_DE_PB_More, HID_DE_PB_More,
    IDOK,           HID_DE_PB_Redial,
    0, 0
};

static DWORD g_adwPrHelp[] =
{
    CID_PR_ST_Text,             HID_PR_ST_Text,
    CID_PR_CB_DisableProtocols, CID_PR_CB_DisableProtocols,
    IDOK,                       HID_PR_PB_Accept,
    IDCANCEL,                   HID_PR_PB_HangUp,
    0, 0
};

static DWORD g_adwUaHelp[] =
{
    CID_UA_ST_UserName,     HID_UA_EB_UserName,
    CID_UA_EB_UserName,     HID_UA_EB_UserName,
    CID_UA_ST_Password,     HID_UA_EB_Password,
    CID_UA_EB_Password,     HID_UA_EB_Password,
    CID_UA_ST_Domain,       HID_UA_EB_Domain,
    CID_UA_EB_Domain,       HID_UA_EB_Domain,
    CID_UA_CB_SavePassword, HID_UA_CB_SavePassword,
    0, 0
};

CONST WCHAR g_pszSavedPasswordToken[] = L"****************";
#define g_dwSavedPasswordTokenLength \
    ( sizeof(g_pszSavedPasswordToken) / sizeof(TCHAR) )

// Save password macro, determines if either User or Global password is saved
// (p) must be a pointer to a DINFO struct (see dial.c)
//
// Whistler bug: 288234 When switching back and forth from "I connect" and
// "Any user connects" password is not caching correctly
//
#define HaveSavedPw(p) \
            ((p)->fHaveSavedPwUser || (p)->fHaveSavedPwGlobal)

//----------------------------------------------------------------------------
// Local datatypes
//----------------------------------------------------------------------------

// Dial dialogs common context block.  This block contains information common
// to more than one dialog in the string of dial-related dialogs.
//
typedef struct
_DINFO
{
    // Caller's  arguments to the RAS API.  Outputs in 'pArgs' are visible to
    // the API which has the address of same.  Careful using 'pszEntry' as
    // 'pEntry->pszEntryName' is generally more appropriate, the latter
    // reflecting the name of any prerequisite entry while the prequisite is
    // being dialed.
    //
    LPTSTR pszPhonebook;
    LPTSTR pszEntry;
    LPTSTR pszPhoneNumber;
    RASDIALDLG* pArgs;

    // Phonebook settings read from the phonebook file.  All access should be
    // thru 'pFile'.  'PFile' is set to either '&filePrereq' or 'pFileMain'
    // depending on 'fFilePrereqOpen'.  'File' will only be used in cases
    // where the open phonebook is not passed thru the reserved word hack, and
    // in that case 'pFileMain' will point to it.  'FilePrereq' is the
    // phonebook file of the prequisite entry which may be different from the
    // main entry.  During prerequisite dial 'pFile' points to 'filePrereq'
    // rather than 'file' and 'fFilePrereqOpen is true.  Otherwise, 'pFile'
    // points to whatever 'pFileMain' points at.
    //
    PBFILE* pFile;
    PBFILE* pFileMain;
    PBFILE file;
    PBFILE filePrereq;
    BOOL fFilePrereqOpen;
    BOOL fIsPublicPbk;

    // Global preferences read via phonebook library.  All access should be
    // thru 'pUser' as 'user' will only be used in cases where the preferences
    // are not passed thru the reserved word hack.
    //
    PBUSER* pUser;
    PBUSER user;

    // User credentials provided by API caller for "during logon" dialing
    // where there is no current user.  If user changes the credentials
    // *pfNoUserChanged is set and the 'pNoUser' credentials updated.
    //
    RASNOUSER* pNoUser;
    BOOL* pfNoUserChanged;

    // Set if the call is unattended, i.e. a call by RASAUTO to redial a
    // failed link.
    //
    BOOL fUnattended;

    // Private flags from calling RAS API, the first informing us he wants to
    // be hidden off the desktop while we dial, and the second that he will
    // close if we return "connected" so we can avoid flicker and not bother
    // restoring him.
    //
    BOOL fMoveOwnerOffDesktop;
    BOOL fForceCloseOnDial;

    // Set when something occurs during dial that affects the phonebook entry.
    // The entry is re-read after a successful connection.
    //
    BOOL fResetAutoLogon;
    DWORD dwfExcludedProtocols;
    DTLLIST* pListPortsToDelete;

    // The entry node and a shortcut pointer to the entry inside.
    //
    DTLNODE* pNode;
    PBENTRY* pEntry;

    // The entry of the main entry that referred to any prerequisite entry
    // that might be contained by 'pEntry'.  If no prerequisite entry is
    // involved this is the same as 'pEntry'.
    //
    PBENTRY* pEntryMain;

    // Set is admin has disabled the save password feature in the registry.
    //
    BOOL fDisableSavePw;

    // Set true if a cached password is available for the entry.
    //
    BOOL fHaveSavedPwUser;      // whether there are saved per-user creds
    BOOL fHaveSavedPwGlobal;    // whether there are saved per-connect creds

    // Set when the dial in progress is the prerequisite entry, rather than
    // the main entry.
    //
    BOOL fPrerequisiteDial;

    // Set when calling RasDial on a connected entry to add a reference only.
    // All interaction with user is skipped in this case.  See bug 272794.
    //
    BOOL fDialForReferenceOnly;

    // The dial parameters used on this connection attempt.  Initialized in
    // RasDialDlgW.  Credentials are updated by DialerDlg.  Callback number is
    // updated by DialProgressDlg.
    //
    RASDIALPARAMS rdp;      // actual dial parameters passed to RasDial
    RASDIALPARAMS rdpu;     // per-user credentials
    RASDIALPARAMS rdpg;     // per-connection credentials

    // The dial parameter extensions used on this connection attempt.  Set in
    // RasDialDlgW, except hwndOwner which is set in DialProgressDlg.
    //
    RASDIALEXTENSIONS rde;
}
DINFO;


// Dialer dialogs argument block.  Used for all 5 variations of the dialer.
//
typedef struct
_DRARGS
{
    DINFO* pDinfo;
    DWORD dwfMode;
    DWORD fReload;
}
DRARGS;


// Dialer dialogs context block.  Used for all 5 variations of the dialer.
//
typedef struct
DRINFO
{
    // Common dial context information including the RAS API arguments.
    //
    DRARGS* pArgs;

    // Handle of the dialog and some of it's controls.
    //
    HWND hwndDlg;
    HWND hwndEbUser;
    HWND hwndEbPw;
    HWND hwndEbDomain;
    HWND hwndCbSavePw;
    HWND hwndRbSaveForMe;
    HWND hwndRbSaveForEveryone;
    HWND hwndClbNumbers;
    HWND hwndStLocations;
    HWND hwndLbLocations;
    HWND hwndPbRules;
    HWND hwndPbProperties;
    HWND hwndBmDialer;

    // Whistler bug: 195480 Dial-up connection dialog - Number of
    // asterisks does not match the length of the password and causes
    // confusion
    //
    WCHAR szPasswordChar;
    HFONT hNormalFont;
    HFONT hItalicFont;

    // TAPI session handle.
    //
    HLINEAPP hlineapp;

    // The phonebook entry link containing the displayed phone number list.
    // Set up only when DR_N mode bit is set.
    //
    DTLNODE* pLinkNode;
    PBLINK* pLink;

    // The index of the item initially selected in the phone number list.
    //
    DWORD iFirstSelectedPhone;

    // Window handles and original window procedure of the subclassed
    // 'hwndClbNumbers' control's edit-box and list-box child windows.
    //
    HWND hwndClbNumbersEb;
    HWND hwndClbNumbersLb;
    WNDPROC wndprocClbNumbersEb;
    WNDPROC wndprocClbNumbersLb;
    INetConnectionUiUtilities * pNetConUtilities;

    // Set if COM has been initialized (necessary for calls to netshell).
    //
    BOOL fComInitialized;

    // Handle to the original bitmap for the dialer if it is modified 
    // in DrSetBitmap
    //
    HBITMAP hbmOrig;
    
}
DRINFO;


// Context of an item in the dialer's 'ClbNumbers' list.
//
typedef struct
_DRNUMBERSITEM
{
    TCHAR* pszNumber;
    PBPHONE* pPhone;
}
DRNUMBERSITEM;


// Subentry state information.
//
typedef struct
_DPSTATE
{
    RASCONNSTATE state;
    DWORD dwError;
    DWORD dwExtendedError;
    TCHAR szExtendedError[ NETBIOS_NAME_LEN + 1 ];
    TCHAR* pszStatusArg;
    TCHAR* pszFormatArg;
    PBDEVICETYPE pbdt;
    DWORD sidState;
    DWORD sidFormatMsg;
    DWORD sidPrevState;
    BOOL fNotPreSwitch;
    HRASCONN hrasconnLink;
}
DPSTATE;


// Dial Progress dialog context block.
//
typedef struct
_DPINFO
{
    // When the block is valid contains the value 0xC0BBC0DE, otherwise 0.
    // Used as a workaround until RasDial is fixed to stop calling
    // RasDialFunc2 after being told not to, see bug 49469.
    //
    DWORD dwValid;

    // RAS API arguments.
    //
    DINFO* pArgs;

    // Handle of this dialog and some of it's controls.
    //
    HWND hwndDlg;
    HWND hwndStState;

    // The saved username and password that authenticated but resulted in a
    // change password event.  If the change password operation fails these
    // are restored to make the redial button work properly.
    //
    TCHAR* pszGoodUserName;
    TCHAR* pszGoodPassword;

    // The handle to the RAS connection being initiated.
    //
    HRASCONN hrasconn;

    // The original window proc we subclassed.
    //
    WNDPROC pOldWndProc;

    // Number of auto-redials not yet attempted on the connection.
    //
    DWORD dwRedialAttemptsLeft;

    // Array of RasDial states, one per subentry, set by DpRasDialFunc2 and
    // used by DpRasDialEvent.
    //
    DPSTATE* pStates;
    DWORD cStates;

    // The number of the most advanced subentry and the "latest" state it has
    // reached.  Note that certain states, like RASCS_AuthNotify, are
    // revisited after reaching a "later" state.  Such changes are ignored.
    //
    RASCONNSTATE state;
    DWORD dwSubEntry;

    // Flag indicating that RasDial callbacks are active.  The callback
    // context must not be destroyed when this flag is set.  Access to this
    // field is protected by 'g_hmutexCallbacks'.  See DpCallbacksFlag().
    //
    BOOL fCallbacksActive;

    //Add a per-thread Terminate flag for whistler bug 277365,291613  gangz
    //
    BOOL fTerminateAsap;
    LONG ulCallbacksActive;

    //for whistler bug 381337
    //
    BOOL fCancelPressed;
}
DPINFO;


// Dial Error dialog argument block.
//
typedef struct
_DEARGS
{
    TCHAR* pszEntry;
    DWORD dwError;
    DWORD sidState;
    TCHAR* pszStatusArg;
    DWORD sidFormatMsg;
    TCHAR* pszFormatArg;
    LONG lRedialCountdown;
    BOOL fPopupOnTop;
}
DEARGS;


// Dial Error dialog context block.
//
typedef struct
_DEINFO
{
    // Caller's arguments to the stub API.
    //
    DEARGS* pArgs;

    // Handle of dialog and controls.
    //
    HWND hwndDlg;
    HWND hwndStText;
    HWND hwndPbRedial;
    HWND hwndPbCancel;
    HWND hwndPbMore;

    // Number of seconds remaining in "Redial=x" countdown or -1 if inactive.
    //
    LONG lRedialCountdown;
}
DEINFO;


// Projection Result dialog argument block.
//
typedef struct
_PRARGS
{
    TCHAR* pszLines;
    BOOL* pfDisableFailedProtocols;
}
PRARGS;


// Change Password dialog argument block.
//
typedef struct
_CPARGS
{
    BOOL fOldPassword;
    TCHAR* pszOldPassword;
    TCHAR* pszNewPassword;
}
CPARGS;


// Change Password dialog context block.
// (unconventional name because CPINFO conflicts with a system header)
//
typedef struct
_CPWINFO
{
    // Caller's arguments to the stub API.
    //
    CPARGS* pArgs;

    // Handle of dialog and controls.
    //
    HWND hwndDlg;
    HWND hwndEbOldPassword;
    HWND hwndEbNewPassword;
    HWND hwndEbNewPassword2;
}
CPWINFO;


// Retry Authentication dialog context block.
//
typedef struct
UAINFO
{
    // Commond dial context including original RAS API arguments.
    //
    DINFO* pArgs;

    // Handle of this dialog and some of it's controls.
    //
    HWND hwndDlg;
    HWND hwndEbUserName;
    HWND hwndEbPassword;
    HWND hwndEbDomain;
    HWND hwndCbSavePw;

    // Set when the password field contains a phony password in place of the
    // "" one we don't really know.
    //
    BOOL fAutoLogonPassword;

    // Set when the Domain field is present.
    //
    BOOL fDomain;
}
UAINFO;

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

BOOL
BeCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

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

VOID
BeFillLvErrors(
    IN HWND hwndLv,
    IN DPINFO* pInfo );

TCHAR*
BeGetErrorPsz(
    IN DWORD dwError );

BOOL
BeInit(
    IN HWND hwndDlg,
    IN DPINFO* pArgs );

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

BOOL
BundlingErrorsDlg(
    IN OUT DPINFO* pInfo );

BOOL
ChangePasswordDlg(
    IN HWND hwndOwner,
    IN BOOL fOldPassword,
    OUT TCHAR* pszOldPassword,
    OUT TCHAR* pszNewPassword );

BOOL
CpCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

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

BOOL
CpInit(
    IN HWND hwndDlg,
    IN CPARGS* pArgs );

BOOL
CcCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

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

BOOL
CcInit(
    IN HWND hwndDlg,
    IN DINFO* pInfo );

VOID
ConnectCompleteDlg(
    IN HWND hwndOwner,
    IN DINFO* pInfo );

BOOL
DcCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

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

BOOL
DcInit(
    IN HWND hwndDlg,
    IN TCHAR* pszNumber );

VOID
DeAdjustPbRedial(
    IN DEINFO* pInfo );

BOOL
DeCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

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

BOOL
DeInit(
    IN HWND hwndDlg,
    IN DEARGS* pArgs );

DWORD
DeleteSavedCredentials(
    IN DINFO* pDinfo,
    IN HWND   hwndDlg,
    IN BOOL   fDefault,
    IN BOOL   fDeleteIdentity );

VOID
DeTerm(
    IN HWND hwndDlg );

BOOL
DialCallbackDlg(
    IN HWND hwndOwner,
    IN OUT TCHAR* pszNumber );

BOOL
DialErrorDlg(
    IN HWND hwndOwner,
    IN TCHAR* pszEntry,
    IN DWORD dwError,
    IN DWORD sidState,
    IN TCHAR* pszStatusArg,
    IN DWORD sidFormatMsg,
    IN TCHAR* pszFormatArg,
    IN LONG lRedialCountdown,
    IN BOOL fPopupOnTop );

BOOL
DialerDlg(
    IN HWND hwndOwner,
    IN OUT DINFO* pInfo );

BOOL
DialProgressDlg(
    IN DINFO* pInfo );

VOID
DpAppendBlankLine(
    IN OUT TCHAR* pszLines );

VOID
DpAppendConnectErrorLine(
    IN OUT TCHAR* pszLines,
    IN DWORD sidProtocol,
    IN DWORD dwError );

VOID
DpAppendConnectOkLine(
    IN OUT TCHAR* pszLines,
    IN DWORD sidProtocol );

VOID
DpAppendFailCodeLine(
    IN OUT TCHAR* pszLines,
    IN DWORD dw );

VOID
DpAppendNameLine(
    IN OUT TCHAR* pszLines,
    IN TCHAR* psz );

VOID
DpAuthNotify(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

VOID
DpCallbackSetByCaller(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

BOOL
DpCallbacksFlag(
    IN DPINFO* pInfo,
    IN INT nSet );

VOID
DpCancel(
    IN DPINFO* pInfo );

BOOL
DpCommand(
    IN DPINFO* pInfo,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

VOID
DpConnectDevice(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

VOID
DpDeviceConnected(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

VOID
DpDial(
    IN DPINFO* pInfo,
    IN BOOL fPauseRestart );

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

VOID
DpError(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

DWORD
DpEvent(
    IN DPINFO* pInfo,
    IN DWORD dwSubEntry );

BOOL
DpInit(
    IN HWND hwndDlg,
    IN DINFO* pArgs );

VOID
DpInitStates(
    DPINFO* pInfo );

BOOL
DpInteractive(
    IN DPINFO* pInfo,
    IN DPSTATE* pState,
    OUT BOOL* pfChange );

BOOL
DpIsLaterState(
    IN RASCONNSTATE stateNew,
    IN RASCONNSTATE stateOld );

BOOL
DpPasswordExpired(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

BOOL
DpProjected(
    IN DPINFO* pInfo,
    IN DPSTATE* pState );

BOOL
DpProjectionError(
    IN RASPPPNBF* pnbf,
    IN RASPPPIPX* pipx,
    IN RASPPPIP* pip,
    OUT BOOL* pfIncomplete,
    OUT DWORD* pdwfFailedProtocols,
    OUT TCHAR** ppszLines,
    OUT DWORD* pdwError );

DWORD WINAPI
DpRasDialFunc2(
    ULONG_PTR dwCallbackId,
    DWORD dwSubEntry,
    HRASCONN hrasconn,
    UINT unMsg,
    RASCONNSTATE state,
    DWORD dwError,
    DWORD dwExtendedError );

VOID
DpTerm(
    IN HWND hwndDlg );

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

BOOL CALLBACK
DrClbNumbersEnumChildProc(
    IN HWND hwnd,
    IN LPARAM lparam );

BOOL CALLBACK
DrClbNumbersEnumWindowsProc(
    IN HWND hwnd,
    IN LPARAM lparam );

BOOL
DrCommand(
    IN DRINFO* pInfo,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

VOID
DrEditSelectedLocation(
    IN DRINFO* pInfo );

DWORD
DrFillLocationList(
    IN DRINFO* pInfo );

VOID
DrFillNumbersList(
    IN DRINFO* pInfo );

DWORD
DrFindAndSubclassClbNumbersControls(
    IN DRINFO* pInfo );

VOID
DrFreeClbNumbers(
    IN DRINFO* pInfo );

BOOL
DrInit(
    IN HWND hwndDlg,
    IN DRARGS* pArgs );

VOID
DrLocationsSelChange(
    IN DRINFO* pInfo );

VOID
DrNumbersSelChange(
    IN DRINFO* pInfo );

DWORD
DrPopulateIdentificationFields(
    IN DRINFO* pInfo, 
    IN BOOL fForMe);

DWORD
DrPopulatePasswordField(
    IN DRINFO* pInfo,
    IN BOOL fInit,
    IN BOOL fDisable );

VOID
DrProperties(
    IN DRINFO* pInfo );

VOID
DrSave(
    IN DRINFO* pInfo );

DWORD
DrSetBitmap(
    IN DRINFO* pInfo);
    
VOID
DrSetClbNumbersText(
    IN DRINFO* pInfo,
    IN TCHAR* pszText );

VOID
DrTerm(
    IN HWND hwndDlg );

LRESULT APIENTRY
DpWndProc(
    HWND hwnd,
    UINT unMsg,
    WPARAM wParam,
    LPARAM lParam );

DWORD
FindEntryAndSetDialParams(
    IN DINFO* pInfo );

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

BOOL
PrCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

BOOL
PrInit(
    IN HWND hwndDlg,
    IN PRARGS* pArgs );

BOOL
ProjectionResultDlg(
    IN HWND hwndOwner,
    IN TCHAR* pszLines,
    OUT BOOL* pfDisableFailedProtocols );

BOOL
RetryAuthenticationDlg(
    IN HWND hwndOwner,
    IN DINFO* pDinfo );

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

BOOL
UaCommand(
    IN UAINFO* pInfo,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

BOOL
UaInit(
    IN HWND   hwndDlg,
    IN DINFO* pArgs );

VOID
UaSave(
    IN UAINFO* pInfo );

VOID
UaTerm(
    IN HWND hwndDlg );

BOOL
VpnDoubleDialDlg(
    IN HWND hwndOwner,
    IN DINFO* pInfo );

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

BOOL
ViCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl );

BOOL
ViInit(
    IN HWND hwndDlg,
    IN DINFO* pInfo );


//-----------------------------------------------------------------------------
// External entry points
//-----------------------------------------------------------------------------

typedef struct EAPFREE_DATA {
    BOOL bInitialized;
    HINSTANCE hLib;
    RASEAPFREE pFreeFunc;
} EAPFREE_DATA;

//Add those OutputDebug_XXXX() functions for debug use when debugging 291613
//        gangz
//
void    OutputDebug_DWCODE(DWORD dwCode)
{
    WCHAR tmpBuf[100];

    wsprintf(tmpBuf, 
             L"The dwCode returned is %x\n", dwCode);
             
    OutputDebugStringW(tmpBuf);
}

void  OutputDebug_NumOfCallbacksActive(ULONG ulCallbacksActive)
{
    WCHAR tmpBuf[100];

    wsprintf(tmpBuf, 
             L"Current CallbacksActive is %x\n", 
             ulCallbacksActive);
             
    OutputDebugStringW(tmpBuf);
}

void OutputDebug_ThreadId()
{
    DWORD dwId;
    WCHAR tmpBuf[100];


    dwId = GetCurrentThreadId();
    
    wsprintf(tmpBuf, L"Current Thread is %x\n", dwId);
    OutputDebugStringW(tmpBuf);
  
}

void OutputDebug_ProcessThreadId()
{
    DWORD dwIdProc, dwIdThread;
    WCHAR tmpBuf[100];

    dwIdProc    = GetCurrentProcessId();
    dwIdThread  = GetCurrentThreadId();
    
    wsprintf(tmpBuf, L"Current Proc is: %x , Thread is: %x\n", dwIdProc, dwIdThread);
    OutputDebugStringW(tmpBuf);
  
}

//
// Raises the appriate eap indentity dialog
//
DWORD
DialerDlgEap (
    IN  HWND hwndOwner,
    IN  PWCHAR lpszPhonebook,
    IN  PWCHAR lpszEntry,
    IN  PBENTRY * pEntry,
    IN  DINFO *pInfo,
    OUT PBYTE * ppUserDataOut,
    OUT DWORD * lpdwSizeOfUserDataOut,
    OUT LPWSTR * lplpwszIdentity,
    OUT PHANDLE phFree
    )
{
    DWORD dwErr, dwInSize = 0;
    PBYTE pbUserIn = NULL;
    HINSTANCE hLib = NULL;
    EAPFREE_DATA * pFreeData = NULL;
    DTLLIST * pListEaps = NULL;
    DTLNODE * pEapcfgNode = NULL;
    EAPCFG * pEapcfg = NULL;
    RASEAPFREE pFreeFunc = NULL;
    RASEAPGETIDENTITY pIdenFunc = NULL;
    DWORD dwFlags;
    DWORD cbData = 0;
    PBYTE pbData = NULL;

    // Initialize the free data handle we'll return
    pFreeData  = Malloc ( sizeof(EAPFREE_DATA) );
    if (pFreeData == NULL)
        return ERROR_NOT_ENOUGH_MEMORY;
    ZeroMemory( pFreeData, sizeof(EAPFREE_DATA) );

    // Make sure we're configured with some list of
    // eap configuration options
    pListEaps = ReadEapcfgList( NULL );
    if (pListEaps == NULL)
    {
        Free(pFreeData);
        return ERROR_CAN_NOT_COMPLETE;
    }

    __try {
        // Find the eap node we're interested in
        pEapcfgNode = EapcfgNodeFromKey(
                        pListEaps,
                        pEntry->dwCustomAuthKey );
        if (pEapcfgNode)
            pEapcfg = (EAPCFG*)DtlGetData( pEapcfgNode );
        else
            return ERROR_CAN_NOT_COMPLETE;

        // Only call eap identity ui if we're told not to
        // get the user name through the standard credentials
        // dialog
        if (pEapcfg->dwStdCredentialFlags &
               EAPCFG_FLAG_RequireUsername)
        {
            return NO_ERROR;
        }

        if(!pInfo->pNoUser)
        {
            // Get the size of the input user data
            dwErr = RasGetEapUserData(
                        NULL,
                        lpszPhonebook,
                        lpszEntry,
                        NULL,
                        &dwInSize);

            // Read in the user data
            if (dwErr != NO_ERROR)  {
                if (dwErr == ERROR_BUFFER_TOO_SMALL) {
                    if (dwInSize == 0)
                    {
                        pbUserIn = NULL;
                        // return ERROR_CAN_NOT_COMPLETE;
                    }
                    else
                    {
                        // Allocate a blob to hold the data
                        pbUserIn = Malloc (dwInSize);
                        if (pbUserIn == NULL)
                            return ERROR_NOT_ENOUGH_MEMORY;

                        // Read in the new blob
                        dwErr = RasGetEapUserData(
                                    NULL,
                                    lpszPhonebook,
                                    lpszEntry,
                                    pbUserIn,
                                    &dwInSize);
                        if (dwErr != NO_ERROR)
                            return dwErr;
                    }
                }
                else
                    return dwErr;
            }
        }
        else
        {
            INTERNALARGS *piargs;

            piargs = (INTERNALARGS *) pInfo->pArgs->reserved;

            if(     (NULL != piargs)
                &&  (NULL != piargs->pvEapInfo)
                // pmay: 386489
                //
                &&  (pEntry->dwCustomAuthKey == EAPCFG_DefaultKey))
            {
                pbUserIn = (BYTE *) piargs->pvEapInfo;
                dwInSize = ((EAPLOGONINFO *) piargs->pvEapInfo)->dwSize;
            }
            else
            {
                pbUserIn = NULL;
                dwInSize = 0;
            }
        }

        // Load the identity library
        hLib = LoadLibrary (pEapcfg->pszIdentityDll);
        if (hLib == NULL)
            return GetLastError();

        // Get pointers to the functions we'll be needing
        pIdenFunc = (RASEAPGETIDENTITY)
                        GetProcAddress(hLib, "RasEapGetIdentity");
        pFreeFunc = (RASEAPFREE) GetProcAddress(hLib, "RasEapFreeMemory");
        if ( (pFreeFunc == NULL) || (pIdenFunc == NULL) )
            return ERROR_CAN_NOT_COMPLETE;

        dwFlags = (pInfo->pNoUser) ? RAS_EAP_FLAG_LOGON : 0;
        if (!pEntry->fAutoLogon && pEntry->fPreviewUserPw)
        {
            dwFlags |= RAS_EAP_FLAG_PREVIEW;
        }

        if(pInfo->fUnattended)
        {
            dwFlags &= ~RAS_EAP_FLAG_PREVIEW;
        }
        
        dwErr = DwGetCustomAuthData(
                        pEntry,
                        &cbData,
                        &pbData);

        if(ERROR_SUCCESS != dwErr)
        {
            return dwErr;
        }

        // Call the eap-provided identity UI
        dwErr = (*(pIdenFunc))(
                    pEntry->dwCustomAuthKey,
                    hwndOwner,
                    dwFlags,
                    lpszPhonebook,
                    lpszEntry,
                    pbData,
                    cbData,
                    pbUserIn,
                    dwInSize,
                    ppUserDataOut,
                    lpdwSizeOfUserDataOut,
                    lplpwszIdentity);
        if (dwErr != NO_ERROR)
            return dwErr;

        // Assign the data used to cleanup later
        pFreeData->bInitialized = TRUE;
        pFreeData->hLib = hLib;
        pFreeData->pFreeFunc = pFreeFunc;
        *phFree = (HANDLE)pFreeData;
    }
    __finally {
        if (pListEaps)
            DtlDestroyList(pListEaps, NULL);
        if (    (!pInfo->pNoUser)
            &&  (pbUserIn))
        {
            Free0(pbUserIn);
        }
        if ((pFreeData) && (!pFreeData->bInitialized)) 
        {
            Free(pFreeData);
            if(NULL != hLib)
            {            
                FreeLibrary(hLib);
            }
        }
    }

    return NO_ERROR;
}

DWORD
DialerEapCleanup (
    IN HANDLE hEapFree,
    IN PBYTE pUserDataOut,
    IN LPWSTR lpwszIdentity)
{
    EAPFREE_DATA * pFreeData = (EAPFREE_DATA*)hEapFree;

    if (pFreeData == NULL)
        return ERROR_INVALID_PARAMETER;

    if (pFreeData->pFreeFunc) {
        if (pUserDataOut)
            (*(pFreeData->pFreeFunc))(pUserDataOut);
        if (lpwszIdentity)
            (*(pFreeData->pFreeFunc))((BYTE*)lpwszIdentity);
    }

    if (pFreeData->hLib)
        FreeLibrary(pFreeData->hLib);

    Free (pFreeData);

    return NO_ERROR;
}

//
// Customizes the dialer flags for the eap provider
// of the given entry;
//
// TODO -- try to optimize this.  The list of eaps
// may not need to be read if we keep enough state
// in the phonebook.
//
DWORD DialerEapAssignMode(
        IN  DINFO* pInfo,
        OUT LPDWORD lpdwfMode)
{
    DWORD dwfMode = *lpdwfMode;
    DTLLIST * pListEaps;
    DTLNODE * pEapcfgNode;
    EAPCFG * pEapcfg;

    // If eap is not used in this entry,
    // then no action is required
    if (! (pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP))
        return NO_ERROR;

    // Make sure we're configured with some list of
    // eap configuration options
    pListEaps = ReadEapcfgList( NULL );
    if (pListEaps == NULL)
        return ERROR_CAN_NOT_COMPLETE;

    // Find the eap node we're interested in
    pEapcfgNode = EapcfgNodeFromKey(
                    pListEaps,
                    pInfo->pEntry->dwCustomAuthKey );
    if (pEapcfgNode)
        pEapcfg = (EAPCFG*)DtlGetData( pEapcfgNode );
    else
    {
        if (pListEaps)
            DtlDestroyList(pListEaps, NULL);
    
        return ERROR_CAN_NOT_COMPLETE;
    }

    // If eap provider requests user name then
    // request identity.
    if (pEapcfg->dwStdCredentialFlags &
           EAPCFG_FLAG_RequireUsername
       )
    {
        // Use the "I" flavors if the eap wants a user
        // name but no password.  
        //
        if (!(pEapcfg->dwStdCredentialFlags &
               EAPCFG_FLAG_RequirePassword)
           )
        {
            // Clear the username+password property (DR_U) if it
            // exists and replace it with the username property 
            // (DR_I).  Only do this if DR_U is already set.  It
            // wont be set for autodial connections or for connections
            // where that option was specifically disabled as can
            // be seen in the DialerDlg function.
            //
            // See whistler bug 30841
            //
            if (dwfMode & DR_U)
            {
                dwfMode &= ~DR_U;
                dwfMode |= DR_I;
            }                
        }
    }
    else
    {
        // Otherwise, make sure that we request neither user name nor password
        // Since domain cannot appear without username clear that also.
        //
        dwfMode &= ~(DR_U | DR_D);
    }

    // Cleanup
    if (pListEaps)
        DtlDestroyList(pListEaps, NULL);

    // Assign the correct mode
    *lpdwfMode = dwfMode;

    return NO_ERROR;
}

BOOL APIENTRY
RasDialDlgA(
    IN LPSTR lpszPhonebook,
    IN LPSTR lpszEntry,
    IN LPSTR lpszPhoneNumber,
    IN OUT LPRASDIALDLG lpInfo )

    // Win32 ANSI entrypoint that displays the dial progress and related
    // dialogs, including authentication, error w/redial, callback, and retry
    // authentication.  'LpszPhonebook' is the full path the phonebook or NULL
    // indicating the default phonebook.  'LpszEntry' is the entry to dial.
    // 'LpszPhoneNumber' is caller's override phone number or NULL to use the
    // one in the entry.  'LpInfo' is caller's additional input/output
    // parameters.
    //
    // Returns true if user establishes a connection, false otherwise.
    //
{
    WCHAR* pszPhonebookW;
    WCHAR* pszEntryW;
    WCHAR* pszPhoneNumberW;
    BOOL fStatus;

    TRACE( "RasDialDlgA" );

    if (!lpInfo)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    if (!lpszEntry)
    {
        lpInfo->dwError = ERROR_INVALID_PARAMETER;
        return FALSE;
    }

    if (lpInfo->dwSize != sizeof(RASDIALDLG))
    {
        lpInfo->dwError = ERROR_INVALID_SIZE;
        return FALSE;
    }

    // Thunk "A" arguments to "W" arguments.
    //
    if (lpszPhonebook)
    {
        pszPhonebookW = StrDupTFromAUsingAnsiEncoding( lpszPhonebook );
        if (!pszPhonebookW)
        {
            lpInfo->dwError = ERROR_NOT_ENOUGH_MEMORY;
            return FALSE;
        }
    }
    else
    {
        pszPhonebookW = NULL;
    }

    pszEntryW = StrDupTFromAUsingAnsiEncoding( lpszEntry );
    if (!pszEntryW)
    {
        Free0( pszPhonebookW );
        lpInfo->dwError = ERROR_NOT_ENOUGH_MEMORY;
        return FALSE;
    }

    if (lpszPhoneNumber)
    {
        pszPhoneNumberW = StrDupTFromAUsingAnsiEncoding( lpszPhoneNumber );
        if (!pszPhoneNumberW)
        {
            Free0( pszPhonebookW );
            Free( pszEntryW );
            lpInfo->dwError = ERROR_NOT_ENOUGH_MEMORY;
            return FALSE;
        }
    }
    else
    {
            pszPhoneNumberW = NULL;
    }

    // Thunk to the equivalent "W" API.
    //
    fStatus = RasDialDlgW( pszPhonebookW, pszEntryW, pszPhoneNumberW, lpInfo );

    Free0( pszPhonebookW );
    Free( pszEntryW );

    return fStatus;
}

DWORD
DoEapProcessing(
    LPRASDIALDLG lpInfo,
    DINFO *pInfo,
    PBYTE *ppbEapUserData,
    WCHAR **ppwszEapIdentity,
    HANDLE *phEapFree,
    BOOL *pfStatus
    )
{
    // If this is an eap connection, then use the eap identity
    // ui to get the user name and password. 
    //
    DWORD dwSize = 0;
    DWORD dwErr = NO_ERROR;

    *pfStatus = TRUE;
                
    // Bring up the Eap dialer dialog
    dwErr = DialerDlgEap(
                lpInfo->hwndOwner,
                pInfo->pFile->pszPath,
                pInfo->pEntry->pszEntryName,
                pInfo->pEntry,
                pInfo,
                ppbEapUserData,
                &dwSize,
                ppwszEapIdentity,
                phEapFree);

    if (dwErr != NO_ERROR)
    {
        if (ERROR_CANCELLED == dwErr)
        {
            dwErr = NO_ERROR;
        }

        *pfStatus = FALSE;

        goto done;
    }

    if(!pInfo->pNoUser)
    {
        // Set the extended dial params accordingly
        pInfo->rde.RasEapInfo.dwSizeofEapInfo = dwSize;
        pInfo->rde.RasEapInfo.pbEapInfo = *ppbEapUserData;
    }
    else if (   (*ppbEapUserData != NULL)
             && (dwSize != 0))
    {
        pInfo->rde.RasEapInfo.dwSizeofEapInfo = dwSize;
        pInfo->rde.RasEapInfo.pbEapInfo = *ppbEapUserData;
    }
    else
    {
        INTERNALARGS *piargs;

        piargs = (INTERNALARGS *) (pInfo->pArgs->reserved);
        if(     (NULL != piargs)
            &&  (NULL != piargs->pvEapInfo)
            // pmay: 386489
            //
            &&  (pInfo->pEntry->dwCustomAuthKey == EAPCFG_DefaultKey))
        {
            pInfo->rde.RasEapInfo.dwSizeofEapInfo =
                        ((EAPLOGONINFO *) piargs->pvEapInfo)->dwSize;

            pInfo->rde.RasEapInfo.pbEapInfo =  (BYTE *) piargs->pvEapInfo;
        }
        else
        {
            pInfo->rde.RasEapInfo.dwSizeofEapInfo = 0;
            pInfo->rde.RasEapInfo.pbEapInfo = NULL;
        }
    }

    if (*ppwszEapIdentity) 
    {
        DWORD dwSize =
            sizeof(pInfo->rdp.szUserName) / sizeof(WCHAR);

        wcsncpy(pInfo->rdp.szUserName, *ppwszEapIdentity,
            dwSize - 1);
        pInfo->rdp.szUserName[dwSize - 1] = 0;

        // Ignore the domain setting if the EAP supplied the 
        // identity.
        pInfo->rdp.szDomain[ 0 ] = L'\0';
    }

done:
    return dwErr;
}

INT
DialDlgDisplayError(
    IN LPRASDIALDLG pInfo,
    IN HWND hwndOwner, 
    IN DWORD dwSid, 
    IN DWORD dwError, 
    IN ERRORARGS* pArgs)
{
    if (pInfo->dwFlags & RASDDFLAG_NoPrompt)
    {
        return 0;
    }

    return ErrorDlg(hwndOwner, dwSid, dwError, pArgs);
}

BOOL APIENTRY
RasDialDlgW(
    IN LPWSTR lpszPhonebook,
    IN LPWSTR lpszEntry,
    IN LPWSTR lpszPhoneNumber,
    IN OUT LPRASDIALDLG lpInfo )

    // Win32 UNICODE entrypoint that displays the dial progress and related
    // dialogs, including authentication, error w/redial, callback, and retry
    // authentication.  'LpszPhonebook' is the full path the phonebook or NULL
    // indicating the default phonebook.  'LpszEntry' is the entry to dial.
    // 'LpszPhoneNumber' is caller's override phone number or NULL to use the
    // one in the entry.  'LpInfo' is caller's additional input/output
    // parameters.
    //
    // Returns true if user establishes a connection, false otherwise.  If
    // 'RASDDFLAG_AutoDialQueryOnly' is set, returns true if user pressed
    // "Dial", false otherwise.
    //
{
    DWORD dwErr;
    BOOL fStatus;
    BOOL fFirstPass;
    DINFO* pInfo;
    LPWSTR pwszEapIdentity = NULL;
    PBYTE pbEapUserData = NULL;
    HANDLE hEapFree = NULL;
    BOOL fCustom = FALSE;
    PVOID pvInfo = NULL;
    HRASCONN hrasconnPrereq = NULL;

    TRACE( "RasDialDlgW" );

    if (!lpInfo)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    if (!lpszEntry)
    {
        lpInfo->dwError = ERROR_INVALID_PARAMETER;
        return FALSE;
    }

    if (lpInfo->dwSize != sizeof(RASDIALDLG))
    {
        lpInfo->dwError = ERROR_INVALID_SIZE;
        return FALSE;
    }

    if (lpszPhoneNumber && lstrlen( lpszPhoneNumber ) > RAS_MaxPhoneNumber)
    {
        lpInfo->dwError = ERROR_INVALID_PARAMETER;
        return FALSE;
    }

    // Load RAS DLL entrypoints which starts RASMAN, if necessary.
    //
    lpInfo->dwError = LoadRas( g_hinstDll, lpInfo->hwndOwner );
    if (lpInfo->dwError != 0)
    {
        // Whistler bug 301784
        //
        // Check specifically for access denied.  
        //
        if (lpInfo->dwError == ERROR_ACCESS_DENIED)
        {
            DialDlgDisplayError( 
                lpInfo,
                lpInfo->hwndOwner, 
                SID_OP_LoadRasAccessDenied, 
                lpInfo->dwError, 
                NULL );
        }
        else
        {
            DialDlgDisplayError(
                lpInfo,
                lpInfo->hwndOwner, 
                SID_OP_LoadRas, 
                lpInfo->dwError, 
                NULL );
        }       
        
        return FALSE;
    }

    // Allocate the context information block and initialize it enough so that
    // it can be destroyed properly.
    //
    pInfo = Malloc( sizeof(*pInfo) );
    if (!pInfo)
    {
        DialDlgDisplayError( 
            lpInfo,
            lpInfo->hwndOwner, 
            SID_OP_LoadDlg,
            ERROR_NOT_ENOUGH_MEMORY, 
            NULL );
            
        lpInfo->dwError = ERROR_NOT_ENOUGH_MEMORY;
        return FALSE;
    }

    ZeroMemory( pInfo, sizeof(*pInfo) );
    pInfo->pszPhonebook = lpszPhonebook;
    pInfo->pszEntry = lpszEntry;
    pInfo->pszPhoneNumber = lpszPhoneNumber;
    pInfo->pArgs = lpInfo;

    fStatus = FALSE;
    dwErr = 0;

    do
    {
        // Load the phonebook file and user preferences, or figure out that
        // caller has already loaded them.
        //
        if (lpInfo->reserved)
        {
            INTERNALARGS* piargs;

            // We've received an open phonebook file and user preferences via
            // the secret hack.
            //
            piargs = (INTERNALARGS* )lpInfo->reserved;
            pInfo->pFile = pInfo->pFileMain = piargs->pFile;
            pInfo->pUser = piargs->pUser;
            pInfo->pNoUser = piargs->pNoUser;
            pInfo->pfNoUserChanged = &piargs->fNoUserChanged;
            pInfo->fMoveOwnerOffDesktop = piargs->fMoveOwnerOffDesktop;
            pInfo->fForceCloseOnDial = piargs->fForceCloseOnDial;

        }
        else
        {
            // Read user preferences from registry.
            //
            dwErr = g_pGetUserPreferences( NULL, &pInfo->user, UPM_Normal );
            if (dwErr != 0)
            {
                DialDlgDisplayError( 
                    lpInfo,
                    lpInfo->hwndOwner, 
                    SID_OP_LoadPrefs, 
                    dwErr, 
                    NULL );
                    
                break;
            }

            pInfo->pUser = &pInfo->user;

            // Load and parse the phonebook file.
            //
            dwErr = ReadPhonebookFile(
                lpszPhonebook, &pInfo->user, NULL, 0, &pInfo->file );
            if (dwErr != 0)
            {
                DialDlgDisplayError( 
                    lpInfo,
                    lpInfo->hwndOwner, 
                    SID_OP_LoadPhonebook,
                    dwErr, 
                    NULL );
                break;
            }

            pInfo->pFile = pInfo->pFileMain = &pInfo->file;
        }

        // Record whether this is a for-all-users phonebook
        //
        // Whistler bug 288596 Autodial has wrong save password option marked -
        // prompts user to save password for all users
        //
        pInfo->fIsPublicPbk =
            (!pInfo->pszPhonebook) || IsPublicPhonebook(pInfo->pszPhonebook);

        if (!pInfo->pNoUser)
        {
            DWORD dwErrR;
            HKEY hkey;

            // See if admin has disabled the "save password" feature.
            //
            pInfo->fDisableSavePw = FALSE;

            dwErrR = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                TEXT("SYSTEM\\CurrentControlSet\\Services\\RasMan\\Parameters"),
                0, KEY_READ, &hkey );

            if (dwErrR == 0)
            {
                DWORD dwResult;

                dwResult = (DWORD )pInfo->fDisableSavePw;
                GetRegDword( hkey, TEXT("DisableSavePassword"), &dwResult );
                pInfo->fDisableSavePw = (BOOL )dwResult;

                RegCloseKey( hkey );
            }
        }

        // Hide parent dialog when initiated by another RAS API that requests
        // it.  This is the first stage of "close on dial" behavior, allowing
        // the parent to appear closed to user though, as owner, it must
        // really stay open until the dial dialogs complete.  At that point it
        // can silently close or reappear as desired.
        //
        if (lpInfo->hwndOwner && pInfo->fMoveOwnerOffDesktop)
        {
            SetOffDesktop( lpInfo->hwndOwner, SOD_MoveOff, NULL );
        }

        // Set true initially, but will be set false by
        // FindEntryAndSetDialParams if the entry has no "dial first" entry
        // associated with it.
        //
        pInfo->fPrerequisiteDial = TRUE;
        fFirstPass = TRUE;
        for (;;)
        {
            pInfo->fDialForReferenceOnly = FALSE;

            // Look up the entry and fill in the RASDIALPARAMS structure
            // accordingly.  This done as a routine so it can be re-done
            // should user press the Properties button.
            //
            dwErr = FindEntryAndSetDialParams( pInfo );
            if (dwErr != 0)
            {
                // we need to maintain 2 phonebooks
                // but we need to do this in case we break existing
                // apps which look specifically in system\ras dir.
                // Feel free to rip this code off, if you feel
                // strongly about it.
                //
                if(     (ERROR_CANNOT_FIND_PHONEBOOK_ENTRY == dwErr)
                    &&  (NULL == lpszPhonebook))
                {
                    DTLNODE *pNode;

                    //
                    // Close the all users phonebook file
                    //
                    ClosePhonebookFile(&pInfo->file);

                    dwErr = GetPbkAndEntryName(
                            lpszPhonebook,
                            lpszEntry,
                            0,
                            &pInfo->file,
                            &pNode);

                    if(     (NULL == pNode)
                        ||  (ERROR_SUCCESS != dwErr))
                    {
                        dwErr = ERROR_CANNOT_FIND_PHONEBOOK_ENTRY;
                        break;
                    }

                    pInfo->pFile = pInfo->pFileMain = &pInfo->file;

                    dwErr = FindEntryAndSetDialParams(pInfo);

                    if(dwErr != 0)
                    {
                        break;
                    }
                }
                else
                {
                    break;
                }
            }

            if(lpInfo->reserved)
            {
                INTERNALARGS *piargs = (INTERNALARGS *) lpInfo->reserved;

                if (pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP)
                {
                    pvInfo = piargs->pvEapInfo;
                }
                else
                {
                    pvInfo = piargs->pNoUser;
                }
            }

            if(pInfo->fPrerequisiteDial
               && (NULL != pInfo->pEntry->pszCustomDialerName)
               && (TEXT('\0') != pInfo->pEntry->pszCustomDialerName[0]))
            {
                RASDIALDLG Info;
                DWORD dwCustomFlags = 0;
                RASNOUSER nouser, *pNoUser = NULL;

                ZeroMemory(&Info, sizeof(RASDIALDLG));
                ZeroMemory(&nouser, sizeof(RASNOUSER));

                Info.dwSize = sizeof(RASDIALDLG);
                Info.hwndOwner = lpInfo->hwndOwner;
                Info.xDlg = lpInfo->xDlg;
                Info.yDlg = lpInfo->yDlg;

                fCustom = TRUE;

                if(pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP)
                {
                    dwCustomFlags  |= RCD_Eap;
                }

                if(     (NULL != pInfo->pNoUser)
                    &&  (RASNOUSER_SmartCard & pInfo->pNoUser->dwFlags)
                    &&  (   (0 == (dwCustomFlags & RCD_Eap))
                        ||  (EAP_RASTLS != pInfo->pEntry->dwCustomAuthKey)
                    ))
                {
                    CopyMemory(&nouser, pInfo->pNoUser, sizeof(RASNOUSER));    
                    ZeroMemory(nouser.szPassword, (PWLEN+1) * sizeof(TCHAR));
                    pvInfo = &nouser;
                }

                // DwCustomDialDlg returns ERROR_SUCCESS if it handled
                // the CustomRasDial. returns E_NOINTERFACE otherwise
                // which implies that there is no custom dlg interface
                // supported for this entry and the default dial should
                // happen
                //
                // Whistler bug 314578 When connecting with CM via Winlogon I
                // get the following error "Error 1:  Incorrect function"
                //
                // This is a case where we call into a custom dialer, ie CM,
                // and we are using creds that we got from winlogon. They are
                // currently encoded and must be decoded before we call out.
                // We have to assume that the Custom Dialer leaves the password
                // un-encoded upon return.
                //
                if ( !(pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP) )
                {
                    // pNoUser is used to encode/decode passwords.  If this
                    // is an EAP connection, then pvInfo will point to an
                    // eap blob, not a "no user" blob.  
                    //
                    pNoUser = pvInfo;
                }                    
                if ( pNoUser )
                {
                    DecodePassword( pNoUser->szPassword );
                }

                if(pInfo->pNoUser)
                {
                    dwCustomFlags |= RCD_Logon;
                }

                dwErr = DwCustomDialDlg(pInfo->pFile->pszPath,
                                        pInfo->pEntry->pszEntryName,
                                        NULL,
                                        &Info,
                                        dwCustomFlags,
                                        &fStatus,
                                        pvInfo,
                                        pInfo->pEntry->pszCustomDialerName);
                if ( pNoUser )
                {
                    EncodePassword( pNoUser->szPassword );
                }

                if(!fStatus)
                {
                    lpInfo->dwError = Info.dwError;
                    break;
                }
                else
                {
                    pInfo->fPrerequisiteDial = FALSE;
                    fCustom = FALSE;
                    continue;
                }
            }
            else if ((NULL != pInfo->pEntry->pszCustomDialerName)
                    && (TEXT('\0') != pInfo->pEntry->pszCustomDialerName[0]))
            {
                DWORD dwCustomFlags = 0;
                RASNOUSER nouser, *pNoUser = NULL;

                ZeroMemory(&nouser, sizeof(RASNOUSER));

                if(pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP)
                {
                    dwCustomFlags  |= RCD_Eap;
                }

                if(     (NULL != pInfo->pNoUser)
                    &&  (RASNOUSER_SmartCard & pInfo->pNoUser->dwFlags)
                    &&  (   (0 == (dwCustomFlags & RCD_Eap))
                        ||  (EAP_RASTLS != pInfo->pEntry->dwCustomAuthKey))
                    )
                {
                    CopyMemory(&nouser, pInfo->pNoUser, sizeof(RASNOUSER));
                    ZeroMemory(nouser.szPassword, (PWLEN+1) * sizeof(TCHAR));
                    pvInfo = &nouser;
                }

                fCustom = TRUE;


                // DwCustomDialDlg returns ERROR_SUCCESS if it handled
                // the CustomRasDial. returns E_NOINTERFACE otherwise
                // which implies that there is no custom dlg interface
                // supported for this entry and the default dial should
                // happen
                //
                // Whistler bug 314578 When connecting with CM via Winlogon I
                // get the following error "Error 1:  Incorrect function"
                //
                // This is a case where we call into a custom dialer, ie CM,
                // and we are using creds that we got from winlogon. They are
                // currently encoded and must be decoded before we call out.
                // We have to assume that the Custom Dialer leaves the password
                // un-encoded upon return.
                //
                if ( !(pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP) )
                {
                    // pNoUser is used to encode/decode passwords.  If this
                    // is an EAP connection, then pvInfo will point to an
                    // eap blob, not a "no user" blob.  
                    //
                    pNoUser = pvInfo;
                }                    
                if ( pNoUser )
                {
                    DecodePassword( pNoUser->szPassword );
                }

                if(pInfo->pNoUser)
                {
                    dwCustomFlags |= RCD_Logon;
                }

                dwErr = DwCustomDialDlg(lpszPhonebook,
                                        lpszEntry,
                                        lpszPhoneNumber,
                                        lpInfo,
                                        dwCustomFlags,
                                        &fStatus,
                                        pvInfo,
                                        pInfo->pEntry->pszCustomDialerName);
                if ( pNoUser )
                {
                    EncodePassword( pNoUser->szPassword );
                }

                break;
            }

            // If a prerequisite entry is already connected, there's no need
            // for any UI but the dial must occur to set the reference in the
            // RASAPI level.
            //
            if (pInfo->fPrerequisiteDial
                && HrasconnFromEntry(
                       pInfo->pFile->pszPath, pInfo->pEntry->pszEntryName ))
            {
                pInfo->fDialForReferenceOnly = TRUE;
            }

            // Set up extension parameter block, except 'hwndOwner' which is
            // set to the Dial Progress dialog window later.
            //
            {
                RASDIALEXTENSIONS* prde = &pInfo->rde;

                ZeroMemory( prde, sizeof(*prde) );
                prde->dwSize = sizeof(*prde);
                prde->dwfOptions = RDEOPT_PausedStates | RDEOPT_PauseOnScript;

                if (pInfo->pNoUser)
                {
                    prde->dwfOptions |= RDEOPT_NoUser;
                }

                if (!pInfo->pszPhoneNumber)
                {
                    prde->dwfOptions |= RDEOPT_UsePrefixSuffix;
                }
            }

            if (        (pInfo->fUnattended)
                &&      ((HaveSavedPw( pInfo ))
                    ||  (pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP)))
            {
                // Popup the countdown to link failure redial version of the
                // dial error dialog, which will lead to a dial unless user
                // stops it.
                //
                fStatus = DialErrorDlg(
                    lpInfo->hwndOwner, pInfo->pEntry->pszEntryName,
                    0, 0, NULL, 0, NULL,
                    GetOverridableParam(
                        pInfo->pUser, pInfo->pEntry, RASOR_RedialSeconds ),
                    GetOverridableParam(
                        pInfo->pUser, pInfo->pEntry,
                        RASOR_PopupOnTopWhenRedialing ) );

                if(!fStatus)
                {
                    break;
                }
                        
                if (pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP)
                {
                    dwErr = DoEapProcessing(
                                lpInfo,
                                pInfo,
                                &pbEapUserData,
                                &pwszEapIdentity,
                                &hEapFree,
                                &fStatus);

                    if(     (NO_ERROR != dwErr)
                        ||  (!fStatus))
                    {
                        break;
                    }
                }
                
            }
            else if (!pInfo->fDialForReferenceOnly)
            {
                if (!pInfo->fUnattended && fFirstPass)
                {
                    // Warn about active NWC LAN connections being blown away,
                    // if indicated.
                    //
                    if (!NwConnectionCheck(
                            lpInfo->hwndOwner,
                            (pInfo->pArgs->dwFlags & RASDDFLAG_PositionDlg),
                            pInfo->pArgs->xDlg, pInfo->pArgs->yDlg,
                            pInfo->pFile, pInfo->pEntry ))
                    {
                        break;
                    }

                    // Popup the double-dial help popup, if indicated.
                    //
                    if (!VpnDoubleDialDlg( lpInfo->hwndOwner, pInfo ))
                    {
                        break;
                    }
                }

                // Check to see if its smartcardlogon case and blank
                // out the password if its not an eap tls connectoid
                //
                if(     (NULL != pInfo->pNoUser)
                    &&  (RASNOUSER_SmartCard & pInfo->pNoUser->dwFlags)
                    &&  (pInfo->pEntry->dwCustomAuthKey != EAP_RASTLS))
                {
                    ZeroMemory(pInfo->rdp.szPassword, (PWLEN+1) * sizeof(TCHAR));
                }

                // Prompt for credentials and/or phone number (or not)
                // as configured in the entry properties.
                //
                if (!DialerDlg( lpInfo->hwndOwner, pInfo ))
                {
                    if(!fFirstPass)
                    {
                        fStatus = FALSE;
                    }
                    break;
                }

                if (pInfo->pEntry->dwAuthRestrictions & AR_F_AuthEAP)
                {
                    dwErr = DoEapProcessing(
                                lpInfo,
                                pInfo,
                                &pbEapUserData,
                                &pwszEapIdentity,
                                &hEapFree,
                                &fStatus);

                    if(     (NO_ERROR != dwErr)
                        ||  (!fStatus))
                    {
                        break;
                    }
                }

                fStatus = TRUE;
            }
            else
            {
                fStatus = TRUE;
            }

            // Dial and show progress.
            //
            if (fStatus
                && !fCustom)
            {

                // Clear this here because beyond this rasman
                // will take care of dropping the prereq link
                // since beyond this point rasdial api will get
                // called. [raos]
                //
                hrasconnPrereq = NULL;

                fStatus = DialProgressDlg( pInfo );

                // Show connect complete dialog unless user has nixed it or
                // it's a prerequisite dial.
                // (AboladeG) Also suppress the dialog in no-prompt mode.
                //
                if (!pInfo->fPrerequisiteDial
                    && fStatus
                    && !pInfo->pUser->fSkipConnectComplete
                    && !(pInfo->pArgs->dwFlags & RASDDFLAG_NoPrompt))
                {
                    //For whistler bug 378078       gangz
                    //We will comment out this status explaination dialog
                    //box because some users complained that it is confusing
                    //
                    // ConnectCompleteDlg( lpInfo->hwndOwner, pInfo );
                }
            }

            // Don't loop a second time to dial the main entry if the
            // prerequisite dial failed.
            //
            if (!fStatus || !pInfo->fPrerequisiteDial)
            {
                break;
            }

            // Save the rasconn of the prereq dial in case we need to hang
            // it up for the case where the vpn dialog fails before rasdial
            // gets called. [raos]
            //
            if (pInfo->fPrerequisiteDial)
            {
                hrasconnPrereq = HrasconnFromEntry(
                   pInfo->pFile->pszPath, pInfo->pEntry->pszEntryName);
            }      
            

            pInfo->fPrerequisiteDial = FALSE;
            fFirstPass = FALSE;
            // Cleanup eap stuff
            if (hEapFree)
            {
                DialerEapCleanup(hEapFree, pbEapUserData, pwszEapIdentity);
                hEapFree = NULL;
                pbEapUserData = NULL;
                pwszEapIdentity = NULL;
            }
        }
    }
    while (FALSE);

    // Unhide parent dialog when initiated by another RAS API.
    //
    if (lpInfo->hwndOwner && pInfo->fMoveOwnerOffDesktop
        && (!fStatus
            || !(pInfo->pUser->fCloseOnDial || pInfo->fForceCloseOnDial)))
    {
        SetOffDesktop( lpInfo->hwndOwner, SOD_MoveBackFree, NULL );
    }

    if(!fCustom)
    {
        // Save the several little user preferences adjustments we may have made.
        //
        g_pSetUserPreferences(
            NULL, pInfo->pUser, (pInfo->pNoUser) ? UPM_Logon : UPM_Normal );

        // Report error, if any.
        //
        if (dwErr)
        {
            DialDlgDisplayError( 
                lpInfo,
                lpInfo->hwndOwner, 
                SID_OP_LoadDlg, 
                dwErr, 
                NULL );
            lpInfo->dwError = dwErr;
        }

        TRACE1("hrasconnPrereq=0x%x",hrasconnPrereq);

        //
        // Drop the connection if we failed to connect the vpn connection
        //
        if(     !fStatus
            &&  (NULL != hrasconnPrereq)
            &&  (pInfo->pEntry)
            &&  (pInfo->pEntry->pszPrerequisiteEntry)
            && *(pInfo->pEntry->pszPrerequisiteEntry))
        {
            g_pRasHangUp(hrasconnPrereq);
        }
    }

    // Clean up.
    //
    if (!lpInfo->reserved)
    {
        if (pInfo->pFileMain)
        {
            ClosePhonebookFile( pInfo->pFileMain );
        }

        if (pInfo->pUser)
        {
            DestroyUserPreferences( pInfo->pUser );
        }
    }

    if (pInfo->fFilePrereqOpen)
    {
        ClosePhonebookFile( &pInfo->filePrereq );
    }

    ZeroMemory( pInfo->rdp.szPassword, sizeof(pInfo->rdp.szPassword) );
    if (pInfo->pListPortsToDelete)
    {
        DtlDestroyList( pInfo->pListPortsToDelete, DestroyPszNode );
    }

    if (hEapFree)
        DialerEapCleanup(hEapFree, pbEapUserData, pwszEapIdentity);
        
    Free( pInfo );

    return fStatus;
}


//----------------------------------------------------------------------------
// Local utilities
// Listed alphabetically
//----------------------------------------------------------------------------

DWORD
RasCredToDialParam(
    IN  TCHAR* pszDefaultUserName,
    IN  TCHAR* pszDefaultDomain,
    IN  RASCREDENTIALS* pCreds,
    OUT RASDIALPARAMS* pParams)
{
    TCHAR* pszComputer = NULL;
    TCHAR* pszLogonDomain = NULL;
    TCHAR* pszUser = NULL;

    // Set the user name, defaulting it if needed
    //
    if (pCreds->dwMask & RASCM_UserName)
    {
        lstrcpyn(
            pParams->szUserName,
            pCreds->szUserName,
            sizeof(pParams->szUserName) / sizeof(TCHAR));
    }            
    else if (pszDefaultUserName)
    {
        lstrcpyn(
            pParams->szUserName,
            pszDefaultUserName,
            sizeof(pParams->szUserName) / sizeof(TCHAR));
    }
    else
    {
        pszUser = GetLogonUser();
        
        if (pszUser)
        {
            lstrcpyn(
                pParams->szUserName,
                pszUser,
                sizeof(pParams->szUserName) / sizeof(TCHAR));
        }
    }

    // Set the domain name, defaulting it if needed
    //
    if (pCreds->dwMask & RASCM_Domain)
    {
        lstrcpyn(
            pParams->szDomain,
            pCreds->szDomain,
            sizeof(pParams->szDomain) / sizeof(TCHAR));
    }            
    else if ( pszDefaultDomain )
    {
        lstrcpyn(
            pParams->szDomain,
            pszDefaultDomain,
            sizeof(pParams->szDomain) / sizeof(TCHAR));
    }
    else
    {
        pszComputer = GetComputer();
        pszLogonDomain = GetLogonDomain();
        
        if ( (pszComputer)      &&
             (pszLogonDomain)   && 
             (lstrcmp( pszComputer, pszLogonDomain ) != 0))
        {
            lstrcpyn( 
                pParams->szDomain, 
                pszLogonDomain,
                sizeof(pParams->szDomain) / sizeof(TCHAR));
        }
    }

    // Fill in the password field
    //
    if (pCreds->dwMask & RASCM_Password)
    {
        // Whistler bug 254385 encode password when not being used
        // Assumed password was encoded previously
        //
        DecodePassword( pCreds->szPassword );
        lstrcpyn(
            pParams->szPassword,
            pCreds->szPassword,
            sizeof(pParams->szPassword) / sizeof(TCHAR) );
        EncodePassword( pCreds->szPassword );
        EncodePassword( pParams->szPassword );
    }

    return NO_ERROR;
}

DWORD 
FindEntryCredentials(
    IN  TCHAR* pszPath,
    IN  TCHAR* pszEntryName,
    IN  TCHAR* pszDefaultUserName,
    IN  TCHAR* pszDefaultDomain,
    OUT RASDIALPARAMS* pUser,       // per user credentials
    OUT RASDIALPARAMS* pGlobal,     // global credentials
    OUT BOOL* pfUser,               // set true if per user creds found
    OUT BOOL* pfGlobal              // set true if global creds found
    )

// Loads the credentials for the given entry into memory.  This routine 
// determines whether per-user or per-connection credentials exist or 
// both. 
// 
// The logic is a little complicated because RasGetCredentials had to 
// support legacy usage of the API.
//
// Here's how it works.  If only one set of credentials is stored for a 
// connection, then RasGetCredentials will return that set regardless of 
// whether the RASCM_DefalutCreds flag is set.  If two sets of credentials
// are saved, then RasGetCredentials will return the per-user credentials
// if the RASCM_DefaultCreds bit is set, and the per-connection credentials
// otherwise.
//
// Here is the algorithm for loading the credentials
//
// 1. Call RasGetCredentials with the RASCM_DefaultCreds bit cleared
//    1a. If nothing is returned, no credentials are saved
//    1b. If the RASCM_DefaultCreds bit is set on return, then only
//        global credentials are saved.
//
// 2. Call RasGetCredentials with the RASCM_DefaultCreds bit set
//    2a. If the RASCM_DefaultCreds bit is set on return, then 
//        both global and per-connection credentials are saved.
//    2b. Otherwise, only per-user credentials are saved.
//
{
    DWORD dwErr;
    RASCREDENTIALS rc1, rc2;
    BOOL fUseLogonDomain;

    TRACE( "FindEntryCredentials" );

    // Initialize
    //
    *pfUser = FALSE;
    *pfGlobal = FALSE;
    ZeroMemory( &rc1, sizeof(rc1) );
    ZeroMemory( &rc2, sizeof(rc2) );
    rc1.dwSize = sizeof(rc1);
    rc2.dwSize = sizeof(rc2);

    do 
    {

        // Look up per-user cached username, password, and domain.
        // See comment '1.' in the function header
        //
        rc1.dwMask = RASCM_UserName | RASCM_Password | RASCM_Domain;
        ASSERT( g_pRasGetCredentials );
        TRACE( "RasGetCredentials per-user" );
        dwErr = g_pRasGetCredentials(pszPath, pszEntryName, &rc1 );
        TRACE2( "RasGetCredentials=%d,m=%d", dwErr, rc1.dwMask );
        if (dwErr != NO_ERROR)
        {
            break;
        }

        // See 1a. in the function header comments
        //
        if (rc1.dwMask == 0)
        {
            dwErr = NO_ERROR;
            break;
        }

        // See 1b. in the function header comments
        //
        else if (rc1.dwMask & RASCM_DefaultCreds)
        {
            *pfGlobal = TRUE;

            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded by RasGetCredentials()
            //
            EncodePassword( rc1.szPassword );
            RasCredToDialParam(
                pszDefaultUserName,
                pszDefaultDomain,
                &rc1,
                pGlobal );

            dwErr = NO_ERROR;
            break;
        }

        // Look up global per-user cached username, password, domain.
        // See comment 2. in the function header
        //
        rc2.dwMask =  
            RASCM_UserName | RASCM_Password | RASCM_Domain | RASCM_DefaultCreds;
        ASSERT( g_pRasGetCredentials );
        TRACE( "RasGetCredentials global" );
        dwErr = g_pRasGetCredentials(pszPath, pszEntryName, &rc2 );
        TRACE2( "RasGetCredentials=%d,m=%d", dwErr, rc2.dwMask );
        if (dwErr != NO_ERROR)
        {
            break;
        }

        // See 2a. in the function header comments
        //
        if (rc2.dwMask & RASCM_DefaultCreds)
        {
            *pfGlobal = TRUE;

            if (rc1.dwMask & RASCM_Password)
            {
                *pfUser = TRUE;
            }

            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded by RasGetCredentials()
            //
            EncodePassword( rc1.szPassword );
            RasCredToDialParam(
                pszDefaultUserName,
                pszDefaultDomain,
                &rc1,
                pUser );

            EncodePassword( rc2.szPassword );
            RasCredToDialParam(
                pszDefaultUserName,
                pszDefaultDomain,
                &rc2,
                pGlobal );
        }

        // See 2b. in the function header comments
        //
        else
        {
            if (rc1.dwMask & RASCM_Password)
            {
                *pfUser = TRUE;
            }

            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded by RasGetCredentials()
            //
            EncodePassword( rc1.szPassword );
            RasCredToDialParam(
                pszDefaultUserName,
                pszDefaultDomain,
                &rc1,
                pUser );
        }

    }while (FALSE);

    // Cleanup
    //
    {
        // Whistler bug 254385 encode password when not being used
        //
        ZeroMemory( rc1.szPassword, sizeof(rc1.szPassword) );
        ZeroMemory( rc2.szPassword, sizeof(rc2.szPassword) );
    }

    return dwErr;
}

DWORD
FindEntryAndSetDialParams(
    IN DINFO* pInfo )

    // Look up the entry and fill in the RASDIALPARAMS parameters accordingly.
    // This routine contains all DINFO context initialization that can be
    // affected by user actions on the property sheet.  'PInfo' is the
    // partially initialized common dial dialog context.
    //
    // 'pInfo->fPrerequisiteDial'is set at entry if the prerequisite entry, if
    // any, should be dialed first.  If there is no prerequisite entry, the
    // flag is cleared and the main entry dialed.
    //
{
    DWORD dwErr;
    RASDIALPARAMS* prdp, *prdpu, *prdpg;

    if (pInfo->fFilePrereqOpen)
    {
        ClosePhonebookFile( pInfo->pFile );
        pInfo->pFile = pInfo->pFileMain;
        pInfo->fFilePrereqOpen = FALSE;
    }

    // Lookup entry node specified by caller and save reference for
    // convenience elsewhere.
    //
    pInfo->pNode = EntryNodeFromName(
        pInfo->pFile->pdtllistEntries, pInfo->pszEntry );
    if (!pInfo->pNode)
    {
        dwErr = ERROR_CANNOT_FIND_PHONEBOOK_ENTRY;
        return dwErr;
    }

    pInfo->pEntry = pInfo->pEntryMain = (PBENTRY* )DtlGetData( pInfo->pNode );
    ASSERT( pInfo->pEntry );

    // Switch to the prerequisite entry, if indicated.
    //
    if (pInfo->fPrerequisiteDial)
    {
        if (pInfo->pEntry->pszPrerequisiteEntry
            && *(pInfo->pEntry->pszPrerequisiteEntry))
        {
            ASSERT( !pInfo->fFilePrereqOpen );

            // GetPbkAndEntryName first looks in the All Users phonebook file
            // if a phonebook file is not specified.  If the entry is not
            // found there it looks in files present in the Users profile.
            // This needs to be done since we are discontinuing the per-user
            // pbk file being set through user preferences.
            //
            dwErr = GetPbkAndEntryName(
                    pInfo->pEntry->pszPrerequisitePbk,
                    pInfo->pEntry->pszPrerequisiteEntry,
                    0,
                    &pInfo->filePrereq,
                    &pInfo->pNode);

            if (dwErr != 0)
            {
                return dwErr;
            }

            pInfo->pFile = &pInfo->filePrereq;
            pInfo->fFilePrereqOpen = TRUE;

            pInfo->pEntry = (PBENTRY* )DtlGetData( pInfo->pNode );
            ASSERT( pInfo->pEntry );
        }
        else
        {
            pInfo->fPrerequisiteDial = FALSE;
        }
    }

    // Set up RasDial parameter blocks.
    //
    prdp = &pInfo->rdp;
    prdpu = &pInfo->rdpu;
    prdpg = &pInfo->rdpg;
    ZeroMemory( prdp, sizeof(*prdp) );
    pInfo->fUnattended = FALSE;
    prdp->dwSize = sizeof(*prdp);
    
    lstrcpyn( 
        prdp->szEntryName, 
        pInfo->pEntry->pszEntryName,  
        sizeof(prdp->szEntryName) / sizeof(TCHAR));

    if (pInfo->pszPhoneNumber)
    {
        lstrcpyn( 
            prdp->szPhoneNumber, 
            pInfo->pszPhoneNumber,
            RAS_MaxPhoneNumber + 1);
    }

    // Whistler bug 272819 Not prompted for callback number
    // We must do this before the init of per-user and global variants
    //
    if (!pInfo->fUnattended)
    {
        // '*' means "behave as defined in user preferences", while leaving it
        // zero would mean "don't request callback if server offers".
        //
        // Whistler bug 224074 use only lstrcpyn's to prevent maliciousness
        //
        lstrcpyn(
            prdp->szCallbackNumber,
            TEXT("*"),
            sizeof(prdp->szCallbackNumber) / sizeof(TCHAR) );
    }

    // Initialze the per-user and global variants
    //
    CopyMemory(prdpu, prdp, sizeof(*prdp));
    CopyMemory(prdpg, prdp, sizeof(*prdp));

    // Set the subentry link to whatever the RasDialDlg caller specified.  See
    // bug 200351.
    //
    prdp->dwSubEntry = pInfo->pArgs->dwSubEntry;

    // If running in "unattended" mode, i.e. called by RASAUTO to redial on
    // link failure, read the user/password/domain and callback number used on
    // the original call.  (Actually found a use for the crappy
    // RasGetEntryDialParams API)
    //
    if (pInfo->pArgs->dwFlags & RASDDFLAG_LinkFailure)
    {
        RASDIALPARAMS rdp;
        BOOL fSavedPw = HaveSavedPw( pInfo );

        ZeroMemory( &rdp, sizeof(rdp) );
        rdp.dwSize = sizeof(rdp);
        
        lstrcpyn( 
            rdp.szEntryName, 
            pInfo->pEntry->pszEntryName,
            sizeof(rdp.szEntryName) / sizeof(TCHAR) 
            );

        //For whistler bug 313509			gangz
        //We use FindEntryCredentials() to get saved password perUser and 
        //perConnection inforation, use RasGetEntryDialParams() to get back
        //Callback Numbers
        //
       {
            RASDIALPARAMS rdTemp; 
            TCHAR * pszTempUser, * pszTempDomain;
            DWORD dwErr = NO_ERROR;

            pszTempUser = pszTempDomain = NULL;
            dwErr = FindEntryCredentials(
                        pInfo->pFile->pszPath,
                        pInfo->pEntry->pszEntryName,
                        pszTempUser,
                        pszTempDomain,                    
                        &rdTemp,
                        &rdTemp,
                        &(pInfo->fHaveSavedPwUser),
                        &(pInfo->fHaveSavedPwGlobal));

             ZeroMemory( &rdTemp, sizeof(rdTemp) );
             Free0(pszTempUser);
             Free0(pszTempDomain);
        }
        
        
        TRACE( "RasGetEntryDialParams" );
        ASSERT( g_pRasGetEntryDialParams );
        dwErr = g_pRasGetEntryDialParams(
            pInfo->pFile->pszPath, &rdp, &fSavedPw );
        TRACE2( "RasGetEntryDialParams=%d,f=%d", dwErr, &fSavedPw );
        TRACEW1( "u=%s", rdp.szUserName );
        //TRACEW1( "p=%s", rdp.szPassword );
        TRACEW1( "d=%s", rdp.szDomain );
        TRACEW1( "c=%s", rdp.szCallbackNumber );

        if (dwErr == 0)
        {
            lstrcpyn( 
                prdp->szUserName, 
                rdp.szUserName,
                sizeof(prdp->szUserName) / sizeof(TCHAR));

            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded by RasGetEntryDialParams()
            //
            lstrcpyn(
                prdp->szPassword,
                rdp.szPassword,
                sizeof(prdp->szPassword) / sizeof(TCHAR) );
            EncodePassword( prdp->szPassword );

            lstrcpyn( 
                prdp->szDomain, 
                rdp.szDomain,
                sizeof(prdp->szDomain) / sizeof(TCHAR));
            lstrcpyn( 
                prdp->szCallbackNumber, 
                rdp.szCallbackNumber,
                sizeof(prdp->szCallbackNumber) / sizeof(TCHAR));

			
            pInfo->fUnattended = TRUE;
        }

        ZeroMemory( rdp.szPassword, sizeof(rdp.szPassword) );
    }

    if (pInfo->pNoUser)
    {
        // Use the credentials we got from API caller, presumably the ones
        // entered at Ctrl-Alt-Del.
        //
        lstrcpyn( 
            prdp->szUserName, 
            pInfo->pNoUser->szUserName,
            sizeof(prdp->szUserName) / sizeof(TCHAR));

        //
        // Don't copy the password if its smartcard logon 
        // and the entry being used is a non-eap connectoid
        //
        // Whistler bug 254385 encode password when not being used
        // Assumed password was encoded by caller of RasDialDlg()
        //
        DecodePassword( pInfo->pNoUser->szPassword );
        
        lstrcpyn(
            prdp->szPassword,
            pInfo->pNoUser->szPassword,
            sizeof(prdp->szPassword) / sizeof(TCHAR) );
        EncodePassword( pInfo->pNoUser->szPassword );
        EncodePassword( prdp->szPassword );

        if (pInfo->pEntry->fPreviewDomain)
        {
            lstrcpyn( 
                prdp->szDomain, 
                pInfo->pNoUser->szDomain,
                sizeof(prdp->szDomain) / sizeof(TCHAR));
        }
        else
        {
            // Don't use Winlogon domain unless "include domain" option is
            // selected.  See bug 387266.
            //
            // Whistler bug 224074 use only lstrcpyn's to prevent maliciousness
            //
            lstrcpyn(
                prdp->szDomain,
                TEXT(""),
                sizeof(prdp->szDomain) / sizeof(TCHAR) );
        }
    }
    else if (!pInfo->fUnattended)
    {
        DWORD dwErrRc;
        BOOL fUseLogonDomain;
        TCHAR* pszDefaultUser;

        dwErrRc = FindEntryCredentials(
                    pInfo->pFile->pszPath,
                    pInfo->pEntry->pszEntryName,
                    pInfo->pEntry->pszOldUser,
                    pInfo->pEntry->pszOldDomain,                    
                    prdpu,
                    prdpg,
                    &(pInfo->fHaveSavedPwUser),
                    &(pInfo->fHaveSavedPwGlobal));

        if (! pInfo->pEntry->fAutoLogon)
        {
            // If saved passwords are disabled, clear here
            //
            if (pInfo->fDisableSavePw)
            {
                pInfo->fHaveSavedPwUser = FALSE;
                pInfo->fHaveSavedPwGlobal = FALSE;
                ZeroMemory(prdp->szPassword, sizeof(prdp->szPassword));
                ZeroMemory(prdpu->szPassword, sizeof(prdpu->szPassword));
                ZeroMemory(prdpg->szPassword, sizeof(prdpg->szPassword));
            }

            // If including domains is disabled, clear here
            //
            if (! pInfo->pEntry->fPreviewDomain)
            {
                // (SteveC) Don't do this in the 'fAutoLogon' case.  See bug
                // 207611.
                //
                ZeroMemory(prdp->szDomain, sizeof(prdp->szDomain));
                ZeroMemory(prdpu->szDomain, sizeof(prdpu->szDomain));
                ZeroMemory(prdpg->szDomain, sizeof(prdpg->szDomain));
            }
        }
        
        if(!pInfo->pEntry->fAutoLogon)
        {
            // Initialize the dial params that will be passed to RasDial.
            //
            // Note that per-user credentials are always used when both 
            // per-user and global credentials are saved.  The per-user
            // credentials should be copied even if there is no saved 
            // password since there may be a saved identity.
            //
            CopyMemory(prdp, prdpu, sizeof(*prdp));
            if (pInfo->fHaveSavedPwGlobal && !pInfo->fHaveSavedPwUser)
            {
                CopyMemory(prdp, prdpg, sizeof(*prdp));
            }
        }
    }

    return 0;
}

//----------------------------------------------------------------------------
// Bundling Errors dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
BundlingErrorsDlg(
    IN OUT DPINFO* pInfo )

    // Popup the Bundling Errors dialog.  'PInfo' is the dialing progress
    // dialog context.
    //
    // Returns true if user chooses to accept the results or false if he
    // chooses to hang up.
    //
{
    INT_PTR nStatus;

    TRACE( "BundlingErrorsDlg" );

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_BE_BundlingErrors ),
            pInfo->hwndDlg,
            BeDlgProc,
            (LPARAM )pInfo );

    if (nStatus == -1)
    {
        ErrorDlg( pInfo->hwndDlg, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
        nStatus = FALSE;
    }

    return (BOOL )nStatus;
}


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

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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return BeInit( hwnd, (DPINFO* )lparam );
        }

        case WM_COMMAND:
        {
            return BeCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
BeCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    DWORD dwErr;

    TRACE3( "BeCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case IDOK:
        case IDCANCEL:
        {
            TRACE1( "%s pressed", (wId==IDOK) ? "OK" : "Cancel" );

            if (IsDlgButtonChecked( hwnd, CID_BE_CB_DisableLink ))
            {
                DWORD i;
                DPINFO* pInfo;
                DPSTATE* pState;

                // Caller says to delete the links that failed in the entry.
                // Create a list of Psz nodes containing the unique port name
                // of each failed link so they can be removed after the state
                // information is freed.
                //
                pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );

                for (i = 0, pState = pInfo->pStates;
                     i < pInfo->cStates;
                     ++i, ++pState)
                {
                    DTLNODE* pNode;
                    DTLNODE* pNodePtd;
                    PBLINK* pLink;

                    if (pState->dwError != 0)
                    {
                        if (!pInfo->pArgs->pListPortsToDelete)
                        {
                            pInfo->pArgs->pListPortsToDelete =
                                DtlCreateList( 0L );
                            if (!pInfo->pArgs->pListPortsToDelete)
                            {
                                continue;
                            }
                        }

                        pNode = DtlNodeFromIndex(
                            pInfo->pArgs->pEntry->pdtllistLinks, (LONG )i );
                        if (!pNode)
                        {
                            continue;
                        }

                        pLink = (PBLINK* )DtlGetData( pNode );

                        pNodePtd = CreatePszNode( pLink->pbport.pszPort );
                        if (!pNodePtd)
                        {
                            continue;
                        }

                        DtlAddNodeLast(
                            pInfo->pArgs->pListPortsToDelete, pNodePtd );
                    }
                }
            }

            EndDialog( hwnd, (wId == IDOK) );
            return TRUE;
        }
    }

    return FALSE;
}


VOID
BeFillLvErrors(
    IN HWND hwndLv,
    IN DPINFO* pInfo )

    // Fill the listview 'hwndLv' with devices and error strings and select
    // the first item.  'PInfo' is the dialing progress dialog context.
    //
{
    INT iItem;
    DWORD i;
    DPSTATE* pState;

    TRACE( "BeFillLvErrors" );

    ListView_DeleteAllItems( hwndLv );

    // Add columns.
    //
    {
        LV_COLUMN col;
        TCHAR* pszHeader0;
        TCHAR* pszHeader1;

        pszHeader0 = PszFromId( g_hinstDll, SID_DeviceColHead );
        pszHeader1 = PszFromId( g_hinstDll, SID_StatusColHead );

        ZeroMemory( &col, sizeof(col) );
        col.mask = LVCF_FMT + LVCF_TEXT;
        col.fmt = LVCFMT_LEFT;
        col.pszText = (pszHeader0) ? pszHeader0 : TEXT("");
        ListView_InsertColumn( hwndLv, 0, &col );

        ZeroMemory( &col, sizeof(col) );
        col.mask = LVCF_FMT + LVCF_SUBITEM + LVCF_TEXT;
        col.fmt = LVCFMT_LEFT;
        col.pszText = (pszHeader1) ? pszHeader1 : TEXT("");
        col.iSubItem = 1;
        ListView_InsertColumn( hwndLv, 1, &col );

        Free0( pszHeader0 );
        Free0( pszHeader1 );
    }

    // Add the modem and adapter images.
    //
    ListView_SetDeviceImageList( hwndLv, g_hinstDll );

    // Load listview with device/status pairs.
    //
    iItem = 0;
    for (i = 0, pState = pInfo->pStates; i < pInfo->cStates; ++i, ++pState)
    {
        LV_ITEM item;
        DTLNODE* pNode;
        PBLINK* pLink;
        TCHAR* psz;

        pNode = DtlNodeFromIndex(
            pInfo->pArgs->pEntry->pdtllistLinks, (LONG )i );
        if (pNode)
        {
            pLink = (PBLINK* )DtlGetData( pNode );

            psz = DisplayPszFromDeviceAndPort(
                      pLink->pbport.pszDevice, pLink->pbport.pszPort );
            if (psz)
            {
                ZeroMemory( &item, sizeof(item) );
                item.mask = LVIF_TEXT + LVIF_IMAGE;
                item.iItem = iItem;
                item.pszText = psz;
                item.iImage =
                    (pLink->pbport.pbdevicetype == PBDT_Modem)
                        ? DI_Modem : DI_Adapter;
                ListView_InsertItem( hwndLv, &item );
                Free( psz );

                if (pState->dwError == 0)
                {
                    psz = PszFromId( g_hinstDll, SID_Connected );
                    ListView_SetItemText( hwndLv, iItem, 1, psz );
                    Free( psz );
                }
                else
                {
                    psz = BeGetErrorPsz( pState->dwError );
                    ListView_SetItemText( hwndLv, iItem, 1, psz );
                    LocalFree( psz );
                }

                ++iItem;
            }
        }
    }

    // Auto-size columns to look good with the text they contain.
    //
    ListView_SetColumnWidth( hwndLv, 0, LVSCW_AUTOSIZE_USEHEADER );
    ListView_SetColumnWidth( hwndLv, 1, LVSCW_AUTOSIZE_USEHEADER );

    // Select the first item.
    //
    ListView_SetItemState( hwndLv, 0, LVIS_SELECTED, LVIS_SELECTED );
}


TCHAR*
BeGetErrorPsz(
    IN DWORD dwError )

    // Returns a string suitable for the Status column with error 'dwError' or
    // NULL on error.  'DwError' is assumed to be non-0.  It is caller's
    // responsiblility to LocalFree the returned string.
    //
{
    TCHAR* pszErrStr;
    TCHAR szErrNumBuf[ MAXLTOTLEN + 1 ];
    TCHAR* pszLineFormat;
    TCHAR* pszLine;
    TCHAR* apszArgs[ 2 ];

    LToT( dwError, szErrNumBuf, 10 );

    pszErrStr = NULL;
    GetErrorText( dwError, &pszErrStr );

    pszLine = NULL;
    pszLineFormat = PszFromId( g_hinstDll, SID_FMT_Error );
    if (pszLineFormat)
    {
        apszArgs[ 0 ] = szErrNumBuf;
        apszArgs[ 1 ] = (pszErrStr) ? pszErrStr : TEXT("");

        FormatMessage(
            FORMAT_MESSAGE_FROM_STRING
                | FORMAT_MESSAGE_ALLOCATE_BUFFER
                | FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pszLineFormat, 0, 0, (LPTSTR )&pszLine, 1,
            (va_list* )apszArgs );

        Free( pszLineFormat );
    }

    Free0( pszErrStr );
    return pszLine;
}


BOOL
BeInit(
    IN HWND hwndDlg,
    IN DPINFO* pArgs )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PArgs' is caller's arguments to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    HWND hwndLvErrors;
    HWND hwndCbDisableLink;

    TRACE( "BeInit" );

    hwndLvErrors = GetDlgItem( hwndDlg, CID_BE_LV_Errors );
    ASSERT( hwndLvErrors );
    hwndCbDisableLink = GetDlgItem( hwndDlg, CID_BE_CB_DisableLink );
    ASSERT( hwndCbDisableLink );

    // Save Dial Progress context as dialog context.
    //
    SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pArgs );

    // Load listview with device/error information.
    //
    BeFillLvErrors( hwndLvErrors, pArgs );

    // Display the finished window above all other windows.  The window
    // position is set to "topmost" then immediately set to "not topmost"
    // because we want it on top but not always-on-top.  Always-on-top alone
    // is incredibly annoying, e.g. it is always on top of the on-line help if
    // user presses the Help button.
    //
    SetWindowPos(
        hwndDlg, HWND_TOPMOST, 0, 0, 0, 0,
        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );

    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    ShowWindow( hwndDlg, SW_SHOW );

    SetWindowPos(
        hwndDlg, HWND_NOTOPMOST, 0, 0, 0, 0,
        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );

    SetFocus( hwndCbDisableLink );
    return FALSE;
}


LVXDRAWINFO*
BeLvErrorsCallback(
    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 =
        { 2, 0, LVXDI_Blend50Dis + LVXDI_DxFill, { 0, 0 } };

    return &info;
}


//----------------------------------------------------------------------------
// Change Password dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
ChangePasswordDlg(
    IN HWND hwndOwner,
    IN BOOL fOldPassword,
    OUT TCHAR* pszOldPassword,
    OUT TCHAR* pszNewPassword )

    // Popup the Change Password dialog.  'HwndOwner' is the owning window.
    // 'FOldPassword' is set true if user must supply an old password, false
    // if no old password is required.  'PszOldPassword' and 'pszNewPassword'
    // are caller's buffers for the returned passwords.
    //
    // Returns true if user presses OK and succeeds, false otherwise.
    //
{
    INT_PTR nStatus;
    CPARGS args;

    TRACE( "ChangePasswordDlg" );

    args.fOldPassword = fOldPassword;
    args.pszOldPassword = pszOldPassword;
    args.pszNewPassword = pszNewPassword;

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            (fOldPassword)
                ? MAKEINTRESOURCE( DID_CP_ChangePassword2 )
                : MAKEINTRESOURCE( DID_CP_ChangePassword ),
            hwndOwner,
            CpDlgProc,
            (LPARAM )&args );

    if (nStatus == -1)
    {
        ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
        nStatus = FALSE;
    }

    return (BOOL )nStatus;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return CpInit( hwnd, (CPARGS* )lparam );
        }

        case WM_HELP:
        case WM_CONTEXTMENU:
        {
            ContextHelp( g_adwCpHelp, hwnd, unMsg, wparam, lparam );
            break;
        }

        case WM_COMMAND:
        {
            return CpCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
CpCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    DWORD dwErr;

    TRACE3( "CpCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case IDOK:
        {
            CPWINFO* pInfo;
            TCHAR szNewPassword[ PWLEN + 1 ];
            TCHAR szNewPassword2[ PWLEN + 1 ];

            TRACE( "OK pressed" );

            pInfo = (CPWINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            szNewPassword[ 0 ] = TEXT('\0');
            GetWindowText(
                pInfo->hwndEbNewPassword, szNewPassword, PWLEN + 1 );
            szNewPassword2[ 0 ] = TEXT('\0');
            GetWindowText(
                pInfo->hwndEbNewPassword2, szNewPassword2, PWLEN + 1 );

            if (lstrcmp( szNewPassword, szNewPassword2 ) != 0)
            {
                // The two passwords don't match, i.e. user made a typo.  Make
                // him re-enter.
                //
                MsgDlg( hwnd, SID_PasswordsDontMatch, NULL );
                SetWindowText( pInfo->hwndEbNewPassword, TEXT("") );
                SetWindowText( pInfo->hwndEbNewPassword2, TEXT("") );
                SetFocus( pInfo->hwndEbNewPassword );
                ZeroMemory( szNewPassword, sizeof(szNewPassword) );
                ZeroMemory( szNewPassword2, sizeof(szNewPassword2) );
                return TRUE;
            }

            if (pInfo->pArgs->fOldPassword)
            {
                pInfo->pArgs->pszOldPassword[ 0 ] = TEXT('\0');

                // Whistler bug 254385 encode password when not being used
                // Assumed password was not encoded by GetWindowText()
                //
                GetWindowText(
                    pInfo->hwndEbOldPassword,
                    pInfo->pArgs->pszOldPassword,
                    PWLEN + 1 );
                EncodePassword( pInfo->pArgs->pszOldPassword );
            }

            // Whistler bug 224074 use only lstrcpyn's to prevent maliciousness
            //
            // pInfo->pArgs->pszNewPassword points back to RASDIALPARAMS->
            // szPassword[ PWLEN + 1 ]
            //
            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded by GetWindowText()
            //
            lstrcpyn(
                pInfo->pArgs->pszNewPassword,
                szNewPassword,
                PWLEN + 1 );
            EncodePassword( pInfo->pArgs->pszNewPassword );
            ZeroMemory( szNewPassword, sizeof(szNewPassword) );
            ZeroMemory( szNewPassword2, sizeof(szNewPassword2) );
            EndDialog( hwnd, TRUE );
            return TRUE;
        }

        case IDCANCEL:
        {
            TRACE( "Cancel pressed" );
            EndDialog( hwnd, FALSE );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
CpInit(
    IN HWND hwndDlg,
    IN CPARGS* pArgs )

    // Called on WM_INITDIALOG.  'HwndDlg' is the handle of the dialog window.
    // 'PArgs' is caller's arguments to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    CPWINFO* pInfo;

    TRACE( "CpInit" );

    // Allocate the dialog context block.  Initialize minimally for proper
    // cleanup, then attach to the dialog window.
    //
    {
        pInfo = Malloc( sizeof(*pInfo) );
        if (!pInfo)
        {
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

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

        SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
        TRACE( "Context set" );
    }

    if (pArgs->fOldPassword)
    {
        pInfo->hwndEbOldPassword =
            GetDlgItem( hwndDlg, CID_CP_EB_OldPassword );
        ASSERT( pInfo->hwndEbOldPassword );
        Edit_LimitText( pInfo->hwndEbOldPassword, PWLEN );
    }
    pInfo->hwndEbNewPassword =
        GetDlgItem( hwndDlg, CID_CP_EB_Password );
    ASSERT( pInfo->hwndEbNewPassword );
    Edit_LimitText( pInfo->hwndEbNewPassword, PWLEN );

    pInfo->hwndEbNewPassword2 =
        GetDlgItem( hwndDlg, CID_CP_EB_ConfirmPassword );
    ASSERT( pInfo->hwndEbNewPassword2 );
    Edit_LimitText( pInfo->hwndEbNewPassword2, PWLEN );

    // Add context help button to title bar.
    //
    AddContextHelpButton( hwndDlg );

    // Display finished window.
    //
    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    SetForegroundWindow( hwndDlg );

    return TRUE;
}


//----------------------------------------------------------------------------
// Connect Complete dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

VOID
ConnectCompleteDlg(
    IN HWND hwndOwner,
    IN DINFO* pInfo )

    // Popup the connection complete dialog.  'HwndOwner' is the owning
    // window.  'PUser' is the user preferences.
    //
{
    INT_PTR nStatus;

    TRACE( "ConnectCompleteDlg" );

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_CC_ConnectComplete ),
            hwndOwner,
            CcDlgProc,
            (LPARAM )pInfo );
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return CcInit( hwnd, (DINFO* )lparam );
        }

        case WM_COMMAND:
        {
            return CcCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }

        case WM_DESTROY:
            //For whistler bug 372078
            //GetCurrentIconEntryType() loads Icon from netshell where the icon is loaded
            //by LoadImage() without LR_SHARED, so I have to destroy it when we are done
            //with it
            //
            {
                HICON hIcon=NULL;
                hIcon = (HICON)SendMessage( GetDlgItem( hwnd, CID_CC_I_Rasmon ),
                                     STM_GETICON,
                                     (WPARAM)0,
                                     (LPARAM)0);
                
                ASSERT(hIcon);
                if( hIcon )
                {
                    DestroyIcon(hIcon);
                }
                else
                {
                    TRACE("CcDlgProc:Destroy Icon");
                }
            }
            
            break;
        
    }

    return FALSE;
}


BOOL
CcCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    TRACE3( "CcCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case IDOK:
        {
            DINFO * pInfo = (DINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            PBUSER* pUser = pInfo->pUser;
            ASSERT( pUser );

            if (IsDlgButtonChecked( hwnd, CID_CC_CB_SkipMessage ))
            {
                pUser->fSkipConnectComplete = TRUE;
                pUser->fDirty = TRUE;
            }
        }

        // ...fall thru...

        case IDCANCEL:
        {
            EndDialog( hwnd, TRUE );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
CcInit(
    IN HWND hwndDlg,
    IN DINFO* pInfo )

    // Called on WM_INITDIALOG.  'HwndDlg' is the handle of dialog.  'PUser'
    // is caller's argument to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    TRACE( "CcInit" );

    // Set the dialog context.
    //
    SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );

    // Set the explanatory text.
    //
    {
        MSGARGS msgargs;

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

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

        if (msgargs.pszOutput)
        {
            SetDlgItemText( hwndDlg, CID_CC_ST_Text, msgargs.pszOutput );
            Free( msgargs.pszOutput );
        }
    }

    // Set the correct icon.    For whistler bug 372078
    //  
    
    SetIconFromEntryType(
        GetDlgItem( hwndDlg, CID_CC_I_Rasmon ),
        pInfo->pEntry->dwType,
        FALSE); //FALSE means Large Icon
    
    // Display finished window.
    //
    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    SetForegroundWindow( hwndDlg );

    return TRUE;
}


//----------------------------------------------------------------------------
// Dial Callback dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
DialCallbackDlg(
    IN HWND hwndOwner,
    IN OUT TCHAR* pszNumber )

    // Popup the Dial Callback dialog.  'HwndOwner' is the owning window.
    // 'PszNumber' is caller's buffer for the number of the local machine that
    // the server will be told to callback.  It contains the default number on
    // entry and the user-edited number on exit.
    //
    // Returns true if user OK and succeeds, false if Cancel or error.
    //
{
    INT_PTR nStatus;

    TRACE( "DialCallbackDlg" );

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_DC_DialCallback ),
            hwndOwner,
            DcDlgProc,
            (LPARAM )pszNumber );

    if (nStatus == -1)
    {
        ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
        nStatus = FALSE;
    }

    return (BOOL )nStatus;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return DcInit( hwnd, (TCHAR* )lparam );
        }

        case WM_HELP:
        case WM_CONTEXTMENU:
        {
            ContextHelp( g_adwDcHelp, hwnd, unMsg, wparam, lparam );
            break;
        }

        case WM_COMMAND:
        {
            return DcCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
DcCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    DWORD dwErr;

    TRACE3( "DcCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case IDOK:
        {
            BOOL fStatus;
            HWND hwndEbNumber;
            TCHAR* pszNumber;

            TRACE( "OK pressed" );

            hwndEbNumber = GetDlgItem( hwnd, CID_DC_EB_Number );
            ASSERT( hwndEbNumber );
            pszNumber = (TCHAR* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pszNumber );
            GetWindowText( hwndEbNumber, pszNumber, RAS_MaxCallbackNumber + 1 );

            if (IsAllWhite( pszNumber ))
            {
                // OK with blank callback number is same as Cancel.
                //
                TRACE( "Blank number cancel" );
                fStatus = FALSE;
            }
            else
            {
                fStatus = TRUE;
            }

            EndDialog( hwnd, fStatus );
            return TRUE;
        }

        case IDCANCEL:
        {
            TRACE( "Cancel pressed" );
            EndDialog( hwnd, FALSE );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
DcInit(
    IN HWND hwndDlg,
    IN TCHAR* pszNumber )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PszNumber' is the callback number.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    HWND hwndEbNumber;

    TRACE( "DcInit" );

    // Stash address of caller's buffer for OK processing.
    //
    ASSERT( pszNumber );
    SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pszNumber );

    // Initialize edit field to caller's default.
    //
    hwndEbNumber = GetDlgItem( hwndDlg, CID_DC_EB_Number );
    ASSERT( hwndEbNumber );
    Edit_LimitText( hwndEbNumber, RAS_MaxCallbackNumber );
    SetWindowText( hwndEbNumber, pszNumber );

    // Add context help button to title bar.
    //
    AddContextHelpButton( hwndDlg );

    // Display finished window.
    //
    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    SetForegroundWindow( hwndDlg );

    return TRUE;
}


//----------------------------------------------------------------------------
// Dial Error dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
DialErrorDlg(
    IN HWND hwndOwner,
    IN TCHAR* pszEntry,
    IN DWORD dwError,
    IN DWORD sidState,
    IN TCHAR* pszStatusArg,
    IN DWORD sidFormatMsg,
    IN TCHAR* pszFormatArg,
    IN LONG lRedialCountdown,
    IN BOOL fPopupOnTop )

    // Popup the Dial Error dialog.  'HwndOwner' is the owning window.
    // 'PszEntry' is the entry being dialed.  'DwError' is the error that
    // occurred or 0 if redialing after a link failure.  'sidStatusArg' is the
    // argument to the 'sidState' 'SidState' is the string ID of the dial
    // state executing when the error occurred.  string or NULL if none.
    // 'SidFormatMsg' is the string containing the format of the error message
    // or 0 to use the default.  'PszFormatArg' is the additional argument to
    // the format message or NULL if none.  'LRedialCountdown' is the number
    // of seconds before auto-redial, or -1 to disable countdown, or -2 to
    // hide the "Redial" button entirely.  'FPopupOnTop' indicates the status
    // window should be brought to the front when redialing.
    //
    // Returns true if user chooses to redial or lets it timeout, false if
    // cancels.
    //
{
    INT_PTR nStatus;
    DEARGS args;

    TRACE( "DialErrorDlg" );

    args.pszEntry = pszEntry;
    args.dwError = dwError;
    args.sidState = sidState;
    args.pszStatusArg = pszStatusArg;
    args.sidFormatMsg = sidFormatMsg;
    args.pszFormatArg = pszFormatArg;
    args.lRedialCountdown = lRedialCountdown;
    args.fPopupOnTop = fPopupOnTop;

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_DE_DialError ),
            hwndOwner,
            DeDlgProc,
            (LPARAM )&args );

    if (nStatus == -1)
    {
        ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
        nStatus = FALSE;
    }

    return (BOOL )nStatus;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return DeInit( hwnd, (DEARGS* )lparam );
        }

        case WM_HELP:
        case WM_CONTEXTMENU:
        {
            ContextHelp( g_adwDeHelp, hwnd, unMsg, wparam, lparam );
            break;
        }

        case WM_COMMAND:
        {
            return DeCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }

        case WM_TIMER:
        {
            DEINFO* pInfo = (DEINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            KillTimer( pInfo->hwndDlg, 1 );
            if (pInfo->lRedialCountdown > 0)
            {
                --pInfo->lRedialCountdown;
            }

            DeAdjustPbRedial( pInfo );

            if (pInfo->lRedialCountdown == 0)
            {
                // Fake a press of the Redial button.  Note that BM_CLICK
                // cannot be used because it doesn't generate the WM_COMMAND
                // when the thread is not the foreground window, due to
                // SetCapture use and restriction.
                //
                SendMessage( pInfo->hwndDlg, WM_COMMAND,
                    MAKEWPARAM( IDOK, BN_CLICKED ),
                    (LPARAM )pInfo->hwndPbRedial );
            }
            else
            {
                SetTimer( pInfo->hwndDlg, 1, 1000L, NULL );
            }

            return TRUE;
        }

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

    return FALSE;
}


VOID
DeAdjustPbRedial(
    IN DEINFO* pInfo )

    // Set the label of the Redial button or disable it as indicated by the
    // redial countdown.  If enabled, the button shows the number of seconds
    // to auto-redial unless this is not the final redial.  'PInfo' is the
    // dialog context block.
    //
{
    TCHAR* psz;

    if (pInfo->lRedialCountdown == -2)
    {
        // Redial button is to be hidden.  See bug 230594.
        //
        SetFocus( pInfo->hwndPbCancel );
        ShowWindow( pInfo->hwndPbRedial, SW_HIDE );
        EnableWindow( pInfo->hwndPbRedial, FALSE );
    }
    else
    {
        // Go ahead and change the label "Redial" or "Redial=%d" as
        // appropriate.
        //
        psz = PszFromId( g_hinstDll, SID_RedialLabel );
        if (psz)
        {
            TCHAR szBuf[ 128 ];

            lstrcpyn( 
                szBuf, 
                psz, 
                (sizeof(szBuf) / sizeof(TCHAR)) - 4);
            Free( psz );

            if (pInfo->lRedialCountdown >= 0)
            {
                TCHAR szNum[ MAXLTOTLEN + 1 ];
                DWORD dwLen, dwSize = sizeof(szBuf)/sizeof(TCHAR);
                LToT( pInfo->lRedialCountdown, szNum, 10 );
                lstrcat( szBuf, TEXT(" = ") );
                dwLen = lstrlen(szBuf) + 1;
                lstrcpyn( szBuf + (dwLen - 1), szNum, dwSize - dwLen );
            }

            SetWindowText( pInfo->hwndPbRedial, szBuf );
        }
    }
}


BOOL
DeCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    DWORD dwErr;

    TRACE3( "DeCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    TRACE2("Current proces:(0x%d), Current Thread:(0x%d)",
            GetCurrentProcessId(),
            GetCurrentThreadId());

    switch (wId)
    {
        case IDOK:
        {
            TRACE( "Redial pressed" );
            EndDialog( hwnd, TRUE );
            return TRUE;
        }

        case IDCANCEL:
        {
            TRACE( "Cancel pressed" );
            EndDialog( hwnd, FALSE );
            return TRUE;
        }

        case CID_DE_PB_More:
        {
            DEINFO* pInfo;
            DWORD dwContext;

            pInfo = (DEINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            if (pInfo->pArgs->dwError >= RASBASE
                && pInfo->pArgs->dwError <= RASBASEEND)
            {
                dwContext = HID_RASERRORBASE - RASBASE + pInfo->pArgs->dwError;
            }
            else if (pInfo->pArgs->dwError == 0)
            {
                dwContext = HID_RECONNECTING;
            }
            else
            {
                dwContext = HID_NONRASERROR;
            }

            WinHelp( hwnd, g_pszHelpFile, HELP_CONTEXTPOPUP, dwContext );
        }
    }

    return FALSE;
}


BOOL
DeInit(
    IN HWND hwndDlg,
    IN DEARGS* pArgs )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PArgs' is caller's arguments to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    DEINFO* pInfo;

    TRACE( "DeInit" );

    // Allocate the dialog context block.  Initialize minimally for proper
    // cleanup, then attach to the dialog window.
    //
    {
        pInfo = Malloc( sizeof(*pInfo) );
        if (!pInfo)
        {
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

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

        SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
        TRACE( "Context set" );
    }

    pInfo->hwndStText = GetDlgItem( hwndDlg, CID_DE_ST_Text );
    ASSERT( pInfo->hwndStText );
    pInfo->hwndPbRedial = GetDlgItem( hwndDlg, IDOK );
    ASSERT( pInfo->hwndPbRedial );
    pInfo->hwndPbCancel = GetDlgItem( hwndDlg, IDCANCEL );
    ASSERT( pInfo->hwndPbCancel );
    pInfo->hwndPbMore = GetDlgItem( hwndDlg, CID_DE_PB_More );
    ASSERT( pInfo->hwndPbMore );

    // Hide/disable "more info" button if WinHelp won't work.  See
    // common\uiutil\ui.c.
    //
    {
        extern BOOL g_fNoWinHelp;

        if (g_fNoWinHelp)
        {
            ShowWindow( pInfo->hwndPbMore, SW_HIDE );
            EnableWindow( pInfo->hwndPbMore, FALSE );
        }
    }

    if (pArgs->dwError == 0)
    {
        TCHAR* pszFormat;
        TCHAR* psz;
        TCHAR* apszArgs[ 1 ];

        // Redialing on link failure.  Set title to "Dial-Up Networking".
        //
        psz = PszFromId( g_hinstDll, SID_PopupTitle );
        if (psz)
        {
            SetWindowText( hwndDlg, psz );
            Free( psz );
        }

        // Set static placeholder text control to "Link to <entry> failed.
        // Reconnect pending...".
        //
        pszFormat = PszFromId( g_hinstDll, SID_DE_LinkFailed );
        if (pszFormat)
        {
            apszArgs[ 0 ] = pArgs->pszEntry;
            psz = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszFormat, 0, 0, (LPTSTR )&psz, 1,
                (va_list* )apszArgs );

            Free( pszFormat );

            if (psz)
            {
                SetWindowText( pInfo->hwndStText, psz );
                LocalFree( psz );
            }
        }
    }
    else
    {
        TCHAR* pszTitleFormat;
        TCHAR* pszTitle;
        TCHAR* apszArgs[ 1 ];
        ERRORARGS args;

        // Set title to "Error Connecting to <entry>".
        //
        pszTitleFormat = GetText( hwndDlg );
        if (pszTitleFormat)
        {
            apszArgs[ 0 ] = pArgs->pszEntry;
            pszTitle = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszTitleFormat, 0, 0, (LPTSTR )&pszTitle, 1,
                (va_list* )apszArgs );

            Free( pszTitleFormat );

            if (pszTitle)
            {
                SetWindowText( hwndDlg, pszTitle );
                LocalFree( pszTitle );
            }
        }

        // Build the error text and load it into the placeholder text control.
        //
        ZeroMemory( &args, sizeof(args) );
        if (pArgs->pszStatusArg)
            args.apszOpArgs[ 0 ] = pArgs->pszStatusArg;
        if (pArgs->pszFormatArg)
            args.apszAuxFmtArgs[ 0 ] = pArgs->pszFormatArg;
        args.fStringOutput = TRUE;

        ErrorDlgUtil( hwndDlg,
            pArgs->sidState, pArgs->dwError, &args, g_hinstDll, 0,
            (pArgs->sidFormatMsg) ? pArgs->sidFormatMsg : SID_FMT_ErrorMsg );

        if (args.pszOutput)
        {
            SetWindowText( pInfo->hwndStText, args.pszOutput );
            LocalFree( args.pszOutput );
        }
    }

    // Stretch the dialog window to a vertical size appropriate for the text
    // we loaded.
    //
    {
        HDC hdc;
        RECT rect;
        RECT rectNew;
        HFONT hfont;
        LONG dyGrow;
        TCHAR* psz;

        psz = GetText( pInfo->hwndStText );
        if (psz)
        {
            GetClientRect( pInfo->hwndStText, &rect );
            hdc = GetDC( pInfo->hwndStText );

            if(NULL != hdc)
            {

                hfont = (HFONT )SendMessage( pInfo->hwndStText, 
                                            WM_GETFONT, 0, 0 );
                if (hfont)
                {
                    SelectObject( hdc, hfont );
                }

                rectNew = rect;
                DrawText( hdc, psz, -1, &rectNew,
                    DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS | DT_NOPREFIX );
                ReleaseDC( pInfo->hwndStText, hdc );
            }

            dyGrow = rectNew.bottom - rect.bottom;
            ExpandWindow( pInfo->hwndDlg, 0, dyGrow );
            ExpandWindow( pInfo->hwndStText, 0, dyGrow );
            SlideWindow( pInfo->hwndPbRedial, pInfo->hwndDlg, 0, dyGrow );
            SlideWindow( pInfo->hwndPbCancel, pInfo->hwndDlg, 0, dyGrow );
            SlideWindow( pInfo->hwndPbMore, pInfo->hwndDlg, 0, dyGrow );

            Free( psz );
        }
    }

    // Set Redial button label or disable the button.  Always choose to redial
    // after 5 seconds for the biplex error, since this will normally solve
    // the problem.  Otherwise, no countdown is used.
    //
    if (pArgs->dwError == ERROR_BIPLEX_PORT_NOT_AVAILABLE)
    {
        pInfo->lRedialCountdown = 5;
    }
    else
    {
        pInfo->lRedialCountdown = pArgs->lRedialCountdown;
    }

    DeAdjustPbRedial( pInfo );

    if (pInfo->lRedialCountdown >= 0)
    {
        SetTimer( pInfo->hwndDlg, 1, 1000L, NULL );
    }

    // Add context help button to title bar.
    //
    AddContextHelpButton( hwndDlg );

    if (pArgs->fPopupOnTop)
    {
        // Display the finished window above all other windows.  The window
        // position is set to "topmost" then immediately set to "not topmost"
        // because we want it on top but not always-on-top.  Always-on-top
        // alone is too annoying.
        //
        SetWindowPos(
            hwndDlg, HWND_TOPMOST, 0, 0, 0, 0,
            SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
    }

    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    ShowWindow( hwndDlg, SW_SHOW );

    if (pArgs->fPopupOnTop)
    {
        SetForegroundWindow( hwndDlg );

        SetWindowPos(
            hwndDlg, HWND_NOTOPMOST, 0, 0, 0, 0,
            SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
    }

    return TRUE;
}

// Helper function to delete credentials
//
// fDeleteDefault specifies whether it is the default credentials that 
// should be deleted.
//
// fDeleteIdentity specifies whether to delete the user and domain names
// in addition to the password. 
//
DWORD
DeleteSavedCredentials(
    IN DINFO* pDinfo,
    IN HWND   hwndDlg,
    IN BOOL   fDefault,
    IN BOOL   fDeleteIdentity )
{
    RASCREDENTIALS rc;
    DWORD dwErr = NO_ERROR;

    TRACE2( "DeleteSavedCredentials: %d %d", fDefault, fDeleteIdentity );

    ZeroMemory(&rc, sizeof(rc));
    rc.dwSize = sizeof(RASCREDENTIALS);
    rc.dwMask = RASCM_Password;

    if (fDeleteIdentity)
    {
        rc.dwMask |= (RASCM_UserName | RASCM_Domain);
    }

    if (    (fDefault)
        &&  (IsPublicPhonebook(pDinfo->pFile->pszPath)))
    {
        rc.dwMask |= RASCM_DefaultCreds;
    }

    dwErr = g_pRasSetCredentials(
                pDinfo->pFile->pszPath,
                pDinfo->pEntry->pszEntryName,
                &rc,
                TRUE );

    if (dwErr != 0)
    {
        ErrorDlg( hwndDlg, SID_OP_UncachePw, dwErr, NULL );
    }

    TRACE1( "DeleteSavedCredentials: RasSetCredentials=%d", dwErr );

    return dwErr;
}

VOID
DeTerm(
    IN HWND hwndDlg )

    // Called on WM_DESTROY.  'HwndDlg' is that handle of the dialog window.
    //
{
    DEINFO* pInfo = (DEINFO* )GetWindowLongPtr( hwndDlg, DWLP_USER );

    TRACE( "DeTerm" );

    if (pInfo)
    {
        Free( pInfo );
    }
}


//----------------------------------------------------------------------------
// Dial Progress dialog
// Listed alphabetically following stub API dialog proc
//----------------------------------------------------------------------------

BOOL
DialProgressDlg(
    IN DINFO* pInfo )

    // Popup the Dial Progress dialog.  'PInfo' is the dialog context.
    //
    // Returns true if user connected successfully, false is he cancelled or
    // hit an error.
    //
{
    INT_PTR nStatus;

    // Run the dialog.
    //
    nStatus =
        DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_DP_DialProgress ),
            pInfo->pArgs->hwndOwner,
            DpDlgProc,
            (LPARAM )pInfo );

    if (nStatus == -1)
    {
        ErrorDlg( pInfo->pArgs->hwndOwner, SID_OP_LoadDlg,
            ERROR_UNKNOWN, NULL );
        pInfo->pArgs->dwError = ERROR_UNKNOWN;
        nStatus = FALSE;
    }

    if (nStatus)
    {
        DWORD  dwErr;
        PBFILE file;

        // Connected successfully, so read possible changes to the entry made
        // by RasDial.
        //
        dwErr = ReadPhonebookFile( pInfo->pFile->pszPath, pInfo->pUser,
                    pInfo->pEntry->pszEntryName, RPBF_ReadOnly, &file );
        if (dwErr == 0)
        {
            DTLNODE* pNodeNew;

            pNodeNew = DtlGetFirstNode( file.pdtllistEntries );
            if (pNodeNew)
            {
                DtlRemoveNode( pInfo->pFile->pdtllistEntries, pInfo->pNode );
                DestroyEntryNode( pInfo->pNode );

                DtlRemoveNode( file.pdtllistEntries, pNodeNew );
                DtlAddNodeLast( pInfo->pFile->pdtllistEntries, pNodeNew );

                pInfo->pNode = pNodeNew;
                pInfo->pEntry = (PBENTRY* )DtlGetData( pNodeNew );
            }

            ClosePhonebookFile( &file );
        }
    }

    // See if we need to change the entry based on what happened while
    // dialing.
    //
    {
        BOOL fChange = FALSE;

        if (pInfo->fResetAutoLogon)
        {
            ASSERT( !pInfo->pNoUser );
            pInfo->pEntry->fAutoLogon = FALSE;
            fChange = TRUE;
        }

        if (pInfo->dwfExcludedProtocols)
        {
            pInfo->pEntry->dwfExcludedProtocols
                |= pInfo->dwfExcludedProtocols;
            fChange = TRUE;
        }

        if (pInfo->pListPortsToDelete)
        {
            DTLNODE* pNode;

            pNode = DtlGetFirstNode( pInfo->pEntry->pdtllistLinks );
            while (pNode)
            {
                DTLNODE* pNodeNext;
                DTLNODE* pNodePtd;
                PBLINK*  pLink;
                TCHAR*   pszPort;

                pNodeNext = DtlGetNextNode( pNode );

                pLink = (PBLINK* )DtlGetData( pNode );
                pszPort = pLink->pbport.pszPort;

                for (pNodePtd = DtlGetFirstNode( pInfo->pListPortsToDelete );
                     pNodePtd;
                     pNodePtd = DtlGetNextNode( pNodePtd ))
                {
                    TCHAR* pszPtd = (TCHAR* )DtlGetData( pNodePtd );

                    if (lstrcmp( pszPtd, pszPort ) == 0)
                    {
                        pNode = DtlRemoveNode(
                            pInfo->pEntry->pdtllistLinks, pNode );
                        DestroyLinkNode( pNode );
                        fChange = TRUE;
                        break;
                    }
                }

                pNode = pNodeNext;
            }
        }

        if (fChange)
        {
            pInfo->pEntry->fDirty = TRUE;
            WritePhonebookFile( pInfo->pFile, NULL );
        }
    }

    return (BOOL )nStatus;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return DpInit( hwnd, (DINFO* )lparam );
        }

        case WM_COMMAND:
        {
            DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

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

        case WM_RASDIAL:
        {
            DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            Sleep( 0 );
            DpDial( pInfo, (BOOL)wparam );
            return TRUE;
        }

        case WM_RASERROR:
        {
            DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            Sleep( 0 );

            //For whistler bug 381337
            //
            if ( !pInfo->fCancelPressed )
            {
                DpError( pInfo, (DPSTATE* )lparam );
            }
            else
            {
                TRACE("DpDlgProc is already canceled, wont respond to WM_RASERROR");
            }
            
            return TRUE;
        }

        case WM_RASBUNDLEERROR:
        {
            DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            Sleep( 0 );
            if (BundlingErrorsDlg( pInfo ))
            {
                EndDialog( pInfo->hwndDlg, TRUE );
            }
            else
            {
                DpCancel( pInfo );
            }
            return TRUE;
        }

        case WM_DESTROY:
        {
            DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            // Whistler Bugs: 344019 SECBUGBASH: leaving leaked password in
            // memory when user changes password over RAS
            //
            // 289587 Failed RAS connections reset password to blank
            //
            if (pInfo->pszGoodPassword)
            {
                ZeroMemory(
                    pInfo->pszGoodPassword,
                    (lstrlen( pInfo->pszGoodPassword ) + 1) * sizeof(TCHAR) );
                Free( pInfo->pszGoodPassword );
                pInfo->pszGoodPassword = NULL;
            }

            if (pInfo->pszGoodUserName)
            {
                Free( pInfo->pszGoodUserName );
                pInfo->pszGoodUserName = NULL;
            }

            if (DpCallbacksFlag( pInfo, -1 ))
            {
                // Callbacks are active.  Stall until they complete.
                //
                TRACE( "Stall until callbacks disabled" );
                PostMessage( hwnd, WM_DESTROY, wparam, lparam );
                return TRUE;
            }

            DpTerm( hwnd );
            break;
        }
    }

    return FALSE;
}


VOID
DpAppendBlankLine(
    IN OUT TCHAR* pszLines )

    // Append a blank line on the end of 'pszLines'.
    //
{
    lstrcat( pszLines, TEXT("\n") );
}


VOID
DpAppendConnectErrorLine(
    IN OUT TCHAR* pszLines,
    IN DWORD sidProtocol,
    IN DWORD dwError )

    // Append a connect error line for protocol 'sidProtocol' and error
    // 'dwError' onto the end of 'pszLines'.
    //
{
#define MAXRASERRORLEN 256

    TCHAR* pszProtocol;
    TCHAR* pszErrStr;
    TCHAR szErrNumBuf[ MAXLTOTLEN + 1 ];

    // Gather the argument strings.
    //
    pszProtocol = PszFromId( g_hinstDll, sidProtocol );
    if (!pszProtocol)
    {
        return;
    }

    LToT( dwError, szErrNumBuf, 10 );

    pszErrStr = NULL;
    GetErrorText( dwError, &pszErrStr );

    // Format the line and append it to caller's line buffer.
    //
    {
        TCHAR* pszLineFormat;
        TCHAR* pszLine;
        TCHAR* apszArgs[ 3 ];

        pszLineFormat = PszFromId( g_hinstDll, SID_FMT_ProjectError );
        if (pszLineFormat)
        {
            apszArgs[ 0 ] = pszProtocol;
            apszArgs[ 1 ] = szErrNumBuf;
            apszArgs[ 2 ] = (pszErrStr) ? pszErrStr : TEXT("");
            pszLine = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszLineFormat, 0, 0, (LPTSTR )&pszLine, 1,
                (va_list* )apszArgs );

            Free( pszLineFormat );

            if (pszLine)
            {
                lstrcat( pszLines, pszLine );
                LocalFree( pszLine );
            }
        }
    }

    Free( pszProtocol );
    Free0( pszErrStr );
}


VOID
DpAppendConnectOkLine(
    IN OUT TCHAR* pszLines,
    IN DWORD sidProtocol )

    // Append a "connected successfully" line for protocol 'sidProtocol' onto
    // the end of 'pszLines'.
    //
{
    TCHAR* pszProtocol;

    // Get the argument string.
    //
    pszProtocol = PszFromId( g_hinstDll, sidProtocol );
    if (!pszProtocol)
    {
        return;
    }

    // Format the line and append it to caller's line buffer.
    //
    {
        TCHAR* pszLineFormat;
        TCHAR* pszLine;
        TCHAR* apszArgs[ 1 ];

        pszLineFormat = PszFromId( g_hinstDll, SID_FMT_ProjectOk );
        if (pszLineFormat)
        {
            apszArgs[ 0 ] = pszProtocol;
            pszLine = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszLineFormat, 0, 0, (LPTSTR )&pszLine, 1,
                (va_list* )apszArgs );

            Free( pszLineFormat );

            if (pszLine)
            {
                lstrcat( pszLines, pszLine );
                LocalFree( pszLine );
            }
        }
    }

    Free( pszProtocol );
}


VOID
DpAppendFailCodeLine(
    IN OUT TCHAR* pszLines,
    IN DWORD dw )

    // Append hexidecimal fail code 'dw' as an extended error line on the end
    // of 'pszLines'.
    //
{
    TCHAR szNumBuf[ MAXLTOTLEN + 1 ];

    // Get the argument string.
    //
    LToT( dw, szNumBuf, 16 );

    // Format the line and append it to caller's line buffer.
    //
    {
        TCHAR* pszLineFormat;
        TCHAR* pszLine;
        TCHAR* apszArgs[ 1 ];

        pszLineFormat = PszFromId( g_hinstDll, SID_FMT_FailCode );
        if (pszLineFormat)
        {
            apszArgs[ 0 ] = szNumBuf;
            pszLine = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszLineFormat, 0, 0, (LPTSTR )&pszLine, 1,
                (va_list* )apszArgs );

            Free( pszLineFormat );

            if (pszLine)
            {
                lstrcat( pszLines, pszLine );
                LocalFree( pszLine );
            }
        }
    }
}


VOID
DpAppendNameLine(
    IN OUT TCHAR* pszLines,
    IN TCHAR* psz )

    // Append NetBIOS name 'psz' as an extended error line on the end of
    // 'pszLines'.
    //
{
    TCHAR* pszLineFormat;
    TCHAR* pszLine;
    TCHAR* apszArgs[ 1 ];

    pszLineFormat = PszFromId( g_hinstDll, SID_FMT_Name );
    if (pszLineFormat)
    {
        apszArgs[ 0 ] = psz;
        pszLine = NULL;

        FormatMessage(
            FORMAT_MESSAGE_FROM_STRING
                | FORMAT_MESSAGE_ALLOCATE_BUFFER
                | FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pszLineFormat, 0, 0, (LPTSTR )&pszLine, 1,
            (va_list* )apszArgs );

        Free( pszLineFormat );

        if (pszLine)
        {
            lstrcat( pszLines, pszLine );
            LocalFree( pszLine );
        }
    }
}


VOID
DpAuthNotify(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // Called on an authentication notify, i.e. a message from RASCAUTH.DLL or
    // RASPPPEN.DLL.  'PInfo' is the dialog context.  'PState' is the current
    // link's context.
    //
{
    PBENTRY* pEntry;

    TRACE( "DpAuthNotify" );

    pEntry = pInfo->pArgs->pEntry;

    if (pState->dwError == ERROR_ACCESS_DENIED && pEntry->fAutoLogon)
    {
        // A third party box has negotiated an authentication protocol that
        // can't deal with the NT one-way-hashed password, i.e. something
        // besides MS-extended CHAP or AMB.  Map the error to a more
        // informative error message.
        //
        pState->dwError = ERROR_CANNOT_USE_LOGON_CREDENTIALS;

        if (!pInfo->pArgs->pNoUser)
        {
            TRACE( "Disable auto-logon" );
            pEntry->fAutoLogon = FALSE;
            pInfo->pArgs->fResetAutoLogon = TRUE;
        }
    }

    if (pState->dwError == ERROR_CHANGING_PASSWORD)
    {
        TRACE( "DpAuthNotify - ERROR_CHANGING_PASSWORD" );

        // Change password failed.  Restore the password that worked for the
        // "button" redial.
        //
        if (pInfo->pszGoodPassword)
        {
            // Whistler bug 254385 encode password when not being used
            // Assumed password was encoded by DpPasswordExpired()
            //
            DecodePassword( pInfo->pszGoodPassword );
            lstrcpyn(
                pInfo->pArgs->rdp.szPassword,
                pInfo->pszGoodPassword,
                sizeof(pInfo->pArgs->rdp.szPassword) / sizeof(TCHAR) );
            EncodePassword( pInfo->pArgs->rdp.szPassword );

            ZeroMemory(
                pInfo->pszGoodPassword,
                (lstrlen( pInfo->pszGoodPassword ) + 1) * sizeof(TCHAR) );
            Free( pInfo->pszGoodPassword );
            pInfo->pszGoodPassword = NULL;
        }

        if (pInfo->pszGoodUserName)
        {
            lstrcpyn(
                pInfo->pArgs->rdp.szUserName,
                pInfo->pszGoodUserName,
                sizeof(pInfo->pArgs->rdp.szUserName) / sizeof(TCHAR) );
            Free( pInfo->pszGoodUserName );
            pInfo->pszGoodUserName = NULL;
        }
    }

    // Update cached credentials, if any, with new password.
    //
    // Whistler Bugs: 344019 SECBUGBASH: leaving leaked password in memory when
    // user changes password over RAS
    //
    // 289587 Failed RAS connections reset password to blank
    //
    if ((pState->sidState == SID_S_Projected) &&
        (pInfo->pszGoodPassword) &&
        (pInfo->pszGoodUserName))
    {
        DWORD dwErr;
        RASCREDENTIALS rc;

        TRACE( "DpAuthNotify - Success changing password, caching if necessary" );

        ZeroMemory( &rc, sizeof(rc) );
        rc.dwSize = sizeof(rc);

        // Look up cached password. Since we are only calling in with the
        // RASCM_Password flag here, with the current implementation of
        // RasGet/SetCredentials, this works for the Set below whether we are
        // saving a Per-User, Global, or the special case of having both saved
        // at the same time. Whew, complicated!
        //
        rc.dwMask = RASCM_Password;
        ASSERT( g_pRasGetCredentials );
        TRACE( "RasGetCredentials" );
        dwErr = g_pRasGetCredentials(
            pInfo->pArgs->pFile->pszPath,
            pInfo->pArgs->pEntry->pszEntryName,
            &rc );
        TRACE2( "RasGetCredentials=%d,m=%x", dwErr, rc.dwMask );

        if (dwErr == 0 && (rc.dwMask & RASCM_Password))
        {
            // Password was cached, so update it.
            //
            DecodePassword( pInfo->pArgs->rdp.szPassword );
            lstrcpyn(
                rc.szPassword,
                pInfo->pArgs->rdp.szPassword,
                sizeof(rc.szPassword) / sizeof(TCHAR) );
            EncodePassword( pInfo->pArgs->rdp.szPassword );

            ASSERT( g_pRasSetCredentials );
            TRACE( "RasSetCredentials(p,FALSE)" );
            dwErr = g_pRasSetCredentials(
                pInfo->pArgs->pFile->pszPath,
                pInfo->pArgs->pEntry->pszEntryName, &rc, FALSE );
            TRACE1( "RasSetCredentials=%d", dwErr );

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

        // Clean up
        //
        ZeroMemory( rc.szPassword, sizeof(rc.szPassword) );

        ZeroMemory(
            pInfo->pszGoodPassword,
            (lstrlen( pInfo->pszGoodPassword ) + 1) * sizeof(TCHAR) );
        Free( pInfo->pszGoodPassword );
        pInfo->pszGoodPassword = NULL;

        Free( pInfo->pszGoodUserName );
        pInfo->pszGoodUserName = NULL;
    }
}


VOID
DpCallbackSetByCaller(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // RASCS_CallbackSetByCaller state handling.  'PInfo' is the dialog
    // context.  'PState' is the subentry state.
    //
    // Returns true if successful, or an error code.
    //
{
    TCHAR* pszDefault;
    TCHAR szNum[ RAS_MaxCallbackNumber + 1 ];

    TRACE( "DpCallbackSetByCaller" );

    pszDefault = pInfo->pArgs->pUser->pszLastCallbackByCaller;
    if (!pszDefault)
    {
        pszDefault = TEXT("");
    }

    lstrcpyn( szNum, pszDefault, RAS_MaxCallbackNumber + 1 );

    if (DialCallbackDlg( pInfo->hwndDlg, szNum ))
    {
        lstrcpyn( pInfo->pArgs->rdp.szCallbackNumber, szNum, RAS_MaxCallbackNumber + 1 );

        if (lstrcmp( szNum, pszDefault ) != 0)
        {
            Free0( pInfo->pArgs->pUser->pszLastCallbackByCaller );
            pInfo->pArgs->pUser->pszLastCallbackByCaller = StrDup( szNum );
            pInfo->pArgs->pUser->fDirty = TRUE;
        }
    }
    else
    {
        pInfo->pArgs->rdp.szCallbackNumber[ 0 ] = TEXT('\0');
    }

    pState->sidState = 0;
}


BOOL
DpCallbacksFlag(
    IN DPINFO* pInfo,
    IN INT nSet )

    // If 'nSet' is not less than 0, the 'pInfo->fCallbacksActive' flag is set
    // to the value 'nSet'.
    //
    // Returns true is callbacks are active false otherwise.
    //
{
    BOOL f;

    TRACE1( "DpCallbacksFlag:nSet=(%d)", nSet );

    f = FALSE;
    if (WaitForSingleObject(
            g_hmutexCallbacks, INFINITE ) == WAIT_OBJECT_0)
    {
        TRACE("DpCallbacksFlag begin:");
        TRACE1("Global active:(%d)", g_ulCallbacksActive);
        TRACE1("Current thread active:(%d)", pInfo->ulCallbacksActive);

        f = pInfo->fCallbacksActive;

        if (nSet > 0)
        {
            ASSERT( !pInfo->fCallbacksActive );
            pInfo->fCallbacksActive = TRUE;
            ++g_ulCallbacksActive;

            pInfo->ulCallbacksActive++;
        }
        else if (nSet == 0)
        {
            pInfo->fCallbacksActive = FALSE;
            
            if ( 0 < pInfo->ulCallbacksActive )
            {
            
            // fix for whistler bug 341662  366237    gangz
            //
                if( 0 < g_ulCallbacksActive )
                {
                     --g_ulCallbacksActive;
                }
                
                pInfo->ulCallbacksActive--;
            }
        }

        TRACE("DpCallbacksFlag result:");
        TRACE1("Global active:(%d)", g_ulCallbacksActive);
        TRACE1("Current thread active:(%d)", pInfo->ulCallbacksActive);

        ReleaseMutex( g_hmutexCallbacks );
    }

   
    TRACE1( "DpCallbacksFlag: This thread's active=%d", f );

    return f;
}


VOID
DpCancel(
    IN DPINFO* pInfo )

    // Kill the dialog and any partially initiated call, as when cancel button
    // is pressed.  'PInfo' is the dialog context block.
    //
{
    TRACE( "DpCancel" );

    // Hide window to prevent visual complaints that arise if RasHangUp takes
    // a long time to complete.
    //
    ShowWindow( pInfo->hwndDlg, SW_HIDE );

    if (pInfo->hrasconn)
    {
        DWORD dwErr;

        ASSERT( g_pRasHangUp );
        TRACE( "RasHangUp" );

        TRACE("DpCancel:call RasHangUp");

        dwErr = g_pRasHangUp( pInfo->hrasconn );

        TRACE1("DpCancel:get dwErr from RasHangUp:(%d)", dwErr);
        TRACE1( "RasHangUp=%d", dwErr );
    }

    EndDialog( pInfo->hwndDlg, FALSE );
}

//return the pInfo->ulCallbacksActive, to get the g_ulCallbacksActive
//use  CallbacksActive()
//
long
DpOnOffPerThreadTerminateFlag(
    DPINFO *pInfo,
    INT nSetTerminateAsap,
    BOOL* pfTerminateAsap )

    // If 'fSetTerminateAsap' >= 0, sets 'pInfo->fTerminateAsap' flag to 'nSetTerminateAsap'.
    // If non-NULL, caller's '*pfTerminateAsap' is filled with the current value of
    // 'pInfo->fTerminateAsap'.
    //
    // Returns the number of Rasdial callback threads active.
    //
{
    LONG ul;

    TRACE1( "DpOnOffPerThreadTerminateFlag: Terminate=(%d)", nSetTerminateAsap );

    ASSERT(pInfo);

    ul = 0;
    if (WaitForSingleObject( g_hmutexCallbacks, INFINITE ) == WAIT_OBJECT_0)
    {
       if( pInfo )
       {
          if (pfTerminateAsap)
            {
                *pfTerminateAsap = pInfo->fTerminateAsap;
            }

            if (nSetTerminateAsap >= 0)
            {
                pInfo->fTerminateAsap = (BOOL )nSetTerminateAsap;
            }
        }
        else
        {
            TRACE( "DpOnOffPerThreadTerminateFlag: Invalid DPINFO input parameter" );
        }

       ul = pInfo->ulCallbacksActive;

        ReleaseMutex( g_hmutexCallbacks );
    }

    TRACE1( "DpOnOffPerThreadTerminateFlag:Current Thread Active=%d", ul );

    return ul;
}

BOOL
DpCommand(
    IN DPINFO* 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.
    //
{
    DWORD dwErr;

    TRACE3( "DpCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    TRACE2("DpCommand:pInfo address (0x%x), Dialog Handle (0x%x)",
            pInfo,
            pInfo->hwndDlg);
            
    TRACE2("DpCommand:Current proces:(0x%d), Current Thread:(0x%d)",
            GetCurrentProcessId(),
            GetCurrentThreadId());

    switch (wId)
    {
        case IDCANCEL:
        {
            ULONG ulCallbacksActive;

            ShowWindow( pInfo->hwndDlg, SW_HIDE );

            //For whistler bug 381337
            //
            if( !pInfo->fCancelPressed)
            {
                TRACE("DpCommand:Cancel pressed");
                pInfo->fCancelPressed = TRUE;
            }
            
            if (pInfo->hrasconn)
            {
                DWORD dwErr;

                ASSERT( g_pRasHangUp );
                TRACE( "RasHangUp" );
                dwErr = g_pRasHangUp( pInfo->hrasconn );
                TRACE1( "RasHangUp=%d", dwErr );
            }

			//set pInfo->fTerminateAsap to Terminate the dial
			//in the current thread, also return the pInfo->ulCallbacksActive,
			//if it is already 0, then we dont need to post the CANCEL window
			//Message again    whistler bug#291613      gangz
			//

           ulCallbacksActive = DpOnOffPerThreadTerminateFlag(pInfo, 1, NULL );

            if ( 0 < ulCallbacksActive )           
            {
                TRACE1( "DpCommand stall, current thread's ulCallbacksActive n=%d", ulCallbacksActive );
                PostMessage( pInfo->hwndDlg, WM_COMMAND, 
                             MAKEWPARAM(wId, wNotification), 
                             (LPARAM) hwndCtrl );

                //For whistler bug 378086       gangz
                //to sleep a bit to give the rasman a break to call our callback 
                //function DpRasDialFunc2()
                //
                Sleep(10);
                
                return TRUE;
            }

            EndDialog( pInfo->hwndDlg, FALSE );

            //Reset the pInfo->fTerminateAsap flag
            //
            DpOnOffPerThreadTerminateFlag(pInfo, 0, NULL );            
            return TRUE;
        }
    }

    return FALSE;
}


VOID
DpConnectDevice(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // RASCS_ConnectDevice state handling.  'PInfo' is the dialog context.
    // 'PState' is the subentry state.
    //
{
    DWORD dwErr;
    RASCONNSTATUS rcs;
    DWORD cb;
    HRASCONN hrasconn;
    TCHAR* pszPhoneNumber;

    TRACE( "DpConnectDevice" );

    // Get fully translated phone number, if any.
    //
    ZeroMemory( &rcs, sizeof(rcs) );
    rcs.dwSize = sizeof(rcs);
    ASSERT( g_pRasGetConnectStatus );
    TRACE1( "RasGetConnectStatus($%08x)", pState->hrasconnLink );
    dwErr = g_pRasGetConnectStatus( pState->hrasconnLink, &rcs );
    TRACE1( "RasGetConnectStatus=%d", dwErr );
    TRACEW1( " dt=%s", rcs.szDeviceType );
    TRACEW1( " dn=%s", rcs.szDeviceName );
    TRACEW1( " pn=%s", rcs.szPhoneNumber );
    if (dwErr != 0)
    {
        pState->pbdt = PBDT_None;
    }

    pState->pbdt = PbdevicetypeFromPszType( rcs.szDeviceType );
    pszPhoneNumber = rcs.szPhoneNumber;

    switch (pState->pbdt)
    {
        case PBDT_Modem:
        {
            Free0( pState->pszStatusArg );
            pState->pszStatusArg = StrDup( pszPhoneNumber );

            if (pInfo->pArgs->pUser->fOperatorDial
                && AllLinksAreModems( pInfo->pArgs->pEntry ))
            {
                pState->sidState = SID_S_ConnectModemOperator;
            }
            else if (pInfo->pArgs->pEntry->fPreviewPhoneNumber)
            {
                pState->sidState = SID_S_ConnectNumber;
            }
            else
            {
                pState->sidState = SID_S_ConnectModemNoNum;
            }
            break;
        }

        case PBDT_Pad:
        {
            Free0( pState->pszStatusArg );
            pState->pszStatusArg = StrDup( rcs.szDeviceName );
            pState->sidState = SID_S_ConnectPad;

            if (pState->dwError == ERROR_X25_DIAGNOSTIC)
            {
                TCHAR* psz;

                // Get the X.25 diagnostic string for display in the
                // custom "diagnostics" error message format.
                //
                Free0( pState->pszFormatArg );
                pState->pszFormatArg =
                    GetRasX25Diagnostic( pState->hrasconnLink );
            }
            break;
        }

        case PBDT_Switch:
        {
            Free0( pState->pszStatusArg );
            pState->pszStatusArg = StrDup( rcs.szDeviceName );

            pState->sidState =
                (pState->fNotPreSwitch)
                    ? SID_S_ConnectPostSwitch
                    : SID_S_ConnectPreSwitch;
            break;
        }

        case PBDT_Null:
        {
            pState->sidState = SID_S_ConnectNull;
            break;
        }

        case PBDT_Parallel:
        {
            pState->sidState = SID_S_ConnectParallel;
            break;
        }

        case PBDT_Irda:
        {
            pState->sidState = SID_S_ConnectIrda;
            break;
        }

        case PBDT_Isdn:
        {
            Free0( pState->pszStatusArg );
            pState->pszStatusArg = StrDup( pszPhoneNumber );
            pState->sidState = SID_S_ConnectNumber;
            break;
        }

        case PBDT_Vpn:
        {
            Free0( pState->pszStatusArg );
            pState->pszStatusArg = StrDup( pszPhoneNumber );
            pState->sidState = SID_S_ConnectVpn;
            break;
        }

        default:
        {
            Free0( pState->pszStatusArg );
            if (pszPhoneNumber[ 0 ] != TEXT('\0'))
            {
                pState->pszStatusArg = StrDup( pszPhoneNumber );
                pState->sidState = SID_S_ConnectNumber;
            }
            else
            {
                pState->pszStatusArg = StrDup( rcs.szDeviceName );
                pState->sidState = SID_S_ConnectDevice;
            }
            break;
        }
    }
}

VOID
DpDeviceConnected(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // RASCS_DeviceConnected state handling.  'PInfo' is the dialog context.
    // 'PState' is the subentry state.
    //
    // Returns 0 if successful, or an error code.
    //
{
    TRACE( "DpDeviceConnected" );

    switch (pState->pbdt)
    {
        case PBDT_Modem:
        {
            pState->sidState = SID_S_ModemConnected;
            pState->fNotPreSwitch = TRUE;
            break;
        }

        case PBDT_Pad:
        {
            pState->sidState = SID_S_PadConnected;
            pState->fNotPreSwitch = TRUE;
            break;
        }

        case PBDT_Switch:
        {
            pState->sidState =
                (pState->fNotPreSwitch)
                    ? SID_S_PostSwitchConnected
                    : SID_S_PreSwitchConnected;
            pState->fNotPreSwitch = TRUE;
            break;
        }

        case PBDT_Null:
        {
            pState->sidState = SID_S_NullConnected;
            pState->fNotPreSwitch = TRUE;
            break;
        }

        case PBDT_Parallel:
        {
            pState->sidState = SID_S_ParallelConnected;
            pState->fNotPreSwitch = TRUE;
            break;
        }

        case PBDT_Irda:
        {
            pState->sidState = SID_S_IrdaConnected;
            pState->fNotPreSwitch = TRUE;
            break;
        }

        default:
        {
            pState->sidState = SID_S_DeviceConnected;
            break;
        }
    }
}


VOID
DpDial(
    IN DPINFO* pInfo,
    IN BOOL fPauseRestart )

    // Dial with the parameters in the 'pInfo' dialog context block.
    // 'FPausedRestart' indicates the dial is restarting from a paused state
    // and dial states should not be reset.
    //
{
    DWORD dwErr;

    TRACE1( "DpDial,fPauseRestart=%d", fPauseRestart );

    if (!fPauseRestart)
    {
        DpInitStates( pInfo );
        
        //comment for bug 277365, 291613 gangz
        //Set the fCallbacksActive to be TRUE
        //
        TRACE("DpDial:Init global actives");

        DpCallbacksFlag( pInfo, 1 );
    }
    else
    {
        TRACE("DpDial:WONT Init global actives for pausedRestart");
    }

    // Whistler bug 254385 encode password when not being used
    // Assumed password was encoded previously
    //
    DecodePassword( pInfo->pArgs->rdp.szPassword );

    TRACE1( "RasDial(h=$%08x)", pInfo->hrasconn );
    ASSERT( g_pRasDial );
    dwErr = g_pRasDial( &pInfo->pArgs->rde, pInfo->pArgs->pFile->pszPath,
            &pInfo->pArgs->rdp, 2, (LPVOID )DpRasDialFunc2, &pInfo->hrasconn );
    TRACE2( "RasDial=%d,h=$%08x", dwErr, pInfo->hrasconn );

    EncodePassword( pInfo->pArgs->rdp.szPassword );

    if (dwErr != 0)
    {
        // This same error will show up via the callback route on restarts
        // from PAUSEd states so avoid double popups in that case.  See bug
        // 367482.
        //
        if (!fPauseRestart)
        {
            ErrorDlg( pInfo->hwndDlg, SID_OP_RasDial, dwErr, NULL );
            DpCallbacksFlag( pInfo, 0 );
        }
        
        // If we receive error 668 here, it means that a rasevent is currently
        // posted for state RASCS_Disconnected.  We shouldn't cancel in this
        // case since we Normal processing of RASCS_Disconnected will allow the
        // user to redial.  Also, calling DpCancel below will insert
        // WM_DESTROY which will process before the popup that the rasevent
        // produces can display.  367482 
        //
        if (dwErr != ERROR_NO_CONNECTION)
        {
            DpCancel( pInfo );
        }            
    }
}


VOID
DpError(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // Popup error dialog for error identified by 'pState' and cancel or
    // redial as indicated by user.  'PInfo' is the dialog context.
    //
{
    DWORD dwErr;
    DWORD dwRedialAttemptsLeft;
    DWORD sidState;

    TRACE( "DpError" );

    // Retrieve additional text from RASMXS on those special errors where the
    // device returned something useful to display.
    //
    if (pState->dwError == ERROR_FROM_DEVICE
        || pState->dwError == ERROR_UNRECOGNIZED_RESPONSE)
    {
        TCHAR* pszMessage;

        dwErr = GetRasMessage( pInfo->hrasconn, &pszMessage );
        if (dwErr == 0)
        {
            pState->sidFormatMsg = SID_FMT_ErrorMsgResp;
            Free0( pState->pszFormatArg );
            pState->pszFormatArg = pszMessage;
        }
    }

    if (pState->sidFormatMsg == 0)
    {
        if (pState->dwExtendedError != 0)
        {
            // Translate extended error code into arguments.
            //
            TCHAR szNum[ 2 + MAXLTOTLEN + 1 ];

            pState->sidFormatMsg = SID_FMT_ErrorMsgExt;

            szNum[ 0 ] = TEXT('0');
            szNum[ 1 ] = TEXT('x');
            LToT( pState->dwExtendedError, szNum + 2, 16 );

            Free0( pState->pszFormatArg );
            pState->pszFormatArg = StrDup( szNum );
        }
        else if (pState->szExtendedError[ 0 ] != TEXT('\0'))
        {
            // Translate extended error string to argument.  Currently, the
            // string is always a NetBIOS name.
            //
            pState->sidFormatMsg = SID_FMT_ErrorMsgName;
            Free0( pState->pszFormatArg );
            pState->pszFormatArg = StrDup( pState->szExtendedError );
        }
    }

    if (pInfo->hrasconn)
    {
        // Hang up before displaying error popup so server side resources are
        // not tied up waiting for client to acknowledge error.
        //
        ASSERT( g_pRasHangUp );
        TRACE( "RasHangUp" );
        dwErr = g_pRasHangUp( pInfo->hrasconn );
        TRACE1( "RasHangUp=%d", dwErr );
        pInfo->hrasconn = NULL;
    }

    if (pInfo->pArgs->pEntry->pszPrerequisiteEntry
        && *pInfo->pArgs->pEntry->pszPrerequisiteEntry)
    {
        // Select "no Redial button" mode for entries that have prerequisite
        // entries.  This is because RASMAN drops the prerequisite entry as
        // soon as the dependent entry fails, thus dooming to defeat a redial
        // of only the dependent entry.  Yes, it should really redial both
        // entries here, but that is not really feasible with the current
        // non-integrated serial implementation of prerequisite entries.  This
        // at least improves the poor behavior cited in bug 230594.
        //
        dwRedialAttemptsLeft = -2;
    }
    else if (pInfo->dwRedialAttemptsLeft <= 0)
    {
        // No auto-redial countdown, but "Redial" button does appear in place
        // of the default "OK".
        //
        dwRedialAttemptsLeft = -1;
    }
    else
    {
        // Auto-redial countdown based on the entries configuration.
        //
        dwRedialAttemptsLeft =
            GetOverridableParam(
                pInfo->pArgs->pUser,
                pInfo->pArgs->pEntry,
                RASOR_RedialSeconds );
    }

    // This hack works around a bug in RasDial API.  See bug 313102.
    //
    sidState = pState ->sidState;
    if (!sidState)
    {
        sidState = SID_S_AuthNotify;
    }

    if (DialErrorDlg(
            pInfo->hwndDlg,
            pInfo->pArgs->pEntry->pszEntryName,
            pState->dwError,
            sidState,
            pState->pszStatusArg,
            pState->sidFormatMsg,
            pState->pszFormatArg,
            dwRedialAttemptsLeft,
            GetOverridableParam(
                pInfo->pArgs->pUser, pInfo->pArgs->pEntry,
                RASOR_PopupOnTopWhenRedialing ) ))
    {
        TRACE( "User redials" );
        if (pInfo->dwRedialAttemptsLeft > 0)
        {
            --pInfo->dwRedialAttemptsLeft;
        }

        TRACE(" Post(DIAL)" );
        PostMessage( pInfo->hwndDlg, WM_RASDIAL, FALSE, 0 );
    }
    else
    {
        TRACE( "User cancels" );
        DpCancel( pInfo );
    }

    //
    // Set the error so that the error is propagated back
    // to the caller of the RasDialDlg api.
    //
    TRACE2("DpError settings error (0x%x) to %d",
            &pInfo->pArgs->pArgs->dwError,
            pState->dwError);
    pInfo->pArgs->pArgs->dwError = pState->dwError;
}


DWORD
DpEvent(
    IN DPINFO* pInfo,
    IN DWORD dwSubEntry )

    // Handle a RasDial callback event on subentry 'dwSubEntry'.  'PInfo' is
    // the dialog context.
    //
    // Return 0 to stop callbacks from RasDial, or 1 to continue callbacks
    // (normal), or 2 to indicate that the phonebook entry has changed and
    // should be re-read by RasDial.
    //
{
    DWORD dwErr;
    DWORD dwCode;
    BOOL fLeader;
    DWORD dwcSuccessLinks, dwcFailedLinks, i;
    DPSTATE* pState;
    BOOL fPartialMultilink;
    BOOL fIsLaterState;

    TRACE( "DpEvent" );

    // Default to "normal" return.
    //
    dwCode = 1;
    fPartialMultilink = FALSE;

    TRACE2("Current proces:(0x%d), Current Thread:(0x%d)",
            GetCurrentProcessId(),
            GetCurrentThreadId());
            
    // Find the associated state information and figure out if this is the
    // most advanced sub-entry.
    //
    pState = &pInfo->pStates[ dwSubEntry - 1 ];
    fIsLaterState = DpIsLaterState( pState->state, pInfo->state );
    if (dwSubEntry == pInfo->dwSubEntry || fIsLaterState)
    {
        fLeader = TRUE;
        if (fIsLaterState)
        {
            pInfo->dwSubEntry = dwSubEntry;
            pInfo->state = pState->state;
        }
    }
    else
    {
        fLeader = FALSE;
        TRACE( "Trailing" );
    }

    // Execute the state.
    //
    TRACE1("State is:(%d)", pState->state);

    switch (pState->state)
    {
        case RASCS_OpenPort:
        {
            pState->pbdt = PBDT_None;
            pState->sidState = SID_S_OpenPort;
            break;
        }

        case RASCS_PortOpened:
        {
            // Should have an hrasconnLink for this subentry now.  Look it up
            // and stash it in our context.
            //
            ASSERT( g_pRasGetSubEntryHandle );
            TRACE1( "RasGetSubEntryHandle(se=%d)", dwSubEntry );
            dwErr = g_pRasGetSubEntryHandle(
                pInfo->hrasconn, dwSubEntry, &pState->hrasconnLink );
            TRACE2( "RasGetSubEntryHandle=%d,hL=$%08x",
                dwErr, pState->hrasconnLink );
            if (dwErr != 0)
            {
                pState->dwError = dwErr;
            }

            pState->sidState = SID_S_PortOpened;
            break;
        }

        case RASCS_ConnectDevice:
        {
            DTLNODE* pNode;
            PBLINK* pLink;

            pNode = DtlNodeFromIndex(
                pInfo->pArgs->pEntry->pdtllistLinks, dwSubEntry - 1 );
            ASSERT( pNode );

            if(NULL == pNode)
            {
                pState->dwError = ERROR_NOT_ENOUGH_MEMORY;
            }
            else
            {
                pLink = (PBLINK* )DtlGetData( pNode );
            }

            if ((pState->dwError == ERROR_PORT_OR_DEVICE
                    && (pLink->pbport.fScriptBeforeTerminal
                        || pLink->pbport.fScriptBefore))
                || (pState->dwError == ERROR_USER_DISCONNECTION
                    && (pInfo->pArgs->pUser->fOperatorDial
                        && AllLinksAreModems( pInfo->pArgs->pEntry ))))
            {
                // This happens when user presses Cancel on the Unimodem
                // "Pre-Dial Terminal Screen" or "Operator Assisted or Manual
                // Dial" dialog.
                //
                TRACE("DpEvent:Call DpCancel() in connectDevice, but still return 1\n");
                DpCancel( pInfo );
                return dwCode;
            }

            DpConnectDevice( pInfo, pState );
            break;
        }

        case RASCS_DeviceConnected:
        {
            DpDeviceConnected( pInfo, pState );
            break;
        }

        case RASCS_AllDevicesConnected:
        {
           pState->sidState = SID_S_AllDevicesConnected;
            break;
        }

        case RASCS_Authenticate:
        {
            pState->sidState = SID_S_Authenticate;
            break;
        }

        case RASCS_AuthNotify:
        {
            DpAuthNotify( pInfo, pState );
            break;
        }

        case RASCS_AuthRetry:
        {
            pState->sidState = SID_S_AuthRetry;
            break;
        }

        case RASCS_AuthCallback:
        {
            pState->sidState = SID_S_AuthCallback;
            break;
        }

        case RASCS_AuthChangePassword:
        {
            pState->sidState = SID_S_AuthChangePassword;
            break;
        }

        case RASCS_AuthProject:
        {
            pState->sidState = SID_S_AuthProject;
            break;
        }

        case RASCS_AuthLinkSpeed:
        {
            pState->sidState = SID_S_AuthLinkSpeed;
            break;
        }

        case RASCS_AuthAck:
        {
            pState->sidState = SID_S_AuthAck;
            break;
        }

        case RASCS_ReAuthenticate:
        {
            pState->sidState = SID_S_ReAuthenticate;
            break;
        }

        case RASCS_Authenticated:
        {
            pState->sidState = SID_S_Authenticated;
            break;
        }

        case RASCS_PrepareForCallback:
        {
            pState->sidState = SID_S_PrepareForCallback;
            break;
        }

        case RASCS_WaitForModemReset:
        {
            pState->sidState = SID_S_WaitForModemReset;
            break;
        }

        case RASCS_WaitForCallback:
        {
            pState->sidState = SID_S_WaitForCallback;
            break;
        }

        case RASCS_Projected:
        {
            if (fLeader)
            {
                // If DpProjected returns FALSE, it detected a fatal error,
                // and the dialing process will stop.  If DpProjected returns
                // with pState->dwError non-zero, we display the error in a
                // redial dialog, if redial is configured.
                //
                if (!DpProjected( pInfo, pState ))
                {
                    TRACE("DpEvent:Call DpCancel() in RASCS_Projected, but still return 1 to DpRasDialFunc2()\n");
                
                    DpCancel( pInfo );
                    return dwCode;
                }
                else if (pState->dwError)
                {
                    TRACE("DpCancel() in RASCS_Projected,return 0 to DpRasDialFunc2()");
                    
                    TRACE( "DpEvent:Post(ERROR), return 0 to DpRasDialFunc2()" );
                    PostMessage( pInfo->hwndDlg,
                        WM_RASERROR, 0, (LPARAM )pState );
                    return 0;
                }
            }
            break;
        }

        case RASCS_Interactive:
        {
            BOOL fChange = FALSE;

            if (!DpInteractive( pInfo, pState, &fChange ))
            {
                DpCancel( pInfo );
                return dwCode;
            }

            if (fChange)
            {
                dwCode = 2;
            }
            break;
        }

        case RASCS_RetryAuthentication:
        {
            if (!RetryAuthenticationDlg(
                pInfo->hwndDlg, pInfo->pArgs ))
            {
                DpCancel( pInfo );
                return dwCode;
            }

            pState->sidState = 0;
            break;
        }

        case RASCS_InvokeEapUI:
        {
            if (g_pRasInvokeEapUI(
                    pInfo->hrasconn, dwSubEntry,
                    &pInfo->pArgs->rde, pInfo->hwndDlg ))
            {
                DpCancel( pInfo );
                return dwCode;
            }

            pState->sidState = 0;
            break;
        }

        case RASCS_CallbackSetByCaller:
        {
            DpCallbackSetByCaller( pInfo, pState );
            break;
        }

        case RASCS_PasswordExpired:
        {
            if (!DpPasswordExpired( pInfo, pState ))
            {
                DpCancel( pInfo );
                return dwCode;
            }
            break;
        }

        case RASCS_SubEntryConnected:
        {
            if (pInfo->cStates > 1)
            {
                pState->sidState = SID_S_SubConnected;
            }
            break;
        }

        case RASCS_SubEntryDisconnected:
        {
            break;
        }

        case RASCS_Connected:
        {
            pState->sidState = SID_S_Connected;
            break;
        }

        case RASCS_Disconnected:
        {
            pState->sidState = SID_S_Disconnected;
            break;
        }

        default:
        {
            pState->sidState = SID_S_Unknown;
            break;
        }
    }

    // Count the successful and failed links.
    //
    {
        DPSTATE* p;

        dwcSuccessLinks = dwcFailedLinks = 0;
        for (i = 0, p = pInfo->pStates; i < pInfo->cStates; ++i, ++p)
        {
            if (p->state == RASCS_SubEntryConnected)
            {
                ++dwcSuccessLinks;
            }

            if (p->dwError)
            {
                ++dwcFailedLinks;
            }
        }
    }
    TRACE3( "s=%d,f=%d,t=%d", dwcSuccessLinks, dwcFailedLinks, pInfo->cStates );

    if (pState->dwError)
    {
        DTLNODE *pdtlnode = NULL;
        DWORD dwIndex = pInfo->pArgs->rdp.dwSubEntry;

        if(0 != dwIndex)
        {

            pdtlnode = DtlNodeFromIndex(
                                pInfo->pArgs->pEntry->pdtllistLinks,
                                dwIndex - 1);
        }

        if (    (dwcFailedLinks == pInfo->cStates)
            ||  (   (RASEDM_DialAll != pInfo->pArgs->pEntry->dwDialMode)
                &&  (dwcSuccessLinks == 0))
            ||  (NULL != pdtlnode))
        {
            // A terminal error state has occurred on all links.  Post a
            // message telling ourselves to popup an error, then release the
            // callback so it doesn't hold the port open while the error popup
            // is up,
            //
            TRACE( "Post(ERROR)" );
            PostMessage( pInfo->hwndDlg, WM_RASERROR, 0, (LPARAM )pState );
            return 0;
        }
        else if (dwcSuccessLinks + dwcFailedLinks == pInfo->cStates)
        {
            // An error occurred on the final link, but some link connected.
            // It would be nice if RasDial would followup with a
            // RASCS_Connected in that case, but it doesn't, so we duplicate
            // the RASCS_Connected-style exit here.
            //
            TRACE( "Post(BUNDLEERROR)" );
            PostMessage( pInfo->hwndDlg,
                WM_RASBUNDLEERROR, 0, (LPARAM )pState );
            return 0;
        }

        // A fatal error has occurred on a link, but there are other links
        // still trying, so let it die quietly.
        //
        TRACE2( "Link %d fails, e=%d", dwSubEntry + 1, pState->dwError );
        return dwCode;
    }

    // Display the status string for this state.
    //
    if (pState->sidState)
    {
        if (pState->sidState != pState->sidPrevState)
        {
            pState->sidPrevState = pState->sidState;
        }

        if (fLeader)
        {
            TCHAR* pszState = PszFromId( g_hinstDll, pState->sidState );

            if (    (NULL != pszState)
                &&  pState->pszStatusArg)
            {
                TCHAR* pszFormattedState;
                TCHAR* pszArg;
                TCHAR* apszArgs[ 1 ];
                DWORD cch;

                pszArg = (pState->pszStatusArg)
                    ? pState->pszStatusArg : TEXT("");

                // Find length of formatted string with text argument (if any)
                // inserted and any progress dots appended.
                //
                cch = lstrlen( pszState ) + lstrlen( pszArg ) + 1;

                pszFormattedState = Malloc( cch * sizeof(TCHAR) );
                if (pszFormattedState)
                {
                    apszArgs[ 0 ] = pszArg;
                    *pszFormattedState = TEXT('\0');

                    FormatMessage(
                        FORMAT_MESSAGE_FROM_STRING
                            | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                        pszState, 0, 0, pszFormattedState, cch,
                        (va_list* )apszArgs );

                    Free( pszState );
                    pszState = pszFormattedState;
                }
            }

            TRACE1("DpEvent:State:'%s'",pszState);
    
            if (pszState)
            {
                SetWindowText( pInfo->hwndStState, pszState );
                InvalidateRect( pInfo->hwndStState, NULL, TRUE );
                UpdateWindow( pInfo->hwndStState );
                LocalFree( pszState );
            }
        }
    }

    if (pState->state & RASCS_PAUSED)
    {
        // Paused state just processed.  Release the callback, and dial again
        // to resume.
        //
        TRACE("DpEvent:Paused, will dial again\nthe global callbacks wont init again");
        TRACE( "Post(DIAL)" );
        PostMessage( pInfo->hwndDlg, WM_RASDIAL, TRUE, 0 );
        return dwCode;
    }

    if (pState->state & RASCS_DONE)
    {
        // Terminal state just processed.
        //
        if (pState->state == RASCS_Connected)
        {
            // For multi-link entries, if there is at least one successful
            // line and at least one failed line, popup the bundling error
            // dialog.
            //
            if (pInfo->cStates > 1)
            {
                DPSTATE* p;

                dwcSuccessLinks = dwcFailedLinks = 0;
                for (i = 0, p = pInfo->pStates; i < pInfo->cStates; ++i, ++p)
                {
                    if (p->dwError == 0)
                    {
                        ++dwcSuccessLinks;
                    }
                    else
                    {
                        ++dwcFailedLinks;
                    }
                }

                if (dwcSuccessLinks > 0 && dwcFailedLinks > 0)
                {
                     TRACE( "Post(BUNDLEERROR)" );
                     PostMessage( pInfo->hwndDlg,
                         WM_RASBUNDLEERROR, 0, (LPARAM )pState );
                     return 0;
                }
            }

            EndDialog( pInfo->hwndDlg, TRUE );
        }
        else
        {
            DpCancel( pInfo );
        }   

        return 0;
    }

    TRACE1("DpEvent:returned dwCode:(%d)", dwCode);
    TRACE("End of DpEvent");
    
    return dwCode;
}


BOOL
DpInit(
    IN HWND hwndDlg,
    IN DINFO* pArgs )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PArgs' is caller's arguments as passed to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    DPINFO* pInfo;
    PBENTRY* pEntry;

    TRACE( "DpInit" );

    // Allocate the dialog context block.  Initialize minimally for proper
    // cleanup, then attach to the dialog window.
    //
    {
        pInfo = Malloc( sizeof(*pInfo) );
        if (!pInfo)
        {
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

        ZeroMemory( pInfo, sizeof(*pInfo) );
        pInfo->dwValid = 0xC0BBC0DE;
        pInfo->pArgs = pArgs;
        pInfo->hwndDlg = hwndDlg;

        //Add a per-thread Terminate flag for whistler bug 291613 gangz
        //
        pInfo->fTerminateAsap = FALSE;
        pInfo->ulCallbacksActive = 0;
        
        SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
        TRACE( "Context set" );
    }

    pInfo->hwndStState = GetDlgItem( hwndDlg, CID_DP_ST_State );
    ASSERT( pInfo->hwndStState );

    pEntry = pArgs->pEntry;

    // Set up our context to be returned by the RasDialFunc2 callback.
    //
    pInfo->pArgs->rdp.dwCallbackId = (ULONG_PTR )pInfo;

    // Subclass the dialog so we can get the result from
    // SendMessage(WM_RASDIALEVENT) in RasDlgFunc2.
    //
    pInfo->pOldWndProc =
        (WNDPROC )SetWindowLongPtr(
            pInfo->hwndDlg, GWLP_WNDPROC, (ULONG_PTR )DpWndProc );

    // Set the title.
    //
    {
        TCHAR* pszTitleFormat;
        TCHAR* pszTitle;
        TCHAR* apszArgs[ 1 ];

        pszTitleFormat = GetText( hwndDlg );
        if (pszTitleFormat)
        {
            apszArgs[ 0 ] = pEntry->pszEntryName;
            pszTitle = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszTitleFormat, 0, 0, (LPTSTR )&pszTitle, 1,
                (va_list* )apszArgs );

            Free( pszTitleFormat );

            if (pszTitle)
            {
                SetWindowText( hwndDlg, pszTitle );
                LocalFree( pszTitle );
            }
        }
    }

    // Set the correct icon. For whistler bug 372078    gangz
    //
    SetIconFromEntryType(
        GetDlgItem( hwndDlg, CID_DP_Icon ),
        pArgs->pEntry->dwType,
        FALSE);     //FALSE means large Icon
    
    // Position the dialog per caller's instructions.
    //
    PositionDlg( hwndDlg,
        pArgs->pArgs->dwFlags & RASDDFLAG_PositionDlg,
        pArgs->pArgs->xDlg, pArgs->pArgs->yDlg );

    // Hide the dialog if "no progress" user preference is set.
    //
    if (!pArgs->pEntry->fShowDialingProgress
        || pArgs->fDialForReferenceOnly)
    {
        SetOffDesktop( hwndDlg, SOD_MoveOff, NULL );
    }

    SetForegroundWindow( hwndDlg );

    // Allocate subentry status array.  It's initialized by DpDial.
    //
    {
        DWORD cb;

        ASSERT( pEntry->pdtllistLinks );
        pInfo->cStates = DtlGetNodes( pEntry->pdtllistLinks );
        cb = sizeof(DPSTATE) * pInfo->cStates;
        pInfo->pStates = Malloc( cb );
        if (!pInfo->pStates)
        {
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

        pInfo->dwRedialAttemptsLeft =
            GetOverridableParam(
                pInfo->pArgs->pUser, pInfo->pArgs->pEntry,
                RASOR_RedialAttempts );
    }

    //for whistler bug 316622   gangz
    //The dwSubEntry is not initialized
    //
    pInfo->pArgs->rdp.dwSubEntry = pInfo->pArgs->pArgs->dwSubEntry;
    
    // Rock and roll.
    //
    DpDial( pInfo, FALSE );

    return TRUE;
}


VOID
DpInitStates(
    DPINFO* pInfo )

    // Resets 'pInfo->pStates' to initial values.  'PInfo' is the dialog
    // context.
    //
{
    DWORD    i;
    DPSTATE* pState;

    for (i = 0, pState = pInfo->pStates; i < pInfo->cStates; ++i, ++pState)
    {
        ZeroMemory( pState, sizeof(*pState) );
        pInfo->state = (RASCONNSTATE )-1;
        pState->dwError = 0;
    }
}


BOOL
DpInteractive(
    IN DPINFO* pInfo,
    IN DPSTATE* pState,
    OUT BOOL* pfChange )

    // RASCS_Interactive handling.  'PInfo' is the dialog context.  'PState'
    // is the subentry state.  '*pfChange' is set true if the entry (i.e. SLIP
    // address) was changed or false otherwise.
    //
    // Returns true if successful, false if cancel.
    //
{
    DWORD dwErr;
    DWORD sidTitle;
    TCHAR szIpAddress[ TERM_IpAddress ];
    TCHAR* pszIpAddress;
    PBENTRY* pEntry;

    TRACE( "DpInteractive" );

    *pfChange = FALSE;
    pEntry = pInfo->pArgs->pEntry;

    if (pEntry->dwBaseProtocol == BP_Slip)
    {
        lstrcpyn( 
            szIpAddress,
            (pEntry->pszIpAddress) ? pEntry->pszIpAddress : TEXT("0.0.0.0"),
            sizeof(szIpAddress) / sizeof(TCHAR));
        pszIpAddress = szIpAddress;
        sidTitle = SID_T_SlipTerminal;
    }
    else
    {
        szIpAddress[0] = TEXT('\0');
        pszIpAddress = szIpAddress;
        sidTitle =
            (pState->sidState == SID_S_ConnectPreSwitch)
                ? SID_T_PreconnectTerminal
                : (pState->sidState == SID_S_ConnectPostSwitch)
                      ? SID_T_PostconnectTerminal
                      : SID_T_ManualDialTerminal;
    }

    if(1 == pEntry->dwCustomScript)
    {
        DWORD dwErr = SUCCESS;

        dwErr = DwCustomTerminalDlg(
                        pInfo->pArgs->pFile->pszPath,
                        pInfo->hrasconn,
                        pEntry,
                        pInfo->hwndDlg,
                        &pInfo->pArgs->rdp,
                        NULL);

        if(SUCCESS == dwErr)
        {
#if 0
            //
            // Reread the phonebook file since the
            // custom script could have written
            // new information to the file.
            // 
            ClosePhonebookFile(pInfo->pArgs->pFile);

            dwErr = ReadPhonebookFile(
                        pInfo->pArgs->pszPhonebook, 
                        &pInfo->pArgs->user, 
                        NULL, 0, &pInfo->pArgs->file );

            if(SUCCESS == dwErr)
            {
                pInfo->pArgs->pFile = &pInfo->pArgs->file;
            }
#endif            
        }
        
        if (dwErr != 0)
        {
            ErrorDlg( pInfo->hwndDlg, SID_OP_ScriptHalted, dwErr, NULL );
        }
        
        return (ERROR_SUCCESS == dwErr);
    }

    if (!TerminalDlg(
            pInfo->pArgs->pEntry, &pInfo->pArgs->rdp, pInfo->hwndDlg,
            pState->hrasconnLink, sidTitle, pszIpAddress ))
    {
        TRACE( "TerminalDlg==FALSE" );
        return FALSE;
    }

    TRACE2( "pszIpAddress=0x%08x(%ls)", pszIpAddress,
        pszIpAddress ? pszIpAddress : TEXT("") );
    TRACE2( "pEntry->pszIpAddress=0x%08x(%ls)", pEntry->pszIpAddress,
        pEntry->pszIpAddress ? pEntry->pszIpAddress : TEXT("") );

    if (pszIpAddress[0]
        && (!pEntry->pszIpAddress
            || lstrcmp( pszIpAddress, pEntry->pszIpAddress ) != 0))
    {
        Free0( pEntry->pszIpAddress );
        pEntry->pszIpAddress = StrDup( szIpAddress );
        pEntry->fDirty = TRUE;
        *pfChange = TRUE;

        dwErr = WritePhonebookFile( pInfo->pArgs->pFile, NULL );
        if (dwErr != 0)
        {
            ErrorDlg( pInfo->hwndDlg, SID_OP_WritePhonebook, dwErr, NULL );
        }
    }

    pState->sidState = 0;
    return TRUE;
}


BOOL
DpIsLaterState(
    IN RASCONNSTATE stateNew,
    IN RASCONNSTATE stateOld )

    // Returns true if 'stateNew' is farther along in the connection than
    // 'stateOld' false if the same or not as far along.
    //
{
    // This array is in the order events normally occur.
    //
    // !!! New EAP states?
    //
    static RASCONNSTATE aState[] =
    {
        (RASCONNSTATE )-1,
        RASCS_OpenPort,
        RASCS_PortOpened,
        RASCS_ConnectDevice,
        RASCS_DeviceConnected,
        RASCS_Interactive,
        RASCS_AllDevicesConnected,
        RASCS_StartAuthentication,
        RASCS_Authenticate,
        RASCS_InvokeEapUI,
        RASCS_AuthNotify,
        RASCS_AuthRetry,
        RASCS_AuthAck,
        RASCS_PasswordExpired,
        RASCS_AuthChangePassword,
        RASCS_AuthCallback,
        RASCS_CallbackSetByCaller,
        RASCS_PrepareForCallback,
        RASCS_WaitForModemReset,
        RASCS_WaitForCallback,
        RASCS_CallbackComplete,
        RASCS_RetryAuthentication,
        RASCS_ReAuthenticate,
        RASCS_Authenticated,
        RASCS_AuthLinkSpeed,
        RASCS_AuthProject,
        RASCS_Projected,
        RASCS_LogonNetwork,
        RASCS_SubEntryDisconnected,
        RASCS_SubEntryConnected,
        RASCS_Disconnected,
        RASCS_Connected,
        (RASCONNSTATE )-2,
    };

    RASCONNSTATE* pState;

    for (pState = aState; *pState != (RASCONNSTATE )-2; ++pState)
    {
        if (*pState == stateNew)
        {
            return FALSE;
        }
        else if (*pState == stateOld)
        {
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
DpPasswordExpired(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // RASCS_PasswordExpired state handling.  'PInfo' is the dialog context.
    // 'PState' is the subentry state.
    //
    // Returns true if successful, false otherwise.
    //
{
    TCHAR szOldPassword[ PWLEN + 1 ];
    BOOL fSuppliedOldPassword;

    TRACE( "DpPasswordExpired" );

    szOldPassword[ 0 ] = TEXT('\0');

    // Stash "good" username and password which are restored if the password
    // change fails.
    //
    pInfo->pszGoodUserName = StrDup( pInfo->pArgs->rdp.szUserName );

    // Whistler bug 254385 encode password when not being used
    // Assumed password was encoded previously
    //
    DecodePassword( pInfo->pArgs->rdp.szPassword );
    pInfo->pszGoodPassword = StrDup( pInfo->pArgs->rdp.szPassword );
    EncodePassword( pInfo->pArgs->rdp.szPassword );
    EncodePassword( pInfo->pszGoodPassword );

    fSuppliedOldPassword =
        (!pInfo->pArgs->pEntry->fAutoLogon || pInfo->pArgs->pNoUser);

    if (!ChangePasswordDlg(
            pInfo->hwndDlg, !fSuppliedOldPassword,
            szOldPassword, pInfo->pArgs->rdp.szPassword ))
    {
        // Whistler bug 254385 encode password when not being used
        //
        ZeroMemory( szOldPassword, sizeof(szOldPassword) );
        return FALSE;
    }

    if (pInfo->pArgs->pNoUser)
    {
        // Whistler bug 254385 encode password when not being used
        // Assumed password was encoded previously
        //
        DecodePassword( pInfo->pArgs->rdp.szPassword );
        lstrcpyn( 
            pInfo->pArgs->pNoUser->szPassword,
            pInfo->pArgs->rdp.szPassword,
            PWLEN + 1);
        EncodePassword( pInfo->pArgs->rdp.szPassword );
        EncodePassword( pInfo->pArgs->pNoUser->szPassword );
        *pInfo->pArgs->pfNoUserChanged = TRUE;
    }

    // The old password (in text form) is explicitly set, since in AutoLogon
    // case a text form has not yet been specified.  The old password in text
    // form is required to change the password.  The "old" private API expects
    // an ANSI argument.
    //
    if (!fSuppliedOldPassword)
    {
        // Whistler bug 254385 encode password when not being used
        // Assumed password was encoded by ChangePasswordDlg()
        //
        CHAR* pszOldPasswordA;

        DecodePassword( szOldPassword );
        pszOldPasswordA = StrDupAFromT( szOldPassword );
        if (pszOldPasswordA)
        {
            ASSERT( g_pRasSetOldPassword );
            g_pRasSetOldPassword( pInfo->hrasconn, pszOldPasswordA );
            ZeroMemory( pszOldPasswordA, lstrlenA( pszOldPasswordA ) );
            Free( pszOldPasswordA );
        }
    }

    ZeroMemory( szOldPassword, sizeof(szOldPassword) );

    if (pInfo->pArgs->rdp.szUserName[ 0 ] == TEXT('\0'))
    {
        // Explicitly set the username, effectively turning off AutoLogon for
        // the "resume" password authentication, where the new password should
        // be used.
        //
        lstrcpyn( pInfo->pArgs->rdp.szUserName, GetLogonUser(), UNLEN + 1 );
    }

    pState->sidState = 0;
    return TRUE;
}


BOOL
DpProjected(
    IN DPINFO* pInfo,
    IN DPSTATE* pState )

    // RASCS_Projected state handling.  'PInfo' is the dialog context.
    // 'PState' is the subentry state.
    //
    // Returns true if successful, false otherwise.
    //
{
    DWORD dwErr;
    RASAMB amb;
    RASPPPNBF nbf;
    RASPPPIPX ipx;
    RASPPPIP ip;
    RASPPPLCP lcp;
    RASSLIP slip;
    RASPPPCCP ccp;
    BOOL fIncomplete;
    DWORD dwfProtocols;
    TCHAR* pszLines;

    TRACE( "DpProjected" );

    pState->sidState = SID_S_Projected;

    //
    // If PORT_NOT_OPEN is indicated, it probably means that the 
    // server disconnected the connection before result dialog
    // was dismissed.  In that case, this is the 2nd time DpProjected
    // is called.  This time, the error is indicated by ras and the
    // state remains "projected".
    //
    // We need to return an error in this case so that the connection
    // isn't hung since this is the last indication RAS will give us.
    //
    // See bug 382254
    //

    TRACE1("DpProjected: dwErr:(%d)", pState->dwError);
    
    if ( (pState->dwError == ERROR_PORT_NOT_OPEN) ||
         (pState->dwError == ERROR_NO_CONNECTION) )     //See bug 169111 whistler
    {
        return FALSE;
    }

    // Do this little dance to ignore the error that comes back from the
    // "all-failed" projection since we detect this in the earlier
    // notification where pState->dwError == 0.  This avoids a race where the
    // API comes back with the error before we can hang him up.  This race
    // would not occur if we called RasHangUp from within the callback thread
    // (as recommended in our API doc).  It's the price we pay for posting the
    // error to the other thread in order to avoid holding the port open while
    // an error dialog is up.
    //
    else if (pState->dwError != 0)
    {
        pState->dwError = 0;
        DpCallbacksFlag( pInfo, 0 );
        return TRUE;
    }

    // Read projection info for all protocols, translating "not requested"
    // into an in-structure code for later reference.
    //
    dwErr = GetRasProjectionInfo(
        pState->hrasconnLink, &amb, &nbf, &ip, &ipx, &lcp, &slip, &ccp );
    if (dwErr != 0)
    {
        ErrorDlg( pInfo->hwndDlg, SID_OP_RasGetProtocolInfo, dwErr, NULL );
        return FALSE;
    }

    if (amb.dwError != ERROR_PROTOCOL_NOT_CONFIGURED)
    {
        // It's an AMB projection.
        //
        if (amb.dwError != 0)
        {
            // Translate AMB projection errors into regular error codes.  AMB
            // does not use the special PPP projection error mechanism.
            //
            pState->dwError = amb.dwError;
            lstrcpyn( 
                pState->szExtendedError, 
                amb.szNetBiosError,
                sizeof(pState->szExtendedError) / sizeof(TCHAR));
        }
        return TRUE;
    }

    // At this point, all projection information has been gathered
    // successfully and we know it's a PPP-based projection.  Now analyze the
    // projection results...
    //
    dwfProtocols = 0;
    fIncomplete = FALSE;
    if (DpProjectionError(
            &nbf, &ipx, &ip,
            &fIncomplete, &dwfProtocols, &pszLines, &pState->dwError ))
    {
        // A projection error occurred.
        //
        if (fIncomplete)
        {
            BOOL fStatus;
            BOOL fDisable;

            // An incomplete projection occurred, i.e. some requested CPs
            // connected and some did not.  Ask the user if what worked is
            // good enough or he wants to bail.
            //
            pState->dwError = 0;
            fDisable = FALSE;
            fStatus = ProjectionResultDlg(
                pInfo->hwndDlg, pszLines, &fDisable );
            Free( pszLines );

            if (fDisable)
            {
                pInfo->pArgs->dwfExcludedProtocols = dwfProtocols;
            }

            // Return now if user chose to hang up.
            //
            if (!fStatus)
            {
                return FALSE;
            }
        }
        else
        {
            // All CPs in the projection failed.  Process as a regular fatal
            // error with 'pState->dwError' set to the first error in NBF, IP,
            // or IPX, but with a format that substitutes the status argument
            // for the "Error nnn: Description" text.  This lets us patch in
            // the special multiple error projection text, while still giving
            // a meaningful help context.
            //
            Free0( pState->pszFormatArg );
            pState->pszFormatArg = pszLines;
            pState->sidFormatMsg = SID_FMT_ErrorMsgProject;
        }
    }

    //
    // pmay: 190394
    //
    // If the admin has a message, display it.
    //
    if ( (pState->dwError == NO_ERROR)     &&
         (wcslen (lcp.szReplyMessage) != 0)
       )
    {
        MSGARGS MsgArgs, *pMsgArgs = &MsgArgs;

        ZeroMemory(pMsgArgs, sizeof(MSGARGS));
        pMsgArgs->dwFlags = MB_OK | MB_ICONINFORMATION;
        pMsgArgs->apszArgs[0] = lcp.szReplyMessage;

        //MsgDlg(
        //    pInfo->hwndDlg,
        //    SID_ReplyMessageFmt,
        //    pMsgArgs);
    }

    pState->sidState = SID_S_Projected;
    return TRUE;
}


BOOL
DpProjectionError(
    IN RASPPPNBF* pnbf,
    IN RASPPPIPX* pipx,
    IN RASPPPIP* pip,
    OUT BOOL* pfIncomplete,
    OUT DWORD* pdwfFailedProtocols,
    OUT TCHAR** ppszLines,
    OUT DWORD* pdwError )

    // Figure out if a projection error occurred and, if so, build the
    // appropriate status/error text lines into '*ppszLines'.  '*PfIncomlete'
    // is set true if at least one CP succeeded and at least one failed.
    // '*pdwfFailedProtocols' is set to the bit mask of NP_* that failed.
    // '*pdwError' is set to the first error that occurred in NBF, IP, or IPX
    // in that order or 0 if none.  'pnbf', 'pipx', and 'pip' are projection
    // information for the respective protocols with dwError set to
    // ERROR_PROTOCOL_NOT_CONFIGURED if the protocols was not requested.
    //
    // This routine assumes that at least one protocol was requested.
    //
    // Returns true if a projection error occurred, false if not.  It's
    // caller's responsiblity to free '*ppszLines'.
    //
{
#define MAXPROJERRLEN 1024

    TCHAR szLines[ MAXPROJERRLEN ];
    BOOL fIp = (pip->dwError != ERROR_PROTOCOL_NOT_CONFIGURED);
    BOOL fIpx = (pipx->dwError != ERROR_PROTOCOL_NOT_CONFIGURED);
    BOOL fNbf = (pnbf->dwError != ERROR_PROTOCOL_NOT_CONFIGURED);
    BOOL fIpBad = (fIp && pip->dwError != 0);
    BOOL fIpxBad = (fIpx && pipx->dwError != 0);
    BOOL fNbfBad = (fNbf && pnbf->dwError != 0);

    TRACE( "DpProjectionError" );

    *pdwfFailedProtocols = 0;
    if (!fNbfBad && !fIpxBad && !fIpBad)
    {
        return FALSE;
    }

    if (fNbfBad)
    {
        *pdwfFailedProtocols |= NP_Nbf;
    }
    if (fIpxBad)
    {
        *pdwfFailedProtocols |= NP_Ipx;
    }
    if (fIpBad)
    {
        *pdwfFailedProtocols |= NP_Ip;
    }

    *pfIncomplete =
        ((fIp && pip->dwError == 0)
         || (fIpx && pipx->dwError == 0)
         || (fNbf && pnbf->dwError == 0));

    szLines[ 0 ] = 0;
    *ppszLines = NULL;
    *pdwError = 0;

    if (fIpBad || (*pfIncomplete && fIp))
    {
        if (fIpBad)
        {
            *pdwError = pip->dwError;
            DpAppendConnectErrorLine( szLines, SID_Ip, pip->dwError );
        }
        else
        {
            DpAppendConnectOkLine( szLines, SID_Ip );
        }
        DpAppendBlankLine( szLines );
    }

    if (fIpxBad || (*pfIncomplete && fIpx))
    {
        if (fIpxBad)
        {
            *pdwError = pipx->dwError;
            DpAppendConnectErrorLine( szLines, SID_Ipx, pipx->dwError );
        }
        else
        {
            DpAppendConnectOkLine( szLines, SID_Ipx );
        }
        DpAppendBlankLine( szLines );
    }

    if (fNbfBad || (*pfIncomplete && fNbf))
    {
        if (fNbfBad)
        {
            *pdwError = pnbf->dwError;
            DpAppendConnectErrorLine( szLines, SID_Nbf, pnbf->dwError );

            if (pnbf->dwNetBiosError)
            {
                DpAppendFailCodeLine( szLines, pnbf->dwNetBiosError );
            }

            if (pnbf->szNetBiosError[ 0 ] != '\0')
            {
                DpAppendNameLine( szLines, pnbf->szNetBiosError );
            }
        }
        else
        {
            DpAppendConnectOkLine( szLines, SID_Nbf );
        }
        DpAppendBlankLine( szLines );
    }

    *ppszLines = StrDup( szLines );
    return TRUE;
}


DWORD WINAPI
DpRasDialFunc2(
    ULONG_PTR dwCallbackId,
    DWORD dwSubEntry,
    HRASCONN hrasconn,
    UINT unMsg,
    RASCONNSTATE state,
    DWORD dwError,
    DWORD dwExtendedError )

    // RASDIALFUNC2 callback to receive RasDial events.
    //
    // Returns 0 to stop callbacks, 1 to continue callbacks (normal), and 2 to
    // tell RAS API that relevant entry information (like SLIP IP address) has
    // changed.
    //
{
    DWORD dwErr;
    DWORD dwCode;
    DPINFO* pInfo;
    DPSTATE* pState;
    BOOL fTerminateAsap;

    TRACE4( "/DpRasDialFunc2(rcs=%d,s=%d,e=%d,x=%d)",
        state, dwSubEntry, dwError, dwExtendedError );

            
    pInfo = (DPINFO* )dwCallbackId;
    if (pInfo->dwValid != 0xC0BBC0DE)
    {
        TRACE( "DpRasDialFunc2 returns for Late callback?" );

        return 0;
    }

    if (dwSubEntry == 0 || dwSubEntry > pInfo->cStates)
    {
        TRACE( "DpRasDialFunc2 returns for Subentry out of range?" );
        return 1;
    }

    pState = &pInfo->pStates[ dwSubEntry - 1 ];
    pState->state = state;
    pState->dwError = dwError;
    pState->dwExtendedError = dwExtendedError;

    // Post the event to the Dial Progress window and wait for it to be
    // processed before returning.  This avoids subtle problems with Z-order
    // and focus when a window is manipulated from two different threads.
    //
    TRACE1("Send RasEvent to Dial Progress window, subEntry:(%d)", dwSubEntry);
    TRACE1("Get dwError=(%d) from RasMan",pState->dwError);
    TRACE2("DpRasDialFunc2:Process:(%x),Thread(%x)", 
            GetCurrentProcessId,
            GetCurrentThreadId);
    TRACE2("DpRasDialFunc2:pInfo address (0x%x), Dialog Handle (0x%x)",
            pInfo, 
            pInfo->hwndDlg);
    
    dwCode = (DWORD)SendMessage( pInfo->hwndDlg, WM_RASEVENT, dwSubEntry, 0 );

    TRACE1( "\\DpRasDialFunc2: dwCode from SendMessage()=%d", dwCode );
    TRACE1("dwCode returned:(%d)", dwCode);

    // When callback function DpRasDialFunc2() returns 0, then RasMan wont 
    //call it again, so we wont return 0 unless all the message returned from
    //RasMan has been processed     for whislter bug 291613     gangz
    //
    {
     long ulCallbacksActive;

    //Check if current thread wants to terminate itself
    //
     ulCallbacksActive = DpOnOffPerThreadTerminateFlag(pInfo,  -1, &fTerminateAsap );

     TRACE1("Current thread's active:(%d)", ulCallbacksActive);
     
     if ( fTerminateAsap )
      {
        //decrease  pInfo->ulCallbacksActive and g_ulCallbacksActive
        //if necessary
        //
        TRACE("Current Thread wants to terminate itself, its fterminateASSP=1!");
        TRACE("Current thread will decrease its own and global active!");

        DpCallbacksFlag( pInfo, 0 );

        //reset per-thread terminateASAP flag
        //
        DpOnOffPerThreadTerminateFlag(pInfo,  0, NULL );
      }
      else
      {
        TRACE("Current Thread does NOT want to terminate itself,its fterminateASAP=0!");
      }

      //return the global number of active callbacks
      //
      ulCallbacksActive =  CallbacksActive( -1, NULL );

      TRACE1( "\\DpRasDialFunc2:g_ulCallbacksActive=%ld", ulCallbacksActive );

      //if g_ulCallbacksActive is already zero, we must return 0 to RasMan
      //

      {
          TRACE1("Global active:(%d)", ulCallbacksActive);
          TRACE1("Current thread's active:(%d)", 
                 DpOnOffPerThreadTerminateFlag(pInfo,  -1, NULL ) );
      }
      
      if ( 0 == ulCallbacksActive )
      {

         if ( 0 == DpOnOffPerThreadTerminateFlag(pInfo, -1, NULL ) )
         {
             TRACE( "Will terminate Callbacks " );
             dwCode = 0;
         }
         else
         {
          //For whistler bug 341662 366237     gangz
          //Something messed up the g_ulCallbacksActive flag
          // Now that g_ulCallbacksActive is 0, DpCallbacksFlag( pInfo, 0 ) wont 
          // decrease it now, it only decrease pInfo->ulCallbacksActive by 1
            TRACE("ReSync accured!");
            DpCallbacksFlag( pInfo, 0 );

          //then call  DpCallbacksFlag( pInfo, 1 ); to increase both the global
          // callbacks active flag g_ulCallbacksActive and per thread
          // pInfo->ulCallbacksActive
          //
            DpCallbacksFlag( pInfo, 1 );
          }
          
       }
       else if ( 0 == dwCode )
       {
        //Other callbacks are still active, wait for them
        //
        dwCode = 1;

        //Clear current pInfo->ulCallbacksActive if necessary when
        //this dwCode is returned by SendMessage() at above
        //
        TRACE("Other callbacks are still active, wait for them");
        TRACE("Just Clear current pInfo->ulCallbacksActive\n");

        DpCallbacksFlag( pInfo, 0 );

        //After cleared current pInfo->ulCallbacksActive, if the number
        //of active callbacks becomes zero, then we should return 0 to RasMan
        //
        ulCallbacksActive =  CallbacksActive( -1, &fTerminateAsap );

        TRACE1( "\\DpRasDialFunc2:g_ulCallbacksActive after clearing cur-thread =%ld", ulCallbacksActive );

        if ( 0 == ulCallbacksActive )
        {
             TRACE( "Will terminate Callbacks " );

             dwCode = 0;
         }
       }
     }

    if (dwCode == 0)
    {
        // Set thread-safe flag indicating callbacks have terminated.
        //
        DpCallbacksFlag( pInfo, 0 );
    }

    // Note: If 'dwCode' is 0, the other thread is racing to terminate the
    //       dialog.  Must not dereference 'pInfo' in this case.


    TRACE1( "\\DpRasDialFunc2:final dwCode returned=%d", dwCode );

    return dwCode;
}


VOID
DpTerm(
    IN HWND hwndDlg )

    // Called on WM_DESTROY.  'HwndDlg' is that handle of the dialog window.
    //
{
    DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwndDlg, DWLP_USER );

    TRACE( "DpTerm" );

    if (pInfo)
    {
        if (pInfo->pOldWndProc)
        {
            SetWindowLongPtr( pInfo->hwndDlg,
                GWLP_WNDPROC, (ULONG_PTR )pInfo->pOldWndProc );
        }

        Free0( pInfo->pStates );
        pInfo->dwValid = 0;

        Free( pInfo );
    }

    //For whistler bug 372078       gangz
    //
    {
        HICON hIcon=NULL;
        hIcon = (HICON)SendMessage( GetDlgItem( hwndDlg, CID_DP_Icon ),
                             STM_GETICON,
                             0,
                             0);
                              
        ASSERT(hIcon);
        if( hIcon )
        {
            DestroyIcon(hIcon);
        }
        else
        {
            TRACE("DpTerm:Destroy Icon failed");
        }
     }
    
}


LRESULT APIENTRY
DpWndProc(
    HWND hwnd,
    UINT unMsg,
    WPARAM wParam,
    LPARAM lParam )

    // Subclassed dialog window procedure.
    //
{
    DPINFO* pInfo = (DPINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
    ASSERT( pInfo );

    if (unMsg == WM_RASEVENT)
    {
        return DpEvent( pInfo, (DWORD )wParam );
    }

    return
        CallWindowProc(
            pInfo->pOldWndProc, hwnd, unMsg, wParam, lParam );
}


//----------------------------------------------------------------------------
// Dialer dialogs
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
DialerDlg(
    IN HWND hwndOwner,
    IN OUT DINFO* pInfo )

    // Determine if it's necessary, and if so, popup one of the variations of
    // the dialer dialog, i.e. the prompter for user/password/domain, phone
    // number, and location.  'HwndOwner' is the owning window.  'PInfo' is
    // the dial dialog common context.
    //
    // Returns true if no dialog is needed or user chooses OK.
    //
{
    INT_PTR nStatus = FALSE;
    int nDid;
    DWORD dwfMode;
    DRARGS args;

    TRACE( "DialerDlg" );

    do
    {
        dwfMode = 0;

        if (!pInfo->pEntry->fAutoLogon
            && pInfo->pEntry->fPreviewUserPw
            && (!(pInfo->pArgs->dwFlags & RASDDFLAG_NoPrompt)
                || (pInfo->fUnattended && !HaveSavedPw( pInfo ))))
        {
            dwfMode |= DR_U;

            if (pInfo->pEntry->fPreviewDomain)
            {
                dwfMode |= DR_D;
            }
        }

        if (pInfo->pEntry->fPreviewPhoneNumber
            && (!(pInfo->pArgs->dwFlags & RASDDFLAG_NoPrompt)
                || (pInfo->fUnattended && !HaveSavedPw( pInfo ))))
        {
            DTLNODE* pNode;
            PBLINK* pLink;

            dwfMode |= DR_N;

            // Location controls mode only when at least one phone number in
            // the list is TAPI-enabled.
            //
            pNode = DtlGetFirstNode( pInfo->pEntry->pdtllistLinks );
            pLink = (PBLINK* )DtlGetData( pNode );
            for (pNode = DtlGetFirstNode( pLink->pdtllistPhones );
                 pNode;
                 pNode = DtlGetNextNode( pNode ))
            {
                PBPHONE* pPhone = (PBPHONE* )DtlGetData( pNode );

                if (pPhone->fUseDialingRules)
                {
                    dwfMode |= DR_L;
                    break;
                }
            }
        }

        // Customize the dialing flags for the type of eap authentication
        // specified for this entry (if any)
        if (DialerEapAssignMode(pInfo, &dwfMode) != NO_ERROR)
            break;

        if (dwfMode == DR_U)
        {
            nDid = DID_DR_DialerU;
        }
        else if (dwfMode == (DR_U | DR_D))
        {
            nDid = DID_DR_DialerUD;
        }
        else if (dwfMode == (DR_U | DR_N))
        {
            nDid = DID_DR_DialerUN;
        }
        else if (dwfMode == (DR_U | DR_N | DR_L))
        {
            nDid = DID_DR_DialerUNL;
        }
        else if (dwfMode == (DR_U | DR_D | DR_N))
        {
            nDid = DID_DR_DialerUDN;
        }
        else if (dwfMode == (DR_U | DR_D | DR_N | DR_L))
        {
            nDid = DID_DR_DialerUDNL;
        }
        else if (dwfMode == DR_N)
        {
            nDid = DID_DR_DialerN;
        }
        else if (dwfMode == (DR_N | DR_L))
        {
            nDid = DID_DR_DialerNL;
        }
        else if (dwfMode == DR_I) {
            nDid = DID_DR_DialerI;
        }
        else if (dwfMode == (DR_I | DR_N)) {
            nDid = DID_DR_DialerIN;
        }
        else if (dwfMode == (DR_I | DR_N | DR_L)) {
            nDid = DID_DR_DialerINL;
        }

        // pmay:  The following 3 permutations of the
        // dialer dialog were added for bug 183577 which
        // states that eap modules (that use DR_I) want to
        // have the domain field available to them as well.
        else if (dwfMode == (DR_I | DR_D)) {
            nDid = DID_DR_DialerID;
        }
        else if (dwfMode == (DR_I | DR_D | DR_N)) {
            nDid = DID_DR_DialerIDN;
        }
        else if (dwfMode == (DR_I | DR_D | DR_N | DR_L)) {
            nDid = DID_DR_DialerIDNL;
        }

        else
        {
            ASSERT( dwfMode == 0 );
            return TRUE;
        }

        args.pDinfo = pInfo;
        args.dwfMode = dwfMode;
        args.fReload = FALSE;

        nStatus =
            (BOOL )DialogBoxParam(
                g_hinstDll,
                MAKEINTRESOURCE( nDid ),
                hwndOwner,
                DrDlgProc,
                (LPARAM )&args );

        if (nStatus == -1)
        {
            ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
            nStatus = FALSE;
        }
    }
    while (args.fReload);

    return (BOOL )nStatus;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return DrInit( hwnd, (DRARGS* )lparam );
        }

        case WM_HELP:
        case WM_CONTEXTMENU:
        {
            ContextHelp( g_adwDrHelp, hwnd, unMsg, wparam, lparam );
            break;
        }

        case WM_COMMAND:
        {
            DRINFO* pInfo = (DRINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

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

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

    return FALSE;
}

VOID
DrGetFriendlyFont(
    IN HWND hwnd,
    IN BOOL fUpdate,
    OUT HFONT* phFont )

    // Whistler bug: 195480 Dial-up connection dialog - Number of asterisks
    // does not match the length of the password and causes confusion
    //
{
    LOGFONT BoldLogFont;
    HFONT   hFont;
    HDC     hdc;

    *phFont = NULL;

    // Get the font used by the specified window
    //
    hFont = (HFONT)SendMessage( hwnd, WM_GETFONT, 0, 0L );
    if (NULL == hFont)
    {
        // If not found then the control is using the system font
        //
        hFont = (HFONT)GetStockObject( SYSTEM_FONT );
    }

    if (hFont && GetObject( hFont, sizeof(BoldLogFont), &BoldLogFont ))
    {
        if (fUpdate)
        {
            BoldLogFont.lfItalic = TRUE;
        }

        hdc = GetDC( hwnd );
        if (hdc)
        {
            *phFont = CreateFontIndirect( &BoldLogFont );
            ReleaseDC( hwnd, hdc );
        }
    }

    return;
}

DWORD
DrEnableDisablePwControls(
    IN DRINFO* pInfo,
    IN BOOL fEnable )
{
    if (pInfo->pArgs->pDinfo->fIsPublicPbk)
    {
        EnableWindow( pInfo->hwndRbSaveForEveryone, fEnable );
    }
    else
    {
        EnableWindow( pInfo->hwndRbSaveForEveryone, FALSE );
    }

    EnableWindow( pInfo->hwndRbSaveForMe, fEnable );

    return NO_ERROR;
}

VOID
DrClearFriendlyPassword(
    IN DRINFO* pInfo,
    IN BOOL fFocus )
{
    SetWindowText( pInfo->hwndEbPw, L"" );

    if (fFocus)
    {
        SendMessage( pInfo->hwndEbPw, EM_SETPASSWORDCHAR,
            pInfo->szPasswordChar, 0 );

        if (pInfo->hNormalFont)
        {
            SendMessage(
                pInfo->hwndEbPw,
                WM_SETFONT,
                (WPARAM)pInfo->hNormalFont,
                MAKELPARAM(TRUE, 0) );
        }

        SetFocus( pInfo->hwndEbPw );
    }

    return;
}

VOID
DrDisplayFriendlyPassword(
    IN DRINFO* pInfo,
    IN TCHAR* pszFriendly )
{
    if (pszFriendly)
    {
        SendMessage( pInfo->hwndEbPw, EM_SETPASSWORDCHAR, 0, 0 );
        SetWindowText( pInfo->hwndEbPw, pszFriendly );
    }
    else
    {
        SetWindowText( pInfo->hwndEbPw, L"" );
    }

    if (pInfo->hItalicFont)
    {
        SendMessage(
            pInfo->hwndEbPw,
            WM_SETFONT,
            (WPARAM)pInfo->hItalicFont,
            MAKELPARAM(TRUE, 0) );
    }

    return;
}

BOOL
DrIsPasswordStyleEnabled(
    IN HWND hWnd )
{
    return SendMessage( hWnd, EM_GETPASSWORDCHAR, 0, 0 ) ? TRUE : FALSE;
}

BOOL
DrCommand(
    IN DRINFO* 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.
    //
{
    DWORD dwErr;

    TRACE3( "DrCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case CID_DR_CLB_Numbers:
        {
            if (wNotification == CBN_SELCHANGE)
            {
                DrNumbersSelChange( pInfo );
                return TRUE;
            }
            break;
        }

        case CID_DR_CB_SavePassword:
        {
            BOOL fEnable = Button_GetCheck( hwndCtrl );
            DrEnableDisablePwControls( pInfo, fEnable );
            DrPopulatePasswordField( pInfo, FALSE, FALSE );
            return TRUE;
        }

        // Whistler bug: 195480 Dial-up connection dialog - Number of asterisks
        // does not match the length of the password and causes confusion
        //
        case CID_DR_EB_Password:
        {
            // This is a hack really so that we restore the Tab Stop to the
            // username field. The reason it had to be removed was because we
            // were receiving complaints that the focus shouldn't always go to
            // the username field if it's non-null. The only way to fix this,
            // since windows sets the initial focus to the first visible non-
            // hidden tab stopped field, is to remove the tab stop temporarily
            // from the username field.
            //
            if (wNotification == EN_KILLFOCUS)
            {
                LONG lStyle = GetWindowLong( pInfo->hwndEbUser, GWL_STYLE );

                if (!(lStyle & WS_TABSTOP))
                {
                    // If we detect tap stop removed from the username field,
                    // restore it. Since this case only fires when the password
                    // was not previously saved on init, we can return here.
                    //
                    SetWindowLong( pInfo->hwndEbUser, GWL_STYLE,
                        lStyle | WS_TABSTOP );
                    return TRUE;
                }
                // If the user leaves the password field w/o typing a new
                // password, and a saved password is present, restore the
                // friendly password text.
                //
                DrPopulatePasswordField( pInfo, FALSE, FALSE );
                return TRUE;
            }
            // If the password field ever receives the focus, clear the
            // friendly password text if applicable.
            //
            else if (wNotification == EN_SETFOCUS &&
                     !DrIsPasswordStyleEnabled( pInfo->hwndEbPw ))
            {
                DrPopulatePasswordField( pInfo, FALSE, TRUE );
                return TRUE;
            }
            break;
        }

        case CID_DR_LB_Locations:
        {
            if (wNotification == CBN_SELCHANGE)
            {
                DrLocationsSelChange( pInfo );
                return TRUE;
            }
            break;
        }

        case CID_DR_PB_Rules:
        {
            DrEditSelectedLocation( pInfo );
            return TRUE;
        }

        case CID_DR_PB_Properties:
        {
            DrProperties( pInfo );
            DrPopulatePasswordField( pInfo, FALSE, FALSE );
            return TRUE;
        }

        case CID_DR_RB_SaveForMe:
        case CID_DR_RB_SaveForEveryone:
        {
            DrPopulatePasswordField( pInfo, FALSE, FALSE );
            DrPopulateIdentificationFields( pInfo, (wId == CID_DR_RB_SaveForMe));
            return TRUE;
        }

        case IDOK:
        case CID_DR_PB_DialConnect:
        {
            DrSave( pInfo );
            EndDialog( pInfo->hwndDlg, TRUE );
            return TRUE;
        }

        case IDCANCEL:
        case CID_DR_PB_Cancel:
        {
            EndDialog( pInfo->hwndDlg, FALSE );
            return TRUE;
        }

        case IDHELP:
        case CID_DR_PB_Help:
        {
            TCHAR* pszCmdLine;

            // Help button now invokes troubleshooting help per bug 210247.
            //
            pszCmdLine = PszFromId( g_hinstDll, SID_DialerHelpCmdLine );
            if (pszCmdLine)
            {
                STARTUPINFO sInfo;
                PROCESS_INFORMATION pInfo;

                ZeroMemory( &sInfo, sizeof(sInfo) );
                sInfo.cb = sizeof(sInfo);
                ZeroMemory( &pInfo, sizeof(pInfo) );
                CreateProcess(
                    NULL, pszCmdLine, NULL, NULL, FALSE,
                    0, NULL, NULL, &sInfo, &pInfo );

                Free( pszCmdLine );
            }
            return TRUE;
        }
    }

    return FALSE;
}


BOOL CALLBACK
DrClbNumbersEnumChildProc(
    IN HWND hwnd,
    IN LPARAM lparam )

    // Standard Windows EnumChildProc routine called back for each child
    // window of the 'ClbNumbers' control.
    //
{
    DRINFO* pInfo;
    LONG lId;

    pInfo = (DRINFO* )lparam;

    // There only one child window and it's the edit window.
    //
    pInfo->hwndClbNumbersEb = hwnd;

    return FALSE;
}


BOOL CALLBACK
DrClbNumbersEnumWindowsProc(
    IN HWND hwnd,
    IN LPARAM lparam )

    // Standard Windows EnumWindowsProc routine called back for each top-level
    // window.
    //
{
    RECT rect;

    GetWindowRect( hwnd, &rect );
    if (rect.right - rect.left == DR_BOGUSWIDTH)
    {
        // This window has the unusual bogus width, so it must be the
        // list-box.
        //
        ((DRINFO* )lparam)->hwndClbNumbersLb = hwnd;
        return FALSE;
    }

    return TRUE;
}


LRESULT APIENTRY
DrClbNumbersEbWndProc(
    HWND hwnd,
    UINT unMsg,
    WPARAM wParam,
    LPARAM lParam )

    // Subclassed combo-box edit-box child window procedure providing "manual
    // edit" behavior.
    //
    // Return value depends on message type.
    //
{
    DRINFO* pInfo;

    switch (unMsg)
    {
        case WM_SETTEXT:
        {
            // Prevent the combo-box from setting the contents of the edit box
            // by discarding the request and reporting success.
            //
            return TRUE;
        }

        case DR_WM_SETTEXT:
        {
            // Convert our private SETTEXT to a regular SETTEXT and pass it on
            // to the edit control.
            //
            unMsg = WM_SETTEXT;
            break;
        }
    }

    // Call the previous window procedure for everything else.
    //
    pInfo = (DRINFO* )GetProp( hwnd, g_contextId );
    ASSERT( pInfo );

    return
        CallWindowProc(
            pInfo->wndprocClbNumbersEb, hwnd, unMsg, wParam, lParam );
}


LRESULT APIENTRY
DrClbNumbersLbWndProc(
    HWND hwnd,
    UINT unMsg,
    WPARAM wParam,
    LPARAM lParam )

    // Subclassed combo-box list-box child window procedure providing "manual
    // edit" behavior.
    //
    // Return value depends on message type.
    //
{
    DRINFO* pInfo;

    pInfo = (DRINFO* )GetProp( hwnd, g_contextId );
    ASSERT( pInfo );

    switch (unMsg)
    {
        case LB_FINDSTRINGEXACT:
        case LB_FINDSTRING:
        {
            // This prevents the edit-box "completion" behavior of the
            // combo-box, i.e. it prevents the edit-box contents from being
            // extended to the closest match in the list.
            //
            return -1;
        }

        case LB_SETCURSEL:
        case LB_SETTOPINDEX:
        {
            // Prevent the "match selection to edit-box" combo-box behavior by
            // discarding any attempts to set the selection or top index to
            // anything other than what we set.
            //
            if (wParam != pInfo->pLink->iLastSelectedPhone)
            {
                return -1;
            }
            break;
        }
    }

    // Call the previous window procedure for everything else.
    //
    return
        CallWindowProc(
            pInfo->wndprocClbNumbersLb, hwnd, unMsg, wParam, lParam );
}


VOID
DrEditSelectedLocation(
    IN DRINFO* pInfo )

    // Called when the Dialing Rules button is pressed.  'PInfo' is the dialog
    // context.
    //
{
    DWORD dwErr;
    INT iSel;
    DRNUMBERSITEM* pItem;

    TRACE( "DrEditSelectedLocation" );

    // Look up the phone number information for the selected number.
    //
    pItem = (DRNUMBERSITEM* )ComboBox_GetItemDataPtr(
        pInfo->hwndClbNumbers, ComboBox_GetCurSel( pInfo->hwndClbNumbers ) );
    ASSERT( pItem );

    if(NULL == pItem)
    {   
        return;
    }
    
    ASSERT( pItem->pPhone->fUseDialingRules );

    // Popup TAPI dialing rules dialog.
    //
    dwErr = TapiLocationDlg(
        g_hinstDll,
        &pInfo->hlineapp,
        pInfo->hwndDlg,
        pItem->pPhone->dwCountryCode,
        pItem->pPhone->pszAreaCode,
        pItem->pPhone->pszPhoneNumber,
        0 );

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

    // Might have changed the location list so re-fill it.
    //
    DrFillLocationList( pInfo );
}


DWORD
DrFillLocationList(
    IN DRINFO* pInfo )

    // Fills the dropdown list of locations and sets the current selection.
    //
    // Returns 0 if successful, or an error code.
    //
{
    DWORD dwErr;
    LOCATION* pLocations;
    LOCATION* pLocation;
    DWORD cLocations;
    DWORD dwCurLocation;
    DWORD i;

    TRACE( "DrFillLocationList" );

    ComboBox_ResetContent( pInfo->hwndLbLocations );

    pLocations = NULL;
    cLocations = 0;
    dwCurLocation = 0xFFFFFFFF;
    dwErr = GetLocationInfo(
        g_hinstDll, &pInfo->hlineapp,
        &pLocations, &cLocations, &dwCurLocation );
    if (dwErr != 0)
    {
        return dwErr;
    }

    for (i = 0, pLocation = pLocations;
         i < cLocations;
         ++i, ++pLocation)
    {
        INT iItem;

        iItem = ComboBox_AddItem(
            pInfo->hwndLbLocations, pLocation->pszName,
            (VOID* )UlongToPtr(pLocation->dwId ));

        if (pLocation->dwId == dwCurLocation)
        {
            ComboBox_SetCurSelNotify( pInfo->hwndLbLocations, iItem );
        }
    }

    FreeLocationInfo( pLocations, cLocations );
    ComboBox_AutoSizeDroppedWidth( pInfo->hwndLbLocations );

    return dwErr;
}


VOID
DrFillNumbersList(
    IN DRINFO* pInfo )

    // Fill the "Dial" combo-box with phone numbers and comments, and
    // re-select the selected item in the list, or if none, the last one
    // selected as specified in the PBLINK.
    //
{
    DTLNODE* pNode;
    PBLINK* pLink;
    PBPHONE* pPhone;
    INT cItems;
    INT i;

    DrFreeClbNumbers( pInfo );

    for (pNode = DtlGetFirstNode( pInfo->pLink->pdtllistPhones ), i = 0;
         pNode;
         pNode = DtlGetNextNode( pNode ), ++i)
    {
        TCHAR szBuf[ RAS_MaxPhoneNumber + RAS_MaxDescription + 3 + 1 ];
        DRNUMBERSITEM* pItem;

        pPhone = (PBPHONE* )DtlGetData( pNode );
        ASSERT( pPhone );

        pItem = Malloc( sizeof(DRNUMBERSITEM) );
        if (!pItem)
        {
            break;
        }

        // Build the "<number> - <comment>" string in 'szBuf'.
        //
        pItem->pszNumber =
            LinkPhoneNumberFromParts(
                g_hinstDll, &pInfo->hlineapp,
                pInfo->pArgs->pDinfo->pUser,  pInfo->pArgs->pDinfo->pEntry,
                pInfo->pLink, i, NULL, FALSE );

        if (!pItem->pszNumber)
        {
            // Should not happen.
            //
            Free( pItem );
            break;
        }

        lstrcpyn( szBuf, pItem->pszNumber,  RAS_MaxPhoneNumber);
        if (pPhone->pszComment && !IsAllWhite( pPhone->pszComment ))
        {
            DWORD dwLen, dwSize = sizeof(szBuf) / sizeof(TCHAR);
            
            lstrcat( szBuf, TEXT(" - ") );
            dwLen = lstrlen(szBuf) + 1;
            lstrcpyn( 
                szBuf + (dwLen - 1), 
                pPhone->pszComment,
                dwSize - dwLen );
        }

        pItem->pPhone = pPhone;

        ComboBox_AddItem( pInfo->hwndClbNumbers, szBuf, pItem );
    }

    // Make the selection and trigger the update of the edit-box to the number
    // without the comment.
    //
    cItems = ComboBox_GetCount( pInfo->hwndClbNumbers );
    if (cItems > 0)
    {
        if ((INT )pInfo->pLink->iLastSelectedPhone >= cItems)
        {
            pInfo->pLink->iLastSelectedPhone = 0;
        }

        ListBox_SetTopIndex(
            pInfo->hwndClbNumbersLb, pInfo->pLink->iLastSelectedPhone );
        ComboBox_SetCurSelNotify(
            pInfo->hwndClbNumbers, pInfo->pLink->iLastSelectedPhone );
    }

    ComboBox_AutoSizeDroppedWidth( pInfo->hwndClbNumbers );
}


VOID
DrFreeClbNumbers(
    IN DRINFO* pInfo )

    // Free up the displayable number string associated with each entry of the
    // phone number combo-box leaving the box empty.
    //
{
    DRNUMBERSITEM* pItem;

    while (pItem = ComboBox_GetItemDataPtr( pInfo->hwndClbNumbers, 0 ))
    {
        ComboBox_DeleteString( pInfo->hwndClbNumbers, 0 );
        Free( pItem->pszNumber );
        Free( pItem );
    }
}


DWORD
DrFindAndSubclassClbNumbersControls(
    IN DRINFO* pInfo )

    // Locate and sub-class the edit-box and list-box child controls of the
    // phone number combo-box.  This is necessary to get "manual edit"
    // behavior, i.e. prevent the combo-box from automatically updating the
    // edit box at various times.  We need this because the phone number
    // comments are to be appended in the list, but not in the edit box.
    // 'PInfo' is the dialog context.
    //
    // Returns 0 if successful or an error code.
    //
{
    DWORD dxOld;

    // Find the edit window which is simply a child enumeration.
    //
    EnumChildWindows(
        pInfo->hwndClbNumbers,
        DrClbNumbersEnumChildProc,
        (LPARAM)pInfo );

    if (!pInfo->hwndClbNumbersEb)
    {
        return ERROR_NOT_FOUND;
    }

    // Find the list window which *sigh* doesn't show up in the child
    // enumeration though it has WS_CHILD style because Windows sets it's
    // parent window to NULL after it is created.  To find it, we set the
    // dropped width to an unusual bogus value, then search all windows for
    // one with that width.
    //
    dxOld = (DWORD )SendMessage(
        pInfo->hwndClbNumbers, CB_GETDROPPEDWIDTH, 0, 0 );
    SendMessage( pInfo->hwndClbNumbers,
        CB_SETDROPPEDWIDTH, (WPARAM )DR_BOGUSWIDTH, 0 );
    EnumWindows( DrClbNumbersEnumWindowsProc, (LPARAM)pInfo );
    SendMessage( pInfo->hwndClbNumbers,
        CB_SETDROPPEDWIDTH, (WPARAM )dxOld, 0 );

    if (!pInfo->hwndClbNumbersLb)
    {
        return ERROR_NOT_FOUND;
    }

    // Subclass the windows after associating the dialog context with them for
    // retrieval in the WndProcs.
    //
    SetProp( pInfo->hwndClbNumbersEb, g_contextId, pInfo );
    SetProp( pInfo->hwndClbNumbersLb, g_contextId, pInfo );

    pInfo->wndprocClbNumbersEb =
        (WNDPROC )SetWindowLongPtr(
            pInfo->hwndClbNumbersEb,
            GWLP_WNDPROC, (ULONG_PTR )DrClbNumbersEbWndProc );

    pInfo->wndprocClbNumbersLb =
        (WNDPROC )SetWindowLongPtr(
            pInfo->hwndClbNumbersLb,
            GWLP_WNDPROC, (ULONG_PTR )DrClbNumbersLbWndProc );


    return 0;
}

void
DrEnsureNetshellLoaded (
    IN DRINFO* pInfo)
{
    // Load the netshell utilities interface.  The interface is freed in PeTerm.
    //
    if (!pInfo->pNetConUtilities)
    {
        // Initialize the NetConnectionsUiUtilities
        //
        HrCreateNetConnectionUtilities( &pInfo->pNetConUtilities );
    }
}

BOOL
DrInit(
    IN HWND hwndDlg,
    IN DRARGS* pArgs )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PArgs' is caller's arguments to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    DRINFO* pInfo;
    PBENTRY* pEntry;
    BOOL fEnableProperties;

    TRACE( "DrInit" );

    // Allocate the dialog context block.  Initialize minimally for proper
    // cleanup, then attach to the dialog window.
    //
    {
        pInfo = Malloc( sizeof(*pInfo) );
        if (!pInfo)
        {
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

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

        SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
        TRACE( "Context set" );
    }

    pEntry = pArgs->pDinfo->pEntry;

    pInfo->hwndBmDialer = GetDlgItem( hwndDlg, CID_DR_BM_Useless );
    ASSERT( pInfo->hwndBmDialer );

    // Look up control handles.
    //
    if ((pArgs->dwfMode & DR_U) ||
        (pArgs->dwfMode & DR_I))
    {
        pInfo->hwndEbUser = GetDlgItem( hwndDlg, CID_DR_EB_User );
        ASSERT( pInfo->hwndEbUser );

        if (pArgs->dwfMode & DR_U)
        {
            pInfo->hwndEbPw = GetDlgItem( hwndDlg, CID_DR_EB_Password );
            ASSERT( pInfo->hwndEbPw );

            pInfo->hwndCbSavePw = GetDlgItem( hwndDlg, CID_DR_CB_SavePassword );
            ASSERT( pInfo->hwndCbSavePw );
            pInfo->hwndRbSaveForMe = GetDlgItem( hwndDlg, CID_DR_RB_SaveForMe );
            ASSERT( pInfo->hwndRbSaveForMe );
            pInfo->hwndRbSaveForEveryone =
                GetDlgItem( hwndDlg, CID_DR_RB_SaveForEveryone );
            ASSERT( pInfo->hwndRbSaveForEveryone );
        }

        if (pArgs->dwfMode & DR_D)
        {
            pInfo->hwndEbDomain = GetDlgItem( hwndDlg, CID_DR_EB_Domain );
            ASSERT( pInfo->hwndEbDomain );
        }
    }

    if (pArgs->dwfMode & DR_N)
    {
        pInfo->hwndClbNumbers = GetDlgItem( hwndDlg, CID_DR_CLB_Numbers );
        ASSERT( pInfo->hwndClbNumbers );

        if (pArgs->dwfMode & DR_L)
        {
            pInfo->hwndStLocations = GetDlgItem( hwndDlg, CID_DR_ST_Locations );
            ASSERT( pInfo->hwndStLocations );
            pInfo->hwndLbLocations = GetDlgItem( hwndDlg, CID_DR_LB_Locations );
            ASSERT( pInfo->hwndLbLocations );
            pInfo->hwndPbRules = GetDlgItem( hwndDlg, CID_DR_PB_Rules );
            ASSERT( pInfo->hwndPbRules );
        }
    }

    pInfo->hwndPbProperties = GetDlgItem( hwndDlg, CID_DR_PB_Properties );
    ASSERT( pInfo->hwndPbProperties );

    // In location-enabled mode, popup TAPI's "first location" dialog if they
    // are uninitialized.  Typically, this will do nothing.
    //
    if (pArgs->dwfMode & DR_L)
    {
        dwErr = TapiNoLocationDlg( g_hinstDll, &pInfo->hlineapp, hwndDlg );
        if (dwErr != 0)
        {
            // Error here is treated as a "cancel" per bug 288385.
            //
            pArgs->pDinfo->pArgs->dwError = 0;
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }
    }

    // Set the title.
    //
    {
        TCHAR* pszTitleFormat;
        TCHAR* pszTitle;
        TCHAR* apszArgs[ 1 ];

        if (pArgs->pDinfo->fUnattended)
        {
            pszTitleFormat = PszFromId( g_hinstDll, SID_DR_ReconnectTitle );
        }
        else
        {
            pszTitleFormat = GetText( hwndDlg );
        }

        if (pszTitleFormat)
        {
            apszArgs[ 0 ] = pEntry->pszEntryName;
            pszTitle = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszTitleFormat, 0, 0, (LPTSTR )&pszTitle, 1,
                (va_list* )apszArgs );

            Free( pszTitleFormat );

            if (pszTitle)
            {
                SetWindowText( hwndDlg, pszTitle );
                LocalFree( pszTitle );
            }
        }
    }

    // Change the Dial button to Connect for non-phone devices.
    //
    if (pEntry->dwType != RASET_Phone)
    {
        TCHAR* psz;

        psz = PszFromId( g_hinstDll, SID_ConnectButton );
        if (psz)
        {
            SetWindowText( GetDlgItem( hwndDlg, CID_DR_PB_DialConnect ), psz );
            Free( psz );
        }
    }

    // Initialize credentials section.
    //
    if (pArgs->dwfMode & DR_U)
    {
        ASSERT( !pEntry->fAutoLogon );

        // Fill credential fields with initial values.
        //
        Edit_LimitText( pInfo->hwndEbUser, UNLEN );
        SetWindowText( pInfo->hwndEbUser, pArgs->pDinfo->rdp.szUserName );
        Edit_LimitText( pInfo->hwndEbPw, PWLEN );

        if (pArgs->dwfMode & DR_D)
        {
            Edit_LimitText( pInfo->hwndEbDomain, DNLEN );
            SetWindowText( pInfo->hwndEbDomain, pArgs->pDinfo->rdp.szDomain );
        }

        if (pArgs->pDinfo->pNoUser || pArgs->pDinfo->fDisableSavePw)
        {
            // Can't stash password without a logon context, so hide the
            // checkbox.
            //
            ASSERT( !HaveSavedPw( pArgs->pDinfo )) ;
            EnableWindow( pInfo->hwndCbSavePw, FALSE );
            EnableWindow( pInfo->hwndRbSaveForMe, FALSE );
            EnableWindow( pInfo->hwndRbSaveForEveryone, FALSE );

            // Whistler bug 400714 RAS does not grab password at winlogon time
            // when Connect dialog is displayed
            //
            // Whistler bug 254385 encode password when not being used
            // Assumed password was encoded previously
            //
            DecodePassword( pArgs->pDinfo->rdp.szPassword );
            SetWindowText( pInfo->hwndEbPw, pArgs->pDinfo->rdp.szPassword );
            EncodePassword( pArgs->pDinfo->rdp.szPassword );
        }
        else
        {
            // Whistler bug: 195480 Dial-up connection dialog - Number of
            // asterisks does not match the length of the password and causes
            // confusion
            //
            // Init the password character. Default to the round dot if we fail
            // to get it.
            //
            pInfo->szPasswordChar = (WCHAR) SendMessage( pInfo->hwndEbPw,
                                                EM_GETPASSWORDCHAR, 0, 0 );
            if (!pInfo->szPasswordChar)
            {
                pInfo->szPasswordChar = 0x25CF;
            }

            // Init the fonts for the password field
            //
            DrGetFriendlyFont( hwndDlg, TRUE, &(pInfo->hItalicFont) );
            DrGetFriendlyFont( hwndDlg, FALSE, &(pInfo->hNormalFont) );

            // Check "save password" and render the type of saved
            // password.
            //
            Button_SetCheck(
               pInfo->hwndCbSavePw,
               HaveSavedPw( pArgs->pDinfo ));

            if ((!pArgs->pDinfo->fIsPublicPbk) ||
                (!HaveSavedPw( pArgs->pDinfo )))
            {
                // If this is a for-me-only connection or if
                // there is no saved password, then  initialize the 
                // pw save type to save-for-me
                //
                Button_SetCheck( pInfo->hwndRbSaveForMe, TRUE );
            }
            else
            {
                // Check the appropriate radio button
                // Note that a per-user password is always used if
                // both a per-user and global password are saved.
                // Dont check global password if its a per-user connectoid
                //
                Button_SetCheck( 
                    (pArgs->pDinfo->fHaveSavedPwUser)   ?
                        pInfo->hwndRbSaveForMe          :
                        pInfo->hwndRbSaveForEveryone,
                    TRUE);
            }

            DrEnableDisablePwControls( pInfo, HaveSavedPw( pArgs->pDinfo ) );

            // Whistler bug: 195480 Dial-up connection dialog - Number of
            // asterisks does not match the length of the password and causes
            // confusion
            //
            DrPopulatePasswordField( pInfo, TRUE, FALSE );

        }
    }

    if (pArgs->dwfMode & DR_N)
    {
        pInfo->pLinkNode = NULL;
        if (pArgs->pDinfo->pArgs->dwSubEntry > 0)
        {
            // Look up the API caller specified link.
            //
            pInfo->pLinkNode =
                DtlNodeFromIndex(
                    pArgs->pDinfo->pEntry->pdtllistLinks,
                    pArgs->pDinfo->pArgs->dwSubEntry - 1 );
        }

        if (!pInfo->pLinkNode)
        {
            // Look up the default (first) link.
            //
            pInfo->pLinkNode =
                DtlGetFirstNode( pArgs->pDinfo->pEntry->pdtllistLinks );
        }

        ASSERT( pInfo->pLinkNode );
        pInfo->pLink = (PBLINK* )DtlGetData( pInfo->pLinkNode );

        dwErr = DrFindAndSubclassClbNumbersControls( pInfo );
        if (dwErr != 0)
        {
            pArgs->pDinfo->pArgs->dwError = ERROR_NOT_FOUND;
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

        // Ignore any "last selected" information when the "try next on fail"
        // flag is set.  New entries will not have "last selected" non-0 in
        // this case but pre-existing entries might, so double-check here.
        // See bug 150958.
        //
        if (pInfo->pLink->fTryNextAlternateOnFail)
        {
            pInfo->pLink->iLastSelectedPhone = 0;
        }

        // Record the initially selected phone number, used to determine
        // whether user has changed the selection.
        //
        pInfo->iFirstSelectedPhone = pInfo->pLink->iLastSelectedPhone;

        DrFillNumbersList( pInfo );

        if (pArgs->dwfMode & DR_L)
        {
            DrFillLocationList( pInfo );
        }
    }

    // danielwe: Bug #222744, scottbri Bug #245310
    // Disable Properties... button if user does not have sufficent rights.
    //
    {
        HRESULT hr;

        hr = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
        if (hr == RPC_E_CHANGED_MODE)
        {
            hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
        }

        if (hr == S_OK || hr == S_FALSE)
        {
            pInfo->fComInitialized = TRUE;
        }
    }

    fEnableProperties = FALSE;
    DrEnsureNetshellLoaded (pInfo);
    if (NULL != pInfo->pNetConUtilities)
    {
        //For whislter bug 409504           gangz
        //for a VPN double dial scenario,if now it is in the prerequiste dial
        //process, we should use DINFO->pEntryMain->pszPrerequisitePbk to check
        //if it is a publicPhonebook
        //
        BOOL fAllUsers = TRUE;
        
        if( pArgs->pDinfo->fPrerequisiteDial )
        {
            fAllUsers = 
                IsPublicPhonebook(pArgs->pDinfo->pEntryMain->pszPrerequisitePbk);
        }
        else
        {
            fAllUsers = IsPublicPhonebook(pArgs->pDinfo->pszPhonebook);
        }
        
        if (((fAllUsers && INetConnectionUiUtilities_UserHasPermission(
                                        pInfo->pNetConUtilities, 
                                        NCPERM_RasAllUserProperties)) ||
            (!fAllUsers && INetConnectionUiUtilities_UserHasPermission(
                                        pInfo->pNetConUtilities, 
                                        NCPERM_RasMyProperties))) &&
            (NULL == pArgs->pDinfo->pNoUser))
        {
            fEnableProperties = TRUE;
        }

        // We only needed it breifly, release it
        INetConnectionUiUtilities_Release(pInfo->pNetConUtilities);
        pInfo->pNetConUtilities = NULL;
    }

    // stevec: 267157-Allow access at win-login if admin enables.
    //
    if (NULL != pArgs->pDinfo->pNoUser
        && pArgs->pDinfo->pUser->fAllowLogonPhonebookEdits)
    {
        fEnableProperties = TRUE;
    }

    EnableWindow( pInfo->hwndPbProperties, fEnableProperties );

    // The help engine doesn't work at win-logon as it requires a user
    // context, so disable the Help button in that case.  See bug 343030.
    //
    if (pArgs->pDinfo->pNoUser)
    {
        HWND hwndPbHelp;

        hwndPbHelp = GetDlgItem( hwndDlg, CID_DR_PB_Help );
        ASSERT( hwndPbHelp );

        EnableWindow( hwndPbHelp, FALSE );
        ShowWindow( hwndPbHelp, SW_HIDE );
    }

    // Set the bitmap to the low res version if that is appropriate
    //
    // Ignore the error -- it is non-critical
    //
    DrSetBitmap(pInfo);

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

    //Add this function for whislter bug  320863    gangz
    //To adjust the bitmap's position and size
    //
    CenterExpandWindowRemainLeftMargin( pInfo->hwndBmDialer,
                                        hwndDlg,
                                        TRUE,
                                        TRUE,
                                        pInfo->hwndEbUser);

    // Adjust the title bar widgets.
    //
    //TweakTitleBar( hwndDlg );
    AddContextHelpButton( hwndDlg );

    return TRUE;
}

VOID
DrLocationsSelChange(
    IN DRINFO* pInfo )

    // Called when a location is selected from the list.  'PInfo' is the
    // dialog context.
    //
{
    DWORD dwErr;
    DWORD dwLocationId;

    TRACE("DuLocationChange");

    // Set global TAPI location based on user's selection.
    //
    dwLocationId = (DWORD )ComboBox_GetItemData(
        pInfo->hwndLbLocations, ComboBox_GetCurSel( pInfo->hwndLbLocations ) );

    dwErr = SetCurrentLocation( g_hinstDll, &pInfo->hlineapp, dwLocationId );
    if (dwErr != 0)
    {
        ErrorDlg( pInfo->hwndDlg, SID_OP_SaveTapiInfo, dwErr, NULL );
    }

    // Location change may cause changes in built numbers so re-fill the
    // numbers combo-box.
    //
    DrFillNumbersList( pInfo );
}


VOID
DrNumbersSelChange(
    IN DRINFO* pInfo )

    // Called when a phone number is selected from the list.  'PInfo' is the
    // dialog context.
    //
{
    INT iSel;
    BOOL fEnable;
    DRNUMBERSITEM* pItem;

    iSel = ComboBox_GetCurSel( pInfo->hwndClbNumbers );
    if (iSel >= 0)
    {
        if (iSel != (INT )pInfo->pLink->iLastSelectedPhone)
        {
            pInfo->pArgs->pDinfo->pEntry->fDirty = TRUE;
        }
        pInfo->pLink->iLastSelectedPhone = (DWORD )iSel;
    }

    pItem = (DRNUMBERSITEM* )ComboBox_GetItemDataPtr(
        pInfo->hwndClbNumbers, iSel );
    ASSERT( pItem );

    if(NULL == pItem)
    {
        return;
    }

    // Enable/disable the location fields based on whether they are relevant
    // to the selected number.
    //
    if (pInfo->pArgs->dwfMode & DR_L)
    {
        fEnable = pItem->pPhone->fUseDialingRules;
        EnableWindow( pInfo->hwndStLocations, fEnable );
        EnableWindow( pInfo->hwndLbLocations, fEnable );
        EnableWindow( pInfo->hwndPbRules, fEnable );
    }

    DrSetClbNumbersText( pInfo, pItem->pszNumber );
}


DWORD
DrPopulateIdentificationFields(
    IN DRINFO* pInfo,
    IN BOOL fForMe )

    // Updates the identification fields in the dialer
    // UI according to whether the all-user or per-user
    // dialparms should be used.
    //
    
{
    RASDIALPARAMS* prdp, *prdpOld;
    BOOL fUpdate;
    TCHAR pszUser[UNLEN + 1];
    INT iCount;

    prdp = (fForMe) 
        ? &(pInfo->pArgs->pDinfo->rdpu) : &(pInfo->pArgs->pDinfo->rdpg);
    prdpOld = (fForMe) 
        ? &(pInfo->pArgs->pDinfo->rdpg) : &(pInfo->pArgs->pDinfo->rdpu);

    iCount = GetWindowText(
                pInfo->hwndEbUser,
                pszUser, 
                UNLEN + 1);
    if (iCount == 0)
    {
        fUpdate = TRUE;
    }
    else
    {
        if (lstrcmp(prdpOld->szUserName, pszUser) == 0)
        {
            fUpdate = TRUE;
        }
        else
        {
            fUpdate = FALSE;
        }
    }

    if (fUpdate)
    {
        if (pInfo->hwndEbUser && *(prdp->szUserName))
        {
            SetWindowText(pInfo->hwndEbUser, prdp->szUserName);
        }
        if (pInfo->hwndEbDomain && *(prdp->szDomain))
        {
            SetWindowText(pInfo->hwndEbDomain, prdp->szDomain);
        }
    }

    return NO_ERROR;
}

DWORD
DrPopulatePasswordField(
    IN DRINFO* pInfo,
    IN BOOL fInit,
    IN BOOL fDisable )
{
    BOOL fSave, fMeOnly;
    TCHAR* pszFriendly = NULL;

    // Whistler bug: 195480 Dial-up connection dialog - Number of asterisks
    // does not match the length of the password and causes confusion
    //
    // Case 1. The user has clicked on the password field. We clear the
    // friendly password and set the font back to normal.
    //
    if (fDisable)
    {
        DrClearFriendlyPassword( pInfo, TRUE );
        return NO_ERROR;
    }

    // Initialze
    //
    fSave = Button_GetCheck( pInfo->hwndCbSavePw );
    fMeOnly = Button_GetCheck( pInfo->hwndRbSaveForMe );
    pszFriendly = PszFromId( g_hinstDll, SID_SavePasswordFrndly );

    // Case 2. Clear the password field if the user a) choose not to save the
    // password and b) has not manually entered a password.
    //
    if ( (!fSave) && !DrIsPasswordStyleEnabled( pInfo->hwndEbPw ) )
    {
        DrClearFriendlyPassword( pInfo, FALSE );
    }

    // Case 3. Show the friendly saved password text if the user a) choose to
    // save the password of himself only and b) there is a per-user password
    // saved and c) the user has not entered a password manually.
    //
    else if ( (fSave) && (fMeOnly) &&
              ((fInit) || ( !DrIsPasswordStyleEnabled( pInfo->hwndEbPw ))) )
    {
        // Whistler bug: 288234 When switching back and forth from
        // "I connect" and "Any user connects" password is not
        // caching correctly
        //
        if (pInfo->pArgs->pDinfo->fHaveSavedPwUser)
        {
            DrDisplayFriendlyPassword(pInfo, pszFriendly );
        }
        else
        {
            DrClearFriendlyPassword( pInfo, FALSE );
        }
    }

    // Case 4. Show the friendly saved password text if the user a) choose to
    // save the password for everyone and b) there is a default password saved
    // and c) the user has not entered a password manually.
    //
    else if ( (fSave) && (!fMeOnly) &&
             ((fInit) || ( !DrIsPasswordStyleEnabled( pInfo->hwndEbPw ))) )
    {
        if (pInfo->pArgs->pDinfo->fHaveSavedPwGlobal)
        {
            DrDisplayFriendlyPassword( pInfo, pszFriendly );
        }
        else
        {
            DrClearFriendlyPassword( pInfo, FALSE );
        }
    }

    // Case 5. Show the friendly saved password text if the user a) choose to
    // save the password for everyone or himself and b) there is a
    // corresponding password saved and c) the user has not entered a password
    // manually.
    //
    // This case catches a) when the user is switching between "me" and
    // "everyone" and b) when the user leaves the focus of the password field
    // but hasn't changed the password
    //
    else if ( (fSave) && !GetWindowTextLength( pInfo->hwndEbPw ) &&
              DrIsPasswordStyleEnabled( pInfo->hwndEbPw ) &&
              ((pInfo->pArgs->pDinfo->fHaveSavedPwGlobal && !fMeOnly) ||
               (pInfo->pArgs->pDinfo->fHaveSavedPwUser && fMeOnly)) )
    {
        DrDisplayFriendlyPassword( pInfo, pszFriendly );
    }

    // NT5 bug: 215432, Whistler bug: 364341
    //
    // Whistler bug: 195480 Dial-up connection dialog - Number of asterisks
    // does not match the length of the password and causes confusion
    //
    // Set focus appropiately
    //
    if (fInit)
    {
        if (!GetWindowTextLength( pInfo->hwndEbUser ))
        {
            SetFocus( pInfo->hwndEbUser );
        }
        else if (!GetWindowTextLength( pInfo->hwndEbPw ))
        {
            SetFocus( pInfo->hwndEbPw );

            // This removes the tab stop property from the username field. This
            // is a hack so we can set the focus properly. Tab stop is put back
            // in DrCommand.
            //
            SetWindowLong( pInfo->hwndEbUser, GWL_STYLE,
                GetWindowLong( pInfo->hwndEbUser, GWL_STYLE ) & ~WS_TABSTOP );
        }
        else
        {
            SetFocus( pInfo->hwndEbUser );
        }
    }

    // Clean up
    //
    Free0( pszFriendly );

    return NO_ERROR;
}

VOID
DrProperties(
    IN DRINFO* pInfo )

    // Called when the Properties button is pressed.  'PInfo' is the dialog
    // context.
    //
{
    BOOL fOk;
    RASENTRYDLG info;
    INTERNALARGS iargs;
    DINFO* pDinfo;

    // First, save any entry related changes user has made on the dial dialog.
    //
    DrSave( pInfo );

    // Set up for parameters for call to RasEntryDlg.
    //
    ZeroMemory( &info, sizeof(info) );
    info.dwSize = sizeof(info);
    info.hwndOwner = pInfo->hwndDlg;

    {
        RECT rect;

        info.dwFlags = RASEDFLAG_PositionDlg;
        GetWindowRect( pInfo->hwndDlg, &rect );
        info.xDlg = rect.left + DXSHEET;
        info.yDlg = rect.top + DYSHEET;
    }

    // The secret hack to share information already loaded with the entry API.
    //
    pDinfo = pInfo->pArgs->pDinfo;
    ZeroMemory( &iargs, sizeof(iargs) );
    iargs.pFile = pDinfo->pFile;
    iargs.pUser = pDinfo->pUser;
    iargs.pNoUser = pDinfo->pNoUser;
    iargs.fNoUser = !!(pDinfo->pNoUser);

    //For whislter bug 234515 set fDisableFirstConnect to be FALSE
    //
    iargs.fDisableFirstConnect = FALSE;
    info.reserved = (ULONG_PTR )&iargs;

    TRACE( "RasEntryDlg" );
    fOk = RasEntryDlg(
        pDinfo->pszPhonebook, pDinfo->pEntry->pszEntryName, &info );
    TRACE1( "RasEntryDlg=%d", fOk );

    if (fOk)
    {
        DWORD dwErr;

        // Reload when user presses OK on properties since that may change the
        // appearance and content of this dialog.  Must first reset the DINFO
        // context parameters based on the replaced PBENTRY from the property
        // sheet.
        //
        dwErr = FindEntryAndSetDialParams( pInfo->pArgs->pDinfo );
        if (dwErr != 0)
        {
            // Should not happen.
            //
            EndDialog( pInfo->hwndDlg, FALSE );
        }

        pInfo->pArgs->fReload = TRUE;
        EndDialog( pInfo->hwndDlg, FALSE );
    }
}

VOID
DrSave(
    IN DRINFO* pInfo )

    // Saves dialog field contents to RASDIALPARAMS, and if appropriate to LSA
    // secret area or NOUSER output argument.  'PInfo' is the dialog context.
    //
{
    DWORD dwErr;
    RASDIALPARAMS* prdp;
    RASCREDENTIALS rc;
    DINFO* pDinfo;

    pDinfo = pInfo->pArgs->pDinfo;

    if ((pInfo->pArgs->dwfMode & DR_U) ||
        (pInfo->pArgs->dwfMode & DR_I))
    {
        // Save credentials into parameter block to be passed to RasDial.
        //
        prdp = &pDinfo->rdp;
        GetWindowText( pInfo->hwndEbUser, prdp->szUserName, UNLEN + 1 );

        if (pInfo->pArgs->dwfMode & DR_U)
        {
            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded by GetWindowText()
            //
            // Whistler bug: 195480 Dial-up connection dialog - Number of
            // asterisks does not match the length of the password and causes
            // confusion
            //
            if (!DrIsPasswordStyleEnabled( pInfo->hwndEbPw ))
            {
                lstrcpyn( prdp->szPassword, g_pszSavedPasswordToken,
                    g_dwSavedPasswordTokenLength );
            }
            else
            {
                GetWindowText( pInfo->hwndEbPw, prdp->szPassword, PWLEN + 1 );
            }

            SetWindowText( pInfo->hwndEbPw, L"" );
            EncodePassword( prdp->szPassword );
        }

        if (pInfo->pArgs->dwfMode & DR_D)
        {
            GetWindowText( pInfo->hwndEbDomain, prdp->szDomain, DNLEN + 1 );
        }

        ZeroMemory( &rc, sizeof(rc) );
        rc.dwSize = sizeof(rc);
        lstrcpyn( rc.szUserName, prdp->szUserName, UNLEN + 1 );

        // Whistler bug 254385 encode password when not being used
        // Assumed password was encoded previously
        //
        DecodePassword( prdp->szPassword );
        lstrcpyn( rc.szPassword, prdp->szPassword, PWLEN + 1 );
        EncodePassword( prdp->szPassword );

        lstrcpyn( rc.szDomain, prdp->szDomain, DNLEN + 1);

        if (pDinfo->pNoUser)
        {
            // Save credentials into output block for return to caller,
            // typically WinLogon.
            //
            lstrcpyn( pDinfo->pNoUser->szUserName, rc.szUserName, UNLEN + 1 );

            // Whistler bug 254385 encode password when not being used
            // Assumed password was not encoded previously
            //
            lstrcpyn( pDinfo->pNoUser->szPassword, rc.szPassword, PWLEN + 1 );
            EncodePassword( pDinfo->pNoUser->szPassword );

            lstrcpyn( pDinfo->pNoUser->szDomain, rc.szDomain, DNLEN + 1 );
            *(pDinfo->pfNoUserChanged) = TRUE;
        }
        else if (pInfo->pArgs->dwfMode & DR_I)
        {
            // Nothing to do
        }
        else if (!pDinfo->fDisableSavePw)
        {
            BOOL fGlobalCreds = FALSE;
            ASSERT( g_pRasSetCredentials );

            if (Button_GetCheck( pInfo->hwndCbSavePw ))
            {
                rc.dwMask = 0;
                
                // If the user elected to save the credentials for 
                // everybody, then clear any previously saved per-user
                // credentials
                //
                fGlobalCreds = Button_GetCheck( pInfo->hwndRbSaveForEveryone );
                if(     (fGlobalCreds)
                    &&  IsPublicPhonebook(pDinfo->pFile->pszPath))
                {
                    DeleteSavedCredentials(
                        pDinfo,
                        pInfo->hwndDlg,
                        FALSE,
                        TRUE );
                    pDinfo->fHaveSavedPwUser = FALSE;
                    rc.dwMask = RASCM_DefaultCreds;
                }

                // If there is currently no saved per-user password and the user
                // opts to save the password himself, then ask whetehr the global
                // password should be deleted if it exists.
                //
                else if (pDinfo->fHaveSavedPwGlobal && !pDinfo->fHaveSavedPwUser)
                {
                    MSGARGS msgargs;
                    ZeroMemory( &msgargs, sizeof(msgargs) );
                    msgargs.dwFlags = MB_ICONQUESTION | MB_YESNO;

                    // Delete the default credentials if the user answers yes
                    //
                    if (IDYES == 
                       MsgDlg(pInfo->hwndDlg, SID_DR_GlobalPassword, &msgargs))
                    {
                        DeleteSavedCredentials(
                            pDinfo,
                            pInfo->hwndDlg,
                            TRUE,
                            TRUE );
                        pDinfo->fHaveSavedPwGlobal = FALSE;
                    }
                }

                // User chose to save password.  Cache username, password, and
                // domain.
                //
                rc.dwMask |= RASCM_UserName | 
                             RASCM_Password | RASCM_Domain;

                TRACE( "RasSetCredentials(u|p|d,FALSE)" );
                dwErr = g_pRasSetCredentials(
                    pDinfo->pFile->pszPath, pDinfo->pEntry->pszEntryName,
                    &rc, FALSE );
                TRACE1( "RasSetCredentials=%d", dwErr );
                
                if (dwErr != 0)
                {
                    ErrorDlg( pInfo->hwndDlg, SID_OP_CachePw,  dwErr, NULL );
                }
                else
                {
                    if (fGlobalCreds)
                    {
                        pDinfo->fHaveSavedPwGlobal = TRUE;
                    }
                    else
                    {
                        // Whistler bug: 288234 When switching back and forth
                        // from "I connect" and "Any user connects" password is
                        // not caching correctly
                        //
                        pDinfo->fHaveSavedPwUser = TRUE;
                    }
                }
            }
            else
            {
                // Delete the global credentials.
                //
                // Note that we have to delete the global identity 
                // as well because we do not support deleting 
                // just the global password.  This is so that 
                // RasSetCredentials can emulate RasSetDialParams.
                //
                DeleteSavedCredentials(
                    pDinfo,
                    pInfo->hwndDlg,
                    TRUE,
                    TRUE );

                // Delete the password saved per-user.  Keep the user name
                // and domain saved, however.
                //
                DeleteSavedCredentials(
                    pDinfo,
                    pInfo->hwndDlg,
                    FALSE,
                    FALSE );

                pDinfo->fHaveSavedPwUser = FALSE;
                pDinfo->fHaveSavedPwGlobal = FALSE;
            }
        }

        ZeroMemory( rc.szPassword, sizeof(rc.szPassword) );
    }

    if (pInfo->pArgs->dwfMode & DR_N)
    {
        TCHAR* pszNumber;
        TCHAR* pszOriginal;
        DTLNODE* pPhoneNode;
        DRNUMBERSITEM* pItem;
        PBPHONE* pPhone;
        BOOL fUserChange;

        pszNumber = GetText( pInfo->hwndClbNumbers );
        if (!pszNumber)
        {
            return;
        }

        pItem = (DRNUMBERSITEM* )ComboBox_GetItemDataPtr(
            pInfo->hwndClbNumbers, pInfo->pLink->iLastSelectedPhone );
        if (pItem)
        {
            pszOriginal = pItem->pszNumber;
        }
        else
        {
            pszOriginal = TEXT("");
        }

        if (lstrcmp( pszNumber, pszOriginal ) != 0
            || (pInfo->pLink->iLastSelectedPhone != pInfo->iFirstSelectedPhone))
        {
            MSGARGS msgargs;
            BOOL fMultiLink;
            BOOL fSingleNumber;

            // The phone number was edited by user to something not originally
            // on the list OR the user selected a different item on the list.
            //
            fSingleNumber = (DtlGetNodes( pInfo->pLink->pdtllistPhones ) == 1);
            fMultiLink = (DtlGetNodes( pDinfo->pEntry->pdtllistLinks ) > 1);

            ZeroMemory( &msgargs, sizeof(msgargs) );
            msgargs.dwFlags = MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2;

            if (fSingleNumber
                && (!fMultiLink || pDinfo->pEntry->fSharedPhoneNumbers)
                    && MsgDlg( pInfo->hwndDlg,
                           SID_SavePreview, &msgargs ) == IDYES)
            {
                // User says he wants to make the change permanent.
                //
                pDinfo->pEntry->fDirty = TRUE;

                if (pItem)
                {
                    pPhone = pItem->pPhone;
                    Free0( pItem->pszNumber );
                    pItem->pszNumber = StrDup( pszNumber );
                }
                else
                {
                    pPhoneNode = CreatePhoneNode();
                    if (pPhoneNode)
                    {
                        DtlAddNodeFirst(
                            pInfo->pLink->pdtllistPhones, pPhoneNode );
                        pPhone = (PBPHONE* )DtlGetData( pPhoneNode );
                    }
                }

                if (pItem)
                {
                    ASSERT( pItem->pPhone );
                    Free0( pPhone->pszPhoneNumber );
                    pPhone->pszPhoneNumber = StrDup( pszNumber );
                    pPhone->fUseDialingRules = FALSE;

                    if (fMultiLink)
                    {
                        DTLNODE* pNode;

                        for (pNode = DtlGetFirstNode(
                                 pDinfo->pEntry->pdtllistLinks );
                             pNode;
                             pNode = DtlGetNextNode( pNode ))
                        {
                            PBLINK* pLink = (PBLINK* )DtlGetData( pNode );
                            ASSERT( pLink );
                            ASSERT( pLink->fEnabled );
                            CopyLinkPhoneNumberInfo( pNode, pInfo->pLinkNode );
                        }
                    }
                }
            }

            fUserChange = TRUE;
        }
        else
        {
            fUserChange = FALSE;
        }

        if (fUserChange || !pInfo->pLink->fTryNextAlternateOnFail)
        {
            if (!*pszNumber)
            {
                TCHAR* psz;

                // We have a problem when user edits in an empty string,
                // because the RasDial API does not accept an empty override
                // phone number.  Convert it to a single blank string in this
                // case, which is as good as we can do.  If user really needs
                // to dial an empty string they can enter one as the entry's
                // permanent phone number.  See bug 179561.
                //
                psz = StrDup( TEXT(" ") );
                if (psz)
                {
                    Free( pszNumber );
                    pszNumber = psz;
                }
            }

            // Set the override phone number to what user typed or selected.
            //
            lstrcpyn( 
                pDinfo->rdp.szPhoneNumber, 
                pszNumber,
                RAS_MaxPhoneNumber + 1);
        }

        Free( pszNumber );
    }

    if (pDinfo->pEntry->fDirty)
    {
        // Write the new phone number and/or "last selected phone number" to
        // the phonebook.
        //
        dwErr = WritePhonebookFile( pDinfo->pFile, NULL );
        if (dwErr != 0)
        {
            ErrorDlg( pInfo->hwndDlg, SID_OP_WritePhonebook, dwErr, NULL );
        }
    }
}

DWORD
DrSetBitmap(
    IN DRINFO* pInfo)

    // Set the appropriate bitmap for this dialer.
    //
{
    DWORD dwErr = NO_ERROR;
    HBITMAP hbmNew = NULL;
    HDC hdc = NULL;
    INT iDepth = 0;

    do
    {
        if (pInfo->hwndBmDialer == NULL)
        {
            dwErr = ERROR_CAN_NOT_COMPLETE;
            break;
        }
    
        // Get the device context for the window
        //
        hdc = GetDC( pInfo->hwndBmDialer );
        if (hdc == NULL)
        {
            dwErr = GetLastError();
            break;
        }

        // If the color depth >= 8bit, the current bitmap
        // is fine (high res is default)
        //
        iDepth = GetDeviceCaps(hdc, NUMCOLORS);
        if ( (iDepth == -1) || (iDepth == 256) )
        {
            dwErr = NO_ERROR;
            break;
        }

        // Load in the low-res bitmap
        //
        hbmNew = LoadBitmap(g_hinstDll, MAKEINTRESOURCE( BID_Dialer ));
        if (hbmNew == NULL)
        {
            dwErr = GetLastError();
            break;
        }

        // Set the low-res bitmap
        //
        pInfo->hbmOrig = (HBITMAP)
            SendMessage( 
                pInfo->hwndBmDialer, 
                STM_SETIMAGE, 
                IMAGE_BITMAP, 
                (LPARAM )hbmNew );
    
    } while (FALSE);

    // Cleanup
    //
    {
        if (hdc)
        {
            ReleaseDC(pInfo->hwndBmDialer, hdc);
        }
    }
    
    return dwErr;
}

VOID
DrSetClbNumbersText(
    IN DRINFO* pInfo,
    IN TCHAR* pszText )

    // Set the text of the 'ClbNumbers' edit box to 'pszText'.  See
    // DrClbNumbersEbWndProc.  'PInfo' is the dialog context.
    //
{
    ASSERT( pInfo->hwndClbNumbersEb );

    SendMessage( pInfo->hwndClbNumbersEb, DR_WM_SETTEXT, 0, (LPARAM )pszText );
}


VOID
DrTerm(
    IN HWND hwndDlg )

    // Called on WM_DESTROY.  'HwndDlg' is that handle of the dialog window.
    //
{
    DRINFO* pInfo = (DRINFO* )GetWindowLongPtr( hwndDlg, DWLP_USER );
    HBITMAP hbmNew = NULL;

    TRACE( "DrTerm" );

    if (pInfo)
    {
        // Note: Don't use 'pInfo->pLinkNode' or 'pInfo->pLink' here as they
        //       are not currently restored prior to exit for post-Property
        //       button reloading.
        //
        if (pInfo->hwndClbNumbers)
        {
            DrFreeClbNumbers( pInfo );

            if (pInfo->wndprocClbNumbersEb)
            {
                SetWindowLongPtr( pInfo->hwndClbNumbersEb,
                    GWLP_WNDPROC, (ULONG_PTR )pInfo->wndprocClbNumbersEb );
            }

            if (pInfo->wndprocClbNumbersLb)
            {
                SetWindowLongPtr( pInfo->hwndClbNumbersLb,
                    GWLP_WNDPROC, (ULONG_PTR )pInfo->wndprocClbNumbersLb );
            }
        }

        // Whistler bug: 195480 Dial-up connection dialog - Number of
        // asterisks does not match the length of the password and causes
        // confusion
        //
        if (pInfo->hItalicFont)
        {
            DeleteObject( pInfo->hItalicFont );
        }

        if (pInfo->hNormalFont)
        {
            DeleteObject( pInfo->hNormalFont );
        }

        if (pInfo->fComInitialized)
        {
            CoUninitialize();
        }

        // Clean up the low-res bitmap if appropriate
        //
        if ( pInfo->hbmOrig )
        {
            hbmNew = (HBITMAP)
                SendMessage( 
                    pInfo->hwndBmDialer, 
                    STM_SETIMAGE, 
                    IMAGE_BITMAP, 
                    (LPARAM ) pInfo->hbmOrig );
                    
            if (hbmNew)
            {
                DeleteObject(hbmNew);
            }
        }

        Free( pInfo );
    }
}


//----------------------------------------------------------------------------
// Projection Result dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
ProjectionResultDlg(
    IN HWND hwndOwner,
    IN TCHAR* pszLines,
    OUT BOOL* pfDisableFailedProtocols )

    // Popup the Projection Result dialog.  'HwndOwner' is the owning window.
    // 'PszLines' is the status line text to display.  See DpProjectionError.
    // 'DwfDisableFailedProtocols' indicates user chose to disable the failed
    // protocols.
    //
    // Returns true if user chooses to redial or lets it timeout, false if
    // cancels.
    //
{
    INT_PTR nStatus;
    PRARGS args;

    TRACE( "ProjectionResultDlg" );

    args.pszLines = pszLines;
    args.pfDisableFailedProtocols = pfDisableFailedProtocols;

    nStatus =
        DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_PR_ProjectionResult ),
            hwndOwner,
            PrDlgProc,
            (LPARAM )&args );

    if (nStatus == -1)
    {
        ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
        nStatus = FALSE;
    }

    return (nStatus) ? TRUE : FALSE;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return PrInit( hwnd, (PRARGS* )lparam );
        }

        case WM_HELP:
        case WM_CONTEXTMENU:
        {
            ContextHelp( g_adwPrHelp, hwnd, unMsg, wparam, lparam );
            break;
        }

        case WM_COMMAND:
        {
            return PrCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
PrCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    DWORD dwErr;

    TRACE3( "PrCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case IDOK:
        case IDCANCEL:
        {
            BOOL fCb;
            BOOL* pfDisable;

            TRACE1( "%s pressed", (wId==IDOK) ? "OK" : "Cancel" );

            fCb = IsDlgButtonChecked( hwnd, CID_PR_CB_DisableProtocols );
            pfDisable = (BOOL* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pfDisable );
            *pfDisable = fCb;
            EndDialog( hwnd, (wId == IDOK) );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
PrInit(
    IN HWND hwndDlg,
    IN PRARGS* pArgs )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PArgs' is caller's arguments to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    HWND hwndStText;
    HWND hwndPbAccept;
    HWND hwndPbHangUp;
    HWND hwndCbDisable;

    TRACE( "PrInit" );

    hwndStText = GetDlgItem( hwndDlg, CID_PR_ST_Text );
    ASSERT( hwndStText );
    hwndPbAccept = GetDlgItem( hwndDlg, IDOK );
    ASSERT( hwndPbAccept );
    hwndPbHangUp = GetDlgItem( hwndDlg, IDCANCEL );
    ASSERT( hwndPbHangUp );
    hwndCbDisable = GetDlgItem( hwndDlg, CID_PR_CB_DisableProtocols );
    ASSERT( hwndCbDisable );

    {
        TCHAR szBuf[ 1024 ];
        TCHAR* psz;

        // Build the message text.
        //
        szBuf[ 0 ] = TEXT('\0');
        psz = PszFromId( g_hinstDll, SID_ProjectionResult1 );
        if (psz)
        {
            lstrcat( szBuf, psz );
            Free( psz );
        }
        lstrcat( szBuf, pArgs->pszLines );
        psz = PszFromId( g_hinstDll, SID_ProjectionResult2 );
        if (psz)
        {
            lstrcat( szBuf, psz );
            Free( psz );
        }

        // Load the text into the static control, then stretch the window to a
        // vertical size appropriate for the text.
        //
        {
            HDC hdc;
            RECT rect;
            RECT rectNew;
            HFONT hfont;
            LONG dyGrow;

            SetWindowText( hwndStText, szBuf );
            GetClientRect( hwndStText, &rect );
            hdc = GetDC( hwndStText );

            if(NULL != hdc)
            {

                hfont = (HFONT )SendMessage( hwndStText, WM_GETFONT, 0, 0 );
                if (hfont)
                    SelectObject( hdc, hfont );

                rectNew = rect;
                DrawText( hdc, szBuf, -1, &rectNew,
                    DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS | DT_NOPREFIX );
                ReleaseDC( hwndStText, hdc );
            }

            dyGrow = rectNew.bottom - rect.bottom;
            ExpandWindow( hwndDlg, 0, dyGrow );
            ExpandWindow( hwndStText, 0, dyGrow );
            SlideWindow( hwndPbAccept, hwndDlg, 0, dyGrow );
            SlideWindow( hwndPbHangUp, hwndDlg, 0, dyGrow );
            SlideWindow( hwndCbDisable, hwndDlg, 0, dyGrow );
        }
    }

    // Save address of caller's BOOL as the dialog context.
    //
    SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pArgs->pfDisableFailedProtocols );

    // Add context help button to title bar.
    //
    AddContextHelpButton( hwndDlg );

    // Display the finished window above all other windows.  The window
    // position is set to "topmost" then immediately set to "not topmost"
    // because we want it on top but not always-on-top.  Always-on-top alone
    // is incredibly annoying, e.g. it is always on top of the on-line help if
    // user presses the Help button.
    //
    SetWindowPos(
        hwndDlg, HWND_TOPMOST, 0, 0, 0, 0,
        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );

    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    ShowWindow( hwndDlg, SW_SHOW );

    SetWindowPos(
        hwndDlg, HWND_NOTOPMOST, 0, 0, 0, 0,
        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );

    return TRUE;
}


//----------------------------------------------------------------------------
// Retry Authentication dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
RetryAuthenticationDlg(
    IN HWND hwndOwner,
    IN DINFO* pDinfo )

    // Pops up the retry authentication dialog.  'PDinfo' is the dial dialog
    // common context.
    //
    // Returns true if user presses OK, false if Cancel or an error occurs.
    //
{
    INT_PTR nStatus;

    TRACE( "RetryAuthenticationDlg" );

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_UA_RetryAuthenticationUD ),
            hwndOwner,
            UaDlgProc,
            (LPARAM )pDinfo );

    if (nStatus == -1)
    {
        ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
        nStatus = FALSE;
    }

    return (BOOL )nStatus;
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return UaInit( hwnd, (DINFO* )lparam );
        }

        case WM_HELP:
        case WM_CONTEXTMENU:
        {
            ContextHelp( g_adwUaHelp, hwnd, unMsg, wparam, lparam );
            break;
        }

        case WM_COMMAND:
        {
            UAINFO* pInfo = (UAINFO* )GetWindowLongPtr( hwnd, DWLP_USER );

            if (!pInfo)
            {
                // This happened in stress one night.  Don't understand how
                // unless it was a WinUser bug or something.  Anyhow, this
                // avoids an AV in such case.
                //
                break;
            }

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

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

    return FALSE;
}


BOOL
UaCommand(
    IN UAINFO* 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.
    //
{
    TRACE3( "UaCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case CID_UA_EB_UserName:
        {
            if (pInfo->fAutoLogonPassword && wNotification == EN_CHANGE)
            {
                // User's changing the username in auto-logon retry mode,
                // which means we have to admit we don't really have the text
                // password and force him to re-enter it.
                //
                pInfo->fAutoLogonPassword = FALSE;
                SetWindowText( pInfo->hwndEbPassword, TEXT("") );
            }
            break;
        }

        case CID_UA_EB_Password:
        {
            if (wNotification == EN_CHANGE)
            {
                pInfo->fAutoLogonPassword = FALSE;
            }
            break;
        }

        case IDOK:
        {
            UaSave( pInfo );
            EndDialog( pInfo->hwndDlg, TRUE );
            return TRUE;
        }

        case IDCANCEL:
        {
            TRACE( "Cancel pressed" );
            EndDialog( pInfo->hwndDlg, FALSE );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
UaInit(
    IN HWND   hwndDlg,
    IN DINFO* pArgs )

    // Called on WM_INITDIALOG.  'hwndDlg' is the handle of the owning window.
    // 'PArgs' is caller's arguments as passed to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    DWORD dwErr;
    UAINFO* pInfo;
    PBENTRY* pEntry;

    TRACE( "UaInit" );

    // Allocate the dialog context block.  Initialize minimally for proper
    // cleanup, then attach to the dialog window.
    //
    {
        pInfo = Malloc( sizeof(*pInfo) );
        if (!pInfo)
        {
            ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
            EndDialog( hwndDlg, FALSE );
            return TRUE;
        }

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

        SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
        TRACE( "Context set" );
    }

    pInfo->fDomain = TRUE;

    pInfo->hwndEbUserName = GetDlgItem( hwndDlg, CID_UA_EB_UserName );
    ASSERT( pInfo->hwndEbUserName );
    pInfo->hwndEbPassword = GetDlgItem( hwndDlg, CID_UA_EB_Password );
    ASSERT( pInfo->hwndEbPassword );
    if (pInfo->fDomain)
    {
        pInfo->hwndEbDomain = GetDlgItem( hwndDlg, CID_UA_EB_Domain );
        ASSERT( pInfo->hwndEbDomain );
    }
    pInfo->hwndCbSavePw = GetDlgItem( hwndDlg, CID_UA_CB_SavePassword );
    ASSERT( pInfo->hwndCbSavePw );

    pEntry = pArgs->pEntry;

    // Set the title.
    //
    {
        TCHAR* pszTitleFormat;
        TCHAR* pszTitle;
        TCHAR* apszArgs[ 1 ];

        pszTitleFormat = GetText( hwndDlg );
        if (pszTitleFormat)
        {
            apszArgs[ 0 ] = pEntry->pszEntryName;
            pszTitle = NULL;

            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING
                    | FORMAT_MESSAGE_ALLOCATE_BUFFER
                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszTitleFormat, 0, 0, (LPTSTR )&pszTitle, 1,
                (va_list* )apszArgs );

            Free( pszTitleFormat );

            if (pszTitle)
            {
                SetWindowText( hwndDlg, pszTitle );
                LocalFree( pszTitle );
            }
        }
    }

    // Fill edit fields with initial values.
    //
    Edit_LimitText( pInfo->hwndEbUserName, UNLEN );
    Edit_LimitText( pInfo->hwndEbPassword, PWLEN );
    if (pInfo->fDomain)
    {
        Edit_LimitText( pInfo->hwndEbDomain, DNLEN );
    }

    {
        BOOL fUserNameSet = FALSE;
        BOOL fPasswordSet = FALSE;

        if (pEntry->fAutoLogon && !pInfo->pArgs->pNoUser)
        {
            // On the first retry use the logged on user's name.  Act like the
            // user's password is in the edit box.  If he changes the username
            // or password we'll have to admit we don't have it, but he'll
            // probably just change the domain.
            //
            if (pArgs->rdp.szUserName[ 0 ] == TEXT('\0'))
            {
                SetWindowText( pInfo->hwndEbUserName, GetLogonUser() );
                fUserNameSet = TRUE;
            }

            if (pArgs->rdp.szPassword[ 0 ] == TEXT('\0'))
            {
                SetWindowText( pInfo->hwndEbPassword, TEXT("********") );
                pInfo->fAutoLogonPassword = TRUE;
                fPasswordSet = TRUE;
            }
        }

        if (!fUserNameSet)
        {
            SetWindowText( pInfo->hwndEbUserName, pArgs->rdp.szUserName );
        }

        if (!fPasswordSet)
        {
            // Whistler bug 254385 encode password when not being used
            // Assumed password was encoded previously
            //
            DecodePassword( pArgs->rdp.szPassword );
            SetWindowText( pInfo->hwndEbPassword, pArgs->rdp.szPassword );
            EncodePassword( pArgs->rdp.szPassword );
        }

        if (pInfo->fDomain)
        {
            SetWindowText( pInfo->hwndEbDomain, pArgs->rdp.szDomain );
        }
    }

    if (pArgs->pNoUser || pArgs->fDisableSavePw)
    {
        // Can't stash password without a logon context, so hide the checkbox.
        //
        ASSERT( !HaveSavedPw( pArgs ) );
        EnableWindow( pInfo->hwndCbSavePw, FALSE );
        ShowWindow( pInfo->hwndCbSavePw, SW_HIDE );
    }
    else
    {
        // Check "save  password" if a password was previously  cached.  Maybe
        // he changed the password while on the LAN.
        //
        Button_SetCheck( pInfo->hwndCbSavePw, HaveSavedPw( pArgs ) );
    }

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

    // Add context help button to title bar.
    //
    AddContextHelpButton( hwndDlg );

    // Set focus to the empty username or empty password, or if both are
    // present to the domain if auto-logon or the password if not.
    //
    if (Edit_GetTextLength( pInfo->hwndEbUserName ) == 0)
    {
        Edit_SetSel( pInfo->hwndEbUserName, 0, -1 );
        SetFocus( pInfo->hwndEbUserName );
    }
    else if (Edit_GetTextLength( pInfo->hwndEbPassword ) == 0
             || !pEntry->fAutoLogon
             || !pInfo->fDomain)
    {
        Edit_SetSel( pInfo->hwndEbPassword, 0, -1 );
        SetFocus( pInfo->hwndEbPassword );
    }
    else
    {
        ASSERT( pInfo->fDomain );
        Edit_SetSel( pInfo->hwndEbDomain, 0, -1 );
        SetFocus( pInfo->hwndEbDomain );
    }

    // Hide the Dial Progress dialog.
    //
    SetOffDesktop( GetParent( hwndDlg ), SOD_MoveOff, NULL );

    return FALSE;
}


VOID
UaSave(
    IN UAINFO* pInfo )

    // Called when the OK button is pressed.
    //
    // Returns true if user presses OK, false if Cancel or an error occurs.
    //
{
    DWORD dwErr;
    PBENTRY* pEntry;
    BOOL fSavePw;
    RASDIALPARAMS* prdp;
    RASCREDENTIALS rc;

    TRACE( "UaSave" );

    prdp = &pInfo->pArgs->rdp;
    GetWindowText( pInfo->hwndEbUserName, prdp->szUserName, UNLEN + 1 );

    // Whistler bug 254385 encode password when not being used
    // Assumed password was not encoded by GetWindowText()
    //
    GetWindowText( pInfo->hwndEbPassword, prdp->szPassword, PWLEN + 1 );
    EncodePassword( prdp->szPassword );
    if (pInfo->fDomain)
    {
        GetWindowText( pInfo->hwndEbDomain, prdp->szDomain, DNLEN + 1 );
        //
        //if the Domain is not empty, set the "include Windows Logon Domain check box on Option Tab" 
        //        for bug  167229 whistler
        //
        if ( ( 0 < lstrlen ( prdp->szDomain ) ) && (!pInfo->pArgs->pEntry->fPreviewDomain ))
        {
            pInfo->pArgs->pEntry->fPreviewDomain = TRUE;
            pInfo->pArgs->pEntry->fDirty = TRUE;
            dwErr = WritePhonebookFile( pInfo->pArgs->pFile, NULL );

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

    pEntry = pInfo->pArgs->pEntry;
    if (pEntry->fAutoLogon && !pInfo->pArgs->pNoUser)
    {
        if (pInfo->fAutoLogonPassword)
        {
            // User did not change username or password, so continue to
            // retrieve logon username and password credentials.
            //
            TRACE( "Retain auto-logon" );
            prdp->szUserName[ 0 ] = TEXT('\0');
            prdp->szPassword[ 0 ] = TEXT('\0');
        }
        else
        {
            // User changed username and/or password so we can no longer
            // retrieve the logon username and password credentials from the
            // system.  Switch the entry to non-auto-logon mode.
            //
            TRACE( "Disable auto-logon" );
            pEntry->fAutoLogon = FALSE;
            pInfo->pArgs->fResetAutoLogon = TRUE;
        }
    }

    ZeroMemory( &rc, sizeof(rc) );
    rc.dwSize = sizeof(rc);
    lstrcpyn( rc.szUserName, prdp->szUserName, UNLEN + 1 );

    // Whistler bug 254385 encode password when not being used
    // Assumed password was encoded previously
    //
    DecodePassword( prdp->szPassword );
    lstrcpyn( rc.szPassword, prdp->szPassword, PWLEN + 1 );
    EncodePassword( prdp->szPassword );

    lstrcpyn( rc.szDomain, prdp->szDomain, DNLEN + 1 );

    if (pInfo->pArgs->pNoUser)
    {
        lstrcpyn( pInfo->pArgs->pNoUser->szUserName, rc.szUserName, UNLEN + 1 );

        // Whistler bug 254385 encode password when not being used
        // Assumed password was not encoded previously
        //
        lstrcpyn( pInfo->pArgs->pNoUser->szPassword, rc.szPassword, PWLEN + 1 );
        EncodePassword( pInfo->pArgs->pNoUser->szPassword );

        lstrcpyn( pInfo->pArgs->pNoUser->szDomain, rc.szDomain, DNLEN + 1 );
        *pInfo->pArgs->pfNoUserChanged = TRUE;
    }
    else if (!pInfo->pArgs->fDisableSavePw)
    {
        ASSERT( g_pRasSetCredentials );

        if (Button_GetCheck( pInfo->hwndCbSavePw ))
        {
            // User chose "save password".  Cache username, password, and
            // domain.
            //
            rc.dwMask = RASCM_UserName | RASCM_Password | RASCM_Domain;

            // Whistler bug: 288234 When switching back and forth from
            // "I connect" and "Any user connects" password is not
            // caching correctly
            //
            if(     (pInfo->pArgs->fHaveSavedPwGlobal)
                &&  !pInfo->pArgs->fHaveSavedPwUser
                &&  IsPublicPhonebook(pInfo->pArgs->pFile->pszPath))
            {
                rc.dwMask |= RASCM_DefaultCreds;
            }
            
            TRACE( "RasSetCredentials(u|p|d,FALSE)" );
            dwErr = g_pRasSetCredentials(
                pInfo->pArgs->pFile->pszPath,
                pInfo->pArgs->pEntry->pszEntryName,
                &rc, FALSE );
            TRACE1( "RasSetCredentials=%d", dwErr );

            if (dwErr != 0)
            {
                ErrorDlg( pInfo->hwndDlg, SID_OP_CachePw,  dwErr, NULL );
            }
        }
        else
        {
            // Whistler bug: 288234 When switching back and forth from
            // "I connect" and "Any user connects" password is not
            // caching correctly
            //
            // User chose not to save password; Cache username and domain only
            //
            rc.dwMask = RASCM_UserName | RASCM_Domain;

            TRACE( "RasSetCredentials(u|d,FALSE)" );
            dwErr = g_pRasSetCredentials(
                pInfo->pArgs->pFile->pszPath,
                pInfo->pArgs->pEntry->pszEntryName,
                &rc, FALSE );
            TRACE1( "RasSetCredentials=%d", dwErr );

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

            // Whistler bug: 288234 When switching back and forth from
            // "I connect" and "Any user connects" password is not
            // caching correctly
            //

            // Delete the password saved per-user; Keep the user name
            // and domain saved, however.
            //
            if (pInfo->pArgs->fHaveSavedPwUser)
            {
                DeleteSavedCredentials(
                    pInfo->pArgs,
                    pInfo->hwndDlg,
                    FALSE,
                    FALSE );
                pInfo->pArgs->fHaveSavedPwUser = FALSE;
            }

            // Delete the global credentials.  
            //
            // Note that we have to delete the global identity 
            // as well because we do not support deleting 
            // just the global password.  This is so that 
            // RasSetCredentials can emulate RasSetDialParams.
            //
            else if (pInfo->pArgs->fHaveSavedPwGlobal)
            {
                DeleteSavedCredentials(
                    pInfo->pArgs,
                    pInfo->hwndDlg,
                    TRUE,
                    TRUE );
                pInfo->pArgs->fHaveSavedPwGlobal = FALSE;
            }
        }
    }

    ZeroMemory( rc.szPassword, sizeof(rc.szPassword) );
}


VOID
UaTerm(
    IN HWND hwndDlg )

    // Called on WM_DESTROY.  'HwndDlg' is that handle of the dialog window.
    //
{
    UAINFO* pInfo = (UAINFO* )GetWindowLongPtr( hwndDlg, DWLP_USER );

    TRACE( "UaTerm" );

    if (pInfo)
    {
        // Restore the Dial Progress dialog.
        //
        SetOffDesktop( GetParent( hwndDlg ), SOD_MoveBackFree, NULL );

        Free( pInfo );
    }
}


//----------------------------------------------------------------------------
// VPN Double Dial help dialog
// Listed alphabetically following stub API and dialog proc
//----------------------------------------------------------------------------

BOOL
VpnDoubleDialDlg(
    IN HWND hwndOwner,
    IN DINFO* pInfo )

    // Popup the VPN double dial help dialog.  'HwndOwner' is the owning
    // window.  'PInfo' is the dialing context information.
    //
    // Returns false if user sees the dialog and decides not to continue, true
    // otherwise.
    //
{
    INT_PTR nStatus;

    TRACE( "VpnDoubleDialDlg" );

    if (pInfo->pEntryMain->dwType != RASET_Vpn
        || !pInfo->fPrerequisiteDial
        || pInfo->pEntryMain->fSkipDoubleDialDialog)
    {
        return TRUE;
    }

    nStatus =
        (BOOL )DialogBoxParam(
            g_hinstDll,
            MAKEINTRESOURCE( DID_VI_VpnInitial ),
            hwndOwner,
            ViDlgProc,
            (LPARAM )pInfo );

    if (nStatus == -1)
    {
        nStatus = FALSE;
    }

    return !!(nStatus);
}


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

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

    switch (unMsg)
    {
        case WM_INITDIALOG:
        {
            return ViInit( hwnd, (DINFO* )lparam );
        }

        case WM_COMMAND:
        {
            return ViCommand(
                hwnd, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
        }
    }

    return FALSE;
}


BOOL
ViCommand(
    IN HWND hwnd,
    IN WORD wNotification,
    IN WORD wId,
    IN HWND hwndCtrl )

    // Called on WM_COMMAND.  'Hwnd' is the dialog window.  '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.
    //
{
    TRACE3( "ViCommand(n=%d,i=%d,c=$%x)",
        (DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );

    switch (wId)
    {
        case IDYES:
        case IDNO:
        {
            // Per bug 261955, the box setting is saved when either the Yes or
            // No, but not the 'X' button, is pressed.
            //
            DINFO* pInfo = (DINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
            ASSERT( pInfo );

            if (IsDlgButtonChecked( hwnd, CID_VI_CB_SkipMessage ))
            {
                pInfo->pEntryMain->fSkipDoubleDialDialog = TRUE;
                pInfo->pEntryMain->fDirty = TRUE;
                WritePhonebookFile( pInfo->pFileMain, NULL );
            }

            EndDialog( hwnd, (wId == IDYES) );
            return TRUE;
        }

        case IDCANCEL:
        {
            EndDialog( hwnd, FALSE );
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
ViInit(
    IN HWND hwndDlg,
    IN DINFO* pInfo )

    // Called on WM_INITDIALOG.  'HwndDlg' is the handle of dialog.  'PUser'
    // is caller's argument to the stub API.
    //
    // Return false if focus was set, true otherwise, i.e. as defined for
    // WM_INITDIALOG.
    //
{
    TRACE( "ViInit" );

    // Set the dialog context.
    //
    SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );

    // Set the explanatory text.
    //
    {
        MSGARGS msgargs;

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

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

        if (msgargs.pszOutput)
        {
            SetDlgItemText( hwndDlg, CID_VI_ST_Explain, msgargs.pszOutput );
            Free( msgargs.pszOutput );
        }
    }

    // Display finished window.
    //
    CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    SetForegroundWindow( hwndDlg );

    return TRUE;
}

DWORD
DwTerminalDlg(
    LPCWSTR lpszPhonebook,
    LPCWSTR lpszEntry,
    RASDIALPARAMS *prdp,
    HWND hwndOwner,
    HRASCONN hRasconn)
{
    DWORD dwErr = ERROR_SUCCESS;
    PBENTRY *pEntry = NULL;
    PBFILE pbfile;
    DTLNODE *pNode = NULL;
    WCHAR szIpAddress[ TERM_IpAddress ];
    DWORD sidTitle;
    WCHAR *pszIpAddress;

    //
    //Initialize memory for Whistler bug 160888
    //
    ZeroMemory(&pbfile, sizeof(PBFILE)); 
    pbfile.hrasfile = -1;

    dwErr = LoadRas( g_hinstDll, hwndOwner );

    if (ERROR_SUCCESS != dwErr)
    {
        goto done;
    }
    
    
    dwErr = GetPbkAndEntryName(
            lpszPhonebook,
            lpszEntry,
            0,
            &pbfile,
            &pNode);

    if(     (NULL == pNode)
        ||  (ERROR_SUCCESS != dwErr))
    {
        dwErr = ERROR_CANNOT_FIND_PHONEBOOK_ENTRY;
        goto done;
    }

    pEntry = (PBENTRY *) DtlGetData(pNode);
    ASSERT(NULL != pEntry);

    if(NULL == pEntry)
    {
        goto done;
    }

    if (pEntry->dwBaseProtocol == BP_Slip)
    {
        lstrcpyn( 
            szIpAddress,
            (pEntry->pszIpAddress) ? pEntry->pszIpAddress : TEXT("0.0.0.0"),
            sizeof(szIpAddress) / sizeof(TCHAR));
        pszIpAddress = szIpAddress;
        sidTitle = SID_T_SlipTerminal;
    }
    else
    {
        szIpAddress[0] = TEXT('\0');
        pszIpAddress = szIpAddress;
        sidTitle = SID_T_PostconnectTerminal;
    }


    if (!TerminalDlg(
            pEntry, prdp, hwndOwner,
            hRasconn, sidTitle, pszIpAddress ))
    {
        TRACE( "TerminalDlg==FALSE" );
        dwErr = E_FAIL;
        goto done;
    }

    TRACE2( "pszIpAddress=0x%08x(%ls)", pszIpAddress,
        pszIpAddress ? pszIpAddress : TEXT("") );
    TRACE2( "pEntry->pszIpAddress=0x%08x(%ls)", pEntry->pszIpAddress,
        pEntry->pszIpAddress ? pEntry->pszIpAddress : TEXT("") );

    if (pszIpAddress[0]
        && (!pEntry->pszIpAddress
            || lstrcmp( pszIpAddress, pEntry->pszIpAddress ) != 0))
    {
        Free0( pEntry->pszIpAddress );
        pEntry->pszIpAddress = StrDup( szIpAddress );
        pEntry->fDirty = TRUE;
        
        dwErr = WritePhonebookFile( &pbfile, NULL );
        if (dwErr != 0)
        {
            ErrorDlg( hwndOwner, SID_OP_WritePhonebook, dwErr, NULL );
        }
    }
    
    done:
        ClosePhonebookFile(&pbfile);
        return dwErr;
        
}