From ae0056953a4926121b2f063e846fc627b6fd691c Mon Sep 17 00:00:00 2001 From: Thomas Faber Date: Sat, 14 Jul 2012 16:13:51 +0000 Subject: [PATCH] =?UTF-8?q?[WINHTTP]=20-=20Sync=20to=20Wine=201.5.4=20-=20?= =?UTF-8?q?F=C3=AFx=20warnings=20See=20issue=20#7186=20for=20more=20detail?= =?UTF-8?q?s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit svn path=/trunk/; revision=56891 --- reactos/dll/win32/winhttp/CMakeLists.txt | 22 +- reactos/dll/win32/winhttp/handle.c | 2 +- reactos/dll/win32/winhttp/main.c | 114 +- reactos/dll/win32/winhttp/net.c | 40 +- reactos/dll/win32/winhttp/pac.js | 244 ++++ reactos/dll/win32/winhttp/request.c | 1378 ++++++++++++++++++- reactos/dll/win32/winhttp/rsrc.rc | 35 + reactos/dll/win32/winhttp/session.c | 810 ++++++++++- reactos/dll/win32/winhttp/url.c | 194 +-- reactos/dll/win32/winhttp/winhttp_private.h | 38 + reactos/dll/win32/winhttp/winhttp_ros.diff | 97 +- reactos/dll/win32/winhttp/winhttp_tlb.idl | 21 + reactos/dll/win32/winhttp/winhttp_tlb.rgs | 37 + reactos/media/doc/README.WINE | 2 +- 14 files changed, 2818 insertions(+), 216 deletions(-) create mode 100644 reactos/dll/win32/winhttp/pac.js create mode 100644 reactos/dll/win32/winhttp/rsrc.rc create mode 100644 reactos/dll/win32/winhttp/winhttp_tlb.idl create mode 100644 reactos/dll/win32/winhttp/winhttp_tlb.rgs diff --git a/reactos/dll/win32/winhttp/CMakeLists.txt b/reactos/dll/win32/winhttp/CMakeLists.txt index 7d1b7db0742..82e19768f2e 100644 --- a/reactos/dll/win32/winhttp/CMakeLists.txt +++ b/reactos/dll/win32/winhttp/CMakeLists.txt @@ -1,5 +1,9 @@ -add_definitions(-D__WINESRC__) +add_typelib(winhttp_tlb.idl) + +add_definitions( + -D__WINESRC__ + -D_WINE) include_directories(${REACTOS_SOURCE_DIR}/include/reactos/wine) spec2def(winhttp.dll winhttp.spec ADD_IMPORTLIB) @@ -12,17 +16,19 @@ list(APPEND SOURCE request.c session.c url.c + rsrc.rc ${CMAKE_CURRENT_BINARY_DIR}/winhttp.def) +set_source_files_properties(rsrc.rc PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/winhttp_tlb.tlb) + add_library(winhttp SHARED ${SOURCE}) set_module_type(winhttp win32dll) -target_link_libraries(winhttp wine) -add_delay_importlibs(winhttp crypt32) -add_importlibs(winhttp shlwapi wininet ws2_32 msvcrt advapi32 kernel32 ntdll) +target_link_libraries(winhttp uuid wine) +add_delay_importlibs(winhttp oleaut32 ole32 crypt32) +add_importlibs(winhttp user32 advapi32 ws2_32 msvcrt kernel32 ntdll) + +# wininet_tlb.tlb needs stdole2.tlb +add_dependencies(winhttp stdole2) add_cd_file(TARGET winhttp DESTINATION reactos/system32 FOR all) - -if(NOT MSVC) - allow_warnings(winhttp) -endif() diff --git a/reactos/dll/win32/winhttp/handle.c b/reactos/dll/win32/winhttp/handle.c index 70107275b9c..7a346a85126 100644 --- a/reactos/dll/win32/winhttp/handle.c +++ b/reactos/dll/win32/winhttp/handle.c @@ -103,7 +103,7 @@ HINTERNET alloc_handle( object_header_t *hdr ) } if (max_handles == next_handle) { - num = max_handles + HANDLE_CHUNK_SIZE; + num = max_handles * 2; if (!(p = heap_realloc_zero( handles, sizeof(ULONG_PTR) * num ))) goto end; handles = p; max_handles = num; diff --git a/reactos/dll/win32/winhttp/main.c b/reactos/dll/win32/winhttp/main.c index b1ce75df4b4..5348d4d4677 100644 --- a/reactos/dll/win32/winhttp/main.c +++ b/reactos/dll/win32/winhttp/main.c @@ -16,19 +16,22 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS #include "config.h" - #include #include "windef.h" #include "winbase.h" #include "objbase.h" +#include "rpcproxy.h" +#include "httprequest.h" #include "winhttp.h" #include "wine/debug.h" - #include "winhttp_private.h" +static HINSTANCE instance; + WINE_DEFAULT_DEBUG_CHANNEL(winhttp); /****************************************************************** @@ -39,6 +42,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) switch(fdwReason) { case DLL_PROCESS_ATTACH: + instance = hInstDLL; DisableThreadLibraryCalls(hInstDLL); break; case DLL_PROCESS_DETACH: @@ -48,13 +52,109 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) return TRUE; } +typedef HRESULT (*fnCreateInstance)( IUnknown *outer, void **obj ); + +struct winhttp_cf +{ + IClassFactory IClassFactory_iface; + fnCreateInstance pfnCreateInstance; +}; + +static inline struct winhttp_cf *impl_from_IClassFactory( IClassFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct winhttp_cf, IClassFactory_iface ); +} + +static HRESULT WINAPI requestcf_QueryInterface( + IClassFactory *iface, + REFIID riid, + void **obj ) +{ + if (IsEqualGUID( riid, &IID_IUnknown ) || + IsEqualGUID( riid, &IID_IClassFactory )) + { + IClassFactory_AddRef( iface ); + *obj = iface; + return S_OK; + } + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI requestcf_AddRef( + IClassFactory *iface ) +{ + return 2; +} + +static ULONG WINAPI requestcf_Release( + IClassFactory *iface ) +{ + return 1; +} + +static HRESULT WINAPI requestcf_CreateInstance( + IClassFactory *iface, + LPUNKNOWN outer, + REFIID riid, + void **obj ) +{ + struct winhttp_cf *cf = impl_from_IClassFactory( iface ); + IUnknown *unknown; + HRESULT hr; + + TRACE("%p, %s, %p\n", outer, debugstr_guid(riid), obj); + + *obj = NULL; + if (outer) + return CLASS_E_NOAGGREGATION; + + hr = cf->pfnCreateInstance( outer, (void **)&unknown ); + if (FAILED(hr)) + return hr; + + hr = IUnknown_QueryInterface( unknown, riid, obj ); + if (FAILED(hr)) + return hr; + + IUnknown_Release( unknown ); + return hr; +} + +static HRESULT WINAPI requestcf_LockServer( + IClassFactory *iface, + BOOL dolock ) +{ + FIXME("%p, %d\n", iface, dolock); + return S_OK; +} + +static const struct IClassFactoryVtbl winhttp_cf_vtbl = +{ + requestcf_QueryInterface, + requestcf_AddRef, + requestcf_Release, + requestcf_CreateInstance, + requestcf_LockServer +}; + +static struct winhttp_cf request_cf = { { &winhttp_cf_vtbl }, WinHttpRequest_create }; + /****************************************************************** * DllGetClassObject (winhttp.@) */ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) { - FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); - return CLASS_E_CLASSNOTAVAILABLE; + IClassFactory *cf = NULL; + + TRACE("%s, %s, %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + if (IsEqualGUID( rclsid, &CLSID_WinHttpRequest )) + { + cf = &request_cf.IClassFactory_iface; + } + if (!cf) return CLASS_E_CLASSNOTAVAILABLE; + return IClassFactory_QueryInterface( cf, riid, ppv ); } /****************************************************************** @@ -70,8 +170,7 @@ HRESULT WINAPI DllCanUnloadNow(void) */ HRESULT WINAPI DllRegisterServer(void) { - FIXME("()\n"); - return S_OK; + return __wine_register_resources( instance ); } /*********************************************************************** @@ -79,6 +178,5 @@ HRESULT WINAPI DllRegisterServer(void) */ HRESULT WINAPI DllUnregisterServer(void) { - FIXME("()\n"); - return S_OK; + return __wine_unregister_resources( instance ); } diff --git a/reactos/dll/win32/winhttp/net.c b/reactos/dll/win32/winhttp/net.c index 2f7b35dfa49..4bac5791f64 100644 --- a/reactos/dll/win32/winhttp/net.c +++ b/reactos/dll/win32/winhttp/net.c @@ -116,6 +116,7 @@ MAKE_FUNCPTR( SSL_connect ); MAKE_FUNCPTR( SSL_shutdown ); MAKE_FUNCPTR( SSL_write ); MAKE_FUNCPTR( SSL_read ); +MAKE_FUNCPTR( SSL_pending ); MAKE_FUNCPTR( SSL_get_error ); MAKE_FUNCPTR( SSL_get_ex_new_index ); MAKE_FUNCPTR( SSL_get_ex_data ); @@ -159,7 +160,7 @@ static void ssl_lock_callback(int mode, int type, const char *file, int line) #endif /* translate a unix error code into a winsock error code */ -#if 0 +#ifndef __REACTOS__ static int sock_get_error( int err ) { #if !defined(__MINGW32__) && !defined (_MSC_VER) @@ -227,6 +228,12 @@ static int sock_get_error( int err ) } #else #define sock_get_error(x) WSAGetLastError() + +static inline int unix_ioctl(int filedes, long request, void *arg) +{ + return ioctlsocket(filedes, request, arg); +} +#define ioctlsocket unix_ioctl #endif #ifdef SONAME_LIBSSL @@ -463,6 +470,7 @@ BOOL netconn_init( netconn_t *conn, BOOL secure ) LOAD_FUNCPTR( SSL_shutdown ); LOAD_FUNCPTR( SSL_write ); LOAD_FUNCPTR( SSL_read ); + LOAD_FUNCPTR( SSL_pending ); LOAD_FUNCPTR( SSL_get_error ); LOAD_FUNCPTR( SSL_get_ex_new_index ); LOAD_FUNCPTR( SSL_get_ex_data ); @@ -536,14 +544,18 @@ BOOL netconn_init( netconn_t *conn, BOOL secure ) pCRYPTO_set_id_callback(ssl_thread_id); num_ssl_locks = pCRYPTO_num_locks(); - ssl_locks = HeapAlloc(GetProcessHeap(), 0, num_ssl_locks * sizeof(CRITICAL_SECTION)); + ssl_locks = heap_alloc(num_ssl_locks * sizeof(CRITICAL_SECTION)); if (!ssl_locks) { set_last_error( ERROR_OUTOFMEMORY ); LeaveCriticalSection( &init_ssl_cs ); return FALSE; } - for (i = 0; i < num_ssl_locks; i++) InitializeCriticalSection( &ssl_locks[i] ); + for (i = 0; i < num_ssl_locks; i++) + { + InitializeCriticalSection( &ssl_locks[i] ); + ssl_locks[i].DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ssl_locks"); + } pCRYPTO_set_locking_callback(ssl_lock_callback); LeaveCriticalSection( &init_ssl_cs ); @@ -572,9 +584,17 @@ void netconn_unload( void ) if (ssl_locks) { int i; - for (i = 0; i < num_ssl_locks; i++) DeleteCriticalSection( &ssl_locks[i] ); - HeapFree( GetProcessHeap(), 0, ssl_locks ); + for (i = 0; i < num_ssl_locks; i++) + { + ssl_locks[i].DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &ssl_locks[i] ); + } + heap_free( ssl_locks ); } + DeleteCriticalSection(&init_ssl_cs); +#endif +#ifndef HAVE_GETADDRINFO + DeleteCriticalSection(&cs_gethostbyname); #endif } @@ -638,6 +658,7 @@ BOOL netconn_connect( netconn_t *conn, const struct sockaddr *sockaddr, unsigned res = sock_get_error( errno ); if (res == WSAEWOULDBLOCK || res == WSAEINPROGRESS) { + // ReactOS: use select instead of poll fd_set outfd; struct timeval tv; @@ -744,8 +765,6 @@ BOOL netconn_send( netconn_t *conn, const void *msg, size_t len, int flags, int BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd ) { - int ret; - *recvd = 0; if (!netconn_connected( conn )) return FALSE; if (!len) return TRUE; @@ -753,6 +772,8 @@ BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd if (conn->secure) { #ifdef SONAME_LIBSSL + int ret; + if (flags & ~(MSG_PEEK | MSG_WAITALL)) FIXME("SSL_read does not support the following flags: %08x\n", flags); @@ -806,7 +827,7 @@ BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd } else memcpy( conn->peek_msg, buf, ret ); } - *recvd = ret; + *recvd += ret; return TRUE; #else return FALSE; @@ -831,7 +852,7 @@ BOOL netconn_query_data_available( netconn_t *conn, DWORD *available ) if (conn->secure) { #ifdef SONAME_LIBSSL - if (conn->peek_msg) *available = conn->peek_len; + *available = pSSL_pending( conn->ssl_conn ) + conn->peek_len; #endif return TRUE; } @@ -843,6 +864,7 @@ BOOL netconn_query_data_available( netconn_t *conn, DWORD *available ) BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen ) { + // ReactOS: use select instead of poll fd_set infd; BOOL ret = FALSE; DWORD recvd = 0; diff --git a/reactos/dll/win32/winhttp/pac.js b/reactos/dll/win32/winhttp/pac.js new file mode 100644 index 00000000000..da644bf7823 --- /dev/null +++ b/reactos/dll/win32/winhttp/pac.js @@ -0,0 +1,244 @@ +/* + * Copyright 2011 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Based on nsProxyAutoConfig.js from mozilla.org. + */ + +function myIpAddress() { + try { + return dns_resolve(''); + } catch (e) { + return '127.0.0.1'; + } +} + +function dnsResolve(host) { + try { + return dns_resolve(host); + } catch (e) { + return null; + } +} + +function dnsDomainIs(host, domain) { + return (host.length >= domain.length && + host.substring(host.length - domain.length) == domain); +} + +function dnsDomainLevels(host) { + return host.split('.').length-1; +} + +function convert_addr(ipchars) { + var bytes = ipchars.split('.'); + var result = ((bytes[0] & 0xff) << 24) | + ((bytes[1] & 0xff) << 16) | + ((bytes[2] & 0xff) << 8) | + (bytes[3] & 0xff); + return result; +} + +function isInNet(ipaddr, pattern, maskstr) { + var test = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(ipaddr); + if (test == null) { + ipaddr = dnsResolve(ipaddr); + if (ipaddr == null) + return false; + } else if (test[1] > 255 || test[2] > 255 || + test[3] > 255 || test[4] > 255) { + return false; // not an IP address + } + var host = convert_addr(ipaddr); + var pat = convert_addr(pattern); + var mask = convert_addr(maskstr); + return ((host & mask) == (pat & mask)); +} + +function isPlainHostName(host) { + return (host.search('\\.') == -1); +} + +function isResolvable(host) { + var ip = dnsResolve(host); + return (ip != null); +} + +function localHostOrDomainIs(host, hostdom) { + return (host == hostdom) || + (hostdom.lastIndexOf(host + '.', 0) == 0); +} + +function shExpMatch(url, pattern) { + pattern = pattern.replace(/\./g, '\\.'); + pattern = pattern.replace(/\*/g, '.*'); + pattern = pattern.replace(/\?/g, '.'); + var newRe = new RegExp('^'+pattern+'$'); + return newRe.test(url); +} + +var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6}; +var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11}; + +function weekdayRange() { + function getDay(weekday) { + if (weekday in wdays) { + return wdays[weekday]; + } + return -1; + } + var date = new Date(); + var argc = arguments.length; + var wday; + if (argc < 1) + return false; + if (arguments[argc - 1] == 'GMT') { + argc--; + wday = date.getUTCDay(); + } else { + wday = date.getDay(); + } + var wd1 = getDay(arguments[0]); + var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1; + return (wd1 == -1 || wd2 == -1) ? false + : (wd1 <= wday && wday <= wd2); +} + +function dateRange() { + function getMonth(name) { + if (name in months) { + return months[name]; + } + return -1; + } + var date = new Date(); + var argc = arguments.length; + if (argc < 1) { + return false; + } + var isGMT = (arguments[argc - 1] == 'GMT'); + + if (isGMT) { + argc--; + } + // function will work even without explict handling of this case + if (argc == 1) { + var tmp = parseInt(arguments[0]); + if (isNaN(tmp)) { + return ((isGMT ? date.getUTCMonth() : date.getMonth()) == getMonth(arguments[0])); + } else if (tmp < 32) { + return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp); + } else { + return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) == tmp); + } + } + var year = date.getFullYear(); + var date1, date2; + date1 = new Date(year, 0, 1, 0, 0, 0); + date2 = new Date(year, 11, 31, 23, 59, 59); + var adjustMonth = false; + for (var i = 0; i < (argc >> 1); i++) { + var tmp = parseInt(arguments[i]); + if (isNaN(tmp)) { + var mon = getMonth(arguments[i]); + date1.setMonth(mon); + } else if (tmp < 32) { + adjustMonth = (argc <= 2); + date1.setDate(tmp); + } else { + date1.setFullYear(tmp); + } + } + for (var i = (argc >> 1); i < argc; i++) { + var tmp = parseInt(arguments[i]); + if (isNaN(tmp)) { + var mon = getMonth(arguments[i]); + date2.setMonth(mon); + } else if (tmp < 32) { + date2.setDate(tmp); + } else { + date2.setFullYear(tmp); + } + } + if (adjustMonth) { + date1.setMonth(date.getMonth()); + date2.setMonth(date.getMonth()); + } + if (isGMT) { + var tmp = date; + tmp.setFullYear(date.getUTCFullYear()); + tmp.setMonth(date.getUTCMonth()); + tmp.setDate(date.getUTCDate()); + tmp.setHours(date.getUTCHours()); + tmp.setMinutes(date.getUTCMinutes()); + tmp.setSeconds(date.getUTCSeconds()); + date = tmp; + } + return ((date1 <= date) && (date <= date2)); +} + +function timeRange() { + var argc = arguments.length; + var date = new Date(); + var isGMT= false; + + if (argc < 1) { + return false; + } + if (arguments[argc - 1] == 'GMT') { + isGMT = true; + argc--; + } + + var hour = isGMT ? date.getUTCHours() : date.getHours(); + var date1, date2; + date1 = new Date(); + date2 = new Date(); + + if (argc == 1) { + return (hour == arguments[0]); + } else if (argc == 2) { + return ((arguments[0] <= hour) && (hour <= arguments[1])); + } else { + switch (argc) { + case 6: + date1.setSeconds(arguments[2]); + date2.setSeconds(arguments[5]); + case 4: + var middle = argc >> 1; + date1.setHours(arguments[0]); + date1.setMinutes(arguments[1]); + date2.setHours(arguments[middle]); + date2.setMinutes(arguments[middle + 1]); + if (middle == 2) { + date2.setSeconds(59); + } + break; + default: + throw 'timeRange: bad number of arguments' + } + } + + if (isGMT) { + date.setFullYear(date.getUTCFullYear()); + date.setMonth(date.getUTCMonth()); + date.setDate(date.getUTCDate()); + date.setHours(date.getUTCHours()); + date.setMinutes(date.getUTCMinutes()); + date.setSeconds(date.getUTCSeconds()); + } + return ((date1 <= date) && (date <= date2)); +} diff --git a/reactos/dll/win32/winhttp/request.c b/reactos/dll/win32/winhttp/request.c index 4bd411c097f..88682e75d3e 100644 --- a/reactos/dll/win32/winhttp/request.c +++ b/reactos/dll/win32/winhttp/request.c @@ -1,7 +1,7 @@ /* * Copyright 2004 Mike McCormack for CodeWeavers * Copyright 2006 Rob Shearman for CodeWeavers - * Copyright 2008 Hans Leidekker for CodeWeavers + * Copyright 2008, 2011 Hans Leidekker for CodeWeavers * Copyright 2009 Juan Lang * * This library is free software; you can redistribute it and/or @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS #include "config.h" #include "wine/port.h" #include "wine/debug.h" @@ -30,6 +31,9 @@ #include "windef.h" #include "winbase.h" +#include "ole2.h" +#include "initguid.h" +#include "httprequest.h" #include "winhttp.h" #include "winhttp_private.h" @@ -423,6 +427,11 @@ BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD q = p; while (*q) { + if (q[0] == '\n' && q[1] == '\r') + { + q[0] = '\r'; + q[1] = '\n'; + } if (q[0] == '\r' && q[1] == '\n') break; q++; } @@ -907,53 +916,68 @@ static BOOL secure_proxy_connect( request_t *request ) #define INET6_ADDRSTRLEN 46 #endif +static WCHAR *addr_to_str( const struct sockaddr *addr ) +{ + char buf[INET6_ADDRSTRLEN]; + const void *src; + + switch (addr->sa_family) + { + case AF_INET: + src = &((struct sockaddr_in *)addr)->sin_addr; + break; + case AF_INET6: + src = &((struct sockaddr_in6 *)addr)->sin6_addr; + break; + default: + WARN("unsupported address family %d\n", addr->sa_family); + return NULL; + } + if (!inet_ntop( addr->sa_family, src, buf, sizeof(buf) )) return NULL; + return strdupAW( buf ); +} + static BOOL open_connection( request_t *request ) { connect_t *connect; - const void *addr; - char address[INET6_ADDRSTRLEN]; - WCHAR *addressW; + WCHAR *addressW = NULL; INTERNET_PORT port; socklen_t slen; + struct sockaddr *saddr; + DWORD len; if (netconn_connected( &request->netconn )) return TRUE; connect = request->connect; port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80); + saddr = (struct sockaddr *)&connect->sockaddr; + slen = sizeof(struct sockaddr); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, strlenW(connect->servername) + 1 ); - - slen = sizeof(connect->sockaddr); - if (!netconn_resolve( connect->servername, port, (struct sockaddr *)&connect->sockaddr, &slen, request->resolve_timeout )) return FALSE; - switch (connect->sockaddr.ss_family) + if (!connect->resolved) { - case AF_INET: - addr = &((struct sockaddr_in *)&connect->sockaddr)->sin_addr; - break; - case AF_INET6: - addr = &((struct sockaddr_in6 *)&connect->sockaddr)->sin6_addr; - break; - default: - WARN("unsupported address family %d\n", connect->sockaddr.ss_family); - return FALSE; + len = strlenW( connect->servername ) + 1; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, len ); + + if (!netconn_resolve( connect->servername, port, saddr, &slen, request->resolve_timeout )) return FALSE; + connect->resolved = TRUE; + + if (!(addressW = addr_to_str( saddr ))) return FALSE; + len = strlenW( addressW ) + 1; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len ); } - inet_ntop( connect->sockaddr.ss_family, addr, address, sizeof(address) ); - addressW = strdupAW( address ); - - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, strlenW(addressW) + 1 ); - - TRACE("connecting to %s:%u\n", address, port); + if (!addressW && !(addressW = addr_to_str( saddr ))) return FALSE; + TRACE("connecting to %s:%u\n", debugstr_w(addressW), port); send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 ); - if (!netconn_create( &request->netconn, connect->sockaddr.ss_family, SOCK_STREAM, 0 )) + if (!netconn_create( &request->netconn, saddr->sa_family, SOCK_STREAM, 0 )) { heap_free( addressW ); return FALSE; } netconn_set_timeout( &request->netconn, TRUE, request->send_timeout ); netconn_set_timeout( &request->netconn, FALSE, request->recv_timeout ); - if (!netconn_connect( &request->netconn, (struct sockaddr *)&connect->sockaddr, slen, request->connect_timeout )) + if (!netconn_connect( &request->netconn, saddr, slen, request->connect_timeout )) { netconn_close( &request->netconn ); heap_free( addressW ); @@ -1029,8 +1053,13 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len WCHAR *req = NULL; char *req_ascii; int bytes_sent; - DWORD len; + DWORD len, i, flags; + flags = WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA; + for (i = 0; i < request->num_accept_types; i++) + { + process_header( request, attr_accept, request->accept_types[i], flags, TRUE ); + } if (session->agent) process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); @@ -1063,6 +1092,8 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len return FALSE; } + if (context) request->hdr.context = context; + if (!(ret = open_connection( request ))) goto end; if (!(req = build_request_string( request ))) goto end; @@ -1070,7 +1101,6 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len TRACE("full request: %s\n", debugstr_a(req_ascii)); len = strlen(req_ascii); - if (context) request->hdr.context = context; send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 ); ret = netconn_send( &request->netconn, req_ascii, len, 0, &bytes_sent ); @@ -1193,7 +1223,7 @@ static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD support size = 0; query_headers( request, level, NULL, NULL, &size, &index ); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; + if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) break; index--; if (!(buffer = heap_alloc( size ))) return FALSE; @@ -1546,7 +1576,7 @@ end: return TRUE; } -static BOOL handle_redirect( request_t *request ) +static BOOL handle_redirect( request_t *request, DWORD status ) { BOOL ret = FALSE; DWORD size, len; @@ -1628,9 +1658,11 @@ static BOOL handle_redirect( request_t *request ) if ((index = get_header_index( request, attr_content_type, 0, TRUE )) >= 0) delete_header( request, index ); if ((index = get_header_index( request, attr_content_length, 0, TRUE )) >= 0 ) delete_header( request, index ); - /* redirects are always GET requests */ - heap_free( request->verb ); - request->verb = strdupW( getW ); + if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !strcmpW( request->verb, postW )) + { + heap_free( request->verb ); + request->verb = strdupW( getW ); + } ret = TRUE; end: @@ -1796,7 +1828,11 @@ static void drain_content( request_t *request ) DWORD bytes_read; char buffer[2048]; - if (!request->content_length) return; + if (!request->content_length) + { + finished_reading( request ); + return; + } for (;;) { if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return; @@ -1824,8 +1860,11 @@ static BOOL receive_response( request_t *request, BOOL async ) for (;;) { - if (!(ret = read_reply( request ))) break; - + if (!(ret = read_reply( request ))) + { + set_last_error( ERROR_WINHTTP_INVALID_SERVER_RESPONSE ); + break; + } size = sizeof(DWORD); query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER; if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break; @@ -1842,10 +1881,10 @@ static BOOL receive_response( request_t *request, BOOL async ) if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS) break; drain_content( request ); - if (!(ret = handle_redirect( request ))) break; + if (!(ret = handle_redirect( request, status ))) break; clear_response_headers( request ); - ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */ + send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */ continue; } else if (status == 401 || status == 407) @@ -1859,7 +1898,7 @@ static BOOL receive_response( request_t *request, BOOL async ) break; } clear_response_headers( request ); - ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); + send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); continue; } break; @@ -2132,3 +2171,1264 @@ BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write release_object( &request->hdr ); return ret; } + +enum request_state +{ + REQUEST_STATE_UNINITIALIZED, + REQUEST_STATE_INITIALIZED, + REQUEST_STATE_CANCELLED, + REQUEST_STATE_OPEN, + REQUEST_STATE_SENT, + REQUEST_STATE_RESPONSE_RECEIVED +}; + +struct winhttp_request +{ + IWinHttpRequest IWinHttpRequest_iface; + LONG refs; + CRITICAL_SECTION cs; + enum request_state state; + HINTERNET hsession; + HINTERNET hconnect; + HINTERNET hrequest; + VARIANT data; + WCHAR *verb; + HANDLE thread; + HANDLE wait; + HANDLE cancel; + char *buffer; + DWORD offset; + DWORD bytes_available; + DWORD bytes_read; + DWORD error; + DWORD logon_policy; + DWORD disable_feature; + LONG resolve_timeout; + LONG connect_timeout; + LONG send_timeout; + LONG receive_timeout; + WINHTTP_PROXY_INFO proxy; +}; + +static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface ) +{ + return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface ); +} + +static ULONG WINAPI winhttp_request_AddRef( + IWinHttpRequest *iface ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + return InterlockedIncrement( &request->refs ); +} + +/* critical section must be held */ +static void cancel_request( struct winhttp_request *request ) +{ + if (request->state <= REQUEST_STATE_CANCELLED) return; + if (request->thread) SetEvent( request->cancel ); + request->state = REQUEST_STATE_CANCELLED; +} + +/* critical section must be held */ +static void free_request( struct winhttp_request *request ) +{ + if (request->state < REQUEST_STATE_INITIALIZED) return; + WinHttpCloseHandle( request->hrequest ); + WinHttpCloseHandle( request->hconnect ); + WinHttpCloseHandle( request->hsession ); + CloseHandle( request->thread ); + CloseHandle( request->wait ); + CloseHandle( request->cancel ); + heap_free( (WCHAR *)request->proxy.lpszProxy ); + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + heap_free( request->buffer ); + heap_free( request->verb ); + VariantClear( &request->data ); +} + +static ULONG WINAPI winhttp_request_Release( + IWinHttpRequest *iface ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + LONG refs = InterlockedDecrement( &request->refs ); + if (!refs) + { + TRACE("destroying %p\n", request); + + EnterCriticalSection( &request->cs ); + cancel_request( request ); + free_request( request ); + LeaveCriticalSection( &request->cs ); + request->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &request->cs ); + heap_free( request ); + } + return refs; +} + +static HRESULT WINAPI winhttp_request_QueryInterface( + IWinHttpRequest *iface, + REFIID riid, + void **obj ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj ); + + if (IsEqualGUID( riid, &IID_IWinHttpRequest ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IUnknown )) + { + *obj = iface; + } + else + { + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + IWinHttpRequest_AddRef( iface ); + return S_OK; +} + +static HRESULT WINAPI winhttp_request_GetTypeInfoCount( + IWinHttpRequest *iface, + UINT *count ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p, %p\n", request, count); + *count = 1; + return S_OK; +} + +enum type_id +{ + IWinHttpRequest_tid, + last_tid +}; + +static ITypeLib *winhttp_typelib; +static ITypeInfo *winhttp_typeinfo[last_tid]; + +static REFIID winhttp_tid_id[] = +{ + &IID_IWinHttpRequest +}; + +static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret ) +{ + HRESULT hr; + + if (!winhttp_typelib) + { + ITypeLib *typelib; + + hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib ); + if (FAILED(hr)) + { + ERR("LoadRegTypeLib failed: %08x\n", hr); + return hr; + } + if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL )) + ITypeLib_Release( typelib ); + } + if (!winhttp_typeinfo[tid]) + { + ITypeInfo *typeinfo; + + hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo ); + if (FAILED(hr)) + { + ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(winhttp_tid_id[tid]), hr); + return hr; + } + if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL )) + ITypeInfo_Release( typeinfo ); + } + *ret = winhttp_typeinfo[tid]; + return S_OK; +} + +static HRESULT WINAPI winhttp_request_GetTypeInfo( + IWinHttpRequest *iface, + UINT index, + LCID lcid, + ITypeInfo **info ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + TRACE("%p, %u, %u, %p\n", request, index, lcid, info); + + return get_typeinfo( IWinHttpRequest_tid, info ); +} + +static HRESULT WINAPI winhttp_request_GetIDsOfNames( + IWinHttpRequest *iface, + REFIID riid, + LPOLESTR *names, + UINT count, + LCID lcid, + DISPID *dispid ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p, %s, %p, %u, %u, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid); + + if (!names || !count || !dispid) return E_INVALIDARG; + + hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI winhttp_request_Invoke( + IWinHttpRequest *iface, + DISPID member, + REFIID riid, + LCID lcid, + WORD flags, + DISPPARAMS *params, + VARIANT *result, + EXCEPINFO *excep_info, + UINT *arg_err ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p, %d, %s, %d, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid), + lcid, flags, params, result, excep_info, arg_err); + + hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags, + params, result, excep_info, arg_err ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI winhttp_request_SetProxy( + IWinHttpRequest *iface, + HTTPREQUEST_PROXY_SETTING proxy_setting, + VARIANT proxy_server, + VARIANT bypass_list ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err = ERROR_SUCCESS; + + TRACE("%p, %u, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server), + debugstr_variant(&bypass_list)); + + EnterCriticalSection( &request->cs ); + switch (proxy_setting) + { + case HTTPREQUEST_PROXYSETTING_DEFAULT: + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + heap_free( (WCHAR *)request->proxy.lpszProxy ); + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + break; + + case HTTPREQUEST_PROXYSETTING_DIRECT: + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; + heap_free( (WCHAR *)request->proxy.lpszProxy ); + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + break; + + case HTTPREQUEST_PROXYSETTING_PROXY: + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + if (V_VT( &proxy_server ) == VT_BSTR) + { + heap_free( (WCHAR *)request->proxy.lpszProxy ); + request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) ); + } + if (V_VT( &bypass_list ) == VT_BSTR) + { + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) ); + } + break; + + default: + err = ERROR_INVALID_PARAMETER; + break; + } + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_SetCredentials( + IWinHttpRequest *iface, + BSTR username, + BSTR password, + HTTPREQUEST_SETCREDENTIALS_FLAGS flags ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */ + DWORD err = ERROR_SUCCESS; + + TRACE("%p, %s, %p\n", request, debugstr_w(username), password); + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_OPEN) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN; + goto done; + } + switch (flags) + { + case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER: + target = WINHTTP_AUTH_TARGET_SERVER; + break; + case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY: + target = WINHTTP_AUTH_TARGET_PROXY; + break; + default: + err = ERROR_INVALID_PARAMETER; + goto done; + } + if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL )) + { + err = get_last_error(); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static void initialize_request( struct winhttp_request *request ) +{ + request->hrequest = NULL; + request->hconnect = NULL; + request->hsession = NULL; + request->thread = NULL; + request->wait = NULL; + request->cancel = NULL; + request->buffer = NULL; + request->verb = NULL; + request->offset = 0; + request->bytes_available = 0; + request->bytes_read = 0; + request->error = ERROR_SUCCESS; + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM; + request->disable_feature = WINHTTP_DISABLE_AUTHENTICATION; + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + request->resolve_timeout = 0; + request->connect_timeout = 60000; + request->send_timeout = 30000; + request->receive_timeout = 30000; + VariantInit( &request->data ); + request->state = REQUEST_STATE_INITIALIZED; +} + +static HRESULT WINAPI winhttp_request_Open( + IWinHttpRequest *iface, + BSTR method, + BSTR url, + VARIANT async ) +{ + static const WCHAR typeW[] = {'*','/','*',0}; + static const WCHAR *acceptW[] = {typeW, NULL}; + static const WCHAR httpsW[] = {'h','t','t','p','s'}; + static const WCHAR user_agentW[] = { + 'M','o','z','i','l','l','a','/','4','.','0',' ','(','c','o','m','p','a','t','i','b','l','e',';',' ', + 'W','i','n','3','2',';',' ','W','i','n','H','t','t','p','.','W','i','n','H','t','t','p', + 'R','e','q','u','e','s','t','.','5',')',0}; + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HINTERNET hsession = NULL, hconnect = NULL, hrequest; + URL_COMPONENTS uc; + WCHAR *hostname, *path = NULL, *verb = NULL; + DWORD err = ERROR_OUTOFMEMORY, len, flags = 0, request_flags = 0; + + TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url), + debugstr_variant(&async)); + + if (!method || !url) return E_INVALIDARG; + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + uc.dwSchemeLength = ~0u; + uc.dwHostNameLength = ~0u; + uc.dwUrlPathLength = ~0u; + uc.dwExtraInfoLength = ~0u; + if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( get_last_error() ); + + EnterCriticalSection( &request->cs ); + if (request->state != REQUEST_STATE_INITIALIZED) + { + cancel_request( request ); + free_request( request ); + initialize_request( request ); + } + if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error; + memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) ); + hostname[uc.dwHostNameLength] = 0; + + if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error; + memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) ); + path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0; + + if (!(verb = strdupW( method ))) goto error; + if (V_BOOL( &async )) flags |= WINHTTP_FLAG_ASYNC; + if (!(hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, flags ))) + { + err = get_last_error(); + goto error; + } + if (!(hconnect = WinHttpConnect( hsession, hostname, uc.nPort, 0 ))) + { + err = get_last_error(); + goto error; + } + len = sizeof(httpsW) / sizeof(WCHAR); + if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) )) + { + request_flags |= WINHTTP_FLAG_SECURE; + } + if (!(hrequest = WinHttpOpenRequest( hconnect, method, path, NULL, NULL, acceptW, request_flags ))) + { + err = get_last_error(); + goto error; + } + if (flags & WINHTTP_FLAG_ASYNC) + { + request->wait = CreateEventW( NULL, FALSE, FALSE, NULL ); + request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL ); + WinHttpSetOption( hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) ); + } + request->state = REQUEST_STATE_OPEN; + request->hsession = hsession; + request->hconnect = hconnect; + request->hrequest = hrequest; + request->verb = verb; + heap_free( hostname ); + heap_free( path ); + LeaveCriticalSection( &request->cs ); + return S_OK; + +error: + WinHttpCloseHandle( hconnect ); + WinHttpCloseHandle( hsession ); + heap_free( hostname ); + heap_free( path ); + heap_free( verb ); + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_SetRequestHeader( + IWinHttpRequest *iface, + BSTR header, + BSTR value ) +{ + static const WCHAR fmtW[] = {'%','s',':',' ','%','s','\r','\n',0}; + static const WCHAR emptyW[] = {0}; + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD len, err = ERROR_SUCCESS; + WCHAR *str; + + TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value)); + + if (!header) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_OPEN) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN; + goto done; + } + if (request->state >= REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND; + goto done; + } + len = strlenW( header ) + 4; + if (value) len += strlenW( value ); + if (!(str = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + sprintfW( str, fmtW, header, value ? value : emptyW ); + if (!WinHttpAddRequestHeaders( request->hrequest, str, len, WINHTTP_ADDREQ_FLAG_REPLACE )) + { + err = get_last_error(); + } + heap_free( str ); + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_GetResponseHeader( + IWinHttpRequest *iface, + BSTR header, + BSTR *value ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD size, err = ERROR_SUCCESS; + + TRACE("%p, %p\n", request, header); + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + if (!header || !value) + { + err = ERROR_INVALID_PARAMETER; + goto done; + } + size = 0; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL )) + { + err = get_last_error(); + if (err != ERROR_INSUFFICIENT_BUFFER) goto done; + } + if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + err = ERROR_SUCCESS; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL )) + { + err = get_last_error(); + SysFreeString( *value ); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_GetAllResponseHeaders( + IWinHttpRequest *iface, + BSTR *headers ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD size, err = ERROR_SUCCESS; + + TRACE("%p, %p\n", request, headers); + + if (!headers) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + size = 0; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL )) + { + err = get_last_error(); + if (err != ERROR_INSUFFICIENT_BUFFER) goto done; + } + if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + err = ERROR_SUCCESS; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL )) + { + err = get_last_error(); + SysFreeString( *headers ); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size ) +{ + struct winhttp_request *request = (struct winhttp_request *)context; + + switch (status) + { + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + request->bytes_available = *(DWORD *)buffer; + request->error = ERROR_SUCCESS; + break; + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + request->bytes_read = size; + request->error = ERROR_SUCCESS; + break; + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + { + WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer; + request->error = result->dwError; + break; + } + default: break; + } + SetEvent( request->wait ); +} + +static void wait_set_status_callback( struct winhttp_request *request, DWORD status ) +{ + if (!request->wait) return; + status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 ); +} + +static DWORD wait_for_completion( struct winhttp_request *request ) +{ + HANDLE handles[2]; + + if (!request->wait) + { + request->error = ERROR_SUCCESS; + return ERROR_SUCCESS; + } + handles[0] = request->wait; + handles[1] = request->cancel; + switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE )) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + request->error = ERROR_CANCELLED; + break; + default: + request->error = get_last_error(); + break; + } + return request->error; +} + +static HRESULT request_receive( struct winhttp_request *request ) +{ + DWORD err, size, total_bytes_read, buflen = 4096; + + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE ); + if (!WinHttpReceiveResponse( request->hrequest, NULL )) + { + return HRESULT_FROM_WIN32( get_last_error() ); + } + if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err ); + if (!strcmpW( request->verb, headW )) + { + request->state = REQUEST_STATE_RESPONSE_RECEIVED; + return S_OK; + } + if (!(request->buffer = heap_alloc( buflen ))) return E_OUTOFMEMORY; + request->buffer[0] = 0; + size = total_bytes_read = 0; + do + { + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE ); + if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available )) + { + err = get_last_error(); + goto error; + } + if ((err = wait_for_completion( request ))) goto error; + if (!request->bytes_available) break; + size += request->bytes_available; + if (buflen < size) + { + char *tmp; + while (buflen < size) buflen *= 2; + if (!(tmp = heap_realloc( request->buffer, buflen ))) + { + err = ERROR_OUTOFMEMORY; + goto error; + } + request->buffer = tmp; + } + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE ); + if (!WinHttpReadData( request->hrequest, request->buffer + request->offset, + request->bytes_available, &request->bytes_read )) + { + err = get_last_error(); + goto error; + } + if ((err = wait_for_completion( request ))) goto error; + total_bytes_read += request->bytes_read; + request->offset += request->bytes_read; + } while (request->bytes_read); + + request->state = REQUEST_STATE_RESPONSE_RECEIVED; + return S_OK; + +error: + heap_free( request->buffer ); + request->buffer = NULL; + return HRESULT_FROM_WIN32( err ); +} + +static DWORD request_set_parameters( struct winhttp_request *request ) +{ + if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy, + sizeof(request->proxy) )) return get_last_error(); + + if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy, + sizeof(request->logon_policy) )) return get_last_error(); + + if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature, + sizeof(request->disable_feature) )) return get_last_error(); + + if (!WinHttpSetTimeouts( request->hrequest, + request->resolve_timeout, + request->connect_timeout, + request->send_timeout, + request->receive_timeout )) return get_last_error(); + return ERROR_SUCCESS; +} + +static void request_set_utf8_content_type( struct winhttp_request *request ) +{ + static const WCHAR fmtW[] = {'%','s',':',' ','%','s',0}; + static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n',0}; + static const WCHAR charset_utf8W[] = {'c','h','a','r','s','e','t','=','u','t','f','-','8',0}; + WCHAR headerW[64]; + int len; + + len = sprintfW( headerW, fmtW, attr_content_type, text_plainW ); + WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW ); + len = sprintfW( headerW, fmtW, attr_content_type, charset_utf8W ); + WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON ); +} + +static HRESULT request_send( struct winhttp_request *request ) +{ + SAFEARRAY *sa = NULL; + VARIANT data; + char *ptr = NULL; + LONG size = 0; + HRESULT hr; + BOOL ret; + DWORD err; + + if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err ); + if (strcmpW( request->verb, getW )) + { + VariantInit( &data ); + if (V_VT( &request->data ) == VT_BSTR) + { + UINT i, cp = CP_ACP; + const WCHAR *str = V_BSTR( &request->data ); + int len = strlenW( str ); + + for (i = 0; i < len; i++) + { + if (str[i] > 127) + { + cp = CP_UTF8; + break; + } + } + size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL ); + if (!(ptr = heap_alloc( size ))) return E_OUTOFMEMORY; + WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL ); + if (cp == CP_UTF8) request_set_utf8_content_type( request ); + } + else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK) + { + sa = V_ARRAY( &data ); + if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr; + if ((hr = SafeArrayGetUBound( sa, 1, &size ) != S_OK)) + { + SafeArrayUnaccessData( sa ); + return hr; + } + size++; + } + } + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT ); + if (!(ret = WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))) + { + err = get_last_error(); + goto error; + } + if ((err = wait_for_completion( request ))) goto error; + if (sa) SafeArrayUnaccessData( sa ); + else heap_free( ptr ); + request->state = REQUEST_STATE_SENT; + return S_OK; + +error: + if (sa) SafeArrayUnaccessData( sa ); + else heap_free( ptr ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT request_send_and_receive( struct winhttp_request *request ) +{ + HRESULT hr = request_send( request ); + if (hr == S_OK) hr = request_receive( request ); + return hr; +} + +static DWORD CALLBACK send_and_receive_proc( void *arg ) +{ + struct winhttp_request *request = (struct winhttp_request *)arg; + return request_send_and_receive( request ); +} + +static HRESULT WINAPI winhttp_request_Send( + IWinHttpRequest *iface, + VARIANT body ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HRESULT hr; + + TRACE("%p, %s\n", request, debugstr_variant(&body)); + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_OPEN) + { + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN ); + } + if (request->state >= REQUEST_STATE_SENT) + { + LeaveCriticalSection( &request->cs ); + return S_OK; + } + VariantClear( &request->data ); + if ((hr = VariantCopyInd( &request->data, &body )) != S_OK) { + LeaveCriticalSection( &request->cs ); + return hr; + } + + if (request->wait) /* async request */ + { + if (!(request->thread = CreateThread( NULL, 0, send_and_receive_proc, request, 0, NULL ))) + { + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( get_last_error() ); + } + } + else hr = request_send_and_receive( request ); + LeaveCriticalSection( &request->cs ); + return hr; +} + +static HRESULT WINAPI winhttp_request_get_Status( + IWinHttpRequest *iface, + LONG *status ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0; + + TRACE("%p, %p\n", request, status); + + if (!status) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER; + if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index )) + { + err = get_last_error(); + } + *status = status_code; + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_get_StatusText( + IWinHttpRequest *iface, + BSTR *status ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err = ERROR_SUCCESS, len = 0, index = 0; + + TRACE("%p, %p\n", request, status); + + if (!status) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index )) + { + err = get_last_error(); + if (err != ERROR_INSUFFICIENT_BUFFER) goto done; + } + if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + index = 0; + err = ERROR_SUCCESS; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index )) + { + err = get_last_error(); + SysFreeString( *status ); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage ) +{ + static const WCHAR utf8W[] = {'u','t','f','-','8',0}; + static const WCHAR charsetW[] = {'c','h','a','r','s','e','t',0}; + WCHAR *buffer, *p; + DWORD size; + + *codepage = CP_ACP; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) && + get_last_error() == ERROR_INSUFFICIENT_BUFFER) + { + if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL )) + { + return get_last_error(); + } + if ((p = strstrW( buffer, charsetW ))) + { + p += strlenW( charsetW ); + while (*p == ' ') p++; + if (*p++ == '=') + { + while (*p == ' ') p++; + if (!strcmpiW( p, utf8W )) *codepage = CP_UTF8; + } + } + heap_free( buffer ); + } + return ERROR_SUCCESS; +} + +static HRESULT WINAPI winhttp_request_get_ResponseText( + IWinHttpRequest *iface, + BSTR *body ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + UINT codepage; + DWORD err = ERROR_SUCCESS; + int len; + + TRACE("%p, %p\n", request, body); + + if (!body) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + if ((err = request_get_codepage( request, &codepage ))) goto done; + len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 ); + if (!(*body = SysAllocStringLen( NULL, len ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len ); + (*body)[len] = 0; + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_get_ResponseBody( + IWinHttpRequest *iface, + VARIANT *body ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + SAFEARRAY *sa; + HRESULT hr; + DWORD err = ERROR_SUCCESS; + char *ptr; + + TRACE("%p, %p\n", request, body); + + if (!body) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) + { + SafeArrayDestroy( sa ); + LeaveCriticalSection( &request->cs ); + return hr; + } + memcpy( ptr, request->buffer, request->offset ); + if ((hr = SafeArrayUnaccessData( sa )) != S_OK) + { + SafeArrayDestroy( sa ); + LeaveCriticalSection( &request->cs ); + return hr; + } + V_VT( body ) = VT_ARRAY|VT_UI1; + V_ARRAY( body ) = sa; + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_get_ResponseStream( + IWinHttpRequest *iface, + VARIANT *body ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI winhttp_request_get_Option( + IWinHttpRequest *iface, + WinHttpRequestOption option, + VARIANT *value ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI winhttp_request_put_Option( + IWinHttpRequest *iface, + WinHttpRequestOption option, + VARIANT value ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HRESULT hr = S_OK; + + TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value)); + + EnterCriticalSection( &request->cs ); + switch (option) + { + case WinHttpRequestOption_EnableRedirects: + { + if (V_BOOL( &value )) + request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS; + else + request->disable_feature |= WINHTTP_DISABLE_REDIRECTS; + break; + } + default: + FIXME("unimplemented option %u\n", option); + hr = E_NOTIMPL; + break; + } + LeaveCriticalSection( &request->cs ); + return hr; +} + +/* critical section must be held */ +static DWORD wait_for_response( struct winhttp_request *request, DWORD timeout ) +{ + HANDLE thread = request->thread; + DWORD err, ret; + + LeaveCriticalSection( &request->cs ); + while ((err = MsgWaitForMultipleObjects( 1, &thread, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1) + { + MSG msg; + while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + } + switch (err) + { + case WAIT_OBJECT_0: + ret = ERROR_SUCCESS; + break; + case WAIT_TIMEOUT: + ret = ERROR_TIMEOUT; + break; + case WAIT_FAILED: + default: + ret = get_last_error(); + break; + } + EnterCriticalSection( &request->cs ); + return ret; +} + +static HRESULT WINAPI winhttp_request_WaitForResponse( + IWinHttpRequest *iface, + VARIANT timeout, + VARIANT_BOOL *succeeded ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000; + + TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded); + + EnterCriticalSection( &request->cs ); + if (!request->thread) + { + LeaveCriticalSection( &request->cs ); + return S_OK; + } + if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED) + { + LeaveCriticalSection( &request->cs ); + return S_OK; + } + switch ((err = wait_for_response( request, msecs ))) + { + case ERROR_TIMEOUT: + if (succeeded) *succeeded = VARIANT_FALSE; + err = ERROR_SUCCESS; + break; + + case ERROR_SUCCESS: + if (succeeded) *succeeded = VARIANT_TRUE; + break; + + default: break; + } + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_Abort( + IWinHttpRequest *iface ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p\n", request); + + EnterCriticalSection( &request->cs ); + cancel_request( request ); + LeaveCriticalSection( &request->cs ); + return S_OK; +} + +static HRESULT WINAPI winhttp_request_SetTimeouts( + IWinHttpRequest *iface, + LONG resolve_timeout, + LONG connect_timeout, + LONG send_timeout, + LONG receive_timeout ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p, %d, %d, %d, %d\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout); + + EnterCriticalSection( &request->cs ); + request->resolve_timeout = resolve_timeout; + request->connect_timeout = connect_timeout; + request->send_timeout = send_timeout; + request->receive_timeout = receive_timeout; + LeaveCriticalSection( &request->cs ); + return S_OK; +} + +static HRESULT WINAPI winhttp_request_SetClientCertificate( + IWinHttpRequest *iface, + BSTR certificate ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy( + IWinHttpRequest *iface, + WinHttpRequestAutoLogonPolicy policy ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HRESULT hr = S_OK; + + TRACE("%p, %u\n", request, policy ); + + EnterCriticalSection( &request->cs ); + switch (policy) + { + case AutoLogonPolicy_Always: + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; + break; + case AutoLogonPolicy_OnlyIfBypassProxy: + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM; + break; + case AutoLogonPolicy_Never: + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; + break; + default: hr = E_INVALIDARG; + break; + } + LeaveCriticalSection( &request->cs ); + return hr; +} + +static const struct IWinHttpRequestVtbl winhttp_request_vtbl = +{ + winhttp_request_QueryInterface, + winhttp_request_AddRef, + winhttp_request_Release, + winhttp_request_GetTypeInfoCount, + winhttp_request_GetTypeInfo, + winhttp_request_GetIDsOfNames, + winhttp_request_Invoke, + winhttp_request_SetProxy, + winhttp_request_SetCredentials, + winhttp_request_Open, + winhttp_request_SetRequestHeader, + winhttp_request_GetResponseHeader, + winhttp_request_GetAllResponseHeaders, + winhttp_request_Send, + winhttp_request_get_Status, + winhttp_request_get_StatusText, + winhttp_request_get_ResponseText, + winhttp_request_get_ResponseBody, + winhttp_request_get_ResponseStream, + winhttp_request_get_Option, + winhttp_request_put_Option, + winhttp_request_WaitForResponse, + winhttp_request_Abort, + winhttp_request_SetTimeouts, + winhttp_request_SetClientCertificate, + winhttp_request_SetAutoLogonPolicy +}; + +HRESULT WinHttpRequest_create( IUnknown *unknown, void **obj ) +{ + struct winhttp_request *request; + + TRACE("%p, %p\n", unknown, obj); + + if (!(request = heap_alloc( sizeof(*request) ))) return E_OUTOFMEMORY; + request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl; + request->refs = 1; + request->state = REQUEST_STATE_UNINITIALIZED; + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + InitializeCriticalSection( &request->cs ); + request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs"); + + *obj = &request->IWinHttpRequest_iface; + TRACE("returning iface %p\n", *obj); + return S_OK; +} diff --git a/reactos/dll/win32/winhttp/rsrc.rc b/reactos/dll/win32/winhttp/rsrc.rc new file mode 100644 index 00000000000..61b48b6c1e7 --- /dev/null +++ b/reactos/dll/win32/winhttp/rsrc.rc @@ -0,0 +1,35 @@ +/* + * Copyright 2008 Robert Shearman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winhttp_tlb.tlb */ +1 TYPELIB winhttp_tlb.tlb + +/* @makedep: winhttp_tlb.rgs */ +1 WINE_REGISTRY winhttp_tlb.rgs + +/* @makedep: pac.js */ +pac.js 40 "pac.js" + +#define WINE_FILEDESCRIPTION_STR "Wine HTTP Library" +#define WINE_FILENAME_STR "winhttp.dll" +#define WINE_FILEVERSION_MAJOR 5 +#define WINE_FILEVERSION_MINOR 1 +#define WINE_FILEVERSION_BUILD 2600 +#define WINE_FILEVERSION_PLATFORMID 2180 + +#include "wine/wine_common_ver.rc" diff --git a/reactos/dll/win32/winhttp/session.c b/reactos/dll/win32/winhttp/session.c index 26daf8b19ac..e1e6841e06f 100644 --- a/reactos/dll/win32/winhttp/session.c +++ b/reactos/dll/win32/winhttp/session.c @@ -28,6 +28,10 @@ #include "winhttp.h" #include "wincrypt.h" #include "winreg.h" +#define COBJMACROS +#include "ole2.h" +#include "dispex.h" +#include "activscp.h" #include "winhttp_private.h" @@ -38,8 +42,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(winhttp); #define DEFAULT_SEND_TIMEOUT 30000 #define DEFAULT_RECEIVE_TIMEOUT 30000 -/* FIXME */ -#define CP_UNIXCP CP_ACP +static const WCHAR global_funcsW[] = {'g','l','o','b','a','l','_','f','u','n','c','s',0}; +static const WCHAR dns_resolveW[] = {'d','n','s','_','r','e','s','o','l','v','e',0}; void set_last_error( DWORD error ) { @@ -217,6 +221,7 @@ HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWST session->hdr.flags = flags; session->hdr.refs = 1; session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; + list_init( &session->hdr.children ); session->resolve_timeout = DEFAULT_RESOLVE_TIMEOUT; session->connect_timeout = DEFAULT_CONNECT_TIMEOUT; session->send_timeout = DEFAULT_SEND_TIMEOUT; @@ -426,6 +431,7 @@ BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT session->proxy_server, colon - session->proxy_server - 1 )) { heap_free( connect->servername ); + connect->resolved = FALSE; if (!(connect->servername = heap_alloc( (colon - session->proxy_server + 1) * sizeof(WCHAR) ))) { @@ -447,6 +453,7 @@ BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT session->proxy_server )) { heap_free( connect->servername ); + connect->resolved = FALSE; if (!(connect->servername = strdupW( session->proxy_server ))) { ret = FALSE; @@ -459,6 +466,7 @@ BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT else if (server) { heap_free( connect->servername ); + connect->resolved = FALSE; if (!(connect->servername = strdupW( server ))) { ret = FALSE; @@ -509,6 +517,7 @@ HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PO connect->hdr.callback = session->hdr.callback; connect->hdr.notify_mask = session->hdr.notify_mask; connect->hdr.context = session->hdr.context; + list_init( &connect->hdr.children ); addref_object( &session->hdr ); connect->session = session; @@ -516,9 +525,7 @@ HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PO if (!(connect->hostname = strdupW( server ))) goto end; connect->hostport = port; - - if (!set_server_for_hostname( connect, server, port )) - goto end; + if (!set_server_for_hostname( connect, server, port )) goto end; if (!(hconnect = alloc_handle( &connect->hdr ))) goto end; connect->hdr.handle = hconnect; @@ -527,7 +534,7 @@ HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PO end: release_object( &connect->hdr ); - + release_object( &session->hdr ); TRACE("returning %p\n", hconnect); return hconnect; } @@ -538,7 +545,7 @@ end: static void request_destroy( object_header_t *hdr ) { request_t *request = (request_t *)hdr; - DWORD i; + unsigned int i; TRACE("%p\n", request); @@ -555,6 +562,8 @@ static void request_destroy( object_header_t *hdr ) heap_free( request->headers[i].value ); } heap_free( request->headers ); + for (i = 0; i < request->num_accept_types; i++) heap_free( request->accept_types[i] ); + heap_free( request->accept_types ); heap_free( request ); } @@ -564,7 +573,7 @@ static void str_to_buffer( WCHAR *buffer, const WCHAR *str, LPDWORD buflen ) if (str) len = strlenW( str ); if (buffer && *buflen > len) { - memcpy( buffer, str, len * sizeof(WCHAR) ); + if (str) memcpy( buffer, str, len * sizeof(WCHAR) ); buffer[len] = 0; } *buflen = len * sizeof(WCHAR); @@ -870,6 +879,39 @@ static const object_vtbl_t request_vtbl = request_set_option }; +static BOOL store_accept_types( request_t *request, const WCHAR **accept_types ) +{ + const WCHAR **types = accept_types; + int i; + + if (!types) return TRUE; + while (*types) + { + request->num_accept_types++; + types++; + } + if (!request->num_accept_types) return TRUE; + if (!(request->accept_types = heap_alloc( request->num_accept_types * sizeof(WCHAR *)))) + { + request->num_accept_types = 0; + return FALSE; + } + types = accept_types; + for (i = 0; i < request->num_accept_types; i++) + { + if (!(request->accept_types[i] = strdupW( *types ))) + { + for (; i >= 0; i--) heap_free( request->accept_types[i] ); + heap_free( request->accept_types ); + request->accept_types = NULL; + request->num_accept_types = 0; + return FALSE; + } + types++; + } + return TRUE; +} + /*********************************************************************** * WinHttpOpenRequest (winhttp.@) */ @@ -883,6 +925,14 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o TRACE("%p, %s, %s, %s, %s, %p, 0x%08x\n", hconnect, debugstr_w(verb), debugstr_w(object), debugstr_w(version), debugstr_w(referrer), types, flags); + if(types && TRACE_ON(winhttp)) { + const WCHAR **iter; + + TRACE("accept types:\n"); + for(iter = types; *iter; iter++) + TRACE(" %s\n", debugstr_w(*iter)); + } + if (!(connect = (connect_t *)grab_object( hconnect ))) { set_last_error( ERROR_INVALID_HANDLE ); @@ -906,6 +956,7 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o request->hdr.callback = connect->hdr.callback; request->hdr.notify_mask = connect->hdr.notify_mask; request->hdr.context = connect->hdr.context; + list_init( &request->hdr.children ); addref_object( &connect->hdr ); request->connect = connect; @@ -937,6 +988,7 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o if (!version || !version[0]) version = http1_1; if (!(request->version = strdupW( version ))) goto end; + if (!(store_accept_types( request, types ))) goto end; if (!(hrequest = alloc_handle( &request->hdr ))) goto end; request->hdr.handle = hrequest; @@ -945,7 +997,7 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o end: release_object( &request->hdr ); - + release_object( &connect->hdr ); TRACE("returning %p\n", hrequest); return hrequest; } @@ -1087,15 +1139,119 @@ BOOL WINAPI WinHttpSetOption( HINTERNET handle, DWORD option, LPVOID buffer, DWO return ret; } +static char *get_computer_name( COMPUTER_NAME_FORMAT format ) +{ + char *ret; + DWORD size = 0; + + GetComputerNameExA( format, NULL, &size ); + if (GetLastError() != ERROR_MORE_DATA) return NULL; + if (!(ret = heap_alloc( size ))) return NULL; + if (!GetComputerNameExA( format, ret, &size )) + { + heap_free( ret ); + return NULL; + } + return ret; +} + +static BOOL is_domain_suffix( const char *domain, const char *suffix ) +{ + int len_domain = strlen( domain ), len_suffix = strlen( suffix ); + + if (len_suffix > len_domain) return FALSE; + if (!strcasecmp( domain + len_domain - len_suffix, suffix )) return TRUE; + return FALSE; +} + +static void printf_addr( const WCHAR *fmt, WCHAR *buf, struct sockaddr_in *addr ) +{ + sprintfW( buf, fmt, + (unsigned int)(ntohl( addr->sin_addr.s_addr ) >> 24 & 0xff), + (unsigned int)(ntohl( addr->sin_addr.s_addr ) >> 16 & 0xff), + (unsigned int)(ntohl( addr->sin_addr.s_addr ) >> 8 & 0xff), + (unsigned int)(ntohl( addr->sin_addr.s_addr ) & 0xff) ); +} + +static WCHAR *build_wpad_url( const struct addrinfo *ai ) +{ + static const WCHAR fmtW[] = + {'h','t','t','p',':','/','/','%','u','.','%','u','.','%','u','.','%','u', + '/','w','p','a','d','.','d','a','t',0}; + WCHAR *ret; + + while (ai && ai->ai_family != AF_INET) ai = ai->ai_next; + if (!ai) return NULL; + + if (!(ret = GlobalAlloc( 0, sizeof(fmtW) + 12 * sizeof(WCHAR) ))) return NULL; + printf_addr( fmtW, ret, (struct sockaddr_in *)ai->ai_addr ); + return ret; +} + /*********************************************************************** * WinHttpDetectAutoProxyConfigUrl (winhttp.@) */ BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, LPWSTR *url ) { - FIXME("0x%08x, %p\n", flags, url); + BOOL ret = FALSE; - set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); - return FALSE; + TRACE("0x%08x, %p\n", flags, url); + + if (!flags || !url) + { + set_last_error( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (flags & WINHTTP_AUTO_DETECT_TYPE_DHCP) FIXME("discovery via DHCP not supported\n"); + if (flags & WINHTTP_AUTO_DETECT_TYPE_DNS_A) + { +#ifdef HAVE_GETADDRINFO + char *fqdn, *domain, *p; + + if (!(fqdn = get_computer_name( ComputerNamePhysicalDnsFullyQualified ))) return FALSE; + if (!(domain = get_computer_name( ComputerNamePhysicalDnsDomain ))) + { + heap_free( fqdn ); + return FALSE; + } + p = fqdn; + while ((p = strchr( p, '.' )) && is_domain_suffix( p + 1, domain )) + { + struct addrinfo *ai; + char *name; + int res; + + if (!(name = heap_alloc( sizeof("wpad") + strlen(p) ))) + { + heap_free( fqdn ); + heap_free( domain ); + return FALSE; + } + strcpy( name, "wpad" ); + strcat( name, p ); + res = getaddrinfo( name, NULL, NULL, &ai ); + heap_free( name ); + if (!res) + { + *url = build_wpad_url( ai ); + freeaddrinfo( ai ); + if (*url) + { + TRACE("returning %s\n", debugstr_w(*url)); + ret = TRUE; + break; + } + } + p++; + } + heap_free( domain ); + heap_free( fqdn ); +#else + FIXME("getaddrinfo not found at build time\n"); +#endif + } + if (!ret) set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); + return ret; } static const WCHAR Connections[] = { @@ -1107,15 +1263,18 @@ static const WCHAR Connections[] = { 'C','o','n','n','e','c','t','i','o','n','s',0 }; static const WCHAR WinHttpSettings[] = { 'W','i','n','H','t','t','p','S','e','t','t','i','n','g','s',0 }; -static const DWORD WINHTTPSETTINGS_MAGIC = 0x18; -static const DWORD WINHTTP_PROXY_TYPE_DIRECT = 1; -static const DWORD WINHTTP_PROXY_TYPE_PROXY = 2; +static const DWORD WINHTTP_SETTINGS_MAGIC = 0x18; +static const DWORD WININET_SETTINGS_MAGIC = 0x46; +static const DWORD PROXY_TYPE_DIRECT = 1; +static const DWORD PROXY_TYPE_PROXY = 2; +static const DWORD PROXY_USE_PAC_SCRIPT = 4; +static const DWORD PROXY_AUTODETECT_SETTINGS = 8; -struct winhttp_settings_header +struct connection_settings_header { DWORD magic; DWORD unknown; /* always zero? */ - DWORD flags; /* one of WINHTTP_PROXY_TYPE_* */ + DWORD flags; /* one or more of PROXY_* */ }; static inline void copy_char_to_wchar_sz(const BYTE *src, DWORD len, WCHAR *dst) @@ -1146,22 +1305,22 @@ BOOL WINAPI WinHttpGetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) l = RegQueryValueExW( key, WinHttpSettings, NULL, &type, NULL, &size ); if (!l && type == REG_BINARY && - size >= sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD)) + size >= sizeof(struct connection_settings_header) + 2 * sizeof(DWORD)) { BYTE *buf = heap_alloc( size ); if (buf) { - struct winhttp_settings_header *hdr = - (struct winhttp_settings_header *)buf; + struct connection_settings_header *hdr = + (struct connection_settings_header *)buf; DWORD *len = (DWORD *)(hdr + 1); l = RegQueryValueExW( key, WinHttpSettings, NULL, NULL, buf, &size ); - if (!l && hdr->magic == WINHTTPSETTINGS_MAGIC && + if (!l && hdr->magic == WINHTTP_SETTINGS_MAGIC && hdr->unknown == 0) { - if (hdr->flags & WINHTTP_PROXY_TYPE_PROXY) + if (hdr->flags & PROXY_TYPE_PROXY) { BOOL sane = FALSE; LPWSTR proxy = NULL; @@ -1267,6 +1426,13 @@ BOOL WINAPI WinHttpGetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) */ BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *config ) { + static const WCHAR settingsW[] = + {'D','e','f','a','u','l','t','C','o','n','n','e','c','t','i','o','n','S','e','t','t','i','n','g','s',0}; + HKEY hkey = NULL; + struct connection_settings_header *hdr = NULL; + DWORD type, offset, len, size = 0; + BOOL ret = FALSE; + TRACE("%p\n", config); if (!config) @@ -1274,28 +1440,597 @@ BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY set_last_error( ERROR_INVALID_PARAMETER ); return FALSE; } + memset( config, 0, sizeof(*config) ); + config->fAutoDetect = TRUE; - /* FIXME: read from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings */ + if (RegOpenKeyExW( HKEY_CURRENT_USER, Connections, 0, KEY_READ, &hkey ) || + RegQueryValueExW( hkey, settingsW, NULL, &type, NULL, &size ) || + type != REG_BINARY || size < sizeof(struct connection_settings_header)) + { + ret = TRUE; + goto done; + } + if (!(hdr = heap_alloc( size ))) goto done; + if (RegQueryValueExW( hkey, settingsW, NULL, &type, (BYTE *)hdr, &size ) || + hdr->magic != WININET_SETTINGS_MAGIC) + { + ret = TRUE; + goto done; + } - FIXME("returning no proxy used\n"); - config->fAutoDetect = FALSE; - config->lpszAutoConfigUrl = NULL; - config->lpszProxy = NULL; - config->lpszProxyBypass = NULL; + config->fAutoDetect = (hdr->flags & PROXY_AUTODETECT_SETTINGS) != 0; + offset = sizeof(*hdr); + if (offset + sizeof(DWORD) > size) goto done; + len = *(DWORD *)((char *)hdr + offset); + offset += sizeof(DWORD); + if (len && hdr->flags & PROXY_TYPE_PROXY) + { + if (!(config->lpszProxy = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done; + copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszProxy ); + } + offset += len; + if (offset + sizeof(DWORD) > size) goto done; + len = *(DWORD *)((char *)hdr + offset); + offset += sizeof(DWORD); + if (len && (hdr->flags & PROXY_TYPE_PROXY)) + { + if (!(config->lpszProxyBypass = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done; + copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszProxyBypass ); + } + offset += len; + if (offset + sizeof(DWORD) > size) goto done; + len = *(DWORD *)((char *)hdr + offset); + offset += sizeof(DWORD); + if (len && (hdr->flags & PROXY_USE_PAC_SCRIPT)) + { + if (!(config->lpszAutoConfigUrl = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done; + copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszAutoConfigUrl ); + } + ret = TRUE; +done: + RegCloseKey( hkey ); + heap_free( hdr ); + if (!ret) + { + heap_free( config->lpszAutoConfigUrl ); + config->lpszAutoConfigUrl = NULL; + heap_free( config->lpszProxy ); + config->lpszProxy = NULL; + heap_free( config->lpszProxyBypass ); + config->lpszProxyBypass = NULL; + } + return ret; +} + +static HRESULT WINAPI dispex_QueryInterface( + IDispatchEx *iface, REFIID riid, void **ppv ) +{ + *ppv = NULL; + + if (IsEqualGUID( riid, &IID_IUnknown ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IDispatchEx )) + *ppv = iface; + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI dispex_AddRef( + IDispatchEx *iface ) +{ + return 2; +} + +static ULONG WINAPI dispex_Release( + IDispatchEx *iface ) +{ + return 1; +} + +static HRESULT WINAPI dispex_GetTypeInfoCount( + IDispatchEx *iface, UINT *info ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetTypeInfo( + IDispatchEx *iface, UINT info, LCID lcid, ITypeInfo **type_info ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetIDsOfNames( + IDispatchEx *iface, REFIID riid, LPOLESTR *names, UINT count, LCID lcid, DISPID *id ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_Invoke( + IDispatchEx *iface, DISPID member, REFIID riid, LCID lcid, WORD flags, + DISPPARAMS *params, VARIANT *result, EXCEPINFO *excep, UINT *err ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_DeleteMemberByName( + IDispatchEx *iface, BSTR name, DWORD flags ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_DeleteMemberByDispID( + IDispatchEx *iface, DISPID id ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetMemberProperties( + IDispatchEx *iface, DISPID id, DWORD flags_fetch, DWORD *flags ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetMemberName( + IDispatchEx *iface, DISPID id, BSTR *name ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetNextDispID( + IDispatchEx *iface, DWORD flags, DISPID id, DISPID *next ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetNameSpaceParent( + IDispatchEx *iface, IUnknown **unk ) +{ + return E_NOTIMPL; +} + +#define DISPID_GLOBAL_DNSRESOLVE 0x1000 + +static HRESULT WINAPI dispex_GetDispID( + IDispatchEx *iface, BSTR name, DWORD flags, DISPID *id ) +{ + if (!strcmpW( name, dns_resolveW )) + { + *id = DISPID_GLOBAL_DNSRESOLVE; + return S_OK; + } + return DISP_E_UNKNOWNNAME; +} + +static HRESULT dns_resolve( const WCHAR *hostname, VARIANT *result ) +{ +#ifdef HAVE_GETADDRINFO + static const WCHAR fmtW[] = {'%','u','.','%','u','.','%','u','.','%','u',0}; + WCHAR addr[16]; + struct addrinfo *ai, *elem; + char *hostnameA; + int res; + + if (hostname[0]) + hostnameA = strdupWA( hostname ); + else + hostnameA = get_computer_name( ComputerNamePhysicalDnsFullyQualified ); + + if (!hostnameA) return E_OUTOFMEMORY; + res = getaddrinfo( hostnameA, NULL, NULL, &ai ); + heap_free( hostnameA ); + if (res) return S_FALSE; + + elem = ai; + while (elem && elem->ai_family != AF_INET) elem = elem->ai_next; + if (!elem) + { + freeaddrinfo( ai ); + return S_FALSE; + } + printf_addr( fmtW, addr, (struct sockaddr_in *)elem->ai_addr ); + freeaddrinfo( ai ); + V_VT( result ) = VT_BSTR; + V_BSTR( result ) = SysAllocString( addr ); + return S_OK; +#else + FIXME("getaddrinfo not found at build time\n"); + return S_FALSE; +#endif +} + +static HRESULT WINAPI dispex_InvokeEx( + IDispatchEx *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params, + VARIANT *result, EXCEPINFO *exep, IServiceProvider *caller ) +{ + if (id == DISPID_GLOBAL_DNSRESOLVE) + { + if (params->cArgs != 1) return DISP_E_BADPARAMCOUNT; + if (V_VT(¶ms->rgvarg[0]) != VT_BSTR) return DISP_E_BADVARTYPE; + return dns_resolve( V_BSTR(¶ms->rgvarg[0]), result ); + } + return DISP_E_MEMBERNOTFOUND; +} + +static const IDispatchExVtbl dispex_vtbl = +{ + dispex_QueryInterface, + dispex_AddRef, + dispex_Release, + dispex_GetTypeInfoCount, + dispex_GetTypeInfo, + dispex_GetIDsOfNames, + dispex_Invoke, + dispex_GetDispID, + dispex_InvokeEx, + dispex_DeleteMemberByName, + dispex_DeleteMemberByDispID, + dispex_GetMemberProperties, + dispex_GetMemberName, + dispex_GetNextDispID, + dispex_GetNameSpaceParent +}; + +static IDispatchEx global_dispex = { &dispex_vtbl }; + +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 ) +{ + if (!strcmpW( name, global_funcsW ) && mask == SCRIPTINFO_IUNKNOWN) + { + *item = (IUnknown *)&global_dispex; + return S_OK; + } + 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 BSTR include_pac_utils( BSTR script ) +{ + static const WCHAR pacjsW[] = {'p','a','c','.','j','s',0}; + HMODULE hmod = GetModuleHandleA( "winhttp.dll" ); + HRSRC rsrc; + DWORD size; + const char *data; + BSTR ret; + int len; + + if (!(rsrc = FindResourceW( hmod, pacjsW, (LPCWSTR)40 ))) return NULL; + size = SizeofResource( hmod, rsrc ); + data = LoadResource( hmod, rsrc ); + + len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 ); + if (!(ret = SysAllocStringLen( NULL, len + SysStringLen( script ) + 1 ))) return NULL; + MultiByteToWideChar( CP_ACP, 0, data, size, ret, len ); + ret[len] = 0; + strcatW( ret, script ); + return ret; +} + +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, full_script = 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 ); + hr = CLSIDFromProgID( jscriptW, &clsid ); + if (hr != S_OK) goto done; + + 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; + + hr = IActiveScript_AddNamedItem( engine, global_funcsW, SCRIPTITEM_GLOBALMEMBERS ); + if (hr != S_OK) goto done; + + if (!(full_script = include_pac_utils( script ))) goto done; + + hr = IActiveScriptParse64_ParseScriptText( parser, full_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] ) = hostname; + V_VT( &args[1] ) = VT_BSTR; + V_BSTR( &args[1] ) = SysAllocString( url ); + + 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[1] ); + if (hr != S_OK) + { + WARN("script failed 0x%08x\n", hr); + goto done; + } + ret = parse_script_result( result, info ); + +done: + SysFreeString( full_script ); + SysFreeString( hostname ); + 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( const WCHAR *url ) +{ + static const WCHAR typeW[] = {'*','/','*',0}; + static const WCHAR *acceptW[] = {typeW, NULL}; + HINTERNET ses, con = NULL, 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 (!(ses = WinHttpOpen( NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0 ))) goto done; + 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 ); + WinHttpCloseHandle( ses ); + 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( pac_url ))) goto done; + ret = run_script( script, url, info ); + SysFreeString( script ); + +done: + GlobalFree( detected_pac_url ); + release_object( &session->hdr ); + return ret; } /*********************************************************************** @@ -1351,7 +2086,7 @@ BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) KEY_WRITE, NULL, &key, NULL ); if (!l) { - DWORD size = sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD); + DWORD size = sizeof(struct connection_settings_header) + 2 * sizeof(DWORD); BYTE *buf; if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) @@ -1363,17 +2098,17 @@ BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) buf = heap_alloc( size ); if (buf) { - struct winhttp_settings_header *hdr = - (struct winhttp_settings_header *)buf; + struct connection_settings_header *hdr = + (struct connection_settings_header *)buf; DWORD *len = (DWORD *)(hdr + 1); - hdr->magic = WINHTTPSETTINGS_MAGIC; + hdr->magic = WINHTTP_SETTINGS_MAGIC; hdr->unknown = 0; if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { BYTE *dst; - hdr->flags = WINHTTP_PROXY_TYPE_PROXY; + hdr->flags = PROXY_TYPE_PROXY; *len++ = strlenW( info->lpszProxy ); for (dst = (BYTE *)len, src = info->lpszProxy; *src; src++, dst++) @@ -1391,7 +2126,7 @@ BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) } else { - hdr->flags = WINHTTP_PROXY_TYPE_DIRECT; + hdr->flags = PROXY_TYPE_DIRECT; *len++ = 0; *len++ = 0; } @@ -1606,7 +2341,6 @@ BOOL WINAPI WinHttpTimeToSystemTime( LPCWSTR string, SYSTEMTIME *time ) while (*s && !isdigitW( *s )) s++; if (*s == '\0') return TRUE; time->wSecond = strtolW( s, &end, 10 ); - s = end; time->wMilliseconds = 0; return TRUE; diff --git a/reactos/dll/win32/winhttp/url.c b/reactos/dll/win32/winhttp/url.c index 6ead33cbf27..d8b423cf8be 100644 --- a/reactos/dll/win32/winhttp/url.c +++ b/reactos/dll/win32/winhttp/url.c @@ -38,7 +38,7 @@ static BOOL set_component( WCHAR **str, DWORD *str_len, WCHAR *value, DWORD len, { if (!*str) { - if (len && (flags & ICU_DECODE)) + if (len && *str_len && (flags & (ICU_DECODE|ICU_ESCAPE))) { set_last_error( ERROR_INVALID_PARAMETER ); return FALSE; @@ -61,12 +61,109 @@ static BOOL set_component( WCHAR **str, DWORD *str_len, WCHAR *value, DWORD len, return TRUE; } -static BOOL decode_url( LPCWSTR url, LPWSTR buffer, LPDWORD buflen ) +static WCHAR *decode_url( LPCWSTR url, DWORD *len ) { - HRESULT hr = UrlCanonicalizeW( url, buffer, buflen, URL_WININET_COMPATIBILITY | URL_UNESCAPE ); - if (hr == E_POINTER) set_last_error( ERROR_INSUFFICIENT_BUFFER ); - if (hr == E_INVALIDARG) set_last_error( ERROR_INVALID_PARAMETER ); - return (SUCCEEDED(hr)) ? TRUE : FALSE; + const WCHAR *p = url; + WCHAR hex[3], *q, *ret; + + if (!(ret = heap_alloc( *len * sizeof(WCHAR) ))) return NULL; + q = ret; + while (*len > 0) + { + if (p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] )) + { + hex[0] = p[1]; + hex[1] = p[2]; + hex[2] = 0; + *q++ = strtolW( hex, NULL, 16 ); + p += 3; + *len -= 3; + } + else + { + *q++ = *p++; + *len -= 1; + } + } + *len = q - ret; + return ret; +} + +static BOOL need_escape( WCHAR c ) +{ + if (isalnumW( c )) return FALSE; + + if (c <= 31 || c >= 127) return TRUE; + else + { + switch (c) + { + case ' ': + case '"': + case '#': + case '%': + case '<': + case '>': + case ']': + case '\\': + case '[': + case '^': + case '`': + case '{': + case '|': + case '}': + case '~': + return TRUE; + default: + return FALSE; + } + } +} + +static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len ) +{ + static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + DWORD ret = len; + unsigned int i; + WCHAR *p = dst; + + for (i = 0; i < len; i++, p++) + { + if (need_escape( src[i] )) + { + p[0] = '%'; + p[1] = hex[(src[i] >> 4) & 0xf]; + p[2] = hex[src[i] & 0xf]; + ret += 2; + p += 2; + } + else *p = src[i]; + } + dst[ret] = 0; + return ret; +} + +static WCHAR *escape_url( LPCWSTR url, DWORD *len ) +{ + WCHAR *ret; + const WCHAR *p, *q; + + if ((p = q = strrchrW( url, '/' ))) + { + while (*q) + { + if (need_escape( *q )) *len += 2; + q++; + } + } + if (!(ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL; + if (!p) strcpyW( ret, url ); + else + { + memcpy( ret, url, (p - url) * sizeof(WCHAR) ); + copy_escape( ret + (p - url), p, q - p ); + } + return ret; } /*********************************************************************** @@ -75,13 +172,10 @@ static BOOL decode_url( LPCWSTR url, LPWSTR buffer, LPDWORD buflen ) BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW uc ) { BOOL ret = FALSE; - WCHAR *p, *q, *r; - WCHAR *url_decoded = NULL; + WCHAR *p, *q, *r, *url_decoded = NULL, *url_escaped = NULL; TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, uc); - if (flags & ICU_ESCAPE) FIXME("flag ICU_ESCAPE not supported\n"); - if (!url || !uc || uc->dwStructSize != sizeof(URL_COMPONENTS)) { set_last_error( ERROR_INVALID_PARAMETER ); @@ -89,30 +183,23 @@ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONEN } if (!len) len = strlenW( url ); - if (flags & ICU_DECODE) + if (flags & ICU_ESCAPE) { - WCHAR *url_tmp; - DWORD url_len = len + 1; - - if (!(url_tmp = HeapAlloc( GetProcessHeap(), 0, url_len * sizeof(WCHAR) ))) + if (!(url_escaped = escape_url( url, &len ))) { set_last_error( ERROR_OUTOFMEMORY ); return FALSE; } - memcpy( url_tmp, url, len * sizeof(WCHAR) ); - url_tmp[len] = 0; - if (!(url_decoded = HeapAlloc( GetProcessHeap(), 0, url_len * sizeof(WCHAR) ))) + url = url_escaped; + } + else if (flags & ICU_DECODE) + { + if (!(url_decoded = decode_url( url, &len ))) { - HeapFree( GetProcessHeap(), 0, url_tmp ); set_last_error( ERROR_OUTOFMEMORY ); return FALSE; } - if (decode_url( url_tmp, url_decoded, &url_len )) - { - len = url_len; - url = url_decoded; - } - HeapFree( GetProcessHeap(), 0, url_tmp ); + url = url_decoded; } if (!(p = strchrW( url, ':' ))) { @@ -207,7 +294,8 @@ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONEN debugstr_wn( uc->lpszExtraInfo, uc->dwExtraInfoLength )); exit: - HeapFree( GetProcessHeap(), 0, url_decoded ); + heap_free( url_decoded ); + heap_free( url_escaped ); return ret; } @@ -232,60 +320,6 @@ static BOOL uses_default_port( INTERNET_SCHEME scheme, INTERNET_PORT port ) return FALSE; } -static BOOL need_escape( WCHAR c ) -{ - if (isalnumW( c )) return FALSE; - - if (c <= 31 || c >= 127) return TRUE; - else - { - switch (c) - { - case ' ': - case '"': - case '#': - case '%': - case '<': - case '>': - case ']': - case '\\': - case '[': - case '^': - case '`': - case '{': - case '|': - case '}': - case '~': - return TRUE; - default: - return FALSE; - } - } -} - -static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len ) -{ - static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - DWORD ret = len; - unsigned int i; - WCHAR *p = dst; - - for (i = 0; i < len; i++, p++) - { - if (need_escape( src[i] )) - { - p[0] = '%'; - p[1] = hex[(src[i] >> 4) & 0xf]; - p[2] = hex[src[i] & 0xf]; - ret += 2; - p += 2; - } - else *p = src[i]; - } - dst[ret] = 0; - return ret; -} - static DWORD comp_length( DWORD len, DWORD flags, WCHAR *comp ) { DWORD ret; diff --git a/reactos/dll/win32/winhttp/winhttp_private.h b/reactos/dll/win32/winhttp/winhttp_private.h index 24440528308..021bc93803f 100644 --- a/reactos/dll/win32/winhttp/winhttp_private.h +++ b/reactos/dll/win32/winhttp/winhttp_private.h @@ -42,9 +42,11 @@ # define closesocket close # define ioctlsocket ioctl #endif +#include "ole2.h" static const WCHAR getW[] = {'G','E','T',0}; static const WCHAR postW[] = {'P','O','S','T',0}; +static const WCHAR headW[] = {'H','E','A','D',0}; static const WCHAR slashW[] = {'/',0}; static const WCHAR http1_0[] = {'H','T','T','P','/','1','.','0',0}; static const WCHAR http1_1[] = {'H','T','T','P','/','1','.','1',0}; @@ -118,6 +120,7 @@ typedef struct INTERNET_PORT hostport; INTERNET_PORT serverport; struct sockaddr_storage sockaddr; + BOOL resolved; } connect_t; typedef struct @@ -156,6 +159,8 @@ typedef struct DWORD content_read; /* bytes read so far */ header_t *headers; DWORD num_headers; + WCHAR **accept_types; + DWORD num_accept_types; } request_t; typedef struct _task_header_t task_header_t; @@ -237,6 +242,39 @@ BOOL add_request_headers( request_t *, LPCWSTR, DWORD, DWORD ) DECLSPEC_HIDDEN; void delete_domain( domain_t * ) DECLSPEC_HIDDEN; BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT port ) DECLSPEC_HIDDEN; +extern HRESULT WinHttpRequest_create( IUnknown *, void ** ) DECLSPEC_HIDDEN; + +static inline const char *debugstr_variant( const VARIANT *v ) +{ + if (!v) return "(null)"; + switch (V_VT(v)) + { + case VT_EMPTY: + return "{VT_EMPTY}"; + case VT_NULL: + return "{VT_NULL}"; + case VT_I4: + return wine_dbg_sprintf( "{VT_I4: %d}", V_I4(v) ); + case VT_R8: + return wine_dbg_sprintf( "{VT_R8: %lf}", V_R8(v) ); + case VT_BSTR: + return wine_dbg_sprintf( "{VT_BSTR: %s}", debugstr_w(V_BSTR(v)) ); + case VT_DISPATCH: + return wine_dbg_sprintf( "{VT_DISPATCH: %p}", V_DISPATCH(v) ); + case VT_BOOL: + return wine_dbg_sprintf( "{VT_BOOL: %x}", V_BOOL(v) ); + case VT_UNKNOWN: + return wine_dbg_sprintf( "{VT_UNKNOWN: %p}", V_UNKNOWN(v) ); + case VT_UINT: + return wine_dbg_sprintf( "{VT_UINT: %u}", V_UINT(v) ); + case VT_BSTR|VT_BYREF: + return wine_dbg_sprintf( "{VT_BSTR|VT_BYREF: ptr %p, data %s}", + V_BSTRREF(v), V_BSTRREF(v) ? debugstr_w( *V_BSTRREF(v) ) : NULL ); + default: + return wine_dbg_sprintf( "{vt %d}", V_VT(v) ); + } +} + static inline void *heap_alloc( SIZE_T size ) { return HeapAlloc( GetProcessHeap(), 0, size ); diff --git a/reactos/dll/win32/winhttp/winhttp_ros.diff b/reactos/dll/win32/winhttp/winhttp_ros.diff index 84b874997ad..9d867da2e9b 100644 --- a/reactos/dll/win32/winhttp/winhttp_ros.diff +++ b/reactos/dll/win32/winhttp/winhttp_ros.diff @@ -1,37 +1,44 @@ ---- wine-1.3.4/dlls/winhttp/net.c 2010-10-01 14:46:44.000000000 -0400 -+++ dll/win32/winhttp/net.c 2010-10-09 17:04:11.000000000 -0400 -@@ -158,6 +158,7 @@ +--- wine-1.5.4/dlls/winhttp/net.c 2012-06-20 14:30:41 +0200 ++++ dll/win32/winhttp/net.c 2012-06-21 18:00:53 +0200 +@@ -160,6 +160,7 @@ static void ssl_lock_callback(int mode, #endif /* translate a unix error code into a winsock error code */ -+#if 0 ++#ifndef __REACTOS__ static int sock_get_error( int err ) { #if !defined(__MINGW32__) && !defined (_MSC_VER) -@@ -223,6 +224,9 @@ +@@ -225,6 +226,15 @@ static int sock_get_error( int err ) #endif return err; } +#else +#define sock_get_error(x) WSAGetLastError() ++ ++static inline int unix_ioctl(int filedes, long request, void *arg) ++{ ++ return ioctlsocket(filedes, request, arg); ++} ++#define ioctlsocket unix_ioctl +#endif #ifdef SONAME_LIBSSL static PCCERT_CONTEXT X509_to_cert_context(X509 *cert) -@@ -632,11 +636,16 @@ +@@ -648,11 +658,17 @@ BOOL netconn_connect( netconn_t *conn, c res = sock_get_error( errno ); if (res == WSAEWOULDBLOCK || res == WSAEINPROGRESS) { - struct pollfd pfd; ++ // ReactOS: use select instead of poll + fd_set outfd; + struct timeval tv; -+ -+ FD_ZERO(&outfd); -+ FD_SET(conn->socket, &outfd); - pfd.fd = conn->socket; - pfd.events = POLLOUT; - if (poll( &pfd, 1, timeout ) > 0) ++ FD_ZERO(&outfd); ++ FD_SET(conn->socket, &outfd); ++ + tv.tv_sec = 0; + tv.tv_usec = timeout * 1000; + @@ -39,26 +46,25 @@ ret = TRUE; else res = sock_get_error( errno ); -@@ -832,7 +841,7 @@ +@@ -848,7 +864,8 @@ BOOL netconn_query_data_available( netco BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen ) { - struct pollfd pfd; ++ // ReactOS: use select instead of poll + fd_set infd; BOOL ret = FALSE; DWORD recvd = 0; -@@ -867,20 +876,22 @@ - return FALSE; +@@ -884,19 +901,21 @@ BOOL netconn_get_next_line( netconn_t *c #endif } -- + - pfd.fd = conn->socket; - pfd.events = POLLIN; -+ + FD_ZERO(&infd); + FD_SET(conn->socket, &infd); -+ ++ while (recvd < *buflen) { - int timeout, res; @@ -74,26 +80,14 @@ - timeout = -1; - if (poll( &pfd, 1, timeout ) > 0) + ptv = NULL; -+ ++ + if (select( 0, &infd, NULL, NULL, ptv ) > 0) { if ((res = recv( conn->socket, &buffer[recvd], 1, 0 )) <= 0) { ---- wine-1.3.4/dlls/winhttp/session.c 2010-10-01 14:46:44.000000000 -0400 -+++ dll/win32/winhttp/session.c 2010-10-09 17:04:11.000000000 -0400 -@@ -38,6 +38,9 @@ - #define DEFAULT_SEND_TIMEOUT 30000 - #define DEFAULT_RECEIVE_TIMEOUT 30000 - -+/* FIXME */ -+#define CP_UNIXCP CP_ACP -+ - void set_last_error( DWORD error ) - { - /* FIXME */ ---- wine-1.3.4/dlls/winhttp/request.c 2010-10-01 14:46:44.000000000 -0400 -+++ dll/win32/winhttp/request.c 2010-10-09 17:04:11.000000000 -0400 -@@ -34,6 +34,8 @@ +--- wine-1.5.4/dlls/winhttp/request.c 2012-06-20 14:30:41 +0200 ++++ dll/win32/winhttp/request.c 2012-06-21 17:32:47 +0200 +@@ -38,6 +38,8 @@ #include "winhttp_private.h" @@ -101,4 +95,43 @@ + WINE_DEFAULT_DEBUG_CHANNEL(winhttp); - static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0}; \ No newline at end of file + static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0}; +--- wine-1.5.4/dlls/winhttp/rsrc.rc 2012-06-20 14:30:41 +0200 ++++ dll/win32/winhttp/rsrc.rc 2012-07-14 15:25:28 +0200 +@@ -16,6 +16,12 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + ++/* @makedep: winhttp_tlb.tlb */ ++1 TYPELIB winhttp_tlb.tlb ++ ++/* @makedep: winhttp_tlb.rgs */ ++1 WINE_REGISTRY winhttp_tlb.rgs ++ + /* @makedep: pac.js */ + pac.js 40 "pac.js" + +--- wine-1.5.4/dlls/winhttp/session.c 2012-07-13 15:34:57 +0200 ++++ dll/win32/winhttp/session.c 2012-06-23 17:51:47 +0200 +@@ -95,6 +95,9 @@ static void session_destroy( object_head + heap_free( session->proxy_username ); + heap_free( session->proxy_password ); + heap_free( session ); ++#ifdef __REACTOS__ ++ WSACleanup(); ++#endif + } + + static BOOL session_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen ) +@@ -203,6 +206,11 @@ HINTERNET WINAPI WinHttpOpen( LPCWSTR ag + { + session_t *session; + HINTERNET handle = NULL; ++#ifdef __REACTOS__ ++ WSADATA wsaData; ++ int error = WSAStartup(MAKEWORD(2, 2), &wsaData); ++ if (error) ERR("WSAStartup failed: %d\n", error); ++#endif + + TRACE("%s, %u, %s, %s, 0x%08x\n", debugstr_w(agent), access, debugstr_w(proxy), debugstr_w(bypass), flags); + diff --git a/reactos/dll/win32/winhttp/winhttp_tlb.idl b/reactos/dll/win32/winhttp/winhttp_tlb.idl new file mode 100644 index 00000000000..74beb57e31e --- /dev/null +++ b/reactos/dll/win32/winhttp/winhttp_tlb.idl @@ -0,0 +1,21 @@ +/* + * Typelib for winhttp + * + * Copyright 2011 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "httprequest.idl" diff --git a/reactos/dll/win32/winhttp/winhttp_tlb.rgs b/reactos/dll/win32/winhttp/winhttp_tlb.rgs new file mode 100644 index 00000000000..283b633ca61 --- /dev/null +++ b/reactos/dll/win32/winhttp/winhttp_tlb.rgs @@ -0,0 +1,37 @@ +HKCR +{ + NoRemove Typelib + { + NoRemove '{662901FC-6951-4854-9EB2-D9A2570F2B2E}' + { + '5.1' = s 'Microsoft WinHTTP Services, version 5.1' + { + '0' { win32 = s '%MODULE%' } + FLAGS = s '0' + } + } + } + NoRemove Interface + { + '{016FE2EC-B2C8-45F8-B23B-39E53A75396B}' = s 'IWinHttpRequest' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{662901FC-6951-4854-9EB2-D9A2570F2B2E}' { val Version = s '5.1' } + } + } + NoRemove CLSID + { + '{2087C2F4-2CEF-4953-A8AB-66779B670495}' = s 'WinHttpRequest Component version 5.1' + { + InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } + ProgId = s 'WinHttp.WinHttpRequest.5.1' + TypeLib = s '{662901FC-6951-4854-9EB2-D9A2570F2B2E}' + Version = s '5.1' + } + } + 'WinHttp.WinHttpRequest.5.1' = s 'WinHttpRequest Component version 5.1' + { + CLSID = s '{2087C2F4-2CEF-4953-A8AB-66779B670495}' + } +} diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 8606f539e2c..74762718b01 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -184,7 +184,7 @@ reactos/dll/win32/wer # Autosync reactos/dll/win32/windowscodecs # Autosync reactos/dll/win32/winemp3.acm # Synced to Wine-1.5.4 reactos/dll/win32/wing32 # Out of sync -reactos/dll/win32/winhttp # Autosync +reactos/dll/win32/winhttp # Synced to Wine-1.5.4 reactos/dll/win32/wininet # Synced to Wine-1.5.4 reactos/dll/win32/winmm # Forked at Wine-20050628 reactos/dll/win32/winmm/midimap # Forked at Wine-20050628