/*++


Copyright (c) 1996  Microsoft Corporation

Module Name:

    proxysup.c

Abstract:

    Contains implementation for proxy server and proxy bypass list
    dialog interface.

    WARNING: Changes in this code need to be syncronizated
    with changes in proxysup.cxx in the WININET project.

    Contents:

Author:

    Arthur L Bierer (arthurbi) 18-Apr-1996

Revision History:

    18-Apr-1996 arthurbi
    Created

--*/

#include "inetcplp.h"

#include <mluisupp.h>

// Disable warning for VC6 (ASSERT macro causing the problem)
#pragma warning(4:4509) // nonstandard extension used: 'ftn' uses SEH and 'object' has destructor

//
// Don't use CRTs so define our own isdigit
//

#undef isdigit
#define isdigit(ch) (ch >= '0' && ch <= '9')

//
// ARRAY_ELEMENTS - returns number of elements in array
//

#define ARRAY_ELEMENTS(array)   (sizeof(array)/sizeof(array[0]))

#define GET_TERMINATOR(string)  while(*string != '\0') string++

#define IS_BLANK(string)        (*string == '\0')


//
// private types
//
typedef enum {
    STATE_START,
    STATE_PROTOCOL,
    STATE_SCHEME,
    STATE_SERVER,
    STATE_PORT,
    STATE_END,
    STATE_ERROR
} PARSER_STATE;

typedef struct tagMY_URL_SCHEME
{
    LPSTR           SchemeName;
    DWORD           SchemeLength;
    INTERNET_SCHEME SchemeType;
    DWORD           dwControlId;
    DWORD           dwPortControlId;
} MY_URL_SCHEME;

const MY_URL_SCHEME UrlSchemeList[] =
{
    NULL,       0,  INTERNET_SCHEME_DEFAULT,IDC_NOTUSED,                  IDC_NOTUSED,
    "ftp",      3,  INTERNET_SCHEME_FTP,    IDC_PROXY_FTP_ADDRESS,        IDC_PROXY_FTP_PORT,
    "gopher",   6,  INTERNET_SCHEME_GOPHER, IDC_PROXY_GOPHER_ADDRESS,     IDC_PROXY_GOPHER_PORT,
    "http",     4,  INTERNET_SCHEME_HTTP,   IDC_PROXY_HTTP_ADDRESS,       IDC_PROXY_HTTP_PORT,
    "https",    5,  INTERNET_SCHEME_HTTPS,  IDC_PROXY_SECURITY_ADDRESS,   IDC_PROXY_SECURITY_PORT,
    "socks",    5,  INTERNET_SCHEME_SOCKS,  IDC_PROXY_SOCKS_ADDRESS,      IDC_PROXY_SOCKS_PORT,
};

#define INTERNET_MAX_PORT_LENGTH    sizeof("123456789")

typedef struct tagBEFOREUSESAME
{
    // addresses
    TCHAR szFTP      [ INTERNET_MAX_URL_LENGTH + 1 ];
    TCHAR szGOPHER   [ INTERNET_MAX_URL_LENGTH + 1 ];
    TCHAR szSECURE   [ INTERNET_MAX_URL_LENGTH + 1 ];
    TCHAR szSOCKS    [ INTERNET_MAX_URL_LENGTH + 1 ];

    // ports
    TCHAR szFTPport      [ INTERNET_MAX_PORT_LENGTH + 1 ];
    TCHAR szGOPHERport   [ INTERNET_MAX_PORT_LENGTH + 1 ];
    TCHAR szSECUREport   [ INTERNET_MAX_PORT_LENGTH + 1 ];
    TCHAR szSOCKSport    [ INTERNET_MAX_PORT_LENGTH + 1 ];

} BEFOREUSESAME, *LPBEFOREUSESAME;

static const int g_iProxies[] = {IDC_PROXY_HTTP_ADDRESS, IDC_PROXY_FTP_ADDRESS, IDC_PROXY_GOPHER_ADDRESS, IDC_PROXY_SECURITY_ADDRESS, IDC_PROXY_SOCKS_ADDRESS};


typedef struct tagPROXYPAGE
{
    LPBEFOREUSESAME lpOldSettings;
    BOOL            fNT;
    LPPROXYINFO     lpi;
    HINSTANCE       hinstUrlMon;    // runtime load URLMON.DLL
} PROXYPAGE, *LPPROXYPAGE;

extern const TCHAR cszLocalString[] = TEXT("<local>");

#define MAX_SCHEME_NAME_LENGTH  sizeof("gopher")

#define MAX_TITLE           80
#define MAX_DIALOG_MESSAGE  300


//
// private function prototypes
//


LPBEFOREUSESAME SaveCurrentSettings(HWND hDlg);
void RestorePreviousSettings(HWND hDlg, LPBEFOREUSESAME lpSave);

BOOL
ProxyDlgInitProxyServers(
    IN HWND hDlg
    );

BOOL
ProxyDlgOK(
    IN HWND hDlg
    );

BOOL
ProxyDlgInit(
    IN HWND hDlg, LPARAM lParam
    );

VOID
EnableProxyControls(
    IN HWND hDlg
    );

BOOL
IsProxyValid(
    IN HWND     hDlg
    );


BOOL
ParseEditCtlForPort(
    IN OUT LPSTR   lpszProxyName,
    IN HWND        hDlg,
    IN DWORD       dwProxyNameCtlId,
    IN DWORD       dwProxyPortCtlId
    );

BOOL
FormatOutProxyEditCtl(
    IN HWND        hDlg,
    IN DWORD       dwProxyNameCtlId,
    IN DWORD       dwProxyPortCtlId,
    OUT LPSTR     lpszOutputStr,
    IN OUT LPDWORD lpdwOutputStrSize,
    IN DWORD       dwOutputStrLength,
    IN BOOL        fDefaultProxy
    );

INTERNET_SCHEME
MapUrlSchemeName(
    IN LPSTR lpszSchemeName,
    IN DWORD dwSchemeNameLength
    );

DWORD
MapUrlSchemeTypeToCtlId(
    IN INTERNET_SCHEME SchemeType,
    IN BOOL        fIdForPortCtl
    );


BOOL
MapCtlIdUrlSchemeName(
    IN DWORD    dwEditCtlId,
    OUT LPSTR   lpszSchemeOut
    );


DWORD
MapAddrCtlIdToPortCtlId(
    IN DWORD    dwEditCtlId
    );

BOOL
RemoveLocalFromExceptionList(
    IN LPTSTR lpszExceptionList
    );



//
// functions
//


/*******************************************************************

    NAME:       ProxyDlgProc

    SYNOPSIS:   Proxy property sheet dialog proc.

********************************************************************/
INT_PTR CALLBACK ProxyDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,
    LPARAM lParam)
{
    LPPROXYPAGE pPxy= (LPPROXYPAGE) GetWindowLongPtr(hDlg, DWLP_USER);

    switch (uMsg)
    {
        
        case WM_INITDIALOG:
        {
            BOOL fInited;
            
            fInited = ProxyDlgInit(hDlg, lParam);
            return fInited;
        }
        
        case WM_NOTIFY:
        {
            NMHDR * lpnm = (NMHDR *) lParam;
            switch (lpnm->code)
            {
                case PSN_APPLY:
                {
                    BOOL fRet = ProxyDlgOK(hDlg);
                    SetPropSheetResult(hDlg,!fRet);
                    return !fRet;
                    break;
                }
                
                case PSN_RESET:
                    SetPropSheetResult(hDlg,FALSE);
                    break;
            }
            break;
        }
        
        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                case IDC_PROXY_ENABLE:
                    EnableProxyControls(hDlg);
                    break;

                case IDC_PROXY_HTTP_ADDRESS:
                case IDC_PROXY_GOPHER_ADDRESS:
                case IDC_PROXY_SECURITY_ADDRESS:
                case IDC_PROXY_FTP_ADDRESS:
                case IDC_PROXY_SOCKS_ADDRESS:

                    if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_KILLFOCUS )
                    {
                        ParseEditCtlForPort(NULL, hDlg, (GET_WM_COMMAND_ID(wParam, lParam)), 0);
                        EnableProxyControls(hDlg);
                    }
                    break;

                case IDC_PROXY_HTTP_PORT:
                case IDC_PROXY_GOPHER_PORT:
                case IDC_PROXY_SECURITY_PORT:
                case IDC_PROXY_FTP_PORT:
                case IDC_PROXY_SOCKS_PORT:

                    if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_KILLFOCUS )
                    {
                        EnableProxyControls(hDlg);
                    }
                    break;


                case IDC_PROXY_USE_SAME_SERVER:

                    if ( GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED )
                    {
                        if (IsDlgButtonChecked(hDlg, IDC_PROXY_USE_SAME_SERVER))
                            pPxy->lpOldSettings = SaveCurrentSettings(hDlg);
                        else if (pPxy->lpOldSettings !=NULL)
                        {
                            RestorePreviousSettings(hDlg, pPxy->lpOldSettings);
                            pPxy->lpOldSettings = NULL;
                        }

                        EnableProxyControls(hDlg);
                    }
                    break;

                case IDOK:

                    if ( GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED )
                    {
                        BOOL fLeaveDialog = TRUE;

                        if (!IsProxyValid(hDlg))
                        {
                            // The proxy is invalid, so we need to ask the user if they want to turn it off.
                            TCHAR szTitle[MAX_TITLE];
                            TCHAR szMessage[MAX_DIALOG_MESSAGE];
                            int nUserResponce;

                            MLLoadShellLangString(IDS_INVALID_PROXY_TITLE, szTitle, ARRAYSIZE(szTitle));
                            MLLoadShellLangString(IDS_INVALID_PROXY, szMessage, ARRAYSIZE(szMessage));

                            // Ask the user if they want to turn off the proxy.
                            nUserResponce = MessageBox(hDlg, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONERROR);
                            if (IDYES == nUserResponce)
                                pPxy->lpi->fEnable = FALSE;
                            else if (IDCANCEL == nUserResponce)
                                fLeaveDialog = FALSE;   // The user hit cancel, so let's not leave the dialog.
                        }

                        if (fLeaveDialog)
                        {
                            //
                            // Read the Ctls and Save out to the proxy..
                            //
                            ProxyDlgOK(hDlg);
                            EndDialog(hDlg, IDOK);
                        }
                    }
                    break;

                case IDCANCEL:
                    if ( GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED )
                    {
                        EndDialog(hDlg, IDCANCEL);
                    }
                    break;

            }
            break;

        case WM_DESTROY:
            if (pPxy->lpOldSettings)
                LocalFree(pPxy->lpOldSettings);

            if (pPxy->hinstUrlMon)
                FreeLibrary(pPxy->hinstUrlMon);

            LocalFree(pPxy);
            return TRUE;

        case WM_HELP:      // F1
            ResWinHelp((HWND) ((LPHELPINFO)lParam)->hItemHandle, IDS_HELPFILE,
                HELP_WM_HELP, (DWORD_PTR)(LPSTR)mapIDCsToIDHs);
            break;

        case WM_CONTEXTMENU:      // right mouse click
            ResWinHelp((HWND)wParam, IDS_HELPFILE,
                HELP_CONTEXTMENU, (DWORD_PTR)(LPSTR)mapIDCsToIDHs);
            break;

        default:
            return FALSE;
    }
    return TRUE;
}


//
// Private Functions.
//

VOID
EnableProxyControls(HWND hDlg)

/*++

Routine Description:

    Enables controls appropriately depending on what
    checkboxes are checked.

Arguments:

    hDlg        - Page Dialog Box.

Return Value:

    VOID

--*/


{
    BOOL fNT = ((LPPROXYPAGE)GetWindowLongPtr(hDlg, DWLP_USER))->fNT;
    
    BOOL fEnable = !g_restrict.fProxy;
            
    BOOL fUseOneProxy = IsDlgButtonChecked(hDlg,IDC_PROXY_USE_SAME_SERVER);
    
    EnableDlgItem(hDlg,IDC_GRP_SETTINGS2, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_EXCEPTIONS_GROUPBOX, fEnable);

    EnableDlgItem(hDlg,IDC_TYPE_TEXT, fEnable);
    EnableDlgItem(hDlg,IDC_ADDR_TEXT, fEnable);
    EnableDlgItem(hDlg,IDC_PORT_TEXT, fEnable);
    EnableDlgItem(hDlg,IDC_EXCEPT_TEXT, fEnable);
    EnableDlgItem(hDlg,IDC_EXCEPT2_TEXT, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_ICON1, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_ICON2, fEnable);
    
    EnableDlgItem(hDlg,IDC_PROXY_HTTP_CAPTION, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_SECURITY_CAPTION, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_FTP_CAPTION, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_GOPHER_CAPTION, fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_SOCKS_CAPTION, fEnable);

    EnableDlgItem(hDlg, IDC_PROXY_USE_SAME_SERVER, fEnable);

    EnableDlgItem(hDlg,IDC_PROXY_HTTP_ADDRESS,fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_HTTP_PORT,fEnable);
           
    EnableDlgItem(hDlg,IDC_PROXY_OVERRIDE,fEnable);

    //
    // If we only want one Proxy, then make all others use the same
    //  proxy.
    //

    EnableDlgItem(hDlg,IDC_PROXY_SECURITY_ADDRESS,!fUseOneProxy && fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_SECURITY_PORT,!fUseOneProxy && fEnable);

    EnableDlgItem(hDlg,IDC_PROXY_FTP_ADDRESS,!fUseOneProxy && fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_FTP_PORT,!fUseOneProxy && fEnable);

    EnableDlgItem(hDlg,IDC_PROXY_GOPHER_ADDRESS,!fUseOneProxy && fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_GOPHER_PORT,!fUseOneProxy && fEnable);

    EnableDlgItem(hDlg,IDC_PROXY_SOCKS_ADDRESS,!fUseOneProxy && fEnable);
    EnableDlgItem(hDlg,IDC_PROXY_SOCKS_PORT,!fUseOneProxy && fEnable);

    //
    // If we only want one proxy, prepopulate the other fields
    //  so they use the the mirror of the first one.
    //

    if (fUseOneProxy)
    {
        TCHAR szProxyName[MAX_URL_STRING+1];
        TCHAR szProxyPort[INTERNET_MAX_PORT_LENGTH];

        GetDlgItemText(hDlg,
            IDC_PROXY_HTTP_ADDRESS,
            szProxyName,
            ARRAYSIZE(szProxyName));

        GetDlgItemText(hDlg,
            IDC_PROXY_HTTP_PORT,
            szProxyPort,
            ARRAYSIZE(szProxyPort));

        SetDlgItemText(hDlg,IDC_PROXY_SECURITY_ADDRESS,szProxyName);
        SetDlgItemText(hDlg,IDC_PROXY_SECURITY_PORT,szProxyPort);

        SetDlgItemText(hDlg,IDC_PROXY_FTP_ADDRESS,szProxyName);
        SetDlgItemText(hDlg,IDC_PROXY_FTP_PORT,szProxyPort);

        SetDlgItemText(hDlg,IDC_PROXY_GOPHER_ADDRESS,szProxyName);
        SetDlgItemText(hDlg,IDC_PROXY_GOPHER_PORT,szProxyPort);

        SetDlgItemText(hDlg,IDC_PROXY_SOCKS_ADDRESS,TEXT(""));
        SetDlgItemText(hDlg,IDC_PROXY_SOCKS_PORT,TEXT(""));
    }
}

//
// SaveCurrentSettings()
//
// Saves current settings... just in case user changes their mind.
//
// Returns a pointer to a structure filled with current settings.
//
LPBEFOREUSESAME SaveCurrentSettings(HWND hDlg)
{
    LPBEFOREUSESAME lpSave = (LPBEFOREUSESAME)LocalAlloc(LPTR, sizeof(*lpSave));

    if (!lpSave)
        return lpSave; // if NULL return NULL

    GetDlgItemText(hDlg, IDC_PROXY_FTP_ADDRESS,      lpSave->szFTP,    INTERNET_MAX_URL_LENGTH);
    GetDlgItemText(hDlg, IDC_PROXY_GOPHER_ADDRESS,   lpSave->szGOPHER, INTERNET_MAX_URL_LENGTH);
    GetDlgItemText(hDlg, IDC_PROXY_SECURITY_ADDRESS, lpSave->szSECURE, INTERNET_MAX_URL_LENGTH);
    GetDlgItemText(hDlg, IDC_PROXY_SOCKS_ADDRESS,    lpSave->szSOCKS,  INTERNET_MAX_URL_LENGTH);

    GetDlgItemText(hDlg, IDC_PROXY_FTP_PORT,      lpSave->szFTPport,    INTERNET_MAX_URL_LENGTH);
    GetDlgItemText(hDlg, IDC_PROXY_GOPHER_PORT,   lpSave->szGOPHERport, INTERNET_MAX_URL_LENGTH);
    GetDlgItemText(hDlg, IDC_PROXY_SECURITY_PORT, lpSave->szSECUREport, INTERNET_MAX_URL_LENGTH);
    GetDlgItemText(hDlg, IDC_PROXY_SOCKS_PORT,    lpSave->szSOCKSport,  INTERNET_MAX_URL_LENGTH);

    return lpSave;

} // SaveCurrentSettings()


//
// RestorePreviousSettings()
//
// Restores settings... just in case user changes their mind.
//
void RestorePreviousSettings(HWND hDlg, LPBEFOREUSESAME lpSave)
{

    if (!lpSave)
    return; // nothing to do

    SetDlgItemText(hDlg, IDC_PROXY_FTP_ADDRESS,      lpSave->szFTP    );
    SetDlgItemText(hDlg, IDC_PROXY_GOPHER_ADDRESS,   lpSave->szGOPHER );
    SetDlgItemText(hDlg, IDC_PROXY_SECURITY_ADDRESS, lpSave->szSECURE );
    SetDlgItemText(hDlg, IDC_PROXY_SOCKS_ADDRESS,    lpSave->szSOCKS  );

    SetDlgItemText(hDlg, IDC_PROXY_FTP_PORT,      lpSave->szFTPport    );
    SetDlgItemText(hDlg, IDC_PROXY_GOPHER_PORT,   lpSave->szGOPHERport );
    SetDlgItemText(hDlg, IDC_PROXY_SECURITY_PORT, lpSave->szSECUREport );
    SetDlgItemText(hDlg, IDC_PROXY_SOCKS_PORT,    lpSave->szSOCKSport  );

    LocalFree(lpSave);  // give back the memory

} // RestorePreviousSettings()


BOOL
ProxyDlgInit(
    IN HWND hDlg, LPARAM lParam
    )

/*++

Routine Description:

    Initialization proc for proxy prop page

Arguments:

    hDlg        - Page Dialog Box.

Return Value:

    BOOL
    Success - TRUE

    Failure - FALSE

--*/


{
    BOOL fSuccess;
    LPPROXYPAGE pPxy;

    pPxy = (LPPROXYPAGE)LocalAlloc(LPTR, sizeof(*pPxy));
    // NOTE: this NULLS lpOldSettings

    if (!pPxy)
        return FALSE;   // no memory?

    OSVERSIONINFOA osvi;
    osvi.dwOSVersionInfoSize = sizeof(osvi);
    GetVersionExA(&osvi);
    
    pPxy->fNT = (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
    ASSERT(pPxy->lpOldSettings == NULL);
        
    // tell dialog where to get info
    SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pPxy);

    pPxy->lpi = (LPPROXYINFO)lParam;
    //
    // Begin by reading and setting the list of proxy
    //  servers we have.
    //

    fSuccess = ProxyDlgInitProxyServers( hDlg );

    if (!fSuccess)
        return FALSE;

    //
    // read settings from registry, for the list of Exclusion Hosts.
    //

    RegEntry re(REGSTR_PATH_INTERNETSETTINGS,HKEY_CURRENT_USER);

    if (re.GetError() == ERROR_SUCCESS)
    {
        BUFFER bufProxyString(MAX_URL_STRING+1);
        
        if (!bufProxyString)
        {
            MsgBox(NULL,IDS_ERROutOfMemory,MB_ICONEXCLAMATION,MB_OK);
            return FALSE;
        }

        //  
        // get proxy override settings from registry and stuff fields
        // Prevent entry of more than (MAX_URL_STRING - ("<local>" + safety))
        SendMessage(GetDlgItem(hDlg, IDC_PROXY_OVERRIDE), EM_SETLIMITTEXT, (WPARAM)ARRAYSIZE(pPxy->lpi->szOverride)-20, 0);
        SetDlgItemText(hDlg, IDC_PROXY_OVERRIDE, pPxy->lpi->szOverride);
    }

    //
    // initialize the UI appropriately
    //

    EnableProxyControls(hDlg);
    
    return TRUE;
}

BOOL
ProxyDlgOK(
    IN HWND hDlg
    )

/*++

Routine Description:

    OK button handler for proxy prop page

Arguments:

    hDlg        - Page Dialog Box.

Return Value:

    BOOL
    Success - TRUE

    Failure - FALSE

--*/


{
    LPPROXYPAGE pPxy = (LPPROXYPAGE)GetWindowLongPtr(hDlg, DWLP_USER);       
    TCHAR szProxyListOutputBuffer[MAX_URL_STRING];
    CHAR  szProxyListOutputBufferA[MAX_URL_STRING];
    DWORD dwBufferOffset = 0;

    //
    // Get the state of our two check boxes.
    //

    BOOL fUseOneProxy =
        IsDlgButtonChecked(hDlg,IDC_PROXY_USE_SAME_SERVER);
    //
    // Open our Registry Key.
    //
    
    RegEntry re(REGSTR_PATH_INTERNETSETTINGS,HKEY_CURRENT_USER);
    if (re.GetError() == ERROR_SUCCESS)
    {       
        //
        //  Now Format, and write out the list of proxies to
        //  the registry.  We special case the case of
        //  only proxy.
        //

        szProxyListOutputBufferA[dwBufferOffset] = '\0';

        if ( fUseOneProxy )
        {
            FormatOutProxyEditCtl(
                                  hDlg,
                                  IDC_PROXY_HTTP_ADDRESS,
                                  IDC_PROXY_HTTP_PORT,
                                  szProxyListOutputBufferA,
                                  &dwBufferOffset,
                                  ARRAY_ELEMENTS(szProxyListOutputBufferA),
                                  TRUE
                                 );
        }
        else
        {
            for (int i = 1; i < ARRAY_ELEMENTS(UrlSchemeList); ++i)
            {
                FormatOutProxyEditCtl(
                                      hDlg,
                                      UrlSchemeList[i].dwControlId,
                                      UrlSchemeList[i].dwPortControlId,
                                      szProxyListOutputBufferA,
                                      &dwBufferOffset,
                                      ARRAY_ELEMENTS(szProxyListOutputBufferA),
                                      FALSE
                                     );

            }
        }

        szProxyListOutputBufferA[dwBufferOffset] = '\0';

#ifdef UNICODE
        SHAnsiToUnicode(szProxyListOutputBufferA, szProxyListOutputBuffer, MAX_URL_STRING);
#else
        lstrcpy(szProxyListOutputBuffer, szProxyListOutputBufferA);
#endif
        StrCpyN(pPxy->lpi->szProxy, szProxyListOutputBuffer, MAX_URL_STRING);

        //
        // Now Write out the Proxy Exception List
        //  (list of addresses to use for local connections)
        //

        szProxyListOutputBuffer[0] = '\0';
        
        GetDlgItemText(hDlg,
                       IDC_PROXY_OVERRIDE,
                       szProxyListOutputBuffer,
                       ARRAY_ELEMENTS(szProxyListOutputBuffer));
                
        StrCpyN(pPxy->lpi->szOverride, szProxyListOutputBuffer, MAX_URL_STRING);
    }
    
    else    
    {
        AssertMsg(0, TEXT("Couldn't save settings to registry!"));
    }


    // let any active (participating) wininet's get notified
    //
    InternetSetOption(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);

    return TRUE;
}


INTERNET_SCHEME
MapUrlSchemeName(
    IN LPSTR lpszSchemeName,
    IN DWORD dwSchemeNameLength
    )

/*++

Routine Description:

    Maps a scheme name/length to a scheme name type

Arguments:

    lpszSchemeName  - pointer to name of scheme to map

    dwSchemeNameLength  - length of scheme (if -1, lpszSchemeName is ASCIZ)

Return Value:

    INTERNET_SCHEME

--*/

{
    if (dwSchemeNameLength == (DWORD)-1)
    {
        dwSchemeNameLength = (DWORD)lstrlenA(lpszSchemeName);
    }
    
    for (int i = 0; i < ARRAY_ELEMENTS(UrlSchemeList); ++i)
    {
        if (UrlSchemeList[i].SchemeLength == dwSchemeNameLength)
        {   
            CHAR chBackup = lpszSchemeName[dwSchemeNameLength];
            lpszSchemeName[dwSchemeNameLength] = '\0';
            
            if(StrCmpIA(UrlSchemeList[i].SchemeName,lpszSchemeName) == 0)
            {
                lpszSchemeName[dwSchemeNameLength] = chBackup;
                return UrlSchemeList[i].SchemeType;
            }
            
            lpszSchemeName[dwSchemeNameLength] = chBackup;
        }
    }
    return INTERNET_SCHEME_UNKNOWN;
}



DWORD
MapUrlSchemeTypeToCtlId(
    IN INTERNET_SCHEME SchemeType,
    IN BOOL        fIdForPortCtl
    )

/*++

Routine Description:

    Maps a scheme to a dlg child control id.

Arguments:

    Scheme    - Scheme to Map

    fIdForPortCtl - If TRUE, means we really want the ID for a the PORT control
            not the ADDRESS control.

Return Value:

    DWORD

--*/

{
    for (int i = 0; i < ARRAY_ELEMENTS(UrlSchemeList); ++i)
    {
        if (SchemeType == UrlSchemeList[i].SchemeType)
        {
            return (fIdForPortCtl ? UrlSchemeList[i].dwPortControlId :
                    UrlSchemeList[i].dwControlId );
        }
    }
    return IDC_NOTUSED;
}

BOOL
MapCtlIdUrlSchemeName(
    IN DWORD    dwEditCtlId,
    OUT LPSTR   lpszSchemeOut
    )

/*++

Routine Description:

    Maps a dlg child control id to String represnting
    the name of the scheme.

Arguments:

    dwEditCtlId   - Edit Control to Map Out.

    lpszSchemeOut - Scheme to Map Out.
            WARNING: ASSUMED to be size of largest scheme type.

Return Value:

    BOOL
    Success - TRUE

    Failure - FALSE

--*/

{
    ASSERT(lpszSchemeOut);

    for (int i = 0; i < ARRAY_ELEMENTS(UrlSchemeList); ++i)
    {
        if (dwEditCtlId == UrlSchemeList[i].dwControlId )
        {
            StrCpyA(lpszSchemeOut, UrlSchemeList[i].SchemeName);
            return TRUE;
        }
    }
    return FALSE;
}


DWORD
MapAddrCtlIdToPortCtlId(
    IN DWORD    dwEditCtlId
    )

/*++

Routine Description:

    Maps a dlg child control id for addresses to
    a dlg control id for ports.

Arguments:

    dwEditCtlId   - Edit Control to Map Out.

Return Value:

    DWORD
    Success - Correctly mapped ID.

    Failure - 0.

--*/

{

    for (int i = 0; i < ARRAY_ELEMENTS(UrlSchemeList); ++i)
    {
        if (dwEditCtlId == UrlSchemeList[i].dwControlId )
        {
            return UrlSchemeList[i].dwPortControlId ;
        }
    }
    return FALSE;
}


BOOL
ProxyDlgInitProxyServers(
    IN HWND hDlg
    )

/*++

Routine Description:

    Parses a list of proxy servers and sets them into the newly created
    Proxy Dialog.

    Ruthlessly stolen from RFirth's proxysup.cxx in WININET by
    ArthurBi.

Arguments:

    hDlg    -      HWin to add our stuff to.


Return Value:

    BOOL
    Success - TRUE

    Failure - FALSE

Comments:

    Designed to handle Proxy string entry of the Form:

      pointer to list of proxies of the form:

      [<protocol>=][<scheme>"://"]<server>[":"<port>][";"*]

      The list can is read from the registry.


--*/

{
    DWORD error = !ERROR_SUCCESS;
    DWORD entryLength;
    LPSTR protocolName;
    DWORD protocolLength;
    LPSTR schemeName;
    DWORD schemeLength;
    LPSTR serverName;
    DWORD serverLength;
    PARSER_STATE state;
    DWORD nSlashes;
    INTERNET_PORT port;
    BOOL done;
    LPSTR lpszList;

    entryLength = 0;
    protocolLength = 0;
    schemeName = NULL;
    schemeLength = 0;
    serverName = NULL;
    serverLength = 0;
    state = STATE_PROTOCOL;
    nSlashes = 0;
    port = 0;
    done = FALSE;


    //
    // Open the Reg Key.
    //

    RegEntry re(REGSTR_PATH_INTERNETSETTINGS,HKEY_CURRENT_USER);

    if (re.GetError() != ERROR_SUCCESS)
        return FALSE; // no REG values..


    //
    // Crack the Registry values, read the proxy list
    //


    BUFFER bufProxyString(MAX_URL_STRING+1);
    BOOL   fProxyEnabled;

    if (!bufProxyString)
    {
        MsgBox(NULL,IDS_ERROutOfMemory,MB_ICONEXCLAMATION,MB_OK);
        return FALSE;
    }

    //
    // is proxy enabled?
    // It should if we got into this dialog.
    //

    fProxyEnabled = (BOOL)re.GetNumber(REGSTR_VAL_PROXYENABLE,0);

    //
    // get proxy server and override settings from registry and stuff fields
    //

    re.GetString(REGSTR_VAL_PROXYSERVER,bufProxyString.QueryPtr(),
        bufProxyString.QuerySize());


    LPPROXYPAGE pPxy = (LPPROXYPAGE)GetWindowLongPtr(hDlg, DWLP_USER);

    // if there's a proxy passed in from the main page, then use it; otherwise use the registry val
#ifndef UNICODE
    lpszList = pPxy->lpi->szProxy;
#else
    char*  szList = NULL;
    LPTSTR lpTmp = pPxy->lpi->szProxy;
    DWORD  cch = lstrlen(lpTmp) + 1;
    szList = new char[2 * cch];
    if (szList)
    {
        SHUnicodeToAnsi(lpTmp, szList, 2 * cch);
        lpszList = szList;
    }
    else
    {
        MsgBox(NULL,IDS_ERROutOfMemory,MB_ICONEXCLAMATION,MB_OK);
        return FALSE;
    }
#endif

    protocolName = lpszList;

    //
    // walk the list, pulling out the various scheme parts
    //

    do
    {
        char ch = *lpszList++;

        if ((nSlashes == 1) && (ch != '/'))
        {
            state = STATE_ERROR;
            break;
        }
        
        switch (ch)
        {
            case '=':
                if ((state == STATE_PROTOCOL) && (entryLength != 0))
                {
                    protocolLength = entryLength;
                    entryLength = 0;
                    state = STATE_SCHEME;
                    schemeName = lpszList;
                }
                else
                {
                    //
                    // '=' can't legally appear anywhere else
                    //
                    state = STATE_ERROR;
                }
                break;
                
            case ':':
                switch (state)
                {
                    case STATE_PROTOCOL:
                        if (*lpszList == '/')
                        {
                            schemeName = protocolName;
                            protocolName = NULL;
                            schemeLength = entryLength;
                            protocolLength = 0;
                            state = STATE_SCHEME;
                        }
                        else if (*lpszList != '\0')
                        {
                            serverName = protocolName;
                            serverLength = entryLength;
                            state = STATE_PORT;
                        }
                        else
                        {
                            state = STATE_ERROR;
                        }
                        entryLength = 0;
                        break;
                        
                    case STATE_SCHEME:
                        if (*lpszList == '/')
                        {
                            schemeLength = entryLength;
                        }
                        else if (*lpszList != '\0')
                        {
                            serverName = schemeName;
                            serverLength = entryLength;
                            state = STATE_PORT;
                        }
                        else
                        {
                            state = STATE_ERROR;
                        }
                        entryLength = 0;
                        break;
                        
                    case STATE_SERVER:
                        serverLength = entryLength;
                        state = STATE_PORT;
                        entryLength = 0;
                        break;

                    default:
                        state = STATE_ERROR;
                        break;
                }
                break;

            case '/':
                if ((state == STATE_SCHEME) && (nSlashes < 2) && (entryLength == 0))
                {
                    if (++nSlashes == 2)
                    {
                        state = STATE_SERVER;
                        serverName = lpszList;
                    }
                }
                else
                {
                    state = STATE_ERROR;
                }
                break;

            case '\v':  // vertical tab, 0x0b
            case '\f':  // form feed, 0x0c
                if (!((state == STATE_PROTOCOL) && (entryLength == 0)))
                {
                    //
                    // can't have embedded whitespace
                    //

                    state = STATE_ERROR;
                }
                break;

            default:
                if (state != STATE_PORT)
                {
                    ++entryLength;
                }
                else if (isdigit(ch))
                {
                    //
                    // we will overflow if >65535
                    //
                    Assert(port < 65535);
                    port = port * 10 + (ch - '0');
                }
                else
                {                   
                    //
                    // STATE_PORT && non-digit character - error
                    //
                    state = STATE_ERROR;
                }
                break;

            case '\0':
                done = TRUE;

                //
                // fall through
                //
            case ' ':
            case '\t':
            case '\n':
            case '\r':
            case ';':
            case ',':
                if (serverLength == 0)
                {
                    serverLength = entryLength;
                }
                if (serverLength != 0)
                {
                    if (serverName == NULL)
                    {
                        serverName = (schemeName != NULL)
                            ? schemeName : protocolName;
                    }

                    ASSERT(serverName != NULL);

                    INTERNET_SCHEME protocol;

                    if (protocolLength != 0)
                    {
                        protocol = MapUrlSchemeName(protocolName, protocolLength);
                    }
                    else
                    {
                        protocol = INTERNET_SCHEME_DEFAULT;
                    }

                    INTERNET_SCHEME scheme;

                    if (schemeLength != 0)
                    {
                        scheme = MapUrlSchemeName(schemeName, schemeLength);
                    }
                    else
                    {
                        scheme = INTERNET_SCHEME_DEFAULT;
                    }

                    //
                    // add an entry if this is a protocol we handle and we don't
                    // already have an entry for it
                    //

                    if ((protocol != INTERNET_SCHEME_UNKNOWN)
                        && (scheme != INTERNET_SCHEME_UNKNOWN))
                    {
                        DWORD dwCtlId = IDC_NOTUSED;
                        DWORD dwPortCtlId = IDC_NOTUSED;
                        CHAR chBackup;

                        error = ERROR_SUCCESS;
                        //
                        // we can only currently handle CERN proxies (unsecure or
                        // secure) so kick out anything that wants to go via a different
                        // proxy scheme
                        //

                        if (protocol == INTERNET_SCHEME_DEFAULT)
                        {
                            CheckDlgButton( hDlg, IDC_PROXY_USE_SAME_SERVER, TRUE );
                            dwCtlId     = IDC_PROXY_HTTP_ADDRESS;
                            dwPortCtlId = IDC_PROXY_HTTP_PORT;
                        }
                        else
                        {
                            dwCtlId     = MapUrlSchemeTypeToCtlId(protocol,FALSE);
                            dwPortCtlId = MapUrlSchemeTypeToCtlId(protocol,TRUE);
                        }

                        //
                        // Set the Field Entry.
                        //

                        LPSTR lpszProxyNameText;

                        if (scheme != INTERNET_SCHEME_DEFAULT)
                        {
                            ASSERT(schemeLength != 0);
                            lpszProxyNameText = schemeName;
                        }
                        else
                            lpszProxyNameText = serverName;

                        chBackup = serverName[serverLength];
                        serverName[serverLength] = '\0';
                        
                        SetDlgItemTextA( hDlg, dwCtlId, lpszProxyNameText );
                        if ( port )
                            SetDlgItemInt( hDlg, dwPortCtlId, port, FALSE );

                        serverName[serverLength] = chBackup;
                        
                    }
                    
                    else
                    {                      
                        //
                        // bad/unrecognised protocol or scheme. Treat it as error
                        // for now
                        //
                        error = !ERROR_SUCCESS;
                    }
                }

                entryLength = 0;
                protocolName = lpszList;
                protocolLength = 0;
                schemeName = NULL;
                schemeLength = 0;
                serverName = NULL;
                serverLength = 0;
                nSlashes = 0;
                port = 0;
                if (error == ERROR_SUCCESS)
                {
                    state = STATE_PROTOCOL;
                }
                else
                {
                    state = STATE_ERROR;
                }
                break;
        }

        if (state == STATE_ERROR)
        {
            break;
        }
        
    } while (!done);

#ifdef UNICODE
    delete [] szList;
#endif

    if (state == STATE_ERROR)
    {
        error = ERROR_INVALID_PARAMETER;
    }

    if ( error == ERROR_SUCCESS )
        error = TRUE;
    else
        error = FALSE;

    return error;
}


BOOL
IsProxyValid(
    IN HWND     hDlg
    )

/*++

Routine Description:

    Determines if the Proxy is valid.  The proxy is invalid if
    all of the proxy entries are empty.

Arguments:

    hDlg      - HWIN of the dialog to play with.

Return Value:

    BOOL
    Success TRUE - Valid.

    FALSE - Invalid.


--*/

{
    BOOL  fProxyIsValid = FALSE;
    TCHAR szProxyUrl[MAX_URL_STRING+1];
    int   iCurrentProxy = 0;

    ASSERT(IsWindow(hDlg));

    for (int iIndex = 0; iIndex < ARRAYSIZE(g_iProxies); iIndex++)
    {
        szProxyUrl[0] = '\0';
        GetDlgItemText(hDlg,
                       g_iProxies[iIndex],
                       szProxyUrl,
                       sizeof(szProxyUrl));
        
        if (szProxyUrl[0])
        {
            fProxyIsValid = TRUE;
            break;
        }
    }

    return fProxyIsValid;
}



BOOL
ParseEditCtlForPort(
    IN OUT LPSTR   lpszProxyName,
    IN HWND        hDlg,
    IN DWORD       dwProxyNameCtlId,
    IN DWORD       dwProxyPortCtlId
    )

/*++

Routine Description:

    Parses a Port Number off then end of a Proxy Server URL that is
    located either in the Proxy Name Edit Box, or passed in as
    a string pointer.

Arguments:

    lpszProxyName - (OPTIONAL) string pointer with Proxy Name to parse, and
            set into the Proxy Name edit ctl field.

    hDlg      - HWIN of the dialog to play with.

    dwProxyNameCtlId -  Res Ctl Id to play with.

    dwProxyPortCtlId -  Res Ctl Id of Port Number Edit Box.

Return Value:

    BOOL
    Success TRUE -

    Failure FALSE


--*/

{
    CHAR   szProxyUrl[MAX_URL_STRING+1];
    LPSTR  lpszPort;
    LPSTR  lpszProxyUrl;

    ASSERT(IsWindow(hDlg));

    if ( dwProxyPortCtlId == 0 )
    {
        dwProxyPortCtlId = MapAddrCtlIdToPortCtlId(dwProxyNameCtlId);
        ASSERT(dwProxyPortCtlId);
    }

    //
    // Get the Proxy String from the Edit Control
    //  (OR) from the Registry [passed in]
    //

    if ( lpszProxyName )
        lpszProxyUrl = lpszProxyName;
    else
    {
    //
    // Need to Grab it out of the edit control.
    //
        GetDlgItemTextA(hDlg,
            dwProxyNameCtlId,
            szProxyUrl,
            sizeof(szProxyUrl));

        lpszProxyUrl = szProxyUrl;
    }

    //
    // Now find the port.
    //

    lpszPort = lpszProxyUrl;

    GET_TERMINATOR(lpszPort);

    lpszPort--;

    //
    // Walk backwards from the end of url looking
    //  for a port number sitting on the end like this
    //  http://proxy:1234
    //

    while ( (lpszPort > lpszProxyUrl) &&
        (*lpszPort != ':')         &&
        (isdigit(*lpszPort))  )
    {
        lpszPort--;
    }

    //
    // If we found a match for our rules
    //  then set the port, otherwise
    //  we assume the user knows what he's
    //  doing.
    //

    if ( *lpszPort == ':'   &&   isdigit(*(lpszPort+1)) )
    {
        *lpszPort = '\0';

        SetDlgItemTextA(hDlg, dwProxyPortCtlId, (lpszPort+1));
    }

    SetDlgItemTextA(hDlg, dwProxyNameCtlId, lpszProxyUrl);
    return TRUE;
}


BOOL
FormatOutProxyEditCtl(
    IN HWND    hDlg,
    IN DWORD       dwProxyNameCtlId,
    IN DWORD       dwProxyPortCtlId,
    OUT LPSTR      lpszOutputStr,
    IN OUT LPDWORD lpdwOutputStrSize,
    IN DWORD       dwOutputStrLength,
    IN BOOL    fDefaultProxy
    )

/*++

Routine Description:

    Combines Proxy URL components into a string that can be saved
    in the registry.  Can be called multiple times to build
    a list of proxy servers, or once to special case a "default"
    proxy.

Arguments:

    hDlg      - HWIN of the dialog to play with.

    dwProxyNameCtlId -  Res Ctl Id to play with.

    dwProxyPortCtlId -  Res Ctl Id of Port Number Edit Box.

    lpszOutputStr    -  The start of the output string to send
            the product of this function.

    lpdwOutputStrSize - The amount of used space in lpszOutputStr
            that is already used.  New output should
            start from (lpszOutputStr + *lpdwOutputStrSize)

    fDefaultProxy     - Default Proxy, don't add scheme= in front of the proxy
            just use plop one proxy into the registry.


Return Value:

    BOOL
    Success TRUE

    Failure FALSE


--*/

{
    LPSTR lpszOutput;
    LPSTR lpszEndOfOutputStr;

    ASSERT(IsWindow(hDlg));
    ASSERT(lpdwOutputStrSize);

    lpszOutput = lpszOutputStr + *lpdwOutputStrSize;
    lpszEndOfOutputStr = lpszOutputStr + dwOutputStrLength;

    ASSERT( lpszEndOfOutputStr > lpszOutput );

    if ( lpszEndOfOutputStr <= lpszOutput )
        return FALSE; // bail out, ran out of space

    //
    // Plop ';' if we're not the first in this string buffer.
    //

    if (*lpdwOutputStrSize != 0  )
    {
        *lpszOutput = ';';

        lpszOutput++;

        if ( lpszEndOfOutputStr <= lpszOutput )
            return FALSE; // bail out, ran out of space
    }

    //
    // Put the schemetype= into the string
    //  ex:  http=
    //

    if ( ! fDefaultProxy )
    {
        if ( lpszEndOfOutputStr <= (MAX_SCHEME_NAME_LENGTH + lpszOutput + 1) )
            return FALSE; // bail out, ran out of space
        
        if (!MapCtlIdUrlSchemeName(dwProxyNameCtlId,lpszOutput))
            return FALSE;
        
        lpszOutput += lstrlenA(lpszOutput);
    
        *lpszOutput = '=';
        lpszOutput++;
    }
    
    //
    // Need to Grab ProxyUrl out of the edit control.
    //
    
    GetDlgItemTextA(hDlg, dwProxyNameCtlId, lpszOutput, (int)(lpszEndOfOutputStr - lpszOutput));

    if ( IS_BLANK(lpszOutput) )
        return FALSE;

    //
    // Now seperate out the port so we can save them seperately.
    //   But go past the Proxy Url while we're at it.
    //      ex: http=http://netscape-proxy
    //

    if (!ParseEditCtlForPort(lpszOutput, hDlg, dwProxyNameCtlId, dwProxyPortCtlId))
        return FALSE;

    lpszOutput += lstrlenA(lpszOutput);

    //
    // Now, add in a ':" for the port number, if we don't
    //  have a port we'll remove it.
    //
    {
        *lpszOutput = ':';
        
        lpszOutput++;
        
        if ( lpszEndOfOutputStr <= lpszOutput )
            return FALSE; // bail out, ran out of space
    }

    //
    // Grab Proxy Port if its around.
    //  Back out the ':' if its not.
    //

    GetDlgItemTextA(hDlg, dwProxyPortCtlId,lpszOutput, (int)(lpszEndOfOutputStr - lpszOutput));

    if ( IS_BLANK(lpszOutput) )
    {
        lpszOutput--;
        
        ASSERT(*lpszOutput == ':');

        *lpszOutput = '\0';
    }
    
    lpszOutput += lstrlenA(lpszOutput);
    
    //
    // Now we're done return our final sizes.
    //
    
    *lpdwOutputStrSize = (DWORD)(lpszOutput - lpszOutputStr);

    return TRUE;
}

BOOL
RemoveLocalFromExceptionList(
    IN LPTSTR lpszExceptionList
    )

/*++

Routine Description:

    Scans a delimited list of entries, and removed "<local>
    if found.  If <local> is found we return TRUE.

Arguments:

    lpszExceptionList - String List of proxy excepion entries.


Return Value:

    BOOL
    TRUE - If found <local>

    FALSE - If local was not found.


--*/

{
    LPTSTR lpszLocalInstToRemove;
    BOOL   fFoundLocal;

    if ( !lpszExceptionList || ! *lpszExceptionList )
    return FALSE;

    fFoundLocal = FALSE;
    lpszLocalInstToRemove = lpszExceptionList;

    //
    // Loop looking "<local>" entries in the list.
    //

    do {

        lpszLocalInstToRemove =
                               StrStrI(lpszLocalInstToRemove,cszLocalString);
        
        
        if ( lpszLocalInstToRemove )
        {
            
            fFoundLocal = TRUE;
            
            //
            // Nuke <local> out of the string. <local>;otherstuff\0
            //  Dest is: '<'local>;otherstuff\0
            //     ??? (OR) ';'<local> if the ; is the first character.???
            //  Src  is: >'o'therstuff\0
            //  size is: sizeof(';otherstuff\0')
            //
            
            MoveMemory(
                       lpszLocalInstToRemove,
                       (lpszLocalInstToRemove+lstrlen(cszLocalString)),
                       lstrlen(lpszLocalInstToRemove+lstrlen(cszLocalString))*sizeof(TCHAR)+sizeof(TCHAR)
                      );
            
        }
        
    } while (lpszLocalInstToRemove && *lpszLocalInstToRemove);

    //
    // If we produced a ; on the end, nuke it.
    //

    lpszLocalInstToRemove = lpszExceptionList;

    GET_TERMINATOR(lpszLocalInstToRemove);

    if ( lpszLocalInstToRemove != lpszExceptionList &&
         *(lpszLocalInstToRemove-1) == ';' )
    {
        *(lpszLocalInstToRemove-1) = '\0';
    }
    
    return fFoundLocal;
}





