winhttp: Implement WinHttpGetProxyForUrl.

Hans Leidekker hans at codeweavers.com
Fri Nov 18 03:36:04 CST 2011


---
 dlls/winhttp/Makefile.in     |    2 +-
 dlls/winhttp/session.c       |  317 +++++++++++++++++++++++++++++++++++++++++-
 dlls/winhttp/tests/winhttp.c |   89 ++++++++++++
 include/winhttp.h            |    5 +
 4 files changed, 409 insertions(+), 4 deletions(-)

diff --git a/dlls/winhttp/Makefile.in b/dlls/winhttp/Makefile.in
index f89cdab..b330c9c 100644
--- a/dlls/winhttp/Makefile.in
+++ b/dlls/winhttp/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = winhttp.dll
 IMPORTLIB = winhttp
 IMPORTS   = uuid user32 advapi32
-DELAYIMPORTS = oleaut32 crypt32
+DELAYIMPORTS = oleaut32 ole32 crypt32
 EXTRALIBS = @SOCKETLIBS@
 
 C_SRCS = \
diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c
index 42ef85b..f0d9974 100644
--- a/dlls/winhttp/session.c
+++ b/dlls/winhttp/session.c
@@ -28,6 +28,9 @@
 #include "winhttp.h"
 #include "wincrypt.h"
 #include "winreg.h"
+#define COBJMACROS
+#include "ole2.h"
+#include "activscp.h"
 
 #include "winhttp_private.h"
 
@@ -1474,16 +1477,324 @@ done:
     return ret;
 }
 
+static HRESULT WINAPI site_QueryInterface(
+    IActiveScriptSite *iface, REFIID riid, void **ppv )
+{
+    *ppv = NULL;
+
+    if (IsEqualGUID( &IID_IUnknown, riid ))
+        *ppv = iface;
+    else if (IsEqualGUID( &IID_IActiveScriptSite, riid ))
+        *ppv = iface;
+    else
+        return E_NOINTERFACE;
+
+    IUnknown_AddRef( (IUnknown *)*ppv );
+    return S_OK;
+}
+
+static ULONG WINAPI site_AddRef(
+    IActiveScriptSite *iface )
+{
+    return 2;
+}
+
+static ULONG WINAPI site_Release(
+    IActiveScriptSite *iface )
+{
+    return 1;
+}
+
+static HRESULT WINAPI site_GetLCID(
+    IActiveScriptSite *iface, LCID *lcid )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_GetItemInfo(
+    IActiveScriptSite *iface, LPCOLESTR name, DWORD mask,
+    IUnknown **item, ITypeInfo **type_info )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_GetDocVersionString(
+    IActiveScriptSite *iface, BSTR *version )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_OnScriptTerminate(
+    IActiveScriptSite *iface, const VARIANT *result, const EXCEPINFO *info )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_OnStateChange(
+    IActiveScriptSite *iface, SCRIPTSTATE state )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_OnScriptError(
+    IActiveScriptSite *iface, IActiveScriptError *error )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_OnEnterScript(
+    IActiveScriptSite *iface )
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI site_OnLeaveScript(
+    IActiveScriptSite *iface )
+{
+    return E_NOTIMPL;
+}
+
+static const IActiveScriptSiteVtbl site_vtbl =
+{
+    site_QueryInterface,
+    site_AddRef,
+    site_Release,
+    site_GetLCID,
+    site_GetItemInfo,
+    site_GetDocVersionString,
+    site_OnScriptTerminate,
+    site_OnStateChange,
+    site_OnScriptError,
+    site_OnEnterScript,
+    site_OnLeaveScript
+};
+
+static IActiveScriptSite script_site = { &site_vtbl };
+
+static BOOL parse_script_result( VARIANT result, WINHTTP_PROXY_INFO *info )
+{
+    static const WCHAR proxyW[] = {'P','R','O','X','Y'};
+    const WCHAR *p;
+    WCHAR *q;
+    int len;
+
+    info->dwAccessType    = WINHTTP_ACCESS_TYPE_NO_PROXY;
+    info->lpszProxy       = NULL;
+    info->lpszProxyBypass = NULL;
+
+    if (V_VT( &result ) != VT_BSTR) return TRUE;
+    TRACE("%s\n", debugstr_w( V_BSTR( &result ) ));
+
+    p = V_BSTR( &result );
+    while (*p == ' ') p++;
+    len = strlenW( p );
+    if (len >= 5 && !memicmpW( p, proxyW, sizeof(proxyW)/sizeof(WCHAR) ))
+    {
+        p += 5;
+        while (*p == ' ') p++;
+        if (!*p || *p == ';') return TRUE;
+        if (!(info->lpszProxy = q = strdupW( p ))) return FALSE;
+        info->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+        for (; *q; q++)
+        {
+            if (*q == ' ' || *q == ';')
+            {
+                *q = 0;
+                break;
+            }
+        }
+    }
+    return TRUE;
+}
+
+static BOOL run_script( const BSTR script, const WCHAR *url, WINHTTP_PROXY_INFO *info )
+{
+    static const WCHAR jscriptW[] = {'J','S','c','r','i','p','t',0};
+    static const WCHAR findproxyW[] = {'F','i','n','d','P','r','o','x','y','F','o','r','U','R','L',0};
+    IActiveScriptParse *parser = NULL;
+    IActiveScript *engine = NULL;
+    IDispatch *dispatch = NULL;
+    BOOL ret = FALSE;
+    CLSID clsid;
+    DISPID dispid;
+    BSTR func = NULL, hostname = NULL;
+    URL_COMPONENTSW uc;
+    VARIANT args[2], result;
+    DISPPARAMS params;
+    HRESULT hr, init;
+
+    memset( &uc, 0, sizeof(uc) );
+    uc.dwStructSize = sizeof(uc);
+    if (!WinHttpCrackUrl( url, 0, 0, &uc )) return FALSE;
+    if (!(hostname = SysAllocStringLen( NULL, uc.dwHostNameLength + 1 ))) return FALSE;
+    memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
+    hostname[uc.dwHostNameLength] = 0;
+
+    init = CoInitialize( NULL );
+    CLSIDFromProgID( jscriptW, &clsid );
+    hr = CoCreateInstance( &clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
+                           &IID_IActiveScript, (void **)&engine );
+    if (hr != S_OK) goto done;
+
+    hr = IActiveScript_QueryInterface( engine, &IID_IActiveScriptParse, (void **)&parser );
+    if (hr != S_OK) goto done;
+
+    hr = IActiveScriptParse64_InitNew( parser );
+    if (hr != S_OK) goto done;
+
+    hr = IActiveScript_SetScriptSite( engine, &script_site );
+    if (hr != S_OK) goto done;
+
+    /* FIXME: make standard functions available to script */
+
+    hr = IActiveScriptParse64_ParseScriptText( parser, script, NULL, NULL, NULL, 0, 0, 0, NULL, NULL );
+    if (hr != S_OK) goto done;
+
+    hr = IActiveScript_SetScriptState( engine, SCRIPTSTATE_STARTED );
+    if (hr != S_OK) goto done;
+
+    hr = IActiveScript_GetScriptDispatch( engine, NULL, &dispatch );
+    if (hr != S_OK) goto done;
+
+    if (!(func = SysAllocString( findproxyW ))) goto done;
+    hr = IDispatch_GetIDsOfNames( dispatch, &IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid );
+    if (hr != S_OK) goto done;
+
+    V_VT( &args[0] ) = VT_BSTR;
+    V_BSTR( &args[0] ) = SysAllocString( url );
+    V_VT( &args[1] ) = VT_BSTR;
+    V_BSTR( &args[1] ) = hostname;
+
+    params.rgvarg = args;
+    params.rgdispidNamedArgs = NULL;
+    params.cArgs = 2;
+    params.cNamedArgs = 0;
+    hr = IDispatch_Invoke( dispatch, dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD,
+                           &params, &result, NULL, NULL );
+    VariantClear( &args[0] );
+    VariantClear( &args[1] );
+    if (hr != S_OK) goto done;
+
+    ret = parse_script_result( result, info );
+
+done:
+    SysFreeString( func );
+    if (dispatch) IDispatch_Release( dispatch );
+    if (parser) IUnknown_Release( parser );
+    if (engine) IActiveScript_Release( engine );
+    if (SUCCEEDED( init )) CoUninitialize();
+    if (!ret) set_last_error( ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT );
+    return ret;
+}
+
+static BSTR download_script( HINTERNET ses, const WCHAR *url )
+{
+    static const WCHAR typeW[] = {'*','/','*',0};
+    static const WCHAR *acceptW[] = {typeW, NULL};
+    HINTERNET con, req = NULL;
+    WCHAR *hostname;
+    URL_COMPONENTSW uc;
+    DWORD size = 4096, offset, to_read, bytes_read, flags = 0;
+    char *tmp, *buffer = NULL;
+    BSTR script = NULL;
+    int len;
+
+    memset( &uc, 0, sizeof(uc) );
+    uc.dwStructSize = sizeof(uc);
+    if (!WinHttpCrackUrl( url, 0, 0, &uc )) return NULL;
+    if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) return NULL;
+    memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
+    hostname[uc.dwHostNameLength] = 0;
+
+    if (!(con = WinHttpConnect( ses, hostname, uc.nPort, 0 ))) goto done;
+    if (uc.nScheme == INTERNET_SCHEME_HTTPS) flags |= WINHTTP_FLAG_SECURE;
+    if (!(req = WinHttpOpenRequest( con, NULL, uc.lpszUrlPath, NULL, NULL, acceptW, flags ))) goto done;
+    if (!WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 )) goto done;
+    if (!(WinHttpReceiveResponse( req, 0 ))) goto done;
+
+    if (!(buffer = heap_alloc( size ))) goto done;
+    to_read = size;
+    offset = 0;
+    for (;;)
+    {
+        if (!WinHttpReadData( req, buffer + offset, to_read, &bytes_read )) goto done;
+        if (!bytes_read) break;
+        to_read -= bytes_read;
+        offset += bytes_read;
+        if (!to_read)
+        {
+            to_read = size;
+            size *= 2;
+            if (!(tmp = heap_realloc( buffer, size ))) goto done;
+            buffer = tmp;
+        }
+    }
+    len = MultiByteToWideChar( CP_ACP, 0, buffer, offset, NULL, 0 );
+    if (!(script = SysAllocStringLen( NULL, len ))) goto done;
+    MultiByteToWideChar( CP_ACP, 0, buffer, offset, script, len );
+    script[len] = 0;
+
+done:
+    WinHttpCloseHandle( req );
+    WinHttpCloseHandle( con );
+    heap_free( buffer );
+    heap_free( hostname );
+    if (!script) set_last_error( ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT );
+    return script;
+}
+
 /***********************************************************************
  *          WinHttpGetProxyForUrl (winhttp.@)
  */
 BOOL WINAPI WinHttpGetProxyForUrl( HINTERNET hsession, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options,
                                    WINHTTP_PROXY_INFO *info )
 {
-    FIXME("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info);
+    WCHAR *detected_pac_url = NULL;
+    const WCHAR *pac_url;
+    session_t *session;
+    BSTR script;
+    BOOL ret = FALSE;
 
-    set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR );
-    return FALSE;
+    TRACE("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info);
+
+    if (!(session = (session_t *)grab_object( hsession )))
+    {
+        set_last_error( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+    if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION)
+    {
+        release_object( &session->hdr );
+        set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
+        return FALSE;
+    }
+    if (!url || !options || !info ||
+        !(options->dwFlags & (WINHTTP_AUTOPROXY_AUTO_DETECT|WINHTTP_AUTOPROXY_CONFIG_URL)) ||
+        ((options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) && !options->dwAutoDetectFlags) ||
+        ((options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) &&
+         (options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL)))
+    {
+        release_object( &session->hdr );
+        set_last_error( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+    if (options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT &&
+        !WinHttpDetectAutoProxyConfigUrl( options->dwAutoDetectFlags, &detected_pac_url ))
+    {
+        set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR );
+        goto done;
+    }
+    if (options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL) pac_url = options->lpszAutoConfigUrl;
+    else pac_url = detected_pac_url;
+
+    if (!(script = download_script( hsession, pac_url ))) goto done;
+    ret = run_script( script, url, info );
+    SysFreeString( script );
+
+done:
+    GlobalFree( detected_pac_url );
+    release_object( &session->hdr );
+    return ret;
 }
 
 /***********************************************************************
diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c
index 5e4892c..0155539 100644
--- a/dlls/winhttp/tests/winhttp.c
+++ b/dlls/winhttp/tests/winhttp.c
@@ -2581,6 +2581,94 @@ static void test_WinHttpGetIEProxyConfigForCurrentUser(void)
     GlobalFree( cfg.lpszProxyBypass );
 }
 
+static void test_WinHttpGetProxyForUrl(void)
+{
+    static const WCHAR urlW[] = {'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0};
+    static const WCHAR emptyW[] = {0};
+    BOOL ret;
+    DWORD error;
+    HINTERNET session;
+    WINHTTP_AUTOPROXY_OPTIONS options;
+    WINHTTP_PROXY_INFO info;
+
+    memset( &options, 0, sizeof(options) );
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( NULL, NULL, NULL, NULL );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_HANDLE, "got %u\n", error );
+
+    session = WinHttpOpen( test_useragent, 0, NULL, NULL, 0 );
+    ok( session != NULL, "failed to open session %u\n", GetLastError() );
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, NULL, NULL, NULL );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, emptyW, NULL, NULL );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, urlW, NULL, NULL );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, urlW, &options, &info );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+    options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, urlW, &options, NULL );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+    options.dwAutoDetectFlags = 0;
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, urlW, &options, &info );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_CONFIG_URL;
+    options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+
+    SetLastError(0xdeadbeef);
+    ret = WinHttpGetProxyForUrl( session, urlW, &options, &info );
+    error = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error );
+
+    options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+    options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+
+    memset( &info, 0, sizeof(info) );
+    ret = WinHttpGetProxyForUrl( session, urlW, &options, &info );
+    if (ret)
+    {
+        trace("%u\n", info.dwAccessType);
+        trace("%s\n", wine_dbgstr_w(info.lpszProxy));
+        trace("%s\n", wine_dbgstr_w(info.lpszProxyBypass));
+        GlobalFree( (WCHAR *)info.lpszProxy );
+        GlobalFree( (WCHAR *)info.lpszProxyBypass );
+    }
+    WinHttpCloseHandle( session );
+}
+
 START_TEST (winhttp)
 {
     static const WCHAR basicW[] = {'/','b','a','s','i','c',0};
@@ -2605,6 +2693,7 @@ START_TEST (winhttp)
     test_IWinHttpRequest();
     test_WinHttpDetectAutoProxyConfigUrl();
     test_WinHttpGetIEProxyConfigForCurrentUser();
+    test_WinHttpGetProxyForUrl();
 
     si.event = CreateEvent(NULL, 0, 0, NULL);
     si.port = 7532;
diff --git a/include/winhttp.h b/include/winhttp.h
index 36f8b9d..077c277 100644
--- a/include/winhttp.h
+++ b/include/winhttp.h
@@ -506,6 +506,11 @@ typedef VOID (CALLBACK *WINHTTP_STATUS_CALLBACK)(HINTERNET,DWORD_PTR,DWORD,LPVOI
 #define WINHTTP_AUTO_DETECT_TYPE_DHCP   0x00000001
 #define WINHTTP_AUTO_DETECT_TYPE_DNS_A  0x00000002
 
+#define WINHTTP_AUTOPROXY_AUTO_DETECT           0x00000001
+#define WINHTTP_AUTOPROXY_CONFIG_URL            0x00000002
+#define WINHTTP_AUTOPROXY_RUN_INPROCESS         0x00010000
+#define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY   0x00020000
+
 typedef struct
 {
     DWORD dwFlags;
-- 
1.7.5.4






More information about the wine-patches mailing list