From 768300c8aa2f977d14910610b8646582f9b376c9 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Fri, 18 Nov 2011 10:36:04 +0100 Subject: [PATCH] winhttp: Implement WinHttpGetProxyForUrl. --- 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 f89cdab6fd6..b330c9c9f90 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 42ef85bf57e..f0d9974e484 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, + ¶ms, &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 5e4892c2425..01555394a25 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 36f8b9df5ef..077c2772c7f 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;