/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    autoprox.cxx

Abstract:

    Contains class implementation for auto-proxy DLLs which can extent WININET's
        abilities (logic) for deciding what proxies to use.

    How auto-proxy works:
        By offloading requests to a specialized Win32 Thread which picks
        up queued up message requests for Queries, Shutdown, and Initialization


    Contents:
        InternetInitializeAutoProxyDll
        AUTO_PROXY_DLLS::CheckForTimerConfigChanges
        AUTO_PROXY_DLLS::DoNestedProxyInfoDownload
        AUTO_PROXY_DLLS::SelectAutoProxyByMime
        AUTO_PROXY_DLLS::SelectAutoProxyByFileExtension
        AUTO_PROXY_DLLS::SelectAutoProxyByDefault
        AUTO_PROXY_DLLS::ReadAutoProxyRegistrySettings
        AUTO_PROXY_DLLS::IsAutoProxyEnabled
        AUTO_PROXY_DLLS::IsAutoProxyGetProxyInfoCallNeeded
        AUTO_PROXY_DLLS::QueueAsyncAutoProxyRequest
        AUTO_PROXY_DLLS::ProcessProxyQueryForInfo
        AUTO_PROXY_DLLS::SafeThreadShutdown
        AUTO_PROXY_DLLS::DoThreadProcessing
        AUTO_PROXY_DLLS::ProcessAsyncAutoProxyRequest
        AUTO_PROXY_DLLS::SignalAsyncRequestCompleted
        AUTO_PROXY_DLLS::FreeAutoProxyInfo

        AUTO_PROXY_LIST_ENTRY::LoadEntry
        AUTO_PROXY_LIST_ENTRY::GetProxyInfoEx
        AUTO_PROXY_LIST_ENTRY::ProxyInfoInvalid
        AUTO_PROXY_LIST_ENTRY::ProxyDllInit
        AUTO_PROXY_LIST_ENTRY::ProxyDllDeInit

        (MatchFileExtensionWithUrl)
        (AutoProxyThreadFunc)

Author:

    <NAME> (arthurbi) 17-Dec-1996

Revision History:

    17-Dec-1996 arthurbi
        Created

--*/

#include <wininetp.h>
#include "autodial.h"
#include "apdetect.h"

//
// definitions
//

#define DEFAULT_SCRIPT_BUFFER_SIZE 4000 // bytes.

//
// BUGBUG [arthurbi] This structures are shared between
//  wininet.dll && jsproxy.dll, shouldn't we move them 
//  into a central file???
//

#define AUTO_PROXY_REG_FLAG_ALLOW_STRUC  0x0001

typedef struct {

    //
    // Size of struct
    //

    DWORD dwStructSize;

    //
    // Buffer to Pass
    //

    LPSTR lpszScriptBuffer;

    //
    // Size of buffer above
    //

    DWORD dwScriptBufferSize;

}  AUTO_PROXY_EXTERN_STRUC, *LPAUTO_PROXY_EXTERN_STRUC;


//
// private templates
//

PRIVATE
BOOL
MatchFileExtensionWithUrl(
    IN LPCSTR lpszFileExtensionList,
    IN LPCSTR lpszUrl
    );


//
// private vars
//


//
// functions
//

INTERNETAPI_(BOOL)
InternetInitializeAutoProxyDll(
    DWORD dwReserved
    )
/*++

Routine Description:

    Stub to make INETCPL work.  Since they expect to call this function.

Arguments:

    none.

Return Value:

    BOOL
        TRUE    - success

--*/

{

    BOOL fSuccess;
    DWORD error = ERROR_SUCCESS;

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "InternetInitializeAutoProxyDll",
                ""
                ));

    if (!GlobalDataInitialized) {
        error = GlobalDataInitialize();
    }
    if (error == ERROR_SUCCESS) {
        GlobalProxyInfo.ClearBadProxyList();
        FixProxySettingsForCurrentConnection(TRUE);
        GlobalProxyInfo.RefreshProxySettings(TRUE);        
    }

    fSuccess = (error == ERROR_SUCCESS ) ? TRUE : FALSE;

    DEBUG_LEAVE(fSuccess);

    return fSuccess;

}


DWORD
WINAPI
AutoProxyThreadFunc(
    LPVOID lpAutoProxyObject
    )
/*++

Routine Description:

    Initialization Win32 Proc called when the auto-proxy thread is started.

Arguments:

    none.

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    return GlobalProxyInfo.DoThreadProcessing(lpAutoProxyObject);
}


//
// methods
//

DWORD
AUTO_PROXY_DLLS::CheckForTimerConfigChanges(
    IN DWORD dwMinsToPoll
    )
/*++

Routine Description:

    Exames the registry to see if a timer has been turned so
      that we poll auto-proxy every so often to check for updates

Arguments:

    none.

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    DWORD error = ERROR_SUCCESS;
    DWORD dwReloadMins = dwMinsToPoll;
    BOOL fEnableTimer = FALSE;

    //
    // Now check to determine if we have to enable the auto-proxy e
    //   this is used to force a re-download every "X" minutes.
    //

    if ( dwReloadMins != 0 )
    {
        if ( dwReloadMins >  MAX_RELOAD_DELAY  )  // too big?...
        {
            dwReloadMins = MAX_RELOAD_DELAY;
        }

        dwReloadMins *= ( 1000 * 60 ); // convert mins to miliseconds
        fEnableTimer = TRUE;
    }

    //
    // enable/disable timer for auto-proxy refresh.
    //

    ResetTimerCounter(
        fEnableTimer, // TRUE/FALSE enable/disable
        dwReloadMins  // conv-ed already to msecs
        );

    error = ERROR_SUCCESS;

    return error;
}

DWORD
AUTO_PROXY_DLLS::StartDownloadOfProxyInfo(
    IN BOOL fForceRefresh
    )

/*++

Routine Description:

    Does the Overall download of proxy-information from an internally accessable Web Server.
        Handles the case where we may have to redownload a secondary script also takes
        care of launching detection for proxies.

Arguments:

    fForceRefresh - TRUE if we are to attempt to refresh the auto-proxy URLs, and force a redection

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    DWORD error = ERROR_SUCCESS;
    BOOL fLocked = FALSE;
    DWORD dwcbAutoConfigProxy = 0;
    LPCSTR lpszAutoProxyUrl = NULL;
    INTERNET_PROXY_INFO_EX  ProxySettings;
    BOOL fProxySettingsAlloced = FALSE;
    BOOL fNeedRegWrite = FALSE;
    BOOL fCachedDialupDetection = FALSE;
    BOOL fNeedHostIPChk = FALSE;


    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::StartDownloadOfProxyInfo",
                "%B",
                fForceRefresh
                ));


    if ( IsOffline() ) 
    {
        error = ERROR_INTERNET_OFFLINE;
        goto quit;
    }

    LockAutoProxy();
    fLocked = TRUE;    

    error = GetProxySettings(&ProxySettings);

    if (error != ERROR_SUCCESS ) {
        goto quit;
    }

    fProxySettingsAlloced = TRUE;

    //
    // Now read from the registry, closing any stale stuff that we may still have
    //   around.  Do we still really need this?  Lets only do this for Refresh cases.
    //

    if ( fForceRefresh )
    {
        DestroyAutoProxyDll(TRUE); // unloads DLLs and frees up reg vars.
        SelectAutoProxy(NULL);

        _Error = ReadAutoProxyRegistrySettings();
        error = _Error;

        if (error != ERROR_SUCCESS ||
           !IsConfigValidForAutoProxyThread() )
        {
            SetState(AUTO_PROXY_DISABLED);
            goto quit;
        }

        if ( GetState() == AUTO_PROXY_DISABLED )
        {
            INET_ASSERT(FALSE);  // do we need this code?
            goto quit;
        }
    }

    //INET_ASSERT(GetState() == AUTO_PROXY_BLOCKED ||
    //            GetState() == AUTO_PROXY_PENDING);

    fLocked = FALSE;
    UnlockAutoProxy();

    //
    // Now begin the work, we should no longer need to touch the Proxy_DLL object
    //   settings until we grab the AutoProxy Lock again.
    //

    do
    {
        if ( IsProxyAutoDetectEnabled(&ProxySettings) )
        {
            CHAR szUrl[1024];
            BOOL fRet;
            DWORD dwDetectFlags = PROXY_AUTO_DETECT_TYPE_DNS_A;


            if ( fForceRefresh ||
                 IsProxyAutoDetectNeeded(&ProxySettings) )
            {

                //
                // Release Old Auto-config URL
                //

                if ( ProxySettings.lpszLastKnownGoodAutoConfigUrl) {
                    FREE_MEMORY(ProxySettings.lpszLastKnownGoodAutoConfigUrl);
                    ProxySettings.lpszLastKnownGoodAutoConfigUrl = NULL;
                }

                // only do dhcp on net connections
                if ( ProxySettings.lpszConnectionName == NULL ) {
                    dwDetectFlags |= PROXY_AUTO_DETECT_TYPE_DHCP;
                }

                //
                // Save out the Host IP addresses, before we start the detection,
                //  after the detection is complete, we confirm that we're still
                //  on the same set of Host IPs, in case the user switched connections.
                //

                error = GetHostAddresses(&(ProxySettings.pdwDetectedInterfaceIp),
                                         &(ProxySettings.dwDetectedInterfaceIpCount));

                if ( error != ERROR_SUCCESS) {
                    goto quit;
                }

                fNeedHostIPChk = TRUE; // because we've saved our IPs

                //
                // Do the actual Detection work
                //

                fRet = DetectAutoProxyUrl(
                         szUrl,
                         ARRAY_ELEMENTS(szUrl),
                         dwDetectFlags
                         );

                GetCurrentGmtTime(&ProxySettings.ftLastKnownDetectTime); // mark when detection was run.

                //
                // Process the Results of detection.
                //

                if ( fRet )
                {
                    ProxySettings.dwAutoDiscoveryFlags |= AUTO_PROXY_FLAG_DETECTION_RUN;

                    ProxySettings.lpszLastKnownGoodAutoConfigUrl = NewString(szUrl);
                    if ( ProxySettings.lpszLastKnownGoodAutoConfigUrl == NULL )
                    {
                        error = ERROR_NOT_ENOUGH_MEMORY;
                        goto quit;
                    }

                    SetExpiredUrl(szUrl);
                    fNeedRegWrite = TRUE;
                }
                else
                {
                    //
                    // Disable auto-detection, if we failed
                    //

                    if ( ! (ProxySettings.dwAutoDiscoveryFlags & (AUTO_PROXY_FLAG_DETECTION_RUN | AUTO_PROXY_FLAG_USER_SET)) )
                    {
                        ProxySettings.dwFlags &= ~(PROXY_TYPE_AUTO_DETECT);
                    }

                    ProxySettings.dwAutoDiscoveryFlags |= AUTO_PROXY_FLAG_DETECTION_RUN;
                    fNeedRegWrite = TRUE;
                }
            } 
            else if ( ProxySettings.lpszLastKnownGoodAutoConfigUrl != NULL &&
                      ProxySettings.lpszConnectionName             != NULL )
                      
            {   
                //
                // If we're not doing any detection, and we're on a dial-up adapter,
                //   then we should remember that fact, since we may be called upon
                //   to actually force a detect, in case the cached URL was stale/bad.
                //

                INET_ASSERT(! fForceRefresh );

                fCachedDialupDetection = TRUE;
            }

            lpszAutoProxyUrl = ProxySettings.lpszLastKnownGoodAutoConfigUrl;
        }

        //
        // Falback if we are unable to detect something
        //

        if ( lpszAutoProxyUrl == NULL &&
             IsProxyAutoConfigEnabled(&ProxySettings))
        {
            lpszAutoProxyUrl = ProxySettings.lpszAutoconfigUrl;
        }

        //
        // Do the actual download of the file
        //

        if (lpszAutoProxyUrl != NULL)
        {
            error = DoNestedProxyInfoDownload(lpszAutoProxyUrl, &ProxySettings, fForceRefresh);

            if ( error != ERROR_SUCCESS )
            {

                //
                // If we're cached + on a dialup, and we fail with the URL,
                //  then perhaps the URL/net is really expired.
                //

                if ( fCachedDialupDetection &&
                     ! fForceRefresh )
                {
                    fForceRefresh = TRUE;
                    continue;
                }

                //
                // Fallback to autoconfig if we failed and we were using
                //  auto-detect
                //

                if ( IsProxyAutoConfigEnabled(&ProxySettings) &&                 
                     lpszAutoProxyUrl != ProxySettings.lpszAutoconfigUrl &&
                     ProxySettings.lpszAutoconfigUrl != NULL )
                {
                    lpszAutoProxyUrl = ProxySettings.lpszAutoconfigUrl;
                    error = DoNestedProxyInfoDownload(lpszAutoProxyUrl, &ProxySettings, fForceRefresh);
                }
            }
        }
    } while (FALSE);

    LockAutoProxy();
    fLocked = TRUE;

    if (lpszAutoProxyUrl == NULL)
    {
        SetState(AUTO_PROXY_DISABLED);
        goto quit;
    }
    else
    {
        // stamp version so we know we just ran detection
        _dwUpdatedProxySettingsVersion = 
            ProxySettings.dwCurrentSettingsVersion;
        SetState(AUTO_PROXY_ENABLED);
    }

    CheckForTimerConfigChanges(ProxySettings.dwAutoconfigReloadDelayMins);

quit:

    if ( error != ERROR_SUCCESS )
    {
        if (!fLocked)
        {
            LockAutoProxy();
            fLocked = TRUE;
        }

        SetState(AUTO_PROXY_DISABLED);
    }

    //
    // Unlock the AutoProxy 
    //

    if ( fLocked ) {
        UnlockAutoProxy();
    }

    if ( fProxySettingsAlloced )
    {
        //
        // We need to save results to registry,
        //   then stamp the version in our global we now we detected for this,
        //   and then finally clean up any allocated stuff.
        //

        SaveDetectedProxySettings(&ProxySettings, fNeedHostIPChk);        

        WipeProxySettings(&ProxySettings);
    }

    DEBUG_LEAVE(error);

    return error;
}

DWORD
AUTO_PROXY_DLLS::DoNestedProxyInfoDownload(
    IN LPCSTR lpszAutoProxy,
    IN LPINTERNET_PROXY_INFO_EX lpProxySettings,
    IN BOOL fForceRefresh
    )
/*++

Routine Description:

    Does the download of proxy-information from an internally accessable Web Server.
        Handles the case where we may have to redownload a secondary script and then
        calls on to DownloadProxyInfo to actually do the dirty work.

Arguments:

    lpszAutoProxy - URL to download

    lpProxySettings - active Proxy Settings Copy for auto-proxy thread.

    fForceRefresh - force refresh of downloaded file

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    LPCSTR lpszNestedAutoProxyUrl = NULL;
    DWORD error;
   
    _pTempProxySettings = lpProxySettings; // in case they reset proxy settings    

    if ( _fInAutoProxyThreadShutDown )
    {
        error = ERROR_INTERNET_SHUTDOWN;
        goto quit;

    }

    //
    // IEAK flag, don't retry INS file unless either one of the following:
    //      1) INS URL is expired
    //      2) caching INS run is disabled
    //      3) we're not forcing a refresh
    //

    if ( !(lpProxySettings->dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_CACHE_INIT_RUN ) ||
         // !(ProxySettings.dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_DETECTION_RUN ) ||
         !fForceRefresh ||         
         IsExpiredUrl(lpszAutoProxy))
    {

        //
        // Now do the actual download of the proxy info.
        //

        error =
            DownloadProxyInfo(lpszAutoProxy, fForceRefresh);

        if ( error != ERROR_SUCCESS )
        {
            goto quit;
        }
    }

    //
    // Now check to determine if we have an overriding Auto-Proxy that
    //   supersides the one we just download.  If we do, we will
    //   need to redownload auto-proxy
    //
    lpszNestedAutoProxyUrl = lpProxySettings->lpszAutoconfigSecondaryUrl;

    if ( lpszNestedAutoProxyUrl ) 
    {
        //
        // Success - now restart the download process for this special Url
        //

        error = DownloadProxyInfo(
                    lpszNestedAutoProxyUrl,
                    fForceRefresh
                    );
    }

    // fall through even in error

    error = ERROR_SUCCESS;

quit:

    INET_ASSERT(_pTempProxySettings);
    _pTempProxySettings = NULL;

    return error;
}



DWORD
AUTO_PROXY_DLLS::DownloadProxyInfo(
    IN LPCSTR lpszAutoProxy,
    IN BOOL fForceRefresh
    )

/*++

Routine Description:

    Does the download of proxy-information from an internally accessable Web Server.
        The data will be checked, written to a temp file, and then an associated
        DLL will be called to handle it.

Arguments:

    lpszUrl - The URL to download.

    fForceRefresh - force file to be reloaded from the wire

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    HINTERNET hInternet = NULL;
    HINTERNET hRequest = NULL;
    HANDLE hFile = NULL;
    CHAR szTemporyFile[MAX_PATH+1];
    DWORD dwTempFileSize;

    HANDLE hLockHandle = NULL;

    BOOL fSuccess;
    DWORD error = ERROR_SUCCESS;

    CHAR szBuffer[MAX_PATH+1];
    DWORD dwBytesRead;

    BOOL fCleanupFile = FALSE;
    BOOL fLocked = FALSE;
    BOOL fBuffering = FALSE; 

    LPSTR lpszScriptBuffer = NULL;
    DWORD dwScriptBufferSize;

    DWORD dwStatusCode = ERROR_SUCCESS;
    DWORD cbSize = sizeof(DWORD);
    int nRetries = 3; // Num auth attempts

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::DownloadProxyInfo",
                "%q",
                lpszAutoProxy
                ));

    INET_ASSERT(lpszAutoProxy);

    //
    // Fire up a mini InternetOpen/InternetOpenUrl to download a
    //  config file found on some internal server.
    //

    hInternet = InternetOpen(
                    (_lpszUserAgent) ? _lpszUserAgent : gszDefaultUserAgent,
                    INTERNET_OPEN_TYPE_DIRECT,
                    NULL,
                    NULL,
                    0
                    );


    if ( !hInternet )
    {
        error = GetLastError();
        INET_ASSERT(error != ERROR_SUCCESS);
            goto quit;
    }


    do 
    {
        hRequest = InternetOpenUrl(
                            hInternet,
                            lpszAutoProxy,
                            "Accept: */*\r\n",
                            (DWORD) -1,
                            (fForceRefresh ?
                                INTERNET_FLAG_RELOAD :
                                0) |
                            INTERNET_FLAG_NEED_FILE,
                            INTERNET_NO_CALLBACK
                            );


         if ( !hRequest )
         {
            //error = ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT;
            error = GetLastError();
            if(GlobalDisplayScriptDownloadFailureUI && !GlobalIsProcessNtService)
            {
                InternetErrorDlg(
                GetDesktopWindow(),
                hRequest,
                ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT,
                FLAGS_ERROR_UI_FILTER_FOR_ERRORS,
                NULL);
            }
            goto quit;
        }

        cbSize = sizeof(dwStatusCode);
        if (HttpQueryInfo(
            hRequest,
            HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
            (LPVOID) &dwStatusCode,
            &cbSize,
            NULL
            ) && HTTP_STATUS_DENIED == dwStatusCode)
        {
            if (GlobalIsProcessNtService)
            {
                error = ERROR_INTERNET_INTERNAL_ERROR;
                goto quit;
            }

            // Check the status code and throw an InternetErrorDlg
            // if auth is needed and try again.
            if (InternetErrorDlg(
                GetDesktopWindow(), 
                hRequest,
                ERROR_INTERNET_INCORRECT_PASSWORD,
                FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
                NULL
                ) != ERROR_INTERNET_FORCE_RETRY)
            {
                nRetries = 0;
            }
            else
            {
		cbSize = sizeof(szBuffer);
		HttpQueryInfo(
			hRequest,
			HTTP_QUERY_WWW_AUTHENTICATE,
			(LPVOID) szBuffer,
			&cbSize,
			NULL
			);
		szBuffer[5]='\0';
		if (!lstrcmpi(szBuffer, "Basic"))
		{
		    HttpSendRequest(
				    hRequest, 
				    "Accept: */*\r\n",
				    (DWORD)-1,
				    NULL,
				    0);
		    cbSize = sizeof(dwStatusCode);
		    if (HttpQueryInfo(
			    hRequest,
			    HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
			    (LPVOID) &dwStatusCode,
			    &cbSize,	
			    NULL
			    ) && HTTP_STATUS_DENIED == dwStatusCode)
		    {
                        InternetCloseHandle(hRequest);
		    }
		    else
		    {
                        GlobalAutoProxyNeedsInit = FALSE;
			break;
		    }
		}
		else
                    InternetCloseHandle(hRequest);
            }
        }
        else
        {
            // Call it good
            GlobalAutoProxyNeedsInit = FALSE;
            break;
        }
    } while ( --nRetries>0);

    if(nRetries <= 0)
    {
        // Still haven't initialized.
        GlobalAutoProxyNeedsInit = TRUE;
    }

    // Reset to success
    error = ERROR_SUCCESS;

    if(HTTP_STATUS_NOT_FOUND == dwStatusCode)
    {
        error = ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT;
        if(GlobalDisplayScriptDownloadFailureUI && !GlobalIsProcessNtService)
        {
            InternetErrorDlg(
                GetDesktopWindow(),
                hRequest,
                ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT,
                FLAGS_ERROR_UI_FILTER_FOR_ERRORS,
                NULL);
        }
        goto quit;
    }


    DWORD dwIndex;
    DWORD dwTempSize;

    dwIndex = 0;
    dwTempSize = sizeof(dwScriptBufferSize);

    dwScriptBufferSize = 0;
    
    if ( ! HttpQueryInfo(hRequest, 
                       (HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER),
                       (LPVOID) &dwScriptBufferSize,
                       &dwTempSize,
                       &dwIndex) )
    {
        // failure, just defaults 
        dwScriptBufferSize = DEFAULT_SCRIPT_BUFFER_SIZE;
        fBuffering = TRUE;
    }
    else if ( dwScriptBufferSize > DEFAULT_SCRIPT_BUFFER_SIZE )
    {
        // success, but too big.
        dwScriptBufferSize = DEFAULT_SCRIPT_BUFFER_SIZE;
        fBuffering = FALSE;
    }
    else
    {
        // success, and this one is just right.
        fBuffering = TRUE; 
    }
   
    lpszScriptBuffer = (LPSTR) 
                        ALLOCATE_MEMORY(LMEM_FIXED, ((dwScriptBufferSize+1)
                            * sizeof(CHAR)));

    if ( lpszScriptBuffer == NULL ) 
    {
        error = ERROR_NOT_ENOUGH_MEMORY;
        goto quit;
    }

    //
    // pull down bytes, and write to file
    //

    DWORD dwBytes;
    dwBytesRead = 0;

    do
    {
        DWORD dwBytesLeft;
        LPSTR lpszDest;

        dwBytes = 0;
        lpszDest = lpszScriptBuffer;
        dwBytesLeft = dwScriptBufferSize;

        fSuccess = InternetReadFile(
                        hRequest,
                        lpszDest,
                        dwBytesLeft,
                        &dwBytes
                        );

        if ( ! fSuccess )
        {
            error = GetLastError();
            goto quit;
        }

        if ( _fInAutoProxyThreadShutDown )
        {
            error = ERROR_INTERNET_SHUTDOWN;
            goto quit;
        }

        if ( dwBytes > 0 )
        {
            if ( dwBytesRead > 0 ) 
            {
                fBuffering = FALSE;
            }

            dwBytesRead += dwBytes;
        }
    } while ( dwBytes != 0 );

    //
    // Figure out what kind of file we're dealing with.
    //  ONLY allow files with the correct extension or the correct MIME type.
    //

    szBuffer[0] = '\0';
    dwBytes = ARRAY_ELEMENTS(szBuffer)-1;

    fSuccess = HttpQueryInfo( hRequest,
                                 HTTP_QUERY_CONTENT_TYPE,
                                 szBuffer,
                                 &dwBytes,
                                 NULL );

    fLocked = TRUE;
    LockAutoProxy();

    if ( !(fSuccess
            && SelectAutoProxyByMime(szBuffer)) )
    {
        if ( ! SelectAutoProxyByFileExtension(lpszAutoProxy) )
        {
            if ( ! SelectAutoProxyByDefault() )
            {
                //
                // Could not find a registered handler for this data.
                //

                error = ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT;
                goto quit;
            }
        }
    }

    //
    // Now, launch the handler to tell them about the file.
    //  we downloaded it first.
    //

    AUTO_PROXY_LIST_ENTRY * papleAutoProxy;
    AUTO_PROXY_EXTERN_STRUC apeStruct;
    LPAUTO_PROXY_EXTERN_STRUC pExtraStruct;
    

    papleAutoProxy = GetSelectedAutoProxyEntry();
    pExtraStruct = NULL;

    //
    // Get a temp file from cache if we need it.
    //

    if ( fBuffering &&
         (papleAutoProxy->_dwFlags & AUTO_PROXY_REG_FLAG_ALLOW_STRUC) &&
         dwBytesRead > 0 )
    {
        // slam a \0 terminator
        INET_ASSERT(dwBytesRead <= dwScriptBufferSize);
        lpszScriptBuffer[dwBytesRead] = '\0';

        pExtraStruct = &apeStruct;
        apeStruct.dwStructSize = sizeof(AUTO_PROXY_EXTERN_STRUC);
        apeStruct.lpszScriptBuffer = lpszScriptBuffer;
        apeStruct.dwScriptBufferSize = dwBytesRead + 1;
    }
    else 
    {
        fCleanupFile = TRUE;

        //Qfe 3430: When parsing ins file, with reference to pac file, wininet needs to 
        //know the connectoid name in order to set the pac file correctly. Currently there
        //is no way for wininet to pass the connectoid name to branding dll. To workaround
        //this, we use the AUTO_PROXY_EXTERN_STRUC to pass the connectoid name in lpszScriptBuffer
        //variable. 
        if(!(papleAutoProxy->_dwFlags & AUTO_PROXY_REG_FLAG_ALLOW_STRUC))
        {
            pExtraStruct = &apeStruct;
            apeStruct.dwStructSize = sizeof(AUTO_PROXY_EXTERN_STRUC);
            apeStruct.lpszScriptBuffer = GlobalProxyInfo.GetConnectionName();
        }

        if ( ! InternetLockRequestFile(hRequest, &hLockHandle) ) 
        {
            fCleanupFile = FALSE;
        }
        
        dwTempFileSize = MAX_PATH;

        if ( ! InternetQueryOption(hRequest, INTERNET_OPTION_DATAFILE_NAME, 
                    szTemporyFile, &dwTempFileSize ) ||
               dwTempFileSize == 0 )
        {
            error = ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT;
            goto quit;
        }
    }

    //
    // The loading and unloading of DLLs is owned/allowed
    //  only by the auto-proxy thread, ONCE THIS THREAD IS RUNNING
    //  so once we're here, we can be assured its safe to
    //  make the following calls without holding the crit sec.
    //

    INET_ASSERT(fLocked);
    UnlockAutoProxy();
    fLocked = FALSE;
    fSuccess = TRUE;

    //
    // If we're not configured, or cannot match the URL
    //  with a Handler to actually run the script,
    //  then we need to error out
    //

    if ( papleAutoProxy == NULL)
    {
        error = ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT;
        goto quit;
    }

    if ( ! papleAutoProxy->_hAutoConfigDLL )
    {
        if ( _fInAutoProxyThreadShutDown )
        {
            error = ERROR_INTERNET_SHUTDOWN;
            goto quit;
        }

        error = papleAutoProxy->LoadEntry();

        if ( error != ERROR_SUCCESS )
        {
            goto quit;
        }
    }
    else if ( papleAutoProxy->_fInitializedSuccessfully &&
              papleAutoProxy->_pProxyDllInit &&
              papleAutoProxy->_pProxyDllDeInit )
    {
        //
        // If this DLL has already been loaded,
        //   then unitialize it before re-initalizeing with
        //   new data, otherwise we risk leaking some its
        //   objects
        //

        papleAutoProxy->ProxyDllDeInit(
            szBuffer,
            0
            );
        INET_ASSERT(papleAutoProxy->_fUnInited);
    }

    //
    // Make the call into the external DLL,
    //  and let it run, possibly initilization and doing a bunch
    //  of stuff.
    //


    fSuccess = papleAutoProxy->ProxyDllInit (
                            AUTO_PROXY_VERSION,
                            szTemporyFile,  // temp file we down loaded
                            szBuffer,       // mime
                            &_aphAutoProxyAPIs,
                            (DWORD_PTR) pExtraStruct
                            );

    if ( _fInAutoProxyThreadShutDown )
    {
        error = ERROR_INTERNET_SHUTDOWN;
        goto quit;
    }

    if ( ! fSuccess )
    {
        error = ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT;
        goto quit;
    }


    if ( papleAutoProxy->IsGetProxyInfoEx() ||
         papleAutoProxy->IsGetProxyInfo()  )
    {
        SetState(AUTO_PROXY_ENABLED);
    }
    else
    {
        SetState(AUTO_PROXY_DISABLED);
    }

quit:

    if ( error != ERROR_SUCCESS )
    {
        if (!fLocked)
        {
            LockAutoProxy();
            fLocked = TRUE;
        }

        SetState(AUTO_PROXY_DISABLED);
    }

    if ( fLocked )
    {
        UnlockAutoProxy();
    }

    if ( fCleanupFile )
    {
        InternetUnlockRequestFile(hLockHandle);
    }

    if ( lpszScriptBuffer )
    {
        FREE_MEMORY(lpszScriptBuffer);
    }

    if ( hRequest )
    {
        InternetCloseHandle(hRequest);
    }

    if ( hInternet )
    {
        InternetCloseHandle(hInternet);
    }

    SetAbortHandle(NULL);

    DEBUG_LEAVE(error);

    return error;
}



BOOL
AUTO_PROXY_DLLS::IsAutoProxyEnabled(
    VOID
    )

/*++

Routine Description:

    Determines whether the auto-proxy thread is enabled and ready
        to accept async proxy requests.  This is needed to prevent
        senceless calls to the auto-proxy thread when the request
        could made more directly to the PROXY_INFO object.

Return Value:

    BOOL
        TRUE - the AutoProxy thread is accepting async requests

        FALSE - the AutoProxy thread is refusung async requests.

--*/

{
    BOOL fIsGetProxyCallProxyNeeded = FALSE;

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "AUTO_PROXY_DLLS::IsAutoProxyEnabled",
                ""
                ));


    LockAutoProxy();

    //
    // If we're downloading new information, OR we have a async thread ready to process
    //   autoproxy queries, then go async and do the request.  The worst case will
    //   result in the async thread being created and then returning the request
    //   unprocessed ( the FSM will resubmit it to the general proxy code )
    //

    if ( GetState() == AUTO_PROXY_ENABLED ||
         GetState() == AUTO_PROXY_BLOCKED ||
         GetState() == AUTO_PROXY_PENDING )
    {
        fIsGetProxyCallProxyNeeded = TRUE;
    }
    else
    {
        fIsGetProxyCallProxyNeeded = FALSE;
    }

    UnlockAutoProxy();

    DEBUG_LEAVE(fIsGetProxyCallProxyNeeded);

    return fIsGetProxyCallProxyNeeded;
}


BOOL
AUTO_PROXY_DLLS::IsAutoProxyGetProxyInfoCallNeeded(
    IN AUTO_PROXY_ASYNC_MSG *pQueryForInfo
    )

/*++

Routine Description:

    Wrapper for IsAutoProxyEnabled, verifies that the message object (pQueryForInfo)
        in question is capible of going async.

Arguments:

    pQueryForInfo - Pointer to Message object that contains state information
                        used in the query.

Return Value:

    BOOL
        TRUE - the AutoProxy thread is accepting async requests

        FALSE - the AutoProxy thread is refusung async requests.

--*/

{

    INET_ASSERT(pQueryForInfo);

    //
    // The caller has explicitly banned auto-proxy calls.
    //

    if ( pQueryForInfo->IsAvoidAsyncCall() )
    {
        return FALSE;
    }

    //
    // We cannot process this request to auto-proxy unless we have an URL
    //

    if ( ! pQueryForInfo->IsUrl() )
    {
        return FALSE;
    }

    //
    // Now check to see if its really enabled.
    //

    return IsAutoProxyEnabled();

}

DWORD
AUTO_PROXY_DLLS::QueueAsyncAutoProxyRequest(
    IN OUT AUTO_PROXY_ASYNC_MSG **ppQueryForInfo
    )

/*++

Routine Description:

    Submits the *ppQueryForInfo Object on the async queue for processing
     by the async auto-proxy thread.

Arguments:

    ppQueryForInfo - Pointer to pointer to Message object that contains state information
                        used in the query.   If the object needs to be allocated on the heap
                        then the pointer will change to reflect the new object ptr.

Return Value:

    DWORD
        Success - ERROR_SUCCESS

        Failure -

--*/


{
    LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
    BOOL fLocked = FALSE;
    DWORD error = ERROR_SUCCESS;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::QueueAsyncAutoProxyRequest",
                "%x",
                ppQueryForInfo
                ));


    //
    // First allocate an object, if its not allocated,
    //  cause we can't pass local stack vars to another thread (duh..)
    //

    if ( ! (*ppQueryForInfo)->IsAlloced() )
    {
        *ppQueryForInfo = new AUTO_PROXY_ASYNC_MSG(*ppQueryForInfo);

        if ( *ppQueryForInfo == NULL )
        {
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto quit;
        }
    }

    LockAutoProxy();  // lock object to prevent anyone messing with thread handles
    fLocked = TRUE;

    //
    // Busy wait for thread to be shutdown before restarting.
    //

    while ( _fInAutoProxyThreadShutDown )
    {
        UnlockAutoProxy();
        Sleep(100);
        LockAutoProxy();
    }

    if ( _hAutoProxyThread == NULL )
    {

        _hAutoProxyThreadEvent = CreateEvent(
                                    NULL,   // pointer to security attributes
                                    FALSE,  // flag for manual-reset event
                                    TRUE,  // flag for initial state
                                    NULL    // event-object name
                                    );


        if ( _hAutoProxyThreadEvent == NULL )
        {
            error = GetLastError();
            goto quit;
        }

        //
        // We block on this event until the thread is actually running,
        //   otherwise we may PROCESS_DETEACH while the auto-proxy
        //   thread is in this quasi-state of having been created, but not actually run.
        //

        _hAutoProxyStartEvent = CreateEvent(
                                    NULL,   // pointer to security attributes
                                    TRUE,  // flag for manual-reset event
                                    FALSE,  // flag for initial state
                                    NULL    // event-object name
                                    );


        if ( _hAutoProxyStartEvent == NULL )
        {
            error = GetLastError();
            goto quit;
        }

        ResetEvent(_hAutoProxyStartEvent);

        _hAutoProxyThread = CreateThread(
                                NULL, // pointer to thread security attributes
                                0,    // starting stack size
                                AutoProxyThreadFunc,
                                this,  // argument
                                0,     // flags
                                &_dwAutoProxyThreadId
                                );


        if ( _hAutoProxyThread == NULL )
        {
            error = GetLastError();
            CloseHandle(_hAutoProxyThreadEvent);
            CloseHandle(_hAutoProxyStartEvent);
            _hAutoProxyThreadEvent = NULL;
            _hAutoProxyStartEvent = NULL;
            goto quit;
        }

        const HANDLE aWaitHandles[2] = {_hAutoProxyStartEvent, _hAutoProxyThread};

        // wait on the thread handle too in case it terminates for some unknown reason
        error = WaitForMultipleObjects(ARRAYSIZE(aWaitHandles),
                                       aWaitHandles,
                                       FALSE,
                                       INFINITE);
        if ( error != WAIT_OBJECT_0 )
        {
            INET_ASSERT(FALSE);
            TerminateThread(_hAutoProxyThread, ERROR_SUCCESS);

            CloseHandle(_hAutoProxyThreadEvent);
            CloseHandle(_hAutoProxyStartEvent);
            CloseHandle(_hAutoProxyThread);
            _hAutoProxyStartEvent = NULL;
            _hAutoProxyThread = NULL;
            _hAutoProxyThreadEvent = NULL;
            _dwAutoProxyThreadId = NULL;

            error = ERROR_INTERNET_INTERNAL_ERROR;
            goto quit;
        }
        else
        {
            CloseHandle(_hAutoProxyStartEvent);
            _hAutoProxyStartEvent = NULL;
        }

    }

    //
    // Now Block the Thread we're on...
    //

    if ( (*ppQueryForInfo)->IsBlockUntilCompletetion() )
    {
        if ( lpThreadInfo &&
             lpThreadInfo->Fsm &&
             lpThreadInfo->IsAsyncWorkerThread &&
             !(lpThreadInfo->Fsm->IsBlocking()))
        {
            //
            // In a FSM, use FSM - thread pool handler to block this.
            //

            (*ppQueryForInfo)->SetBlockedOnFsm(TRUE);

            lpThreadInfo->Fsm->SetState(FSM_STATE_CONTINUE);
            lpThreadInfo->Fsm->SetNextState(FSM_STATE_CONTINUE);

            error = BlockWorkItem(
                lpThreadInfo->Fsm,
                (DWORD_PTR) *ppQueryForInfo,    // block the FSM on ourselves
                INFINITE                        // we block foreever
                );


            if ( error != ERROR_SUCCESS )
            {
                goto quit;
            }

            InsertAtTailOfSerializedList(&_AsyncQueueList, &(*ppQueryForInfo)->_List);
            SetEvent(_hAutoProxyThreadEvent);

            error = ERROR_IO_PENDING;
        }
        else
        {
            AcquireBlockedRequestQueue();

            InsertAtTailOfSerializedList(&_AsyncQueueList, &(*ppQueryForInfo)->_List);
            SetEvent(_hAutoProxyThreadEvent);

            UnlockAutoProxy();
            fLocked = FALSE;

            error = BlockThreadOnEvent(
                (DWORD_PTR) *ppQueryForInfo,
                INFINITE,   // we need to block forever !
                TRUE        // release BlockedRequestQueue
                );
        }
    }
    else
    {
        InsertAtTailOfSerializedList(&_AsyncQueueList, &(*ppQueryForInfo)->_List);
        SetEvent(_hAutoProxyThreadEvent);
    }

quit:

    if ( fLocked )
    {
        UnlockAutoProxy();
        fLocked = FALSE;
    }

    DEBUG_LEAVE(error);

    return error;
}

DWORD
AUTO_PROXY_DLLS::ProcessProxyQueryForInfo(
    IN OUT AUTO_PROXY_ASYNC_MSG **ppQueryForInfo
    )

/*++

Routine Description:

    Performs a query for proxy information using an external DLL's entry points to
      anwser our query.  If we are not on the correct thread, we call QueueAsyncAutoProxyRequest
      to marshall our call across to it.

Arguments:

    ppQueryForInfo - Pointer to pointer to Message object that contains state information
                        used in the query.   If the object needs to be allocated on the heap
                        then the pointer will change to reflect the new object ptr.

Return Value:

    DWORD
        Success - ERROR_SUCCESS

        Failure -

--*/

{
    AUTO_PROXY_LIST_ENTRY * papleAutoProxy;
    DWORD error = ERROR_SUCCESS;
    BOOL fUnlocked = FALSE;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::ProcessProxyQueryForInfo",
                "%x",
                ppQueryForInfo
                ));


    INET_ASSERT(ppQueryForInfo);
    INET_ASSERT(*ppQueryForInfo);
    INET_ASSERT((*ppQueryForInfo)->QueryForInfoMessage() == PROXY_MSG_GET_PROXY_INFO);
    INET_ASSERT(IsOnAsyncAutoProxyThread());

    LockAutoProxy();

    //
    // this function should only ever be executed on the auto-proxy thread
    //

    if ( GetState() == AUTO_PROXY_DISABLED )
    {
        //
        // Fall back to a normal proxy query.
        //

        (*ppQueryForInfo)->SetAvoidAsyncCall(TRUE);
        goto quit;
    }

    papleAutoProxy =
            GetSelectedAutoProxyEntry();

    //
    // We should be the only ones to create or destroy
    //  auto-proxy info, therefore we release the holly
    //  lock of auto-proxy.  If I'm wrong another thread
    //  could cause us trouble ...
    //

    UnlockAutoProxy();
    fUnlocked = TRUE;

    if ( papleAutoProxy )
    {
        //
        // If GetProxyInfoEx is supported, we defer to it to handle
        //  everything.
        //

        if ( papleAutoProxy->IsGetProxyInfoEx() )
        {
            error = papleAutoProxy->GetProxyInfoEx(
                *ppQueryForInfo
                );

            goto quit;
        }
        else if ( papleAutoProxy->IsGetProxyInfo() )
        {
            error = papleAutoProxy->GetProxyInfo(
                *ppQueryForInfo
                );

            goto quit;
        }
        else
        {
            //
            // Fall back to a normal proxy query.
            //

            (*ppQueryForInfo)->SetAvoidAsyncCall(TRUE);
        }
    }
    else
    {
        //
        // Fall back to a normal proxy query.
        //

        (*ppQueryForInfo)->SetAvoidAsyncCall(TRUE);
    }


quit:

    if ( !fUnlocked )
    {
        UnlockAutoProxy();
    }

    DEBUG_LEAVE(error);

    return error;
}

VOID
AUTO_PROXY_DLLS::SafeThreadShutdown(
    BOOL fItsTheFinalShutDown
    )
/*++

Routine Description:

    Performs a shutdown of the auto-proxy thread by the auto-proxy thread itself.

Arguments:

    fItsTheFinalShutDown - TRUE, if we are shutting down at the end of a process

Return Value:

    none.

--*/
{
    DEBUG_ENTER((DBG_PROXY,
                None,
                "AUTO_PROXY_DLLS::SafeThreadShutdown",
                "%B",
                fItsTheFinalShutDown
                ));


    //
    // Attempt to Shut down thread due to no-activity OR
    //  due to process shutdown.
    //

    LockAutoProxy();

    if ( IsSerializedListEmpty(&_AsyncQueueList) ||
         fItsTheFinalShutDown )
    {
        //HANDLE hAutoProxyThreadEvent = _hAutoProxyThreadEvent;
        //HANDLE hAutoProxyThread = _hAutoProxyThread;

        //_hAutoProxyThread = NULL;
        //_hAutoProxyThreadEvent = NULL;
        _dwAutoProxyThreadId = 0;

        //CloseHandle(hAutoProxyThreadEvent);
                //CloseHandle(hAutoProxyThread);

        UnlockAutoProxy();

        if ( fItsTheFinalShutDown )
        {
            //DestroyAutoProxyMsgQueue();
            DestroyAutoProxyDll(TRUE); // unloads DLLs and frees up reg vars
        }

        DEBUG_LEAVE(0);

        ExitThread(ERROR_SUCCESS);

        INET_ASSERT(FALSE);  //we will never get here...
    }

    UnlockAutoProxy();

    DEBUG_LEAVE(0);

}

#define NLA_ERROR_RETRY_INTERVAL    (5*60*1000)

class NLA_NET_CHANGE {
private:
    HANDLE          _hLookup;
    WSAOVERLAPPED   _ovlp;
    DWORD           _lastInitAttempt;

    BOOLEAN
    Initialize ();

    BOOLEAN
    EnumerateNetChanges ();
    
public:
    NLA_NET_CHANGE () {
        _ovlp.hEvent = NULL;
        if (GlobalPlatformWhistler &&
                _I_WSALookupServiceBeginW!=NULL) {
            _hLookup = NULL;
            _lastInitAttempt = GetTickCount ()-NLA_ERROR_RETRY_INTERVAL-1;
        }
        else {
#if DBG
            dprintf ("NLA not available: whistler-%ld, lookup:-%#x\n",
                        GlobalPlatformWhistler, _I_WSALookupServiceBeginW);
#endif
            _hLookup = INVALID_HANDLE_VALUE;
        }
    };

    ~NLA_NET_CHANGE () {
        if (_hLookup!=NULL && _hLookup!=INVALID_HANDLE_VALUE) {
#if DBG
            dprintf ("NLA - cleaning up");
#endif
            _I_WSALookupServiceEnd (_hLookup);
            CloseHandle (_ovlp.hEvent);
        }
    };

    BOOLEAN
    IsNetChanged () {
        if (_hLookup!=INVALID_HANDLE_VALUE) {
            if ((_hLookup!=NULL) || 
                    ( ((GetTickCount ()-_lastInitAttempt)
                                        >NLA_ERROR_RETRY_INTERVAL) &&
                        Initialize () ) ) {
                if (HasOverlappedIoCompleted (&_ovlp)) {
#if DBG
                    dprintf ("NLA change detected, status:%lx\n",
                            _ovlp.Internal);
#endif
                    return EnumerateNetChanges ();
                }
            }
        }
        return FALSE;
    };

    HANDLE
    GetChangeEvent () {
        return _ovlp.hEvent;
    };

};



BOOLEAN
NLA_NET_CHANGE::Initialize (
    )
/*++

Routine Description:

    Initializes Network Location Awareness lookup query.

Arguments:


Return Value:

    TRUE    - succes
    FALSE   - failure

--*/
{
    WSAQUERYSETW    qset;
    GUID    nlaServiceClassId = NLA_SERVICE_CLASS_GUID;
    DWORD   qSize;
    INT     error;

    INET_ASSERT (_ovlp.hEvent==NULL);
    INET_ASSERT (_hLookup==NULL);

    //
    // Create event for async notifications.
    //
    _ovlp.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (_ovlp.hEvent!=NULL) {

        ZeroMemory (&qset, sizeof (qset));
        qset.dwSize = sizeof (qset);
        qset.lpServiceClassId = &nlaServiceClassId;
        qset.dwNameSpace = NS_NLA;

        if (_I_WSALookupServiceBeginW (
                        &qset,
                        LUP_RETURN_NAME|LUP_RETURN_BLOB,
                        &_hLookup)!=SOCKET_ERROR) {
            DEBUG_PRINT (PROXY, INFO, ("Nla Lookup created\n"));
#if DBG
            dprintf ("NLA Lookup created, handle:%#x\n", _hLookup);
#endif

            //
            // Just walk through the net list and setup notification.
            //
            return EnumerateNetChanges ();

        }
        else {
            error = GetLastError ();
            DEBUG_PRINT (PROXY, ERROR,
                ("NLA Lookup creation failed, err: %ld\n",
                error));
#if DBG
            dprintf ("NLA Lookup creation failed, err: %ld\n",error);
#endif
        }
        _hLookup = NULL;
        CloseHandle (_ovlp.hEvent);
        _ovlp.hEvent = NULL;
    }
    else {
        error = GetLastError ();
        DEBUG_PRINT (PROXY, ERROR,
            ("NLA notification event creation failed, err: %ld\n",
            error));
#if DBG
        dprintf ("NLA notification event creation failed, err: %ld\n",
            error);
#endif
    }

    INET_ASSERT (error!=NO_ERROR);

    //
    // Remember when we failed, so we do not retry very often.
    //
    _lastInitAttempt = GetTickCountWrap ();

    return FALSE;
}


BOOLEAN
NLA_NET_CHANGE::EnumerateNetChanges (
    )
/*++

Routine Description:

    Enumerates Network entries returned by the NLA query.
    If this is called after change notification, only
    changes since last enumeration are returned.

Arguments:
    None.

Return Value:
    TRUE    - there were any relevant changes
    FALSE   - no relevant changes.

--*/
{
    DWORD   error = NO_ERROR;

    // Stack buffer which should be sufficient for most cases.
    struct {
        WSAQUERYSETW    set;
        WCHAR           name[128];
        NLA_BLOB        blob;
    } q;
    LPWSAQUERYSETW  pqSet = &q.set;
    DWORD   qSize = sizeof (q), retSize;
    BOOLEAN changed = FALSE;

    INET_ASSERT (_hLookup!=NULL && _hLookup!=INVALID_HANDLE_VALUE);
    INET_ASSERT (_ovlp.hEvent!=NULL);
    INET_ASSERT (HasOverlappedIoCompleted (&_ovlp));

    do {// Outer loop - keep calling while we are getting notifications.

        do {// Inner loop - keep calling while enumerating through networks.
            retSize = qSize;
            error = _I_WSALookupServiceNextW (
                            _hLookup,
                            0,
                            &retSize,
                            pqSet);
            if (error!=SOCKET_ERROR) {
                INET_ASSERT (error==NO_ERROR);
                //
                // Success, see if results in this query are relevant
                //
                DEBUG_PRINT (PROXY, INFO,
                             ("NLA change for %ws-%lx\n",
                                 pqSet->lpszServiceInstanceName,
                                 pqSet->dwOutputFlags));
#if DBG
                dprintf ("NLA change for %ws-%lx\n",
                                 pqSet->lpszServiceInstanceName,
                                 pqSet->dwOutputFlags);
#endif
                if ((pqSet->lpBlob!=NULL) &&
                        (pqSet->lpBlob->pBlobData!=NULL) &&
                        (((LPNLA_BLOB)pqSet->lpBlob->pBlobData)->header.type==NLA_INTERFACE) ) {
#if DBG
                    dprintf ("    type:%ld, speed:%ld, name: %s\n",
                                ((LPNLA_BLOB)pqSet->lpBlob->pBlobData)->data.interfaceData.dwType,
                                ((LPNLA_BLOB)pqSet->lpBlob->pBlobData)->data.interfaceData.dwSpeed,
                                ((LPNLA_BLOB)pqSet->lpBlob->pBlobData)->data.interfaceData.adapterName);
#endif
                    changed = TRUE;
                }
            }
            else {
                error = GetLastError ();
                INET_ASSERT (error!=NO_ERROR);
                if (error==WSAEFAULT) {

                    INET_ASSERT (qSize<retSize);

                    //
                    // Not enough space for query results.
                    // Allocate whatever is requested.
                    // First free what we have know is necessary.
                    //
                    if (pqSet!=NULL && pqSet!=&q.set) {
                        FREE_MEMORY (pqSet);
                    }
                    pqSet = (LPWSAQUERYSETW)ALLOCATE_MEMORY (LMEM_FIXED, retSize);
                    if (pqSet!=NULL) {
                        qSize = retSize;
                        error = NO_ERROR;
                    }
                    else {
                        DEBUG_PRINT (PROXY, ERROR,
                            ("NLA - Failed to allocate %ld bytes for query\n",
                            retSize));
#if DBG
                        dprintf 
                            ("NLA - Failed to allocate %ld bytes for query\n",
                            retSize);
#endif
                        error = ERROR_NOT_ENOUGH_MEMORY;
                    }
                }
            }
        }
        while ( error==NO_ERROR );

        if (error == WSA_E_NO_MORE) {
            WSACOMPLETION   wsacmpl;
            DWORD           count;

            //
            // This is expected error indicating that we got all that
            // was available at the moment. Set up new notification.
            //
            wsacmpl.Type = NSP_NOTIFY_EVENT;
            wsacmpl.Parameters.Event.lpOverlapped = &_ovlp;
            error = _I_WSANSPIoctl (_hLookup, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &count, &wsacmpl);
            if (error!=SOCKET_ERROR) {
                //
                // Something has changed, we need to enumerate again.
                //
                INET_ASSERT (error==NO_ERROR);
#if DBG
                dprintf ("NLA notification signaled immediately");
#endif
            }
            else {
                //
                // Failure, get the error and get out.
                //
                error = GetLastError ();
                INET_ASSERT (error!=NO_ERROR);
            }
        }
    }
    while (error==NO_ERROR);

    if (pqSet!=NULL && pqSet!=&q.set) {
        FREE_MEMORY (pqSet);
    }
    else {
        INET_ASSERT (qSize==sizeof (q));
    }

    if (error!=WSA_IO_PENDING) {
        DEBUG_PRINT (PROXY, ERROR,
            ("NLA - Failed to setup notification, error: %ld\n",
            error));
#if DBG
        dprintf (
            "NLA - Failed to setup notification, error: %ld\n",
            error);
#endif
        //
        // Error other then pending means that we failed.
        // Cleanup everything - query will get recreated
        // next time someone asks for changes.
        //
        CloseHandle (_ovlp.hEvent);
        _ovlp.hEvent = NULL;
        _I_WSALookupServiceEnd (_hLookup);
        _hLookup = NULL;
        //
        // Remember when we failed, so we do not retry very often.
        //
        _lastInitAttempt = GetTickCountWrap ();
    }


    return changed;
}


DWORD
AUTO_PROXY_DLLS::DoThreadProcessing(
    VOID
    )
/*++

Routine Description:

    Main function for the auto-proxy thread, maintains a generic loop
      that dispatchs events/messages sent to our thread for processing

Arguments:

    none.

Return Value:

    none.

--*/
{
    DWORD error  = ERROR_SUCCESS;
    LPINTERNET_THREAD_INFO lpThreadInfo;
    HANDLE EventArray[2];
    NLA_NET_CHANGE  nlaChange;
    BOOLEAN         needRefresh;

    EventArray[0] = _hAutoProxyThreadEvent;

    INET_ASSERT(IsOnAsyncAutoProxyThread());
    INET_ASSERT(_hAutoProxyStartEvent);

    SetEvent(_hAutoProxyStartEvent);

    lpThreadInfo = InternetGetThreadInfo();

    if (lpThreadInfo == NULL) {
        error = ERROR_INTERNET_INTERNAL_ERROR;
        goto quit;
    }

    //
    // Mark ourselves as the Auto-proxy thread...
    //

    _InternetSetAutoProxy(lpThreadInfo);

    while ( TRUE )
    {
        BOOLEAN networkChanged;
        //
        // 1. Check for shut down.
        //

        if ( _fInAutoProxyThreadShutDown )
        {
            SafeThreadShutdown(GlobalDynaUnload);
        }


        //
        // 2. Check for timer causing a refresh of registry settings
        //    and network location changes,
        //      thus causing a redownload of settings
        //    Be carefull to call both functions since they have side-effects.
        //

        needRefresh = nlaChange.IsNetChanged ();
        needRefresh = ChkForAndUpdateTimerCounter() || needRefresh;
        if (needRefresh)
        {

            //
            // Finally, start a download of the new stuff.
            //

            error = StartDownloadOfProxyInfo(FALSE /*full refresh*/);
        }

        //
        // 3. Wait for new messages to come in,
        //      for shutdown we should pass right past it
        //


        error = WaitForMultipleObjects(
                    (EventArray[1]=nlaChange.GetChangeEvent())==NULL ? 1 : 2,
                    EventArray, 
                    FALSE,
                    _dwWaitTimeOut
                    );
        if (error==WAIT_OBJECT_0+1) {
            //
            // NLA change identified, skip processing, just redownload.
            //
#if DBG
            dprintf ("NLA change event signalled\n");
#endif
            continue;
        }


        //
        // 4. Check to see if we're ready to shut down the thread,
        //    due to a process termination or whatnot.
        //

        if ( _fInAutoProxyThreadShutDown )
        {
            SafeThreadShutdown(TRUE);
        }

        GlobalProxyInfo.CheckForExpiredEntries();

        //
        // 5. If we've idled let us shut down until we're needed again
        //

        //
        // BUGBUG [arthurbi] Theoredically this should work,
        //   and the thread should shutdown on idle, too risky?
        //

        //if ( error == WAIT_FAILED && GetLastError() == WAIT_TIMEOUT)
        //{
            //SafeThreadShutdown(FALSE);
        //}

        //
        // 6. Walk and process the list the messages to our thread asking for
        //     information or reinitalization
        //

        error = ProcessAsyncAutoProxyRequest();

        INET_ASSERT( (error == ERROR_INTERNET_SHUTDOWN) ? _fInAutoProxyThreadShutDown : TRUE );
    }

quit:

    return error;
}

DWORD
AUTO_PROXY_DLLS::ProcessAsyncAutoProxyRequest(
    VOID
    )

/*++

Routine Description:

    Walks the list of queued messages and processes them one by one by
      either rerunning the download/initalization or executing a query for
      proxy information

Arguments:

    none.

Return Value:

    none.

--*/

{

    DWORD error = ERROR_SUCCESS;
    BOOL fForceRefresh;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::ProcessAsyncAutoProxyRequest",
                ""
                ));

    while (!IsSerializedListEmpty(&_AsyncQueueList)) {

        //
        // If we're shuting down, then quit right away
        //

        if ( _fInAutoProxyThreadShutDown )
        {
            error = ERROR_INTERNET_SHUTDOWN;
            goto quit;

        }

        LPVOID entry = SlDequeueHead(&_AsyncQueueList);
        AUTO_PROXY_ASYNC_MSG *pQueryForInfo =
            CONTAINING_RECORD(entry, AUTO_PROXY_ASYNC_MSG, _List);


        //
        // If the request has been unblocked, then destroy
        //   the request. ( this is typically due to cancel )
        //

        if ( pQueryForInfo->IsBlockUntilCompletetion() )
        {
            DWORD dwBlockCnt;

            if ( pQueryForInfo->IsBlockedOnFsm() )
            {

                dwBlockCnt = CheckForBlockedWorkItems(
                                1,
                                (DWORD_PTR) pQueryForInfo // blocked on message
                                );

                if ( dwBlockCnt == 0 )
                {
                    delete pQueryForInfo;
                    continue;
                }
            }
        }

        switch ( pQueryForInfo->QueryForInfoMessage() )
        {
            case PROXY_MSG_INIT:

                fForceRefresh = pQueryForInfo->IsForceRefresh();

                //
                // First peak ahead to make sure we're not asked to download
                //  the same darn thing over and over again.
                //

                while (!IsSerializedListEmpty(&_AsyncQueueList))
                {

                    LPVOID entry = SlDequeueHead(&_AsyncQueueList);
                    AUTO_PROXY_ASYNC_MSG *pQueryForInfo =
                        CONTAINING_RECORD(entry, AUTO_PROXY_ASYNC_MSG, _List);

                    if ( pQueryForInfo->QueryForInfoMessage() == PROXY_MSG_INIT )
                    {
                        INET_ASSERT(!pQueryForInfo->IsBlockUntilCompletetion());
                        if ( pQueryForInfo->IsForceRefresh() ) {
                            fForceRefresh = TRUE;
                        }
                        delete pQueryForInfo;
                    }
                    else
                    {
                        InsertAtHeadOfSerializedList(&_AsyncQueueList, &pQueryForInfo->_List);
                        break;
                    }
                }

                //
                // Finally, start a download of the new stuff.
                //

                if ( error == ERROR_SUCCESS )
                {
                    error = StartDownloadOfProxyInfo(fForceRefresh);
                }

                if ( error == ERROR_INTERNET_SHUTDOWN )
                {
                    goto quit;
                }

                break;

            case PROXY_MSG_SELF_DESTRUCT:

                //
                // Destroy ourselves and AUTO_PROXY_DLLS object.
                //
                //

                SafeThreadShutdown(TRUE);

                INET_ASSERT(FALSE); // never returns...

                break;


            case PROXY_MSG_GET_PROXY_INFO:

                //
                // If we've been updated then we need refresh settings from 
                //   the net.
                //

                if ( (_dwUpdatedProxySettingsVersion != _ProxySettings.dwCurrentSettingsVersion) ) 
                {
                    error = StartDownloadOfProxyInfo(FALSE /* no full refresh*/);
                }


                //
                // The strait call into GetProxyInfo
                //

                error = ProcessProxyQueryForInfo(
                    &pQueryForInfo
                    );

                if ( error == ERROR_INTERNET_SHUTDOWN )
                {
                    goto quit;
                }

                break;


            case PROXY_MSG_SET_BAD_PROXY:
            case PROXY_MSG_DEINIT:
            case PROXY_MSG_INVALID:
            default:

                INET_ASSERT(FALSE);
                break;
        }

        //
        // Wake up the caller if they are blocking on this completeion
        //

        if ( pQueryForInfo->IsBlockUntilCompletetion() )
        {
            SignalAsyncRequestCompleted(pQueryForInfo); // the thread that gets woken up now owns the object
        }
        else
        {
            delete pQueryForInfo;
        }
    }

quit:

    DEBUG_LEAVE(error);

    return error;
}

DWORD
AUTO_PROXY_DLLS::SignalAsyncRequestCompleted(
    IN AUTO_PROXY_ASYNC_MSG *pQueryForInfo
    )
/*++

Routine Description:

    Notifies a blocked thread or FSM that we have completed its message based request and
      it now can continue.

Arguments:

    pQueryForInfo - the orginating request

Return Value:

    none.

--*/

{
    DWORD dwCntUnBlocked;

    INET_ASSERT ( pQueryForInfo );

    if ( pQueryForInfo->IsBlockedOnFsm() )
    {
        dwCntUnBlocked = UnblockWorkItems(
                            1,
                            (DWORD_PTR) pQueryForInfo, // blocked on message
                            ERROR_SUCCESS,
                            TP_NO_PRIORITY_CHANGE
                            );

        //
        // If we were unable to unblock it, then we need to free it.
        //

        if ( dwCntUnBlocked == 0 )
        {
            delete pQueryForInfo;
            pQueryForInfo = NULL;
        }

    }
    else
    {

        dwCntUnBlocked = SignalThreadOnEvent(
                            (DWORD_PTR) pQueryForInfo,  // blocked on message
                            1,                          // should only be one item thats blocked by us
                            ERROR_SUCCESS
                            );

        //if ( dwCntUnBlocked == 0 )
        //{
        //    delete pQueryForInfo;
        //    pQueryForInfo = NULL;
        //}

    }

    return ERROR_SUCCESS;
}

VOID
AUTO_PROXY_DLLS::WipeProxySettings(
    LPINTERNET_PROXY_INFO_EX lpProxySettings
    )

/*++

Routine Description:

    Frees proxy settings

Arguments:

    lpProxySettings - pointer to listing of proxy settings

Return Value:

    none.

--*/

{
    if ( lpProxySettings )
    {
        if ( lpProxySettings->lpszConnectionName ) {
            FREE_MEMORY(lpProxySettings->lpszConnectionName);
        }

        if ( lpProxySettings->lpszProxy ) {
            FREE_MEMORY(lpProxySettings->lpszProxy);
        }

        if ( lpProxySettings->lpszProxyBypass ) {
            FREE_MEMORY(lpProxySettings->lpszProxyBypass);
        }

        if ( lpProxySettings->lpszLastKnownGoodAutoConfigUrl ) {
            FREE_MEMORY(lpProxySettings->lpszLastKnownGoodAutoConfigUrl);
        }

        if (lpProxySettings->lpszAutoconfigUrl) {
            FREE_MEMORY(lpProxySettings->lpszAutoconfigUrl);
        }

        if (lpProxySettings->lpszAutoconfigSecondaryUrl) {
            FREE_MEMORY(lpProxySettings->lpszAutoconfigSecondaryUrl);
        }

        if (lpProxySettings->pdwDetectedInterfaceIp) {
            FREE_MEMORY(lpProxySettings->pdwDetectedInterfaceIp);
        }
    }
}


DWORD
AUTO_PROXY_DLLS::SetProxySettings(
    IN LPINTERNET_PROXY_INFO_EX  lpProxySettings,
    IN BOOL fModifiedInProcess,
    IN BOOL fAllowOverwrite
    )

/*++

Routine Description:

    Write Auto-proxy Settings on the current auto-proxy object,
        this writes the settings into the object, but does not
        refresh it.

Arguments:

    lpProxySettings - List of new settings that we'd like to write

    fAllowOverwrite - Allow update of the settings in the object even if the version counter
                    hasn't changed

Return Value:

    DWORD
        ERROR_SUCCESS - if we have a valid proxy config

        other-errors - if we were not able to make the settings

--*/


{
    DWORD error = ERROR_SUCCESS;
    BOOL fGoPending = FALSE;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::SetProxySettings",
                "%x %B, %B",
                lpProxySettings,
                fModifiedInProcess,
                fAllowOverwrite
                ));

    LockAutoProxy();

    if ( fAllowOverwrite ||
         lpProxySettings->dwCurrentSettingsVersion == _ProxySettings.dwCurrentSettingsVersion )
    {
        BOOL fConnectionNameChange = FALSE;

        _fModifiedInProcess = fModifiedInProcess;

        UPDATE_GLOBAL_PROXY_VERSION();

        //
        // Check to see if we're changing the connection name,
        //  if so than this means we should plan on resetting
        //  the version below, so that we can redetect the new 
        //  connection.
        //

        if ( ! IsConnectionMatch( 
                _ProxySettings.lpszConnectionName,
                lpProxySettings->lpszConnectionName))
        {
            fConnectionNameChange = TRUE;
        }

        WipeProxySettings();

        _ProxySettings = *lpProxySettings;

        if ( fModifiedInProcess ) {
            // update version, only if we don't plan to write out these settings
            _ProxySettings.dwCurrentSettingsVersion++;
        }

        //
        // minus -1 so that when we get to the auto-proxy thread,
        //  we can reload/detect or do what's needed to keep settings current
        //

        if ( _dwUpdatedProxySettingsVersion != _ProxySettings.dwCurrentSettingsVersion ||             
             _dwUpdatedProxySettingsVersion == 0 /* init state */ ||
             fConnectionNameChange /* connection switch */ ) 
        {
            _dwUpdatedProxySettingsVersion = (DWORD) (_ProxySettings.dwCurrentSettingsVersion - 1);
            fGoPending = TRUE;
        }

        //
        // we shouldn't care about these settings,
        //   but we need to copy them anyway in
        //   case we save the stuff to the registry store
        //

        _ProxySettings.lpszProxy =
            lpProxySettings->lpszProxy ?
            NewString(lpProxySettings->lpszProxy) :
            NULL;

        _ProxySettings.lpszProxyBypass =
            lpProxySettings->lpszProxyBypass ?
            NewString(lpProxySettings->lpszProxyBypass) :
            NULL;

        _ProxySettings.lpszConnectionName =
            lpProxySettings->lpszConnectionName ?
            NewString(lpProxySettings->lpszConnectionName) :
            NULL;


        //
        // Copy strings, cause we may be on another thread
        //

        _ProxySettings.lpszAutoconfigUrl =
            lpProxySettings->lpszAutoconfigUrl ?
            NewString(lpProxySettings->lpszAutoconfigUrl) :
            NULL;

        _ProxySettings.lpszAutoconfigSecondaryUrl = 
            lpProxySettings->lpszAutoconfigSecondaryUrl ?
            NewString(lpProxySettings->lpszAutoconfigSecondaryUrl) :
            NULL;

        _ProxySettings.lpszLastKnownGoodAutoConfigUrl =
            lpProxySettings->lpszLastKnownGoodAutoConfigUrl ?
            NewString(lpProxySettings->lpszLastKnownGoodAutoConfigUrl) :
            NULL;

        //
        // Copy of IP host addresses from last detection
        //

        if ( lpProxySettings->dwDetectedInterfaceIpCount > 0 &&
             lpProxySettings->pdwDetectedInterfaceIp != NULL )
        {
            _ProxySettings.pdwDetectedInterfaceIp = (LPDWORD)
                ALLOCATE_MEMORY(LMEM_FIXED, lpProxySettings->dwDetectedInterfaceIpCount
                                * sizeof(DWORD));

            if (_ProxySettings.pdwDetectedInterfaceIp == NULL )
            {
                error = ERROR_NOT_ENOUGH_MEMORY;
                goto quit;
            }

            memcpy(_ProxySettings.pdwDetectedInterfaceIp, lpProxySettings->pdwDetectedInterfaceIp,
                        lpProxySettings->dwDetectedInterfaceIpCount * sizeof(DWORD));
        }


        if (fGoPending &&
            IsConfigValidForAutoProxyThread())            
        {
            //
            // Enable a forced refresh if the user orders it through the UI,
            //   for unknown new connections we don't block the user on auto-detect,
            //   since he didn't really order it
            //

            if ( IsStaticFallbackEnabled() ) {
                SetState(AUTO_PROXY_PENDING);
            } else {
                SetState(AUTO_PROXY_BLOCKED);
            }
        }
    }

quit:

    UnlockAutoProxy();

    DEBUG_LEAVE(error);

    return error;
}


DWORD
AUTO_PROXY_DLLS::GetProxySettings(
    OUT LPINTERNET_PROXY_INFO_EX  lpProxySettings,
    IN BOOL fCheckVersion
    )

/*++

Routine Description:

    Reads Auto-proxy Settings off the current auto-proxy object,
        this allocates individual fields as needed to store the result

Arguments:

    lpProxySettings - Returns the result of the auto-proxy settings

Return Value:

    DWORD
        ERROR_SUCCESS - if we have a valid proxy config

        other-errors - if we were not able to make the settings

--*/

{
    DWORD error = ERROR_SUCCESS;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::GetProxySettings",
                "%x %B",
                lpProxySettings
                ));

    LockAutoProxy();

    if ( fCheckVersion &&
         lpProxySettings->dwCurrentSettingsVersion == _ProxySettings.dwCurrentSettingsVersion )
    {
        goto quit; // no change
    }


    *lpProxySettings = _ProxySettings;

    //
    // Copy strings, cause we may be on another thread
    //

    lpProxySettings->lpszProxy =
        _ProxySettings.lpszProxy ?
        NewString(_ProxySettings.lpszProxy) :
        NULL;

    lpProxySettings->lpszProxyBypass =
        _ProxySettings.lpszProxyBypass ?
        NewString(_ProxySettings.lpszProxyBypass) :
        NULL;

    lpProxySettings->lpszConnectionName =
        _ProxySettings.lpszConnectionName ?
        NewString(_ProxySettings.lpszConnectionName) :
        NULL;

    lpProxySettings->lpszAutoconfigUrl =
        _ProxySettings.lpszAutoconfigUrl ?
        NewString(_ProxySettings.lpszAutoconfigUrl) :
        NULL;

    lpProxySettings->lpszAutoconfigSecondaryUrl = 
        _ProxySettings.lpszAutoconfigSecondaryUrl ?
        NewString(_ProxySettings.lpszAutoconfigSecondaryUrl) :
        NULL;

    lpProxySettings->lpszLastKnownGoodAutoConfigUrl =
        _ProxySettings.lpszLastKnownGoodAutoConfigUrl ?
        NewString(_ProxySettings.lpszLastKnownGoodAutoConfigUrl) :
        NULL;

    //
    // Copy of IP host addresses from last detection
    //

    if ( _ProxySettings.dwDetectedInterfaceIpCount > 0 &&
         _ProxySettings.pdwDetectedInterfaceIp != NULL )
    {
        lpProxySettings->pdwDetectedInterfaceIp = (LPDWORD)
            ALLOCATE_MEMORY(LMEM_FIXED, _ProxySettings.dwDetectedInterfaceIpCount
                            * sizeof(DWORD));

        if (lpProxySettings->pdwDetectedInterfaceIp == NULL )
        {
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto quit;
        }

        memcpy(lpProxySettings->pdwDetectedInterfaceIp, _ProxySettings.pdwDetectedInterfaceIp,
                    _ProxySettings.dwDetectedInterfaceIpCount * sizeof(DWORD));
    }

quit:

    UnlockAutoProxy();

    DEBUG_LEAVE(error);

    return error;
}


DWORD
AUTO_PROXY_DLLS::RefreshProxySettings(
    IN BOOL fForceRefresh
    )

/*++

Routine Description:

    Syncronizes the Auto-Proxy engine with the state of the various settings,
      and updates the various state keepers of the results.

        - If there is no proxy settings, or auto-proxy is not needed or not detected,
          we disable the auto-proxy system.

        - If we have auto-proxy information or need to detect for some, then we fire up
            the auto-proxy thread and send it a message to initalize itself.

Arguments:

    none.

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/


{
    DWORD error = ERROR_SUCCESS;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::RefreshProxySettings",
                "%B",
                fForceRefresh
                ));

    LockAutoProxy();

    if (! IsOnAsyncAutoProxyThread() )
    {
        AUTO_PROXY_ASYNC_MSG *pQueryForInfo;

        error = _Error;
        if ( error != ERROR_SUCCESS)
        {            
            goto quit; // obj is not initalized properly
        }

        if (!IsConfigValidForAutoProxyThread() )
        {
            goto quit; // disable & bail, we're not setup for this
        }

        pQueryForInfo = new AUTO_PROXY_ASYNC_MSG(PROXY_MSG_INIT);

        if (pQueryForInfo == NULL )
        {
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto quit;
        }

        //
        // Enabled a forced refresh if the user orders it through the UI,
        //   for unknown new connections we don't block the user on auto-detect,
        //   since he didn't really order it
        //

        pQueryForInfo->SetForceRefresh(fForceRefresh);

        if ( !fForceRefresh && IsStaticFallbackEnabled() ) {
            SetState(AUTO_PROXY_PENDING);
        } else {
            SetState(AUTO_PROXY_BLOCKED);
        }

        error = QueueAsyncAutoProxyRequest(
                &pQueryForInfo  // don't worry, this request won't block us.
                );
    }

quit:

    if ( error != ERROR_SUCCESS  && 
         error != ERROR_IO_PENDING )
    {
        SetState(AUTO_PROXY_DISABLED); // disable in case something critical happens
    }

    UnlockAutoProxy();

    DEBUG_LEAVE(error);

    return error;
}

DWORD
AUTO_PROXY_DLLS::StartBackroundDetectionIfNeeded(
    VOID
    )
{
    DWORD error = ERROR_SUCCESS;

    LockAutoProxy();
    if ( _hAutoProxyThread == NULL ) {
        error = RefreshProxySettings(FALSE);
    }
    UnlockAutoProxy();

    return error;
}

DWORD
AUTO_PROXY_DLLS::QueryProxySettings(
    IN OUT AUTO_PROXY_ASYNC_MSG **ppQueryForInfo
    )

/*++

Routine Description:

    Performs a query for proxy information using auto-proxy to anwser our query.

    Assumes this is not called from auto-proxy thread/

Arguments:

    ppQueryForInfo - Pointer to pointer to Message object that contains state information
                        used in the query.   If the object needs to be allocated on the heap
                        then the pointer will change to reflect the new object ptr.

Return Value:

    DWORD
        Success - ERROR_SUCCESS

        Failure -

--*/

{
    AUTO_PROXY_LIST_ENTRY * papleAutoProxy;
    DWORD error = ERROR_SUCCESS;
    BOOL fUnlocked = FALSE;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::QueryProxySettings",
                "%x",
                ppQueryForInfo
                ));


    INET_ASSERT(ppQueryForInfo);
    INET_ASSERT(*ppQueryForInfo);
    INET_ASSERT((*ppQueryForInfo)->QueryForInfoMessage() == PROXY_MSG_GET_PROXY_INFO);
    //INET_ASSERT(!IsOnAsyncAutoProxyThread());

    LockAutoProxy();

    //
    // ALWAYS force this function to exec it auto-proxy calls on the
    //   async auto-proxy thread.
    //

    if ( IsAutoProxy() &&
        !IsOnAsyncAutoProxyThread() &&
         IsAutoProxyGetProxyInfoCallNeeded(*ppQueryForInfo))
    {
        if ( GetState() == AUTO_PROXY_PENDING )
        {

            //
            // If we're doing a pending detection, then
            //  fallback to standby settings, and if we fail
            //  with standby settings, we should get re-called to here
            //  and if we're then still detecting, then
            //  we'll block on the detection result
            //

            if ( ! (*ppQueryForInfo)->IsBackroundDetectionPending() )
            {
                error = StartBackroundDetectionIfNeeded();
                if (error != ERROR_SUCCESS ) {
                    goto quit;
                }
                (*ppQueryForInfo)->SetBackroundDetectionPending(TRUE);
                if ( ! (*ppQueryForInfo)->IsAlloced() )
                {
                    *ppQueryForInfo = new AUTO_PROXY_ASYNC_MSG(*ppQueryForInfo);

                    if ( *ppQueryForInfo == NULL ) {
                        error = ERROR_NOT_ENOUGH_MEMORY;
                    }
                }
                goto quit;
            }
        }
        else if (!(_ProxySettings.dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_DONT_CACHE_PROXY_RESULT) &&
                   GlobalAutoProxyCacheEnable)
        {
            (*ppQueryForInfo)->SetCanCacheResult(TRUE);
        }

        //
        // If we need to show indication during detection,
        //   then do so now before we block
        //

        if ( (*ppQueryForInfo)->IsShowIndication() &&
                (GetState() == AUTO_PROXY_PENDING ||
                 GetState() == AUTO_PROXY_BLOCKED))
        {
            UnlockAutoProxy();
            fUnlocked = TRUE;

            InternetIndicateStatus(INTERNET_STATUS_DETECTING_PROXY, NULL, 0);
        }
        else
        {
            UnlockAutoProxy();
            fUnlocked = TRUE;
        }

        // always disable unless we're ready to renter on a failure
        (*ppQueryForInfo)->SetBackroundDetectionPending(FALSE);

        error = QueueAsyncAutoProxyRequest(ppQueryForInfo);
        goto quit;
    }

    // always disabled it unless we're ready to reenter on a failure
    (*ppQueryForInfo)->SetBackroundDetectionPending(FALSE);

quit:

    if ( !fUnlocked )
    {
        UnlockAutoProxy();
    }

    DEBUG_LEAVE(error);

    return error;
}

BOOL
AUTO_PROXY_DLLS::IsConfigValidForAutoProxyThread(
    VOID
    )
/*++

Routine Description:

    Decide if we need the auto-thread to either download auto-proxy ot
      go off and detect for auto-proxy on the network

Arguments:

    none.

Return Value:

    BOOL
        TRUE - if we have a valid proxy config

        FALSE - if we not valid

--*/

{
    LPINTERNET_PROXY_INFO_EX lpProxySettings = &_ProxySettings;

    if ( (lpProxySettings->dwFlags & PROXY_TYPE_AUTO_PROXY_URL) &&
          lpProxySettings->lpszAutoconfigUrl != NULL &&
         *lpProxySettings->lpszAutoconfigUrl != '\0' )
    {
        return TRUE; // old behavior for auto-proxy URL config
    }

    if ( IsProxyAutoDetectEnabled(lpProxySettings))
    {
        return TRUE;
    }

    return FALSE; // do nothing
}

BOOL
AUTO_PROXY_DLLS::IsStaticFallbackEnabled(
    VOID
    )
/*++

Routine Description:

    Decide if we need to block on the auto-proxy information,
     or if we can fallback to static settings when auto-proxy is initalizing/detecting.

Arguments:

    none.

Return Value:

    BOOL
        TRUE - if we keep going with static settings

        FALSE - we'll need to block requests until auto-proxy is intialized

--*/

{

    if ( (_ProxySettings.dwFlags & PROXY_TYPE_AUTO_PROXY_URL) &&
          _ProxySettings.lpszAutoconfigUrl != NULL &&
         *_ProxySettings.lpszAutoconfigUrl != '\0' )
    {
        return FALSE; // block, don't bypass, this is old behavior for Auto-proxy URLs
    }

    if (  IsProxyAutoDetectEnabled() )

    {
        if ( !(_ProxySettings.dwAutoDiscoveryFlags & (AUTO_PROXY_FLAG_DETECTION_RUN | AUTO_PROXY_FLAG_USER_SET)) ) {
            return TRUE; // detection SHOULD NOT BLOCK THE FIRST TIME, in case it doesn't work
        }

        if ( _ProxySettings.lpszConnectionName != NULL )
        {
            if ( _ProxySettings.lpszLastKnownGoodAutoConfigUrl == NULL ) 
            {
                return TRUE; // detection SHOULD NOT BE DEPENDED upon with Dialup, unless it has something
            }

            if ( _ProxySettings.dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_DETECTION_SUSPECT)
            {
                return TRUE; // detection should not block when we're in a hosed state.
            }
        }
    }



    return FALSE; // block
}

VOID
AUTO_PROXY_DLLS::SetExpiredUrl(
    LPCSTR lpszUrl
    )
/*++

Routine Description:

    Sets default expiry time on the if none is specified on the cached URL. 

Arguments:

    lpszUrl -

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/
{
    CACHE_ENTRY_INFOEX Cei;
    DWORD dwCeiSize = sizeof(INTERNET_CACHE_ENTRY_INFOA);
    BOOL fRet;

    fRet = GetUrlCacheEntryInfoExA(
            lpszUrl,
            (INTERNET_CACHE_ENTRY_INFOA *) &Cei,
            &dwCeiSize,
            NULL,
            NULL,
            NULL,
            INTERNET_CACHE_FLAG_GET_STRUCT_ONLY
            );


    if ( fRet && 
         (FT2LL(Cei.ExpireTime) == LONGLONG_ZERO))
    {
        //
        // Set default expiry as: current time + some default expiry
        //

        GetCurrentGmtTime(&Cei.ExpireTime);

        *(LONGLONG *) &(Cei.ExpireTime) += (12 * (24 * ONE_HOUR_DELTA));

        //
        // Re-Save Cache entry with updated default expiry time for PAC/INS file.
        //

        SetUrlCacheEntryInfoA(
                lpszUrl,
                (INTERNET_CACHE_ENTRY_INFOA *) &Cei,
                CACHE_ENTRY_EXPTIME_FC
                );

    }

}

BOOL
AUTO_PROXY_DLLS::IsExpiredUrl(
    LPCSTR lpszUrl
    )
/*++

Routine Description:

    Is Url Expired?  If it isn't don't force a reload/update of data

Arguments:

    lpszUrl -

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/
{
    CACHE_ENTRY_INFOEX Cei;
    DWORD dwCeiSize = sizeof(INTERNET_CACHE_ENTRY_INFOA);
    BOOL fRet;

    fRet = GetUrlCacheEntryInfoExA(
            lpszUrl,
            (INTERNET_CACHE_ENTRY_INFOA *) &Cei,
            &dwCeiSize,
            NULL,
            NULL,
            NULL,
            INTERNET_CACHE_FLAG_GET_STRUCT_ONLY 
            );

    if (!fRet) {
        return TRUE; // expired, not in the cache
    }

    if ( IsExpired(&Cei, 0, &fRet) )
    {
        return TRUE; // expired, like really it is
    }

    return FALSE; // not expired
}


BOOL
AUTO_PROXY_DLLS::IsProxyAutoDetectNeeded(
    LPINTERNET_PROXY_INFO_EX lpProxySettings
    )

/*++

Routine Description:

  Detects whether we need to actually run a detection on the network,
    or whether we can resuse current results from previous runs

Arguments:

    lpProxySettings - structure to fill

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    DWORD addressCount;
    LPDWORD * addressList;
    LPHOSTENT lpHostent;
    BOOL fSuspectBadDetect = FALSE;

    INET_ASSERT(IsProxyAutoDetectEnabled(lpProxySettings));

    // we haven't detected before on this connection, so we need to do it.
    if ( !(lpProxySettings->dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_DETECTION_RUN) ) {
        return TRUE; // detect needed
    }

    // if we're flagged to ALWAYS force detection, then do this
    if (lpProxySettings->dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_ALWAYS_DETECT) {
        return TRUE; // detect needed
    }

    //
    // Check for an expired detected Url, detect it its expired
    //  Since this is RAS we can't rely on the host IP staying 
    //  the same everytime.
    //

    if (  lpProxySettings->lpszLastKnownGoodAutoConfigUrl && 
          lpProxySettings->lpszConnectionName &&          
        ! IsExpiredUrl(lpProxySettings->lpszLastKnownGoodAutoConfigUrl))
    {
        // if we're suspecting bad settings, make sure to redirect.
        if ( ! (lpProxySettings->dwAutoDiscoveryFlags & AUTO_PROXY_FLAG_DETECTION_SUSPECT) ) {
            return FALSE; 
        }

        //otherwise, we'll be careful
        fSuspectBadDetect = TRUE;
    }

    //
    // Check for IP addresses that no longer match, indicating a network change
    //

    __try
    {
        lpHostent = _I_gethostbyname(NULL);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        g_fGetHostByNameNULLFails = TRUE;
        lpHostent = NULL;
    }
    ENDEXCEPT

    if (lpHostent &&
        lpProxySettings->pdwDetectedInterfaceIp == NULL)
    {
        return TRUE; // detect needed, no current IPs saved from last run
    }

    if ( lpHostent != NULL &&
         lpProxySettings->pdwDetectedInterfaceIp != NULL)
    {

        for ( addressCount = 0;
              lpHostent->h_addr_list[addressCount] != NULL;
              addressCount++ );  // gather count

        if ( addressCount != lpProxySettings->dwDetectedInterfaceIpCount ) {
            return TRUE; // detect needed, the IP count is different
        }

        if ( fSuspectBadDetect) {
            return FALSE; // detect NOT needed, because the IP addresses may change from dialup/to dialup
        }

        for (DWORD i = 0; i < addressCount; i++)
        {
            //dwAddress[iCount] = *((LPDWORD)(ph->h_addr_list[iCount]));

            if ( *((LPDWORD)(lpHostent->h_addr_list[i])) != lpProxySettings->pdwDetectedInterfaceIp[i] ) {
                return TRUE; // detect needed, mismatched values
            }
        }
    }


    return FALSE; // default, do not need to redetect
}


DWORD
AUTO_PROXY_DLLS::GetHostAddresses(
    DWORD ** ppdwDetectedInterfaceIp,
    DWORD *  pdwDetectedInterfaceIpCount
    )
/*++

Routine Description:

  Copies out the current host information into an
    a array/ProxyInfoStruct for later comparision.

Arguments:

    lpProxySettings - structure to fill

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    DWORD addressCount;
    LPDWORD * addressList;
    LPHOSTENT lpHostent;
    DWORD error = ERROR_SUCCESS;

    *pdwDetectedInterfaceIpCount = 0;

    if ( *ppdwDetectedInterfaceIp )
    {
        FREE_MEMORY(*ppdwDetectedInterfaceIp);
        *ppdwDetectedInterfaceIp = NULL;
    }

    //
    // Gather IP addresses and start copying them over
    //

    __try
    {
        lpHostent = _I_gethostbyname(NULL);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        g_fGetHostByNameNULLFails = TRUE;
        lpHostent = NULL;
    }
    ENDEXCEPT

    if (lpHostent == NULL ) {
        goto quit;
    }

    for ( addressCount = 0;
          lpHostent->h_addr_list[addressCount] != NULL;
          addressCount++ );  // gather count

    *ppdwDetectedInterfaceIp = (LPDWORD)
        ALLOCATE_MEMORY(LMEM_FIXED, addressCount
                        * sizeof(DWORD));

    if ( *ppdwDetectedInterfaceIp == NULL )
    {
        error = ERROR_NOT_ENOUGH_MEMORY;
        goto quit;
    }
    
    if ( *ppdwDetectedInterfaceIp != NULL)
    {
        *pdwDetectedInterfaceIpCount = addressCount;

        for (DWORD i = 0; i < addressCount; i++)
        {
            (*ppdwDetectedInterfaceIp)[i] =
                *((LPDWORD)(lpHostent->h_addr_list[i]));
        }
    }

quit:
    return error;
}


VOID
AUTO_PROXY_DLLS::FreeAutoProxyInfo(
    VOID
    )

/*++

Routine Description:

    Attempts to shutdown the auto-proxy thread (from outside of it), by first signalling
      it with an event and boolean, and then if that fails, forcibly forcing a shutdown
      with TerminateThread.

Arguments:

    none.

Return Value:

    none.

--*/

{
    DWORD dwError;

    LockAutoProxy();

    if ( _hAutoProxyThread != NULL &&
         ! IsOnAsyncAutoProxyThread() )
    {
        HANDLE hAutoProxyThread;
        BOOL fAlreadyInShutDown;

        fAlreadyInShutDown =
            InterlockedExchange((LPLONG)&_fInAutoProxyThreadShutDown, TRUE);

        INET_ASSERT ( ! fAlreadyInShutDown );

        SetEvent(_hAutoProxyThreadEvent);

        if ( _hInternetAbortHandle != NULL )
        {
            InternetCloseHandle(_hInternetAbortHandle);
        }

        hAutoProxyThread = _hAutoProxyThread;

        UnlockAutoProxy();

        //
        // Wait for shutdown of auto-proxy thread.
        //

        if ( hAutoProxyThread )
        {
            dwError = WaitForSingleObject(hAutoProxyThread,
                GlobalIsProcessNtService? INFINITE : 60000);

            if ( dwError != WAIT_OBJECT_0 )
            {
                INET_ASSERT(FALSE);
                // Whistler bug #124628: delete the autoproxy critical section
                // before terminating the autoproxy thread in case the thread
                // is holding the CS.
                SuspendThread(hAutoProxyThread);
                DeleteCriticalSection(&_CritSec);
                
                // reinitialize cs since it will be deleted in the destructor
                memset((LPVOID) &_CritSec, 0, sizeof(CRITICAL_SECTION));
                InitializeCriticalSection(&_CritSec);

                TerminateThread(hAutoProxyThread,ERROR_SUCCESS);

                _hAutoProxyThreadEvent = NULL;
                _hAutoProxyThread = NULL;

                InterlockedExchange((LPLONG)&_fInAutoProxyThreadShutDown, FALSE);
                return;
            }
        }

        LockAutoProxy();

        CloseHandle(_hAutoProxyThread);
        CloseHandle(_hAutoProxyThreadEvent);

        _hAutoProxyThreadEvent = NULL;
        _hAutoProxyThread = NULL;

        InterlockedExchange((LPLONG)&_fInAutoProxyThreadShutDown, FALSE);

        UnlockAutoProxy();
    }
    else
    {
        UnlockAutoProxy();
    }
}

BOOL
AUTO_PROXY_LIST_ENTRY::ProxyInfoInvalid(
    IN LPSTR lpszMime,
    IN LPSTR lpszUrl,
    IN DWORD dwUrlLength,
    IN LPSTR lpszProxyHostName,
    IN DWORD dwProxyHostNameLength
    )
{
    BOOL success = TRUE; // don't care if it doesn't succeed

    if ( ! _hAutoConfigDLL )
    {
        if ( LoadEntry() != ERROR_SUCCESS )
            return FALSE;
    }

    if ( ! _fInitializedSuccessfully )
    {
        return FALSE;
    }

    if ( _pProxyInfoInvalid )
    {

        INET_ASSERT(_hAutoConfigDLL);
        INET_ASSERT(!IsBadCodePtr((FARPROC)_pProxyInfoInvalid));

        success = (_pProxyInfoInvalid) ( lpszMime,
                                          lpszUrl,
                                          dwUrlLength,
                                          lpszProxyHostName,
                                          dwProxyHostNameLength
                                          );
    }

    return success;
}



BOOL
AUTO_PROXY_LIST_ENTRY::ProxyDllDeInit(
    IN LPSTR lpszMime,
    IN DWORD dwReserved
    )
{
    BOOL success = TRUE; // don't care if it doesn't succeed

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "AUTO_PROXY_LIST_ENTRY::ProxyDllDeInit",
                "%s, %u",
                lpszMime,
                dwReserved
                ));


    INET_ASSERT(_hAutoConfigDLL);

    if ( !_hAutoConfigDLL  )
    {
        DEBUG_LEAVE(FALSE);
        return FALSE;
    }

    if ( ! _fInitializedSuccessfully )
    {
        DEBUG_LEAVE(FALSE);
        return FALSE;
    }

    if ( _pProxyDllDeInit )
    {

        INET_ASSERT(_hAutoConfigDLL);
        INET_ASSERT(!IsBadCodePtr((FARPROC)_pProxyDllDeInit));
#ifdef INET_DEBUG
        INET_ASSERT(!_fUnInited);

        _fUnInited = TRUE;
#endif

        success = (_pProxyDllDeInit) ( lpszMime,
                                       dwReserved
                                       );
    }

    DEBUG_LEAVE(success);
    return success;
}



DWORD
AUTO_PROXY_LIST_ENTRY::GetProxyInfoEx(
    IN AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo
    )
{
    BOOL success = FALSE;
    DWORD error = ERROR_SUCCESS;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_LIST_ENTRY::GetProxyInfoEx",
                "%x [%s, %s, %u, %s, %d, %x, %x]",
                pQueryForProxyInfo,
                InternetMapScheme(pQueryForProxyInfo->_tUrlProtocol),
                pQueryForProxyInfo->_lpszUrl,
                pQueryForProxyInfo->_dwUrlLength,
                pQueryForProxyInfo->_lpszUrlHostName,
                pQueryForProxyInfo->_dwUrlHostNameLength,
                pQueryForProxyInfo->_lpszProxyHostName,
                &pQueryForProxyInfo->_dwProxyHostNameLength
                ));

    //if ( ! _hAutoConfigDLL )
    //{
    //    if ( LoadEntry() != ERROR_SUCCESS )
    //    {
    //        DEBUG_LEAVE(FALSE);
    //        return FALSE;
    //    }
    //}

    if ( ! _fInitializedSuccessfully )
    {
        DEBUG_LEAVE(FALSE);
        error = ERROR_INTERNET_INTERNAL_ERROR;
        goto quit;
    }

    INET_ASSERT(_hAutoConfigDLL);
    INET_ASSERT(_pGetProxyInfoEx);
    INET_ASSERT(!IsBadCodePtr((FARPROC)_pGetProxyInfoEx));

    success = (_pGetProxyInfoEx) ( pQueryForProxyInfo->_tUrlProtocol,
                                    pQueryForProxyInfo->_lpszUrl,
                                    pQueryForProxyInfo->_dwUrlLength,
                                    pQueryForProxyInfo->_lpszUrlHostName,
                                    pQueryForProxyInfo->_dwUrlHostNameLength,
                                    pQueryForProxyInfo->_nUrlPort,
                                    &(pQueryForProxyInfo->_tProxyScheme),
                                    &(pQueryForProxyInfo->_lpszProxyHostName),
                                    &(pQueryForProxyInfo->_dwProxyHostNameLength),
                                    &(pQueryForProxyInfo->_nProxyHostPort)
                                    );

quit:

    pQueryForProxyInfo->_dwQueryResult = (DWORD) success;

    DEBUG_LEAVE(error);
    return error;

}



DWORD
AUTO_PROXY_LIST_ENTRY::GetProxyInfo(
    IN AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo
    )
{
    BOOL success = FALSE;
    DWORD error = ERROR_SUCCESS;
    LPSTR lpszAutoProxyReturnInfo;
    DWORD dwAutoProxyReturnInfoSize;

    INET_ASSERT(pQueryForProxyInfo);

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_LIST_ENTRY::GetProxyInfo",
                "%x [%s, %u, %s, %d, %x, %x]",
                pQueryForProxyInfo,
                pQueryForProxyInfo->_lpszUrl,
                pQueryForProxyInfo->_dwUrlLength,
                pQueryForProxyInfo->_lpszUrlHostName,
                pQueryForProxyInfo->_dwUrlHostNameLength,
                pQueryForProxyInfo->_lpszProxyHostName,
                &pQueryForProxyInfo->_dwProxyHostNameLength
                ));

    //if ( ! _hAutoConfigDLL )
    //{
    //    if ( LoadEntry() != ERROR_SUCCESS )
    //    {
    //        DEBUG_LEAVE(FALSE);
    //        return FALSE;
    //    }
    //}

    if ( ! _fInitializedSuccessfully )
    {
        DEBUG_LEAVE(FALSE);
        error = ERROR_INTERNET_INTERNAL_ERROR;
        goto quit;
    }

    INET_ASSERT ( _pGetProxyInfo );
    INET_ASSERT(_hAutoConfigDLL);
    INET_ASSERT(!IsBadCodePtr((FARPROC)_pGetProxyInfo));

    success = (_pGetProxyInfo) (    pQueryForProxyInfo->_lpszUrl,
                                    pQueryForProxyInfo->_dwUrlLength,
                                    pQueryForProxyInfo->_lpszUrlHostName,
                                    pQueryForProxyInfo->_dwUrlHostNameLength,
                                    &lpszAutoProxyReturnInfo,
                                    &dwAutoProxyReturnInfoSize
                                    );


    if ( success )
    {
        if ( pQueryForProxyInfo->_tUrlProtocol == INTERNET_SCHEME_HTTPS )
        {
            pQueryForProxyInfo->_tProxyScheme = INTERNET_SCHEME_HTTPS;
        }
        else
        {
            pQueryForProxyInfo->_tProxyScheme = INTERNET_SCHEME_HTTP;
        }

        INET_ASSERT(pQueryForProxyInfo->_pProxyState == NULL);

        pQueryForProxyInfo->_pProxyState = new PROXY_STATE(lpszAutoProxyReturnInfo,
                                                          dwAutoProxyReturnInfoSize,
                                                          TRUE,  // parse netscape-style proxy list
                                                          pQueryForProxyInfo->_tProxyScheme,
                                                          pQueryForProxyInfo->_nProxyHostPort
                                                          );


        INET_ASSERT(lpszAutoProxyReturnInfo);
        GlobalFree(lpszAutoProxyReturnInfo);  // clean up jsproxy.dll return string

        if ( pQueryForProxyInfo->_pProxyState  == NULL )
        {
           error = ERROR_NOT_ENOUGH_MEMORY;
           goto quit;
        }

        error = pQueryForProxyInfo->_pProxyState->GetError();

        if ( error != ERROR_SUCCESS )
        {
            goto quit;
        }
    }


quit:

    pQueryForProxyInfo->_dwQueryResult = (DWORD) success;

    DEBUG_LEAVE(error);
    return error;

}


BOOL
AUTO_PROXY_LIST_ENTRY::ProxyDllInit (
    IN DWORD                  dwVersion,
    IN LPSTR                  lpszDownloadedTempFile,
    IN LPSTR                  lpszMime,
    IN AUTO_PROXY_HELPER_APIS *pAutoProxyCallbacks,
    IN DWORD_PTR              dwReserved
    )
{
    BOOL success = FALSE;

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "AUTO_PROXY_LIST_ENTRY::ProxyDllInit",
                "%u, %s, %s, %x, %u",
                dwVersion,
                lpszDownloadedTempFile,
                lpszMime,
                pAutoProxyCallbacks,
                dwReserved
                ));


    INET_ASSERT ( _hAutoConfigDLL );

    if ( _pProxyDllInit )
    {

        INET_ASSERT(_hAutoConfigDLL);
        INET_ASSERT(!IsBadCodePtr((FARPROC)_pProxyDllInit));

        success = (_pProxyDllInit) ( dwVersion,
                                     lpszDownloadedTempFile,
                                     lpszMime,
                                     pAutoProxyCallbacks,
                                     dwReserved
                                     );

#ifdef INET_DEBUG
        _fUnInited = FALSE;
#endif
        _fInitializedSuccessfully = success;

    }

    DEBUG_LEAVE(success);
    return success;
}

VOID
AUTO_PROXY_LIST_ENTRY::UnloadEntry(
    VOID
    )

/*++

Routine Description:

    Unloads the DLL function entry points for an Auto-Proxy DLL.

    WARNING: Must be called only on Auto-proxy thread context!

Arguments:

    none.

Return Value:

    none.

--*/

{
    DEBUG_ENTER((DBG_OBJECTS,
                None,
                "AUTO_PROXY_LIST_ENTRY::UnloadEntry",
                NULL
                ));


    if ( _fInitializedSuccessfully &&
         _pProxyDllDeInit )
    {
        ProxyDllDeInit(
            _lpszMimeType,
            0
            );
    }

    if ( _hAutoConfigDLL )
    {
        // HACK HACK This call will block an dll unload time because of the 
        // loader critical section. Ideally we should move this free to the 
        // main thread, but since we are in the midst of IE5 RC's we are doing
        // the least risky change i.e. leaking jsproxy.dll in the process. 
        if (!GlobalDynaUnload)
        {
            FreeLibrary(_hAutoConfigDLL);
        }

        _pGetProxyInfo      = NULL;
        _pGetProxyInfoEx    = NULL;
        _pProxyDllInit      = NULL;
        _pProxyDllDeInit    = NULL;
        _pProxyInfoInvalid  = NULL;
    }

    DEBUG_LEAVE(0);
}


DWORD
AUTO_PROXY_LIST_ENTRY::LoadEntry(
    VOID
    )

/*++

Routine Description:

    Loads the DLL function entry points for an Auto-Proxy DLL.

Arguments:

    none.

Return Value:

    DWORD
        ERROR_SUCCESS    - success

        Win32 Error code - failure

--*/

{
    DWORD error = ERROR_SUCCESS;

    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_LIST_ENTRY::LoadEntry",
                ""
                ));

    if ( _lpszFileExtensions == NULL  ||
         _lpszDllFilePath == NULL )
    {
        error =  ERROR_NOT_ENOUGH_MEMORY;
        goto quit;
    }

    _pGetProxyInfo      = NULL;
    _pProxyInfoInvalid  = NULL;
    _pProxyDllDeInit    = NULL;
    _pProxyDllInit      = NULL;
    _pGetProxyInfoEx    = NULL;

    if ( _hAutoConfigDLL )
    {
        FreeLibrary(_hAutoConfigDLL);
        _hAutoConfigDLL  = NULL;
    }

    _hAutoConfigDLL = LoadLibrary(_lpszDllFilePath);

    if ( _hAutoConfigDLL == NULL )
    {
        error = GetLastError();
        goto quit;
    }



    _pGetProxyInfo     = (GET_PROXY_INFO_FN)
                        GetProcAddress(_hAutoConfigDLL, GET_PROXY_INFO_FN_NAME);


    _pGetProxyInfoEx   = (GET_PROXY_INFO_EX_FN)
                        GetProcAddress(_hAutoConfigDLL, GET_PROXY_INFO_EX_FN_NAME);


    _pProxyInfoInvalid = (PROXY_INFO_INVALID_FN)
                        GetProcAddress(_hAutoConfigDLL, PROXY_INFO_INVALID_FN_NAME);


    _pProxyDllDeInit   = (PROXY_DLL_DEINIT_FN)
                        GetProcAddress(_hAutoConfigDLL, PROXY_DLL_DEINIT_FN_NAME);


    _pProxyDllInit     = (PROXY_DLL_INIT_FN)
                        GetProcAddress(_hAutoConfigDLL, PROXY_DLL_INIT_FN_NAME );


    if ( !_pProxyDllInit && !_pProxyDllDeInit && !_pProxyInfoInvalid && !_pGetProxyInfo && !_pGetProxyInfoEx)
    {
        error = GetLastError();
        goto quit;
    }

    if ( !_pProxyDllInit )
    {
        //
        // If they don't export this entry point, than we can't initialize them, so
        //  we pretend to have initialized them.
        //

        _fInitializedSuccessfully = TRUE;
    }

quit:

    DEBUG_LEAVE(error);

    return error;
}


BOOL
AUTO_PROXY_DLLS::SelectAutoProxyByMime(
    IN LPSTR lpszMimeType
    )

/*++

Routine Description:

    Sets an internal pointer inside the object to point to a found auto-proxy DLL.
        The MIME type given is used to find the match.

Arguments:

    lpszMimeType - The Mime type to search on.

Return Value:

    BOOL
        TRUE    - success

        FALSE - failure

--*/


{

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "AUTO_PROXY_DLLS::SelectAutoProxyByMime",
                "%s",
                lpszMimeType
                ));

    BOOL found = FALSE;
    AUTO_PROXY_LIST_ENTRY * info = NULL;

    LockAutoProxy();

    LockSerializedList(&_List);

    for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
        entry != (PLIST_ENTRY)SlSelf(&_List);
        entry = entry->Flink)
    {
        info = CONTAINING_RECORD(entry, AUTO_PROXY_LIST_ENTRY, _List);

        if (info->_lpszMimeType && lstrcmpi(lpszMimeType, info->_lpszMimeType) == 0 )
        {
            found = TRUE;
            break;
        }
    }

    UnlockSerializedList(&_List);

    if ( found )
    {
        SelectAutoProxy(info);
    }

    UnlockAutoProxy();

    DEBUG_LEAVE(found);

    return found;

}

BOOL
AUTO_PROXY_DLLS::SelectAutoProxyByDefault(
    VOID
    )
{

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "AUTO_PROXY_DLLS::SelectAutoProxyByDefault",
                NULL
                ));

    BOOL found = FALSE;
    AUTO_PROXY_LIST_ENTRY * info = NULL;

    LockAutoProxy();

    LockSerializedList(&_List);

    for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
        entry != (PLIST_ENTRY)SlSelf(&_List);
        entry = entry->Flink)
    {
        info = CONTAINING_RECORD(entry, AUTO_PROXY_LIST_ENTRY, _List);

        if (info->IsDefault() )
        {
            found = TRUE;
            break;
        }
    }

    UnlockSerializedList(&_List);

    if ( found )
    {
        SelectAutoProxy(info);
    }

    DEBUG_LEAVE(found);

    UnlockAutoProxy();

    return found;
}

BOOL
AUTO_PROXY_DLLS::SelectAutoProxyByFileExtension(
    LPCSTR lpszAutoProxyPath
    )
{

    DEBUG_ENTER((DBG_PROXY,
                Bool,
                "AUTO_PROXY_DLLS::SelectAutoProxyByFileExtension",
                "%s",
                lpszAutoProxyPath
                ));

    BOOL found = FALSE;
    AUTO_PROXY_LIST_ENTRY * info = NULL;

    LockAutoProxy();

    LockSerializedList(&_List);

    for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
        entry != (PLIST_ENTRY)SlSelf(&_List);
        entry = entry->Flink)
    {
        info = CONTAINING_RECORD(entry, AUTO_PROXY_LIST_ENTRY, _List);

        if (info->_lpszFileExtensions &&
                MatchFileExtensionWithUrl(info->_lpszFileExtensions, lpszAutoProxyPath) )
        {
            found = TRUE;
            break;
        }
    }

    UnlockSerializedList(&_List);

    if ( found )
    {
        SelectAutoProxy(info);
    }

    UnlockAutoProxy();

    DEBUG_LEAVE(found);

    return found;
}

DWORD
AUTO_PROXY_DLLS::GetAutoProxyStringEntry(
    IN LPSTR lpszRegName,
    IN OUT LPSTR * lplpszAllocatedRegValue
    )
{
    DWORD dwcbNewValue = 0;
    DWORD error = ERROR_SUCCESS;

    if ( *lplpszAllocatedRegValue )
    {
        FREE_MEMORY(*lplpszAllocatedRegValue);
        *lplpszAllocatedRegValue = NULL;
    }

    error =
        InternetReadRegistryString(
            lpszRegName,
            NULL,
            &dwcbNewValue
            );

    if ( error == ERROR_SUCCESS )
    {
        dwcbNewValue++;
        *lplpszAllocatedRegValue = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, dwcbNewValue);

        if ( *lplpszAllocatedRegValue == NULL )
        {
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto quit;
        }

        error =
            InternetReadRegistryString(
                lpszRegName,
                *lplpszAllocatedRegValue,
                &dwcbNewValue
                );

        if ( error != ERROR_SUCCESS )
        {
            INET_ASSERT((error == ERROR_SUCCESS));
            FREE_MEMORY(*lplpszAllocatedRegValue);
            *lplpszAllocatedRegValue = NULL;
        }
    }

quit:

    return error;
}



DWORD
AUTO_PROXY_DLLS::ReadAutoProxyRegistrySettings(
    VOID
    )

/*++

Routine Description:

    Scans Registry for, and builds linked list of AutoProxy DLLs.

Return Value:

  Success - ERROR_SUCCESS

  Failure -

--*/

{
    HKEY hkSecurity = NULL; // main security key
    DWORD error = ERROR_SUCCESS;
    DWORD dwcbAutoConfigProxy = 0;
    HKEY hkSPMKey = NULL;
    const static CHAR cszMainSecKey[]
        = CSZMAINSECKEY;
    DEBUG_ENTER((DBG_PROXY,
                Dword,
                "AUTO_PROXY_DLLS::ReadAutoProxyRegistrySettings",
                ""
                ));

    LockAutoProxy();

    //
    // QFE 1169:  Use the HKCU value as the user agent string if told to do so.
    //            Yes this looks ugly, but it's just to prevent a whole bunch
    //            of ifs and handlers for failures.

    DWORD dwcbBufLen = 0;
    
    if (ERROR_SUCCESS == InternetReadRegistryDwordKey(
            HKEY_CURRENT_USER,
            "AutoConfigCustomUA",
            &dwcbBufLen) &&
        dwcbBufLen)
    {       
        if (NULL == _hInstUrlmon)
        {
            _hInstUrlmon = LoadLibrary("Urlmon.dll");
        }       
        if (_hInstUrlmon)
        {        
            typedef HRESULT (*PFNOBTAINUA)(DWORD, LPSTR, DWORD*);
            CHAR lpszUserAgent[MAX_PATH];
            DWORD cbSize = MAX_PATH;

            PFNOBTAINUA pfnUA = (PFNOBTAINUA)GetProcAddress(_hInstUrlmon,"ObtainUserAgentString");
            if (pfnUA)
            {
                HRESULT hr = (*pfnUA)(0, lpszUserAgent, &cbSize);
                if(S_OK == hr)
                {
                    if ( _lpszUserAgent && (lstrcmpi(_lpszUserAgent, lpszUserAgent) != 0) )
                    {
                        _lpszUserAgent = (LPSTR)
                            FREE_MEMORY(_lpszUserAgent);
                    }
                    if ( _lpszUserAgent == NULL )
                    {
                        _lpszUserAgent = NewString(lpszUserAgent);
                    }
                }
            }
        }
    }

    if (REGOPENKEY( HKEY_CLASSES_ROOT, cszMainSecKey, &hkSecurity ) != ERROR_SUCCESS)
    {
        error = GetLastError();
        goto quit;
    }


    DWORD dwIndex;
    dwIndex = 0;

    do
    {
        CHAR szMime[256];
#ifdef unix
        CHAR szUnixHackMime[256];
#endif /* unix */
        if ( hkSPMKey != NULL )
        {
            REGCLOSEKEY(hkSPMKey);
            hkSPMKey = NULL;
        }

        //
        // Enumerate a List of MIME types, that we accept for auto-proxy
        //

        if (RegEnumKey( hkSecurity, dwIndex, szMime, sizeof(szMime)) != ERROR_SUCCESS )
        {
            goto quit;
        }

        dwIndex++;

        //
        // Open a potential MIME type entry.
        //

        if (REGOPENKEY( hkSecurity, szMime, &hkSPMKey ) != ERROR_SUCCESS)
        {
            INET_ASSERT(0);
            continue;
        }

        //
        // Grab the DLL file name
        //

        DWORD dwType, cbBuf;

        char szDll[MAX_PATH];
        cbBuf = sizeof(szDll);
        if (ERROR_SUCCESS != RegQueryValueEx
                (hkSPMKey, "DllFile", NULL, &dwType, (LPBYTE) szDll, &cbBuf)
            ||  ((dwType != REG_SZ) && (dwType != REG_EXPAND_SZ)) )
        {
            continue; // no DLL name
        }


        if ( dwType == REG_EXPAND_SZ )
        {
            DWORD dwSize;
            char szDllPathBeforeExpansion[MAX_PATH];

            lstrcpy(szDllPathBeforeExpansion, szDll);

            dwSize = ExpandEnvironmentStrings(szDllPathBeforeExpansion, szDll, ARRAY_ELEMENTS(szDll));

            if (dwSize > ARRAY_ELEMENTS(szDll) || dwSize == 0 )
            {
                INET_ASSERT(FALSE);
                continue;  // not enough room to expand vars?
            }
        }

        //
        // Grab the list of File extensions that are permitted for this Mime Type
        //


        char szFileExtensions[MAX_PATH];
        cbBuf = sizeof(szFileExtensions);
        if (ERROR_SUCCESS != RegQueryValueEx
                (hkSPMKey, "FileExtensions", NULL, &dwType, (LPBYTE) szFileExtensions, &cbBuf)
            || (dwType != REG_SZ))
        {
            continue; // no DLL name
        }


        //
        // Determine whether its the defaut entry.
        //

        DWORD fIsDefault;
        cbBuf = sizeof(fIsDefault);
        if (ERROR_SUCCESS != RegQueryValueEx
                (hkSPMKey, "Default", NULL, &dwType, (LPBYTE) &fIsDefault, &cbBuf)
            || ((dwType != REG_DWORD) && (dwType != REG_BINARY)))
        {
            INET_ASSERT (cbBuf == sizeof(DWORD));
            fIsDefault = 0;
        }

        //
        // Determine whether we have any Flags that need to be read
        //  from the registry.
        //

        DWORD dwFlags;

        cbBuf = sizeof(dwFlags);
        if (ERROR_SUCCESS != RegQueryValueEx
                (hkSPMKey, "Flags", NULL, &dwType, (LPBYTE) &dwFlags, &cbBuf)
            || ((dwType != REG_DWORD) && (dwType != REG_BINARY)))
        {
            dwFlags = 0;
        }


        //
        // Now build an entry for it, and add it to our list.
        //
#ifndef unix
        AUTO_PROXY_LIST_ENTRY *apleAutoProxy
             = new AUTO_PROXY_LIST_ENTRY(szDll, szFileExtensions, szMime, fIsDefault, dwFlags);
#else
        strcpy(szUnixHackMime,"application/");
        strcat(szUnixHackMime,szMime);
        AUTO_PROXY_LIST_ENTRY *apleAutoProxy
             = new AUTO_PROXY_LIST_ENTRY(szDll, szFileExtensions, szUnixHackMime, fIsDefault, dwFlags);
#endif /* unix */
        if (!apleAutoProxy)
        {
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto quit;
        }

        if (fIsDefault)
        {
            InsertAtTailOfSerializedList(&_List, &apleAutoProxy->_List);
        }
        else
        {
            InsertAtHeadOfSerializedList(&_List, &apleAutoProxy->_List);
        }

    } while (1);

    error = ERROR_SUCCESS;

quit:

    if ( hkSPMKey != NULL )
    {
        REGCLOSEKEY(hkSPMKey);
    }

    if ( hkSecurity != NULL )
    {
        REGCLOSEKEY(hkSecurity);
    }

    UnlockAutoProxy();

    DEBUG_LEAVE(error);

    return error;
}

AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG(
    IN INTERNET_SCHEME isUrlScheme,
    IN LPSTR lpszUrl,
    IN LPSTR lpszUrlHostName,
    IN DWORD dwUrlHostNameLength
    )
{
    URL_COMPONENTS urlComponents;

    Initalize();

    if ( lpszUrl )
    {
        _lpszUrl      = lpszUrl;
        _dwUrlLength  = lstrlen(lpszUrl);
        _tUrlProtocol = isUrlScheme;
        _pmProxyQuery = PROXY_MSG_GET_PROXY_INFO;
        _pmaAllocMode = MSG_ALLOC_STACK_ONLY;

        memset(&urlComponents, 0, sizeof(urlComponents));
        urlComponents.dwStructSize = sizeof(urlComponents);
        urlComponents.lpszHostName = lpszUrlHostName;
        urlComponents.dwHostNameLength = dwUrlHostNameLength;

        //
        // parse out the host name and port. The host name will be decoded; the
        // original URL will not be modified
        //

        if (InternetCrackUrl(lpszUrl, 0, ICU_DECODE, &urlComponents))
        {
           _nUrlPort            = urlComponents.nPort;
           _lpszUrlHostName     = urlComponents.lpszHostName;
           _dwUrlHostNameLength = urlComponents.dwHostNameLength;

           if ( _tUrlProtocol == INTERNET_SCHEME_UNKNOWN )
           {
               _tUrlProtocol = urlComponents.nScheme;
           }

        }
        else
        {
            _Error = GetLastError();
        }
    }
    else
    {
        _Error = ERROR_NOT_ENOUGH_MEMORY;
    }
}

AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG(
    IN INTERNET_SCHEME isUrlScheme,
    IN LPSTR lpszUrlHostName,
    IN DWORD dwUrlHostNameLength
    )
{

    Initalize();

    _tUrlProtocol = isUrlScheme;
    _pmProxyQuery = PROXY_MSG_GET_PROXY_INFO;
    _pmaAllocMode = MSG_ALLOC_STACK_ONLY;
    _lpszUrlHostName     = lpszUrlHostName;
    _dwUrlHostNameLength = dwUrlHostNameLength;
}

AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG(
    IN INTERNET_SCHEME isUrlScheme,
    IN LPSTR lpszUrl,
    IN DWORD dwUrlLength,
    IN LPSTR lpszUrlHostName,
    IN DWORD dwUrlHostNameLength,
    IN INTERNET_PORT nUrlPort
    )
{
    Initalize();

    _tUrlProtocol        = isUrlScheme;
    _pmProxyQuery        = PROXY_MSG_GET_PROXY_INFO;
    _pmaAllocMode        = MSG_ALLOC_STACK_ONLY;
    _nUrlPort            = nUrlPort;
    _lpszUrlHostName     = lpszUrlHostName;
    _dwUrlHostNameLength = dwUrlHostNameLength;
    _lpszUrl             = lpszUrl;
    _dwUrlLength         = dwUrlLength;

}

VOID
AUTO_PROXY_ASYNC_MSG::SetProxyMsg(
    IN INTERNET_SCHEME isUrlScheme,
    IN LPSTR lpszUrl,
    IN DWORD dwUrlLength,
    IN LPSTR lpszUrlHostName,
    IN DWORD dwUrlHostNameLength,
    IN INTERNET_PORT nUrlPort
    )
{
    _tUrlProtocol        = isUrlScheme;
    _pmProxyQuery        = PROXY_MSG_GET_PROXY_INFO;
    _pmaAllocMode        = MSG_ALLOC_STACK_ONLY;
    _nUrlPort            = nUrlPort;
    _lpszUrlHostName     = lpszUrlHostName;
    _dwUrlHostNameLength = dwUrlHostNameLength;
    _lpszUrl             = lpszUrl;
    _dwUrlLength         = dwUrlLength;
}


AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG(
    IN PROXY_MESSAGE_TYPE pmProxyQuery
    )
{
    Initalize();

    _pmaAllocMode = MSG_ALLOC_HEAP_MSG_OBJ_OWNS;
    _pmProxyQuery = pmProxyQuery;
}

AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG(
    IN AUTO_PROXY_ASYNC_MSG *pStaticAutoProxy
    )
{
    Initalize();

    _tUrlProtocol          = pStaticAutoProxy->_tUrlProtocol;
    _lpszUrl               = (pStaticAutoProxy->_lpszUrl) ? NewString(pStaticAutoProxy->_lpszUrl) : NULL;
    _dwUrlLength           = pStaticAutoProxy->_dwUrlLength;
    _lpszUrlHostName       =
                (pStaticAutoProxy->_lpszUrlHostName ) ?
                NewString(pStaticAutoProxy->_lpszUrlHostName, pStaticAutoProxy->_dwUrlHostNameLength) :
                NULL;
    _dwUrlHostNameLength   = pStaticAutoProxy->_dwUrlHostNameLength;
    _nUrlPort              = pStaticAutoProxy->_nUrlPort;
    _tProxyScheme          = pStaticAutoProxy->_tProxyScheme;

    //
    // ProxyHostName is something that is generated by the request,
    //   therefore it should not be copied OR freed.
    //

    INET_ASSERT( pStaticAutoProxy->_lpszProxyHostName == NULL );
    //_lpszProxyHostName     = (pStaticAutoProxy->_lpszProxyHostName ) ? NewString(pStaticAutoProxy->_lpszProxyHostName) : NULL;


    _dwProxyHostNameLength = pStaticAutoProxy->_dwProxyHostNameLength;
    _nProxyHostPort        = pStaticAutoProxy->_nProxyHostPort;
    _pmProxyQuery          = pStaticAutoProxy->_pmProxyQuery;
    _pmaAllocMode          = MSG_ALLOC_HEAP_MSG_OBJ_OWNS;
    _pProxyState           = pStaticAutoProxy->_pProxyState;

    INET_ASSERT(_pProxyState == NULL);

    _dwQueryResult         = pStaticAutoProxy->_dwQueryResult;
    _Error                 = pStaticAutoProxy->_Error;
    _MessageFlags.Dword    = pStaticAutoProxy->_MessageFlags.Dword;
    _dwProxyVersion        = pStaticAutoProxy->_dwProxyVersion;
}

AUTO_PROXY_ASYNC_MSG::~AUTO_PROXY_ASYNC_MSG(
    VOID
    )
{
    DEBUG_ENTER((DBG_OBJECTS,
                None,
                "~AUTO_PROXY_ASYNC_MSG",
                NULL
                ));

    if ( IsAlloced() )
    {
        DEBUG_PRINT(OBJECTS,
                    INFO,
                    ("Freeing Allocated MSG ptr=%x\n",
                    this
                    ));


        if ( _lpszUrl )
        {
            //DEBUG_PRINT(OBJECTS,
            //            INFO,
            //            ("Url ptr=%x, %q\n",
            //            _lpszUrl,
            //            _lpszUrl
            //            ));

            FREE_MEMORY(_lpszUrl);
        }

        if ( _lpszUrlHostName )
        {
            FREE_MEMORY(_lpszUrlHostName);
        }


        if ( _pProxyState )
        {
            delete _pProxyState;
        }
    }
    if (_bFreeProxyHostName && (_lpszProxyHostName != NULL)) {
        FREE_MEMORY(_lpszProxyHostName);
    }

    DEBUG_LEAVE(0);
}



//
// functions
//


PRIVATE
BOOL
MatchFileExtensionWithUrl(
    IN LPCSTR lpszFileExtensionList,
    IN LPCSTR lpszUrl
    )

/*++

Routine Description:

    Matches a Url with a string of externsions (looks like ".ins;.jv").
    Confims the Url is using one the approved externsions.

Arguments:

    lpszFileExtensionList - the list of file extensions to check the Url against.

    lpszUrl  - the url to examine.

Return Value:

    BOOL
        TRUE    - success

        FALSE   - the Url does not contain any of the extensions.

--*/


{

    LPCSTR lpszExtension, lpszExtensionOffset;
    LPCSTR lpszQuestion;

    //
    // We need to be careful about checking for a period on the end of an URL
    //   Example: if we have: "http://auto-proxy-srv/fooboo.exe?autogenator.com.ex" ?
    //

    lpszQuestion = strchr( lpszUrl, '?' );

    lpszUrl = ( lpszQuestion ) ? lpszQuestion : lpszUrl;

    lpszExtension = strrchr( lpszUrl, '.' );


    if ( lpszExtension )
    {
        lpszExtensionOffset = lpszExtension;
        BOOL fMatching = FALSE;
        BOOL fCurrentMatch = FALSE;
        BOOL fFirstByte = TRUE;

        while ( *lpszFileExtensionList && *lpszExtensionOffset )
        {

            if ( toupper(*lpszFileExtensionList) == toupper(*lpszExtensionOffset) )
            {
                fCurrentMatch = TRUE;
            }
            else
            {
                fCurrentMatch = FALSE;
            }

            if ( *lpszFileExtensionList == ';')
            {
                lpszExtensionOffset = lpszExtension-1;
                fFirstByte = TRUE;

                if ( fMatching )
                {
                    return TRUE;
                }
            }
            else if ( fFirstByte || fMatching )
            {
                fMatching = fCurrentMatch;
                fFirstByte = FALSE;
            }

            lpszExtensionOffset++;
            lpszFileExtensionList++;

            if ( *lpszExtensionOffset == '\0' )
            {
                lpszExtensionOffset = lpszExtension;
            }
        }

        if ( fMatching )
        {
            return TRUE;
        }

    }

    return FALSE;
}

#ifdef unix
static BOOL fForceAutoProxSync = TRUE;
extern "C"
void unixForceAutoProxSync()
{
    if(fForceAutoProxSync)
    {
       if (!GlobalDataInitialized)
          GlobalDataInitialize();

       if (GlobalDataInitialized)
       {
          GlobalProxyInfo.SetRefreshDisabled(FALSE);
          FixProxySettingsForCurrentConnection(TRUE);
          GlobalProxyInfo.ReleaseQueuedRefresh();
          fForceAutoProxSync = FALSE;
       }
    }
}
#endif /* unix */

