https://git.reactos.org/?p=reactos.git;a=commitdiff;h=9048bdf976df38c94e2adb...
commit 9048bdf976df38c94e2adb6faf976ccb83e952c2 Author: Amine Khaldi amine.khaldi@reactos.org AuthorDate: Sat Jan 20 13:55:24 2018 +0100 Commit: Amine Khaldi amine.khaldi@reactos.org CommitDate: Sat Jan 20 13:55:24 2018 +0100
[WINHTTP] Sync with Wine 3.0. CORE-14225 --- dll/win32/winhttp/cookie.c | 2 +- dll/win32/winhttp/net.c | 56 +- dll/win32/winhttp/request.c | 2091 ++++++++++++++++++----------------- dll/win32/winhttp/session.c | 38 +- dll/win32/winhttp/winhttp_private.h | 9 +- media/doc/README.WINE | 2 +- 6 files changed, 1116 insertions(+), 1082 deletions(-)
diff --git a/dll/win32/winhttp/cookie.c b/dll/win32/winhttp/cookie.c index eb37d6f852..2d2dbf325e 100644 --- a/dll/win32/winhttp/cookie.c +++ b/dll/win32/winhttp/cookie.c @@ -140,7 +140,7 @@ static cookie_t *parse_cookie( const WCHAR *string )
if (*p++ == '=') { - while (*p && *p == ' ') p++; + while (*p == ' ') p++; len = strlenW( p ); while (len && p[len - 1] == ' ') len--;
diff --git a/dll/win32/winhttp/net.c b/dll/win32/winhttp/net.c index 5c4ace898c..bf78b22647 100644 --- a/dll/win32/winhttp/net.c +++ b/dll/win32/winhttp/net.c @@ -131,7 +131,7 @@ static int sock_send(int fd, const void *msg, size_t len, int flags) int ret; do { - ret = send(fd, msg, len, flags); + if ((ret = send(fd, msg, len, flags)) == -1) WARN("send error %s\n", strerror(errno)); } while(ret == -1 && errno == EINTR); return ret; @@ -142,7 +142,7 @@ static int sock_recv(int fd, void *msg, size_t len, int flags) int ret; do { - ret = recv(fd, msg, len, flags); + if ((ret = recv(fd, msg, len, flags)) == -1) WARN("recv error %s\n", strerror(errno)); } while(ret == -1 && errno == EINTR); return ret; @@ -241,41 +241,6 @@ static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD secu return err; }
-static SecHandle cred_handle; -static BOOL cred_handle_initialized; - -static CRITICAL_SECTION init_sechandle_cs; -static CRITICAL_SECTION_DEBUG init_sechandle_cs_debug = { - 0, 0, &init_sechandle_cs, - { &init_sechandle_cs_debug.ProcessLocksList, - &init_sechandle_cs_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": init_sechandle_cs") } -}; -static CRITICAL_SECTION init_sechandle_cs = { &init_sechandle_cs_debug, -1, 0, 0, 0, 0 }; - -static BOOL ensure_cred_handle(void) -{ - BOOL ret = TRUE; - - EnterCriticalSection(&init_sechandle_cs); - - if(!cred_handle_initialized) { - SECURITY_STATUS res; - - res = AcquireCredentialsHandleW(NULL, (WCHAR*)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, NULL, - NULL, NULL, &cred_handle, NULL); - if(res == SEC_E_OK) { - cred_handle_initialized = TRUE; - }else { - WARN("AcquireCredentialsHandleW failed: %u\n", res); - ret = FALSE; - } - } - - LeaveCriticalSection(&init_sechandle_cs); - return ret; -} - #ifdef __REACTOS__ static BOOL winsock_initialized = FALSE; BOOL netconn_init_winsock() @@ -300,9 +265,6 @@ BOOL netconn_init_winsock()
void netconn_unload( void ) { - if(cred_handle_initialized) - FreeCredentialsHandle(&cred_handle); - DeleteCriticalSection(&init_sechandle_cs); #ifndef HAVE_GETADDRINFO DeleteCriticalSection(&cs_gethostbyname); #endif @@ -439,7 +401,7 @@ BOOL netconn_close( netconn_t *conn ) return TRUE; }
-BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname, DWORD security_flags ) +BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle ) { SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}}; SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs}; @@ -455,14 +417,11 @@ BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname, DWORD security_fl const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
- if(!ensure_cred_handle()) - return FALSE; - read_buf = heap_alloc(read_buf_size); if(!read_buf) return FALSE;
- status = InitializeSecurityContextW(&cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0, + status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0, &ctx, &out_desc, &attrs, NULL);
assert(status != SEC_E_OK); @@ -514,7 +473,6 @@ BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname, DWORD security_fl
size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0); if(size < 1) { - WARN("recv error\n"); status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; break; } @@ -523,7 +481,7 @@ BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname, DWORD security_fl
in_bufs[0].cbBuffer += size; in_bufs[0].pvBuffer = read_buf; - status = InitializeSecurityContextW(&cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc, + status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc, 0, NULL, &out_desc, &attrs, NULL); TRACE("InitializeSecurityContext ret %08x\n", status);
@@ -650,10 +608,8 @@ static BOOL read_ssl_chunk(netconn_t *conn, void *buf, SIZE_T buf_size, SIZE_T * conn->extra_buf = NULL; }else { buf_len = sock_recv(conn->socket, conn->ssl_buf+conn->extra_len, ssl_buf_size-conn->extra_len, 0); - if(buf_len < 0) { - WARN("recv failed\n"); + if(buf_len < 0) return FALSE; - }
if(!buf_len) { *eof = TRUE; diff --git a/dll/win32/winhttp/request.c b/dll/win32/winhttp/request.c index 128d5cb14c..f3098e34d0 100644 --- a/dll/win32/winhttp/request.c +++ b/dll/win32/winhttp/request.c @@ -29,6 +29,7 @@ #include <winuser.h> #include <httprequest.h> #include <httprequestid.h> +#include <schannel.h>
#include "inet_ntop.c"
@@ -390,7 +391,7 @@ static BOOL delete_header( request_t *request, DWORD index ) return TRUE; }
-BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only ) +static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only ) { int index; header_t hdr; @@ -507,7 +508,7 @@ BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD BOOL ret; request_t *request;
- TRACE("%p, %s, 0x%x, 0x%08x\n", hrequest, debugstr_w(headers), len, flags); + TRACE("%p, %s, %u, 0x%08x\n", hrequest, debugstr_wn(headers, len), len, flags);
if (!headers || !len) { @@ -838,1325 +839,1367 @@ BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, return ret; }
-static LPWSTR concatenate_string_list( LPCWSTR *list, int len ) -{ - LPCWSTR *t; - LPWSTR str; - - for( t = list; *t ; t++ ) - len += strlenW( *t ); - len++; - - str = heap_alloc( len * sizeof(WCHAR) ); - if (!str) return NULL; - *str = 0; +#undef ARRAYSIZE +#define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0]))
- for( t = list; *t ; t++ ) - strcatW( str, *t ); +static const WCHAR basicW[] = {'B','a','s','i','c',0}; +static const WCHAR ntlmW[] = {'N','T','L','M',0}; +static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t',0}; +static const WCHAR digestW[] = {'D','i','g','e','s','t',0}; +static const WCHAR negotiateW[] = {'N','e','g','o','t','i','a','t','e',0};
- return str; +static const struct +{ + const WCHAR *str; + unsigned int len; + DWORD scheme; } +auth_schemes[] = +{ + { basicW, ARRAYSIZE(basicW) - 1, WINHTTP_AUTH_SCHEME_BASIC }, + { ntlmW, ARRAYSIZE(ntlmW) - 1, WINHTTP_AUTH_SCHEME_NTLM }, + { passportW, ARRAYSIZE(passportW) - 1, WINHTTP_AUTH_SCHEME_PASSPORT }, + { digestW, ARRAYSIZE(digestW) - 1, WINHTTP_AUTH_SCHEME_DIGEST }, + { negotiateW, ARRAYSIZE(negotiateW) - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE } +}; +static const unsigned int num_auth_schemes = sizeof(auth_schemes)/sizeof(auth_schemes[0]);
-static LPWSTR build_header_request_string( request_t *request, LPCWSTR verb, - LPCWSTR path, LPCWSTR version ) +static enum auth_scheme scheme_from_flag( DWORD flag ) { - static const WCHAR crlf[] = {'\r','\n',0}; - static const WCHAR space[] = { ' ',0 }; - static const WCHAR colon[] = { ':',' ',0 }; - static const WCHAR twocrlf[] = {'\r','\n','\r','\n', 0}; - LPWSTR requestString; - DWORD len, n; - LPCWSTR *req; - UINT i; - LPWSTR p; + int i;
- /* allocate space for an array of all the string pointers to be added */ - len = (request->num_headers) * 4 + 10; - req = heap_alloc( len * sizeof(LPCWSTR) ); - if (!req) return NULL; + for (i = 0; i < num_auth_schemes; i++) if (flag == auth_schemes[i].scheme) return i; + return SCHEME_INVALID; +}
- /* add the verb, path and HTTP version string */ - n = 0; - req[n++] = verb; - req[n++] = space; - req[n++] = path; - req[n++] = space; - req[n++] = version; +static DWORD auth_scheme_from_header( WCHAR *header ) +{ + unsigned int i;
- /* Append custom request headers */ - for (i = 0; i < request->num_headers; i++) + for (i = 0; i < num_auth_schemes; i++) { - if (request->headers[i].is_request) - { - req[n++] = crlf; - req[n++] = request->headers[i].field; - req[n++] = colon; - req[n++] = request->headers[i].value; - - TRACE("Adding custom header %s (%s)\n", - debugstr_w(request->headers[i].field), - debugstr_w(request->headers[i].value)); - } + if (!strncmpiW( header, auth_schemes[i].str, auth_schemes[i].len ) && + (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme; } - - if( n >= len ) - ERR("oops. buffer overrun\n"); - - req[n] = NULL; - requestString = concatenate_string_list( req, 4 ); - heap_free( req ); - if (!requestString) return NULL; - - /* - * Set (header) termination string for request - * Make sure there are exactly two new lines at the end of the request - */ - p = &requestString[strlenW(requestString)-1]; - while ( (*p == '\n') || (*p == '\r') ) - p--; - strcpyW( p+1, twocrlf ); - - return requestString; + return 0; }
-static BOOL read_reply( request_t *request ); - -static BOOL secure_proxy_connect( request_t *request ) +static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD supported, LPDWORD first ) { - static const WCHAR verbConnect[] = {'C','O','N','N','E','C','T',0}; - static const WCHAR fmt[] = {'%','s',':','%','u',0}; + DWORD index = 0, supported_schemes = 0, first_scheme = 0; BOOL ret = FALSE; - LPWSTR path; - connect_t *connect = request->connect;
- path = heap_alloc( (strlenW( connect->hostname ) + 13) * sizeof(WCHAR) ); - if (path) + for (;;) { - LPWSTR requestString; - - sprintfW( path, fmt, connect->hostname, connect->hostport ); - requestString = build_header_request_string( request, verbConnect, - path, http1_1 ); - heap_free( path ); - if (requestString) - { - LPSTR req_ascii = strdupWA( requestString ); + WCHAR *buffer; + DWORD size, scheme;
- heap_free( requestString ); - if (req_ascii) - { - int len = strlen( req_ascii ), bytes_sent; + size = 0; + query_headers( request, level, NULL, NULL, &size, &index ); + if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) break;
- ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent ); - heap_free( req_ascii ); - if (ret) - ret = read_reply( request ); - } + index--; + if (!(buffer = heap_alloc( size ))) return FALSE; + if (!query_headers( request, level, NULL, buffer, &size, &index )) + { + heap_free( buffer ); + return FALSE; } - } - return ret; -} + scheme = auth_scheme_from_header( buffer ); + heap_free( buffer ); + if (!scheme) continue;
-#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 46 -#endif + if (!first_scheme) first_scheme = scheme; + supported_schemes |= scheme;
-static WCHAR *addr_to_str( struct sockaddr_storage *addr ) -{ - char buf[INET6_ADDRSTRLEN]; - void *src; + ret = TRUE; + }
- switch (addr->ss_family) + if (ret) { - 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->ss_family); - return NULL; + *supported = supported_schemes; + *first = first_scheme; } - if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL; - return strdupAW( buf ); + return ret; }
-static CRITICAL_SECTION connection_pool_cs; -static CRITICAL_SECTION_DEBUG connection_pool_debug = +/*********************************************************************** + * WinHttpQueryAuthSchemes (winhttp.@) + */ +BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target ) { - 0, 0, &connection_pool_cs, - { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") } -}; -static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 }; + BOOL ret = FALSE; + request_t *request;
-static struct list connection_pool = LIST_INIT( connection_pool ); + TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
-void release_host( hostdata_t *host ) -{ - LONG ref; + if (!(request = (request_t *)grab_object( hrequest ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return FALSE; + } + if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST) + { + release_object( &request->hdr ); + set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); + return FALSE; + } + if (!supported || !first || !target) + { + release_object( &request->hdr ); + set_last_error( ERROR_INVALID_PARAMETER ); + return FALSE;
- EnterCriticalSection( &connection_pool_cs ); - if (!(ref = --host->ref)) list_remove( &host->entry ); - LeaveCriticalSection( &connection_pool_cs ); - if (ref) return; + }
- assert( list_empty( &host->connections ) ); - heap_free( host->hostname ); - heap_free( host ); -} + if (query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first )) + { + *target = WINHTTP_AUTH_TARGET_SERVER; + ret = TRUE; + } + else if (query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first )) + { + *target = WINHTTP_AUTH_TARGET_PROXY; + ret = TRUE; + }
-static BOOL connection_collector_running; + release_object( &request->hdr ); + if (ret) set_last_error( ERROR_SUCCESS ); + return ret; +}
-static DWORD WINAPI connection_collector(void *arg) +static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 ) { - unsigned int remaining_connections; - netconn_t *netconn, *next_netconn; - hostdata_t *host, *next_host; - ULONGLONG now; + UINT n = 0, x; + static const char base64enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- do + while (len > 0) { - /* FIXME: Use more sophisticated method */ - Sleep(5000); - remaining_connections = 0; - now = GetTickCount64(); - - EnterCriticalSection(&connection_pool_cs); + /* first 6 bits, all from bin[0] */ + base64[n++] = base64enc[(bin[0] & 0xfc) >> 2]; + x = (bin[0] & 3) << 4;
- LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, hostdata_t, entry) - { - LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, netconn_t, entry) - { - if (netconn->keep_until < now) - { - TRACE("freeing %p\n", netconn); - list_remove(&netconn->entry); - netconn_close(netconn); - } - else - { - remaining_connections++; - } - } + /* next 6 bits, 2 from bin[0] and 4 from bin[1] */ + if (len == 1) + { + base64[n++] = base64enc[x]; + base64[n++] = '='; + base64[n++] = '='; + break; } + base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)]; + x = (bin[1] & 0x0f) << 2;
- if (!remaining_connections) connection_collector_running = FALSE; - - LeaveCriticalSection(&connection_pool_cs); - } while(remaining_connections); + /* next 6 bits 4 from bin[1] and 2 from bin[2] */ + if (len == 2) + { + base64[n++] = base64enc[x]; + base64[n++] = '='; + break; + } + base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
- FreeLibraryAndExitThread( winhttp_instance, 0 ); + /* last 6 bits, all from bin [2] */ + base64[n++] = base64enc[bin[2] & 0x3f]; + bin += 3; + len -= 3; + } + base64[n] = 0; + return n; }
-static void cache_connection( netconn_t *netconn ) +static inline char decode_char( WCHAR c ) { - TRACE( "caching connection %p\n", netconn ); + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '+') return 62; + if (c == '/') return 63; + return 64; +}
- EnterCriticalSection( &connection_pool_cs ); +static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf ) +{ + unsigned int i = 0; + char c0, c1, c2, c3; + const WCHAR *p = base64;
- netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT; - list_add_head( &netconn->host->connections, &netconn->entry ); + while (len > 4) + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0; + if ((c3 = decode_char( p[3] )) > 63) return 0;
- if (!connection_collector_running) + if (buf) + { + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); + buf[i + 2] = (c2 << 6) | c3; + } + len -= 4; + i += 3; + p += 4; + } + if (p[2] == '=') { - HMODULE module; - HANDLE thread; + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0;
- GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)winhttp_instance, &module ); + if (buf) buf[i] = (c0 << 2) | (c1 >> 4); + i++; + } + else if (p[3] == '=') + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0;
- thread = CreateThread(NULL, 0, connection_collector, NULL, 0, NULL); - if (thread) + if (buf) { - CloseHandle( thread ); - connection_collector_running = TRUE; + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); } - else + i += 2; + } + else + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0; + if ((c3 = decode_char( p[3] )) > 63) return 0; + + if (buf) { - FreeLibrary( winhttp_instance ); + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); + buf[i + 2] = (c2 << 6) | c3; } + i += 3; } - - LeaveCriticalSection( &connection_pool_cs ); + return i; }
-static BOOL open_connection( request_t *request ) +static struct authinfo *alloc_authinfo(void) { - BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE; - hostdata_t *host = NULL, *iter; - netconn_t *netconn = NULL; - connect_t *connect; - WCHAR *addressW = NULL; - INTERNET_PORT port; - DWORD len; + struct authinfo *ret;
- if (request->netconn) goto done; + if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
- connect = request->connect; - port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80); + SecInvalidateHandle( &ret->cred ); + SecInvalidateHandle( &ret->ctx ); + memset( &ret->exp, 0, sizeof(ret->exp) ); + ret->scheme = 0; + ret->attr = 0; + ret->max_token = 0; + ret->data = NULL; + ret->data_len = 0; + ret->finished = FALSE; + return ret; +}
- EnterCriticalSection( &connection_pool_cs ); +void destroy_authinfo( struct authinfo *authinfo ) +{ + if (!authinfo) return;
- LIST_FOR_EACH_ENTRY( iter, &connection_pool, hostdata_t, entry ) - { - if (iter->port == port && !strcmpW( connect->servername, iter->hostname ) && !is_secure == !iter->secure) - { - host = iter; - host->ref++; - break; - } - } + if (SecIsValidHandle( &authinfo->ctx )) + DeleteSecurityContext( &authinfo->ctx ); + if (SecIsValidHandle( &authinfo->cred )) + FreeCredentialsHandle( &authinfo->cred );
- if (!host) + heap_free( authinfo->data ); + heap_free( authinfo ); +} + +static BOOL get_authvalue( request_t *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len ) +{ + DWORD size, index = 0; + for (;;) { - if ((host = heap_alloc( sizeof(*host) ))) - { - host->ref = 1; - host->secure = is_secure; - host->port = port; - list_init( &host->connections ); - if ((host->hostname = strdupW( connect->servername ))) - { - list_add_head( &connection_pool, &host->entry ); - } - else - { - heap_free( host ); - host = NULL; - } - } + size = len; + if (!query_headers( request, level, NULL, buffer, &size, &index )) return FALSE; + if (auth_scheme_from_header( buffer ) == scheme) break; } + return TRUE; +}
- LeaveCriticalSection( &connection_pool_cs ); +static BOOL do_authorization( request_t *request, DWORD target, DWORD scheme_flag ) +{ + struct authinfo *authinfo, **auth_ptr; + enum auth_scheme scheme = scheme_from_flag( scheme_flag ); + const WCHAR *auth_target, *username, *password; + WCHAR auth_value[2048], *auth_reply; + DWORD len = sizeof(auth_value), len_scheme, flags; + BOOL ret, has_auth_value;
- if (!host) return FALSE; + if (scheme == SCHEME_INVALID) return FALSE;
- for (;;) + switch (target) { - EnterCriticalSection( &connection_pool_cs ); - if (!list_empty( &host->connections )) + case WINHTTP_AUTH_TARGET_SERVER: + has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len ); + auth_ptr = &request->authinfo; + auth_target = attr_authorization; + if (request->creds[TARGET_SERVER][scheme].username) { - netconn = LIST_ENTRY( list_head( &host->connections ), netconn_t, entry ); - list_remove( &netconn->entry ); + if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE; + username = request->creds[TARGET_SERVER][scheme].username; + password = request->creds[TARGET_SERVER][scheme].password; } - LeaveCriticalSection( &connection_pool_cs ); - if (!netconn) break; + else + { + if (!has_auth_value) return FALSE; + username = request->connect->username; + password = request->connect->password; + } + break;
- if (netconn_is_alive( netconn )) break; - TRACE("connection %p no longer alive, closing\n", netconn); - netconn_close( netconn ); - netconn = NULL; - } + case WINHTTP_AUTH_TARGET_PROXY: + if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len )) + return FALSE; + auth_ptr = &request->proxy_authinfo; + auth_target = attr_proxy_authorization; + if (request->creds[TARGET_PROXY][scheme].username) + { + username = request->creds[TARGET_PROXY][scheme].username; + password = request->creds[TARGET_PROXY][scheme].password; + } + else + { + username = request->connect->session->proxy_username; + password = request->connect->session->proxy_password; + } + break;
- if (!connect->resolved && netconn) - { - connect->sockaddr = netconn->sockaddr; - connect->resolved = TRUE; + default: + WARN("unknown target %x\n", target); + return FALSE; } + authinfo = *auth_ptr;
- if (!connect->resolved) + switch (scheme) { - len = strlenW( host->hostname ) + 1; - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len ); + case SCHEME_BASIC: + { + int userlen, passlen;
- if (!netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout )) - { - release_host( host ); - return FALSE; - } - connect->resolved = TRUE; + if (!username || !password) return FALSE; + if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
- if (!(addressW = addr_to_str( &connect->sockaddr ))) - { - release_host( host ); - return FALSE; - } - len = strlenW( addressW ) + 1; - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len ); - } + userlen = WideCharToMultiByte( CP_UTF8, 0, username, strlenW( username ), NULL, 0, NULL, NULL ); + passlen = WideCharToMultiByte( CP_UTF8, 0, password, strlenW( password ), NULL, 0, NULL, NULL );
- if (!netconn) + authinfo->data_len = userlen + 1 + passlen; + if (!(authinfo->data = heap_alloc( authinfo->data_len ))) return FALSE; + + WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL ); + authinfo->data[userlen] = ':'; + WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL ); + + authinfo->scheme = SCHEME_BASIC; + authinfo->finished = TRUE; + break; + } + case SCHEME_NTLM: + case SCHEME_NEGOTIATE: { - if (!addressW && !(addressW = addr_to_str( &connect->sockaddr ))) + SECURITY_STATUS status; + SecBufferDesc out_desc, in_desc; + SecBuffer out, in; + ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE; + const WCHAR *p; + BOOL first = FALSE; + + if (!authinfo) { - release_host( host ); - return FALSE; - } + TimeStamp exp; + SEC_WINNT_AUTH_IDENTITY_W id; + WCHAR *domain, *user;
- TRACE("connecting to %s:%u\n", debugstr_w(addressW), port); + if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
- send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 ); + first = TRUE; + domain = (WCHAR *)username; + user = strchrW( username, '\' );
- if (!(netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout ))) - { - heap_free( addressW ); - release_host( host ); - return FALSE; - } - netconn_set_timeout( netconn, TRUE, request->send_timeout ); - netconn_set_timeout( netconn, FALSE, request->recv_timeout ); - if (is_secure) - { - if (connect->session->proxy_server && - strcmpiW( connect->hostname, connect->servername )) + if (user) user++; + else { - if (!secure_proxy_connect( request )) + user = (WCHAR *)username; + domain = NULL; + } + id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + id.User = user; + id.UserLength = strlenW( user ); + id.Domain = domain; + id.DomainLength = domain ? user - domain - 1 : 0; + id.Password = (WCHAR *)password; + id.PasswordLength = strlenW( password ); + + status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str, + SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL, + &authinfo->cred, &exp ); + if (status == SEC_E_OK) + { + PSecPkgInfoW info; + status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info ); + if (status == SEC_E_OK) { - heap_free( addressW ); - netconn_close( netconn ); - return FALSE; + authinfo->max_token = info->cbMaxToken; + FreeContextBuffer( info ); } } - if (!netconn_secure_connect( netconn, connect->hostname, request->security_flags )) + if (status != SEC_E_OK) { - heap_free( addressW ); - netconn_close( netconn ); + WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n", + debugstr_w(auth_schemes[scheme].str), status); + heap_free( authinfo ); return FALSE; } + authinfo->scheme = scheme; } + else if (authinfo->finished) return FALSE;
- request->netconn = netconn; - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 ); - } - else - { - TRACE("using connection %p\n", netconn); + if ((strlenW( auth_value ) < auth_schemes[authinfo->scheme].len || + strncmpiW( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len ))) + { + ERR("authentication scheme changed from %s to %s\n", + debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value)); + destroy_authinfo( authinfo ); + *auth_ptr = NULL; + return FALSE; + } + in.BufferType = SECBUFFER_TOKEN; + in.cbBuffer = 0; + in.pvBuffer = NULL;
- netconn_set_timeout( netconn, TRUE, request->send_timeout ); - netconn_set_timeout( netconn, FALSE, request->recv_timeout ); - request->netconn = netconn; + in_desc.ulVersion = 0; + in_desc.cBuffers = 1; + in_desc.pBuffers = ∈ + + p = auth_value + auth_schemes[scheme].len; + if (*p == ' ') + { + int len = strlenW( ++p ); + in.cbBuffer = decode_base64( p, len, NULL ); + if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) { + destroy_authinfo( authinfo ); + *auth_ptr = NULL; + return FALSE; + } + decode_base64( p, len, in.pvBuffer ); + } + out.BufferType = SECBUFFER_TOKEN; + out.cbBuffer = authinfo->max_token; + if (!(out.pvBuffer = heap_alloc( authinfo->max_token ))) + { + heap_free( in.pvBuffer ); + destroy_authinfo( authinfo ); + *auth_ptr = NULL; + return FALSE; + } + out_desc.ulVersion = 0; + out_desc.cBuffers = 1; + out_desc.pBuffers = &out; + + status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx, + first ? request->connect->servername : NULL, flags, 0, + SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0, + &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp ); + heap_free( in.pvBuffer ); + if (status == SEC_E_OK) + { + heap_free( authinfo->data ); + authinfo->data = out.pvBuffer; + authinfo->data_len = out.cbBuffer; + authinfo->finished = TRUE; + TRACE("sending last auth packet\n"); + } + else if (status == SEC_I_CONTINUE_NEEDED) + { + heap_free( authinfo->data ); + authinfo->data = out.pvBuffer; + authinfo->data_len = out.cbBuffer; + TRACE("sending next auth packet\n"); + } + else + { + ERR("InitializeSecurityContextW failed with error 0x%08x\n", status); + heap_free( out.pvBuffer ); + destroy_authinfo( authinfo ); + *auth_ptr = NULL; + return FALSE; + } + break; + } + default: + ERR("invalid scheme %u\n", scheme); + return FALSE; } + *auth_ptr = authinfo;
-done: - request->read_pos = request->read_size = 0; - request->read_chunked = FALSE; - request->read_chunked_size = ~0u; - request->read_chunked_eof = FALSE; - heap_free( addressW ); - return TRUE; -} + len_scheme = auth_schemes[authinfo->scheme].len; + len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3; + if (!(auth_reply = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
-void close_connection( request_t *request ) -{ - if (!request->netconn) return; + memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) ); + auth_reply[len_scheme] = ' '; + encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
- send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 ); - netconn_close( request->netconn ); - request->netconn = NULL; - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 ); + flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE; + ret = process_header( request, auth_target, auth_reply, flags, TRUE ); + heap_free( auth_reply ); + return ret; }
-static BOOL add_host_header( request_t *request, DWORD modifier ) +static LPWSTR concatenate_string_list( LPCWSTR *list, int len ) { - BOOL ret; - DWORD len; - WCHAR *host; - static const WCHAR fmt[] = {'%','s',':','%','u',0}; - connect_t *connect = request->connect; - INTERNET_PORT port; + LPCWSTR *t; + LPWSTR str;
- port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80); + for( t = list; *t ; t++ ) + len += strlenW( *t ); + len++;
- if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT) - { - return process_header( request, attr_host, connect->hostname, modifier, TRUE ); - } - len = strlenW( connect->hostname ) + 7; /* sizeof(":65335") */ - if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE; - sprintfW( host, fmt, connect->hostname, port ); - ret = process_header( request, attr_host, host, modifier, TRUE ); - heap_free( host ); - return ret; -} + str = heap_alloc( len * sizeof(WCHAR) ); + if (!str) return NULL; + *str = 0;
-static void clear_response_headers( request_t *request ) -{ - unsigned int i; + for( t = list; *t ; t++ ) + strcatW( str, *t );
- for (i = 0; i < request->num_headers; i++) - { - if (!request->headers[i].field) continue; - if (!request->headers[i].value) continue; - if (request->headers[i].is_request) continue; - delete_header( request, i ); - i--; - } + return str; }
-/* remove some amount of data from the read buffer */ -static void remove_data( request_t *request, int count ) +static LPWSTR build_header_request_string( request_t *request, LPCWSTR verb, + LPCWSTR path, LPCWSTR version ) { - if (!(request->read_size -= count)) request->read_pos = 0; - else request->read_pos += count; -} + static const WCHAR crlf[] = {'\r','\n',0}; + static const WCHAR space[] = { ' ',0 }; + static const WCHAR colon[] = { ':',' ',0 }; + static const WCHAR twocrlf[] = {'\r','\n','\r','\n', 0}; + LPWSTR requestString; + DWORD len, n; + LPCWSTR *req; + UINT i; + LPWSTR p;
-/* read some more data into the read buffer */ -static BOOL read_more_data( request_t *request, int maxlen, BOOL notify ) -{ - int len; - BOOL ret; + /* allocate space for an array of all the string pointers to be added */ + len = (request->num_headers) * 4 + 10; + req = heap_alloc( len * sizeof(LPCWSTR) ); + if (!req) return NULL;
- if (request->read_chunked_eof) return FALSE; + /* add the verb, path and HTTP version string */ + n = 0; + req[n++] = verb; + req[n++] = space; + req[n++] = path; + req[n++] = space; + req[n++] = version;
- if (request->read_size && request->read_pos) + /* Append custom request headers */ + for (i = 0; i < request->num_headers; i++) { - /* move existing data to the start of the buffer */ - memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size ); - request->read_pos = 0; + if (request->headers[i].is_request) + { + req[n++] = crlf; + req[n++] = request->headers[i].field; + req[n++] = colon; + req[n++] = request->headers[i].value; + + TRACE("Adding custom header %s (%s)\n", + debugstr_w(request->headers[i].field), + debugstr_w(request->headers[i].value)); + } } - if (maxlen == -1) maxlen = sizeof(request->read_buf);
- if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 ); + if( n >= len ) + ERR("oops. buffer overrun\n");
- ret = netconn_recv( request->netconn, request->read_buf + request->read_size, - maxlen - request->read_size, 0, &len ); + req[n] = NULL; + requestString = concatenate_string_list( req, 4 ); + heap_free( req ); + if (!requestString) return NULL;
- if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) ); + /* + * Set (header) termination string for request + * Make sure there are exactly two new lines at the end of the request + */ + p = &requestString[strlenW(requestString)-1]; + while ( (*p == '\n') || (*p == '\r') ) + p--; + strcpyW( p+1, twocrlf );
- request->read_size += len; - return ret; + return requestString; }
-/* discard data contents until we reach end of line */ -static BOOL discard_eol( request_t *request, BOOL notify ) -{ - do - { - char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size ); - if (eol) - { - remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) ); - break; - } - request->read_pos = request->read_size = 0; /* discard everything */ - if (!read_more_data( request, -1, notify )) return FALSE; - } while (request->read_size); - return TRUE; -} +static BOOL read_reply( request_t *request );
-/* read the size of the next chunk */ -static BOOL start_next_chunk( request_t *request, BOOL notify ) +static BOOL secure_proxy_connect( request_t *request ) { - DWORD chunk_size = 0; - - assert(!request->read_chunked_size || request->read_chunked_size == ~0u); - - if (request->read_chunked_eof) return FALSE; - - /* read terminator for the previous chunk */ - if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE; + static const WCHAR verbConnect[] = {'C','O','N','N','E','C','T',0}; + static const WCHAR fmt[] = {'%','s',':','%','u',0}; + BOOL ret = FALSE; + LPWSTR path; + connect_t *connect = request->connect;
- for (;;) + path = heap_alloc( (strlenW( connect->hostname ) + 13) * sizeof(WCHAR) ); + if (path) { - while (request->read_size) - { - char ch = request->read_buf[request->read_pos]; - if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0'; - else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10; - else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10; - else if (ch == ';' || ch == '\r' || ch == '\n') - { - TRACE("reading %u byte chunk\n", chunk_size); + LPWSTR requestString;
- if (request->content_length == ~0u) request->content_length = chunk_size; - else request->content_length += chunk_size; + sprintfW( path, fmt, connect->hostname, connect->hostport ); + requestString = build_header_request_string( request, verbConnect, + path, http1_1 ); + heap_free( path ); + if (requestString) + { + LPSTR req_ascii = strdupWA( requestString );
- request->read_chunked_size = chunk_size; - if (!chunk_size) request->read_chunked_eof = TRUE; + heap_free( requestString ); + if (req_ascii) + { + int len = strlen( req_ascii ), bytes_sent;
- return discard_eol( request, notify ); + ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent ); + heap_free( req_ascii ); + if (ret) + ret = read_reply( request ); } - remove_data( request, 1 ); - } - if (!read_more_data( request, -1, notify )) return FALSE; - if (!request->read_size) - { - request->content_length = request->content_read = 0; - request->read_chunked_size = 0; - return TRUE; } } + return ret; }
-static BOOL refill_buffer( request_t *request, BOOL notify ) +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + +static WCHAR *addr_to_str( struct sockaddr_storage *addr ) { - int len = sizeof(request->read_buf); + char buf[INET6_ADDRSTRLEN]; + void *src;
- if (request->read_chunked) - { - if (request->read_chunked_eof) return FALSE; - if (request->read_chunked_size == ~0u || !request->read_chunked_size) - { - if (!start_next_chunk( request, notify )) return FALSE; - } - len = min( len, request->read_chunked_size ); - } - else if (request->content_length != ~0u) + switch (addr->ss_family) { - len = min( len, request->content_length - request->content_read ); + 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->ss_family); + return NULL; } - - if (len <= request->read_size) return TRUE; - if (!read_more_data( request, len, notify )) return FALSE; - if (!request->read_size) request->content_length = request->content_read = 0; - return TRUE; + if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL; + return strdupAW( buf ); }
-static void finished_reading( request_t *request ) +static CRITICAL_SECTION connection_pool_cs; +static CRITICAL_SECTION_DEBUG connection_pool_debug = { - static const WCHAR closeW[] = {'c','l','o','s','e',0}; + 0, 0, &connection_pool_cs, + { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") } +}; +static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
- BOOL close = FALSE; - WCHAR connection[20]; - DWORD size = sizeof(connection); +static struct list connection_pool = LIST_INIT( connection_pool );
- if (!request->netconn) return; +void release_host( hostdata_t *host ) +{ + LONG ref;
- if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE; - else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) || - query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL )) - { - if (!strcmpiW( connection, closeW )) close = TRUE; - } - else if (!strcmpW( request->version, http1_0 )) close = TRUE; - if (close) - { - close_connection( request ); - return; - } + EnterCriticalSection( &connection_pool_cs ); + if (!(ref = --host->ref)) list_remove( &host->entry ); + LeaveCriticalSection( &connection_pool_cs ); + if (ref) return;
- cache_connection( request->netconn ); - request->netconn = NULL; + assert( list_empty( &host->connections ) ); + heap_free( host->hostname ); + heap_free( host ); }
-/* return the size of data available to be read immediately */ -static DWORD get_available_data( request_t *request ) -{ - if (request->read_chunked) return min( request->read_chunked_size, request->read_size ); - return request->read_size; -} +static BOOL connection_collector_running;
-/* check if we have reached the end of the data to read */ -static BOOL end_of_read_data( request_t *request ) +static DWORD WINAPI connection_collector(void *arg) { - if (!request->content_length) return TRUE; - if (request->read_chunked) return request->read_chunked_eof; - if (request->content_length == ~0u) return FALSE; - return (request->content_length == request->content_read); -} + unsigned int remaining_connections; + netconn_t *netconn, *next_netconn; + hostdata_t *host, *next_host; + ULONGLONG now;
-static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) -{ - int count, bytes_read = 0; + do + { + /* FIXME: Use more sophisticated method */ + Sleep(5000); + remaining_connections = 0; + now = GetTickCount64();
- if (end_of_read_data( request )) goto done; + EnterCriticalSection(&connection_pool_cs);
- while (size) - { - if (!(count = get_available_data( request ))) + LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, hostdata_t, entry) { - if (!refill_buffer( request, async )) goto done; - if (!(count = get_available_data( request ))) goto done; - } - count = min( count, size ); - memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count ); - remove_data( request, count ); - if (request->read_chunked) request->read_chunked_size -= count; - size -= count; - bytes_read += count; - request->content_read += count; - if (end_of_read_data( request )) goto done; - } - if (request->read_chunked && !request->read_chunked_size) refill_buffer( request, async ); + LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, netconn_t, entry) + { + if (netconn->keep_until < now) + { + TRACE("freeing %p\n", netconn); + list_remove(&netconn->entry); + netconn_close(netconn); + } + else + { + remaining_connections++; + } + } + }
-done: - TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length ); + if (!remaining_connections) connection_collector_running = FALSE;
- if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read ); - if (read) *read = bytes_read; - if (end_of_read_data( request )) finished_reading( request ); - return TRUE; + LeaveCriticalSection(&connection_pool_cs); + } while(remaining_connections); + + FreeLibraryAndExitThread( winhttp_instance, 0 ); }
-/* read any content returned by the server so that the connection can be reused */ -static void drain_content( request_t *request ) +static void cache_connection( netconn_t *netconn ) { - DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read; - char buffer[2048]; + TRACE( "caching connection %p\n", netconn );
- refill_buffer( request, FALSE ); - for (;;) + EnterCriticalSection( &connection_pool_cs ); + + netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT; + list_add_head( &netconn->host->connections, &netconn->entry ); + + if (!connection_collector_running) { - if (request->read_chunked) size = sizeof(buffer); + HMODULE module; + HANDLE thread; + + GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)winhttp_instance, &module ); + + thread = CreateThread(NULL, 0, connection_collector, NULL, 0, NULL); + if (thread) + { + CloseHandle( thread ); + connection_collector_running = TRUE; + } else { - if (bytes_total >= bytes_left) return; - size = min( sizeof(buffer), bytes_left - bytes_total ); + FreeLibrary( winhttp_instance ); } - if (!read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return; - bytes_total += bytes_read; } + + LeaveCriticalSection( &connection_pool_cs ); }
-static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional, - DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async ) +static DWORD map_secure_protocols( DWORD mask ) { - static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0}; - static const WCHAR no_cache[] = {'n','o','-','c','a','c','h','e',0}; - static const WCHAR length_fmt[] = {'%','l','d',0}; - - BOOL ret = FALSE; - connect_t *connect = request->connect; - session_t *session = connect->session; - WCHAR *req = NULL; - char *req_ascii; - int bytes_sent; - DWORD len; - - clear_response_headers( request ); - drain_content( request ); + DWORD ret = 0; + if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT; + if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT; + if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT; + if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT; + if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT; + return ret; +}
- if (session->agent) - process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); +static BOOL ensure_cred_handle( session_t *session ) +{ + SCHANNEL_CRED cred; + SECURITY_STATUS status;
- if (connect->hostname) - add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW ); + if (session->cred_handle_initialized) return TRUE;
- if (total_len || (request->verb && !strcmpW( request->verb, postW ))) - { - WCHAR length[21]; /* decimal long int + null */ - sprintfW( length, length_fmt, total_len ); - process_header( request, attr_content_length, length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); - } - if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE)) - { - process_header( request, attr_connection, keep_alive, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); - } - if (request->hdr.flags & WINHTTP_FLAG_REFRESH) - { - process_header( request, attr_pragma, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); - process_header( request, attr_cache_control, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); - } - if (headers && !add_request_headers( request, headers, headers_len, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE )) + memset( &cred, 0, sizeof(cred) ); + cred.dwVersion = SCHANNEL_CRED_VERSION; + cred.grbitEnabledProtocols = map_secure_protocols( session->secure_protocols ); + if ((status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, &cred, + NULL, NULL, &session->cred_handle, NULL )) != SEC_E_OK) { - TRACE("failed to add request headers\n"); - return FALSE; - } - if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request )) - { - WARN("failed to add cookie headers\n"); + WARN( "AcquireCredentialsHandleW failed: 0x%08x\n", status ); return FALSE; } + session->cred_handle_initialized = TRUE; + return TRUE; +}
- if (context) request->hdr.context = context; - - if (!(ret = open_connection( request ))) goto end; - if (!(req = build_request_string( request ))) goto end; +static BOOL open_connection( request_t *request ) +{ + BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE; + hostdata_t *host = NULL, *iter; + netconn_t *netconn = NULL; + connect_t *connect; + WCHAR *addressW = NULL; + INTERNET_PORT port; + DWORD len;
- if (!(req_ascii = strdupWA( req ))) goto end; - TRACE("full request: %s\n", debugstr_a(req_ascii)); - len = strlen(req_ascii); + if (request->netconn) goto done;
- send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 ); + connect = request->connect; + port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
- ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent ); - heap_free( req_ascii ); - if (!ret) goto end; + EnterCriticalSection( &connection_pool_cs );
- if (optional_len) + LIST_FOR_EACH_ENTRY( iter, &connection_pool, hostdata_t, entry ) { - if (!netconn_send( request->netconn, optional, optional_len, &bytes_sent )) goto end; - request->optional = optional; - request->optional_len = optional_len; - len += optional_len; + if (iter->port == port && !strcmpW( connect->servername, iter->hostname ) && !is_secure == !iter->secure) + { + host = iter; + host->ref++; + break; + } } - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
-end: - if (async) + if (!host) { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 ); - else + if ((host = heap_alloc( sizeof(*host) ))) { - WINHTTP_ASYNC_RESULT result; - result.dwResult = API_SEND_REQUEST; - result.dwError = get_last_error(); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); + host->ref = 1; + host->secure = is_secure; + host->port = port; + list_init( &host->connections ); + if ((host->hostname = strdupW( connect->servername ))) + { + list_add_head( &connection_pool, &host->entry ); + } + else + { + heap_free( host ); + host = NULL; + } } } - heap_free( req ); - return ret; -} - -static void task_send_request( task_header_t *task ) -{ - send_request_t *s = (send_request_t *)task; - send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE ); - heap_free( s->headers ); -}
-/*********************************************************************** - * WinHttpSendRequest (winhttp.@) - */ -BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len, - LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context ) -{ - BOOL ret; - request_t *request; + LeaveCriticalSection( &connection_pool_cs );
- TRACE("%p, %s, 0x%x, %u, %u, %lx\n", - hrequest, debugstr_w(headers), headers_len, optional_len, total_len, context); + if (!host) return FALSE;
- if (!(request = (request_t *)grab_object( hrequest ))) + for (;;) { - set_last_error( ERROR_INVALID_HANDLE ); - return FALSE; + EnterCriticalSection( &connection_pool_cs ); + if (!list_empty( &host->connections )) + { + netconn = LIST_ENTRY( list_head( &host->connections ), netconn_t, entry ); + list_remove( &netconn->entry ); + } + LeaveCriticalSection( &connection_pool_cs ); + if (!netconn) break; + + if (netconn_is_alive( netconn )) break; + TRACE("connection %p no longer alive, closing\n", netconn); + netconn_close( netconn ); + netconn = NULL; } - if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST) + + if (!connect->resolved && netconn) { - release_object( &request->hdr ); - set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); - return FALSE; + connect->sockaddr = netconn->sockaddr; + connect->resolved = TRUE; }
- if (headers && !headers_len) headers_len = strlenW( headers ); - - if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) + if (!connect->resolved) { - send_request_t *s; + len = strlenW( host->hostname ) + 1; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
- if (!(s = heap_alloc( sizeof(send_request_t) ))) return FALSE; - s->hdr.request = request; - s->hdr.proc = task_send_request; - s->headers = strdupW( headers ); - s->headers_len = headers_len; - s->optional = optional; - s->optional_len = optional_len; - s->total_len = total_len; - s->context = context; + if (!netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout )) + { + release_host( host ); + return FALSE; + } + connect->resolved = TRUE;
- addref_object( &request->hdr ); - ret = queue_task( (task_header_t *)s ); + if (!(addressW = addr_to_str( &connect->sockaddr ))) + { + release_host( host ); + return FALSE; + } + len = strlenW( addressW ) + 1; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len ); } - else - ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE ); - - release_object( &request->hdr ); - if (ret) set_last_error( ERROR_SUCCESS ); - return ret; -}
-#undef ARRAYSIZE -#define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0])) + if (!netconn) + { + if (!addressW && !(addressW = addr_to_str( &connect->sockaddr ))) + { + release_host( host ); + return FALSE; + }
-static const WCHAR basicW[] = {'B','a','s','i','c',0}; -static const WCHAR ntlmW[] = {'N','T','L','M',0}; -static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t',0}; -static const WCHAR digestW[] = {'D','i','g','e','s','t',0}; -static const WCHAR negotiateW[] = {'N','e','g','o','t','i','a','t','e',0}; + TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
-static const struct -{ - const WCHAR *str; - unsigned int len; - DWORD scheme; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 ); + + if (!(netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout ))) + { + heap_free( addressW ); + release_host( host ); + return FALSE; + } + netconn_set_timeout( netconn, TRUE, request->send_timeout ); + netconn_set_timeout( netconn, FALSE, request->recv_timeout ); + if (is_secure) + { + if (connect->session->proxy_server && + strcmpiW( connect->hostname, connect->servername )) + { + if (!secure_proxy_connect( request )) + { + heap_free( addressW ); + netconn_close( netconn ); + return FALSE; + } + } + if (!ensure_cred_handle( connect->session ) || + !netconn_secure_connect( netconn, connect->hostname, request->security_flags, + &connect->session->cred_handle )) + { + heap_free( addressW ); + netconn_close( netconn ); + return FALSE; + } + } + + request->netconn = netconn; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 ); + } + else + { + TRACE("using connection %p\n", netconn); + + netconn_set_timeout( netconn, TRUE, request->send_timeout ); + netconn_set_timeout( netconn, FALSE, request->recv_timeout ); + request->netconn = netconn; + } + +done: + request->read_pos = request->read_size = 0; + request->read_chunked = FALSE; + request->read_chunked_size = ~0u; + request->read_chunked_eof = FALSE; + heap_free( addressW ); + return TRUE; } -auth_schemes[] = -{ - { basicW, ARRAYSIZE(basicW) - 1, WINHTTP_AUTH_SCHEME_BASIC }, - { ntlmW, ARRAYSIZE(ntlmW) - 1, WINHTTP_AUTH_SCHEME_NTLM }, - { passportW, ARRAYSIZE(passportW) - 1, WINHTTP_AUTH_SCHEME_PASSPORT }, - { digestW, ARRAYSIZE(digestW) - 1, WINHTTP_AUTH_SCHEME_DIGEST }, - { negotiateW, ARRAYSIZE(negotiateW) - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE } -}; -static const unsigned int num_auth_schemes = sizeof(auth_schemes)/sizeof(auth_schemes[0]);
-static enum auth_scheme scheme_from_flag( DWORD flag ) +void close_connection( request_t *request ) { - int i; + if (!request->netconn) return;
- for (i = 0; i < num_auth_schemes; i++) if (flag == auth_schemes[i].scheme) return i; - return SCHEME_INVALID; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 ); + netconn_close( request->netconn ); + request->netconn = NULL; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 ); }
-static DWORD auth_scheme_from_header( WCHAR *header ) +static BOOL add_host_header( request_t *request, DWORD modifier ) { - unsigned int i; + BOOL ret; + DWORD len; + WCHAR *host; + static const WCHAR fmt[] = {'%','s',':','%','u',0}; + connect_t *connect = request->connect; + INTERNET_PORT port;
- for (i = 0; i < num_auth_schemes; i++) + port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80); + + if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT) { - if (!strncmpiW( header, auth_schemes[i].str, auth_schemes[i].len ) && - (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme; + return process_header( request, attr_host, connect->hostname, modifier, TRUE ); } - return 0; + len = strlenW( connect->hostname ) + 7; /* sizeof(":65335") */ + if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE; + sprintfW( host, fmt, connect->hostname, port ); + ret = process_header( request, attr_host, host, modifier, TRUE ); + heap_free( host ); + return ret; }
-static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD supported, LPDWORD first ) +static void clear_response_headers( request_t *request ) { - DWORD index = 0, supported_schemes = 0, first_scheme = 0; - BOOL ret = FALSE; + unsigned int i;
- for (;;) + for (i = 0; i < request->num_headers; i++) { - WCHAR *buffer; - DWORD size, scheme; - - size = 0; - query_headers( request, level, NULL, NULL, &size, &index ); - if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) break; - - index--; - if (!(buffer = heap_alloc( size ))) return FALSE; - if (!query_headers( request, level, NULL, buffer, &size, &index )) - { - heap_free( buffer ); - return FALSE; - } - scheme = auth_scheme_from_header( buffer ); - heap_free( buffer ); - if (!scheme) continue; - - if (!first_scheme) first_scheme = scheme; - supported_schemes |= scheme; - - ret = TRUE; + if (!request->headers[i].field) continue; + if (!request->headers[i].value) continue; + if (request->headers[i].is_request) continue; + delete_header( request, i ); + i--; } +}
- if (ret) - { - *supported = supported_schemes; - *first = first_scheme; - } - return ret; +/* remove some amount of data from the read buffer */ +static void remove_data( request_t *request, int count ) +{ + if (!(request->read_size -= count)) request->read_pos = 0; + else request->read_pos += count; }
-/*********************************************************************** - * WinHttpQueryAuthSchemes (winhttp.@) - */ -BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target ) +/* read some more data into the read buffer */ +static BOOL read_more_data( request_t *request, int maxlen, BOOL notify ) { - BOOL ret = FALSE; - request_t *request; + int len; + BOOL ret;
- TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target); + if (request->read_chunked_eof) return FALSE;
- if (!(request = (request_t *)grab_object( hrequest ))) - { - set_last_error( ERROR_INVALID_HANDLE ); - return FALSE; - } - if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST) + if (request->read_size && request->read_pos) { - release_object( &request->hdr ); - set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); - return FALSE; + /* move existing data to the start of the buffer */ + memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size ); + request->read_pos = 0; } - if (!supported || !first || !target) - { - release_object( &request->hdr ); - set_last_error( ERROR_INVALID_PARAMETER ); - return FALSE; + if (maxlen == -1) maxlen = sizeof(request->read_buf);
- } + if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
- if (query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first )) - { - *target = WINHTTP_AUTH_TARGET_SERVER; - ret = TRUE; - } - else if (query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first )) - { - *target = WINHTTP_AUTH_TARGET_PROXY; - ret = TRUE; - } + ret = netconn_recv( request->netconn, request->read_buf + request->read_size, + maxlen - request->read_size, 0, &len );
- release_object( &request->hdr ); - if (ret) set_last_error( ERROR_SUCCESS ); + if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) ); + + request->read_size += len; return ret; }
-static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 ) +/* discard data contents until we reach end of line */ +static BOOL discard_eol( request_t *request, BOOL notify ) { - UINT n = 0, x; - static const char base64enc[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - while (len > 0) + do { - /* first 6 bits, all from bin[0] */ - base64[n++] = base64enc[(bin[0] & 0xfc) >> 2]; - x = (bin[0] & 3) << 4; - - /* next 6 bits, 2 from bin[0] and 4 from bin[1] */ - if (len == 1) - { - base64[n++] = base64enc[x]; - base64[n++] = '='; - base64[n++] = '='; - break; - } - base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)]; - x = (bin[1] & 0x0f) << 2; - - /* next 6 bits 4 from bin[1] and 2 from bin[2] */ - if (len == 2) + char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size ); + if (eol) { - base64[n++] = base64enc[x]; - base64[n++] = '='; + remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) ); break; } - base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)]; - - /* last 6 bits, all from bin [2] */ - base64[n++] = base64enc[bin[2] & 0x3f]; - bin += 3; - len -= 3; - } - base64[n] = 0; - return n; + request->read_pos = request->read_size = 0; /* discard everything */ + if (!read_more_data( request, -1, notify )) return FALSE; + } while (request->read_size); + return TRUE; }
-static inline char decode_char( WCHAR c ) +/* read the size of the next chunk */ +static BOOL start_next_chunk( request_t *request, BOOL notify ) { - if (c >= 'A' && c <= 'Z') return c - 'A'; - if (c >= 'a' && c <= 'z') return c - 'a' + 26; - if (c >= '0' && c <= '9') return c - '0' + 52; - if (c == '+') return 62; - if (c == '/') return 63; - return 64; -} + DWORD chunk_size = 0;
-static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf ) -{ - unsigned int i = 0; - char c0, c1, c2, c3; - const WCHAR *p = base64; + assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
- while (len > 4) + if (request->read_chunked_eof) return FALSE; + + /* read terminator for the previous chunk */ + if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE; + + for (;;) { - if ((c0 = decode_char( p[0] )) > 63) return 0; - if ((c1 = decode_char( p[1] )) > 63) return 0; - if ((c2 = decode_char( p[2] )) > 63) return 0; - if ((c3 = decode_char( p[3] )) > 63) return 0; + while (request->read_size) + { + char ch = request->read_buf[request->read_pos]; + if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0'; + else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10; + else if (ch == ';' || ch == '\r' || ch == '\n') + { + TRACE("reading %u byte chunk\n", chunk_size);
- if (buf) + if (request->content_length == ~0u) request->content_length = chunk_size; + else request->content_length += chunk_size; + + request->read_chunked_size = chunk_size; + if (!chunk_size) request->read_chunked_eof = TRUE; + + return discard_eol( request, notify ); + } + remove_data( request, 1 ); + } + if (!read_more_data( request, -1, notify )) return FALSE; + if (!request->read_size) { - buf[i + 0] = (c0 << 2) | (c1 >> 4); - buf[i + 1] = (c1 << 4) | (c2 >> 2); - buf[i + 2] = (c2 << 6) | c3; + request->content_length = request->content_read = 0; + request->read_chunked_size = 0; + return TRUE; } - len -= 4; - i += 3; - p += 4; } - if (p[2] == '=') - { - if ((c0 = decode_char( p[0] )) > 63) return 0; - if ((c1 = decode_char( p[1] )) > 63) return 0; +}
- if (buf) buf[i] = (c0 << 2) | (c1 >> 4); - i++; - } - else if (p[3] == '=') - { - if ((c0 = decode_char( p[0] )) > 63) return 0; - if ((c1 = decode_char( p[1] )) > 63) return 0; - if ((c2 = decode_char( p[2] )) > 63) return 0; +static BOOL refill_buffer( request_t *request, BOOL notify ) +{ + int len = sizeof(request->read_buf);
- if (buf) + if (request->read_chunked) + { + if (request->read_chunked_eof) return FALSE; + if (request->read_chunked_size == ~0u || !request->read_chunked_size) { - buf[i + 0] = (c0 << 2) | (c1 >> 4); - buf[i + 1] = (c1 << 4) | (c2 >> 2); + if (!start_next_chunk( request, notify )) return FALSE; } - i += 2; + len = min( len, request->read_chunked_size ); } - else + else if (request->content_length != ~0u) { - if ((c0 = decode_char( p[0] )) > 63) return 0; - if ((c1 = decode_char( p[1] )) > 63) return 0; - if ((c2 = decode_char( p[2] )) > 63) return 0; - if ((c3 = decode_char( p[3] )) > 63) return 0; - - if (buf) - { - buf[i + 0] = (c0 << 2) | (c1 >> 4); - buf[i + 1] = (c1 << 4) | (c2 >> 2); - buf[i + 2] = (c2 << 6) | c3; - } - i += 3; + len = min( len, request->content_length - request->content_read ); } - return i; + + if (len <= request->read_size) return TRUE; + if (!read_more_data( request, len, notify )) return FALSE; + if (!request->read_size) request->content_length = request->content_read = 0; + return TRUE; }
-static struct authinfo *alloc_authinfo(void) +static void finished_reading( request_t *request ) { - struct authinfo *ret; + static const WCHAR closeW[] = {'c','l','o','s','e',0};
- if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL; + BOOL close = FALSE; + WCHAR connection[20]; + DWORD size = sizeof(connection);
- SecInvalidateHandle( &ret->cred ); - SecInvalidateHandle( &ret->ctx ); - memset( &ret->exp, 0, sizeof(ret->exp) ); - ret->scheme = 0; - ret->attr = 0; - ret->max_token = 0; - ret->data = NULL; - ret->data_len = 0; - ret->finished = FALSE; - return ret; -} + if (!request->netconn) return;
-void destroy_authinfo( struct authinfo *authinfo ) -{ - if (!authinfo) return; + if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE; + else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) || + query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL )) + { + if (!strcmpiW( connection, closeW )) close = TRUE; + } + else if (!strcmpW( request->version, http1_0 )) close = TRUE; + if (close) + { + close_connection( request ); + return; + }
- if (SecIsValidHandle( &authinfo->ctx )) - DeleteSecurityContext( &authinfo->ctx ); - if (SecIsValidHandle( &authinfo->cred )) - FreeCredentialsHandle( &authinfo->cred ); + cache_connection( request->netconn ); + request->netconn = NULL; +}
- heap_free( authinfo->data ); - heap_free( authinfo ); +/* return the size of data available to be read immediately */ +static DWORD get_available_data( request_t *request ) +{ + if (request->read_chunked) return min( request->read_chunked_size, request->read_size ); + return request->read_size; }
-static BOOL get_authvalue( request_t *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len ) +/* check if we have reached the end of the data to read */ +static BOOL end_of_read_data( request_t *request ) { - DWORD size, index = 0; - for (;;) - { - size = len; - if (!query_headers( request, level, NULL, buffer, &size, &index )) return FALSE; - if (auth_scheme_from_header( buffer ) == scheme) break; - } - return TRUE; + if (!request->content_length) return TRUE; + if (request->read_chunked) return request->read_chunked_eof; + if (request->content_length == ~0u) return FALSE; + return (request->content_length == request->content_read); }
-static BOOL do_authorization( request_t *request, DWORD target, DWORD scheme_flag ) +static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) { - struct authinfo *authinfo, **auth_ptr; - enum auth_scheme scheme = scheme_from_flag( scheme_flag ); - const WCHAR *auth_target, *username, *password; - WCHAR auth_value[2048], *auth_reply; - DWORD len = sizeof(auth_value), len_scheme, flags; - BOOL ret; + int count, bytes_read = 0;
- if (scheme == SCHEME_INVALID) return FALSE; + if (end_of_read_data( request )) goto done;
- switch (target) + while (size) { - case WINHTTP_AUTH_TARGET_SERVER: - if (!get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len )) - return FALSE; - auth_ptr = &request->authinfo; - auth_target = attr_authorization; - if (request->creds[TARGET_SERVER][scheme].username) - { - username = request->creds[TARGET_SERVER][scheme].username; - password = request->creds[TARGET_SERVER][scheme].password; - } - else + if (!(count = get_available_data( request ))) { - username = request->connect->username; - password = request->connect->password; + if (!refill_buffer( request, async )) goto done; + if (!(count = get_available_data( request ))) goto done; } - break; + count = min( count, size ); + memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count ); + remove_data( request, count ); + if (request->read_chunked) request->read_chunked_size -= count; + size -= count; + bytes_read += count; + request->content_read += count; + if (end_of_read_data( request )) goto done; + } + if (request->read_chunked && !request->read_chunked_size) refill_buffer( request, async );
- case WINHTTP_AUTH_TARGET_PROXY: - if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len )) - return FALSE; - auth_ptr = &request->proxy_authinfo; - auth_target = attr_proxy_authorization; - if (request->creds[TARGET_PROXY][scheme].username) - { - username = request->creds[TARGET_PROXY][scheme].username; - password = request->creds[TARGET_PROXY][scheme].password; - } +done: + TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length ); + + if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read ); + if (read) *read = bytes_read; + if (end_of_read_data( request )) finished_reading( request ); + return TRUE; +} + +/* read any content returned by the server so that the connection can be reused */ +static void drain_content( request_t *request ) +{ + DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read; + char buffer[2048]; + + refill_buffer( request, FALSE ); + for (;;) + { + if (request->read_chunked) size = sizeof(buffer); else { - username = request->connect->session->proxy_username; - password = request->connect->session->proxy_password; + if (bytes_total >= bytes_left) return; + size = min( sizeof(buffer), bytes_left - bytes_total ); } - break; - - default: - WARN("unknown target %x\n", target); - return FALSE; + if (!read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return; + bytes_total += bytes_read; } - authinfo = *auth_ptr; +}
- switch (scheme) - { - case SCHEME_BASIC: - { - int userlen, passlen; +static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional, + DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async ) +{ + static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0}; + static const WCHAR no_cache[] = {'n','o','-','c','a','c','h','e',0}; + static const WCHAR length_fmt[] = {'%','l','d',0};
- if (!username || !password) return FALSE; - if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE; + BOOL ret = FALSE; + connect_t *connect = request->connect; + session_t *session = connect->session; + WCHAR *req = NULL; + char *req_ascii; + int bytes_sent; + DWORD len, i, flags;
- userlen = WideCharToMultiByte( CP_UTF8, 0, username, strlenW( username ), NULL, 0, NULL, NULL ); - passlen = WideCharToMultiByte( CP_UTF8, 0, password, strlenW( password ), NULL, 0, NULL, NULL ); + clear_response_headers( request ); + drain_content( request );
- authinfo->data_len = userlen + 1 + passlen; - if (!(authinfo->data = heap_alloc( authinfo->data_len ))) return FALSE; + 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 );
- WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL ); - authinfo->data[userlen] = ':'; - WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL ); + if (connect->hostname) + add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
- authinfo->scheme = SCHEME_BASIC; - authinfo->finished = TRUE; - break; + if (request->creds[TARGET_SERVER][SCHEME_BASIC].username) + do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC ); + + if (total_len || (request->verb && !strcmpW( request->verb, postW ))) + { + WCHAR length[21]; /* decimal long int + null */ + sprintfW( length, length_fmt, total_len ); + process_header( request, attr_content_length, length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); } - case SCHEME_NTLM: - case SCHEME_NEGOTIATE: + if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE)) { - SECURITY_STATUS status; - SecBufferDesc out_desc, in_desc; - SecBuffer out, in; - ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE; - const WCHAR *p; - BOOL first = FALSE; + process_header( request, attr_connection, keep_alive, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); + } + if (request->hdr.flags & WINHTTP_FLAG_REFRESH) + { + process_header( request, attr_pragma, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); + process_header( request, attr_cache_control, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); + } + if (headers && !add_request_headers( request, headers, headers_len, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE )) + { + TRACE("failed to add request headers\n"); + return FALSE; + } + if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request )) + { + WARN("failed to add cookie headers\n"); + return FALSE; + }
- if (!authinfo) - { - TimeStamp exp; - SEC_WINNT_AUTH_IDENTITY_W id; - WCHAR *domain, *user; + if (context) request->hdr.context = context;
- if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE; + if (!(ret = open_connection( request ))) goto end; + if (!(req = build_request_string( request ))) goto end;
- first = TRUE; - domain = (WCHAR *)username; - user = strchrW( username, '\' ); + if (!(req_ascii = strdupWA( req ))) goto end; + TRACE("full request: %s\n", debugstr_a(req_ascii)); + len = strlen(req_ascii);
- if (user) user++; - else - { - user = (WCHAR *)username; - domain = NULL; - } - id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - id.User = user; - id.UserLength = strlenW( user ); - id.Domain = domain; - id.DomainLength = domain ? user - domain - 1 : 0; - id.Password = (WCHAR *)password; - id.PasswordLength = strlenW( password ); + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
- status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str, - SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL, - &authinfo->cred, &exp ); - if (status == SEC_E_OK) - { - PSecPkgInfoW info; - status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info ); - if (status == SEC_E_OK) - { - authinfo->max_token = info->cbMaxToken; - FreeContextBuffer( info ); - } - } - if (status != SEC_E_OK) - { - WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n", - debugstr_w(auth_schemes[scheme].str), status); - heap_free( authinfo ); - return FALSE; - } - authinfo->scheme = scheme; - } - else if (authinfo->finished) return FALSE; + ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent ); + heap_free( req_ascii ); + if (!ret) goto end;
- if ((strlenW( auth_value ) < auth_schemes[authinfo->scheme].len || - strncmpiW( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len ))) + if (optional_len) + { + if (!netconn_send( request->netconn, optional, optional_len, &bytes_sent )) goto end; + request->optional = optional; + request->optional_len = optional_len; + len += optional_len; + } + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) ); + +end: + if (async) + { + if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 ); + else { - ERR("authentication scheme changed from %s to %s\n", - debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value)); - destroy_authinfo( authinfo ); - *auth_ptr = NULL; - return FALSE; + WINHTTP_ASYNC_RESULT result; + result.dwResult = API_SEND_REQUEST; + result.dwError = get_last_error(); + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); } - in.BufferType = SECBUFFER_TOKEN; - in.cbBuffer = 0; - in.pvBuffer = NULL; + } + heap_free( req ); + return ret; +}
- in_desc.ulVersion = 0; - in_desc.cBuffers = 1; - in_desc.pBuffers = ∈ +static void task_send_request( task_header_t *task ) +{ + send_request_t *s = (send_request_t *)task; + send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE ); + heap_free( s->headers ); +}
- p = auth_value + auth_schemes[scheme].len; - if (*p == ' ') - { - int len = strlenW( ++p ); - in.cbBuffer = decode_base64( p, len, NULL ); - if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) { - destroy_authinfo( authinfo ); - *auth_ptr = NULL; - return FALSE; - } - decode_base64( p, len, in.pvBuffer ); - } - out.BufferType = SECBUFFER_TOKEN; - out.cbBuffer = authinfo->max_token; - if (!(out.pvBuffer = heap_alloc( authinfo->max_token ))) - { - heap_free( in.pvBuffer ); - destroy_authinfo( authinfo ); - *auth_ptr = NULL; - return FALSE; - } - out_desc.ulVersion = 0; - out_desc.cBuffers = 1; - out_desc.pBuffers = &out; +/*********************************************************************** + * WinHttpSendRequest (winhttp.@) + */ +BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len, + LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context ) +{ + BOOL ret; + request_t *request;
- status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx, - first ? request->connect->servername : NULL, flags, 0, - SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0, - &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp ); - heap_free( in.pvBuffer ); - if (status == SEC_E_OK) - { - heap_free( authinfo->data ); - authinfo->data = out.pvBuffer; - authinfo->data_len = out.cbBuffer; - authinfo->finished = TRUE; - TRACE("sending last auth packet\n"); - } - else if (status == SEC_I_CONTINUE_NEEDED) - { - heap_free( authinfo->data ); - authinfo->data = out.pvBuffer; - authinfo->data_len = out.cbBuffer; - TRACE("sending next auth packet\n"); - } - else - { - ERR("InitializeSecurityContextW failed with error 0x%08x\n", status); - heap_free( out.pvBuffer ); - destroy_authinfo( authinfo ); - *auth_ptr = NULL; - return FALSE; - } - break; + TRACE("%p, %s, %u, %u, %u, %lx\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional_len, + total_len, context); + + if (!(request = (request_t *)grab_object( hrequest ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return FALSE; } - default: - ERR("invalid scheme %u\n", scheme); + if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST) + { + release_object( &request->hdr ); + set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); return FALSE; } - *auth_ptr = authinfo;
- len_scheme = auth_schemes[authinfo->scheme].len; - len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3; - if (!(auth_reply = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; + if (headers && !headers_len) headers_len = strlenW( headers );
- memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) ); - auth_reply[len_scheme] = ' '; - encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 ); + if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) + { + send_request_t *s;
- flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE; - ret = process_header( request, auth_target, auth_reply, flags, TRUE ); - heap_free( auth_reply ); + if (!(s = heap_alloc( sizeof(send_request_t) ))) return FALSE; + s->hdr.request = request; + s->hdr.proc = task_send_request; + s->headers = strdupW( headers ); + s->headers_len = headers_len; + s->optional = optional; + s->optional_len = optional_len; + s->total_len = total_len; + s->context = context; + + addref_object( &request->hdr ); + ret = queue_task( (task_header_t *)s ); + } + else + ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE ); + + release_object( &request->hdr ); + if (ret) set_last_error( ERROR_SUCCESS ); return ret; }
diff --git a/dll/win32/winhttp/session.c b/dll/win32/winhttp/session.c index 429f505914..7ab10a66e4 100644 --- a/dll/win32/winhttp/session.c +++ b/dll/win32/winhttp/session.c @@ -71,6 +71,7 @@ static void session_destroy( object_header_t *hdr ) TRACE("%p\n", session);
if (session->unload_event) SetEvent( session->unload_event ); + if (session->cred_handle_initialized) FreeCredentialsHandle( &session->cred_handle );
LIST_FOR_EACH_SAFE( item, next, &session->cookie_cache ) { @@ -155,6 +156,17 @@ static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffe hdr->redirect_policy = policy; return TRUE; } + case WINHTTP_OPTION_SECURE_PROTOCOLS: + { + if (buflen != sizeof(session->secure_protocols)) + { + set_last_error( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + session->secure_protocols = *(DWORD *)buffer; + TRACE("0x%x\n", session->secure_protocols); + return TRUE; + } case WINHTTP_OPTION_DISABLE_FEATURE: set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); return FALSE; @@ -577,6 +589,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 ); for (i = 0; i < TARGET_MAX; i++) { for (j = 0; j < SCHEME_MAX; j++) @@ -1002,14 +1016,32 @@ static const object_vtbl_t request_vtbl =
static BOOL store_accept_types( request_t *request, const WCHAR **accept_types ) { - static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0}; - static const DWORD flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA; const WCHAR **types = accept_types; + DWORD i;
if (!types) return TRUE; while (*types) { - process_header( request, attr_accept, *types, flags, TRUE ); + 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 - 1] ); + heap_free( request->accept_types ); + request->accept_types = NULL; + request->num_accept_types = 0; + return FALSE; + } types++; } return TRUE; diff --git a/dll/win32/winhttp/winhttp_private.h b/dll/win32/winhttp/winhttp_private.h index 7e80dcab4e..1e6d16a238 100644 --- a/dll/win32/winhttp/winhttp_private.h +++ b/dll/win32/winhttp/winhttp_private.h @@ -135,6 +135,9 @@ typedef struct LPWSTR proxy_password; struct list cookie_cache; HANDLE unload_event; + CredHandle cred_handle; + BOOL cred_handle_initialized; + DWORD secure_protocols; } session_t;
typedef struct @@ -235,6 +238,8 @@ typedef struct char read_buf[8192]; /* buffer for already read but not returned data */ header_t *headers; DWORD num_headers; + WCHAR **accept_types; + DWORD num_accept_types; struct authinfo *authinfo; struct authinfo *proxy_authinfo; HANDLE task_wait; @@ -313,7 +318,7 @@ void netconn_unload( void ) DECLSPEC_HIDDEN; ULONG netconn_query_data_available( netconn_t * ) DECLSPEC_HIDDEN; BOOL netconn_recv( netconn_t *, void *, size_t, int, int * ) DECLSPEC_HIDDEN; BOOL netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr_storage *, int ) DECLSPEC_HIDDEN; -BOOL netconn_secure_connect( netconn_t *, WCHAR *, DWORD ) DECLSPEC_HIDDEN; +BOOL netconn_secure_connect( netconn_t *, WCHAR *, DWORD, CredHandle * ) DECLSPEC_HIDDEN; BOOL netconn_send( netconn_t *, const void *, size_t, int * ) DECLSPEC_HIDDEN; DWORD netconn_set_timeout( netconn_t *, BOOL, int ) DECLSPEC_HIDDEN; BOOL netconn_is_alive( netconn_t * ) DECLSPEC_HIDDEN; @@ -329,8 +334,6 @@ void destroy_authinfo( struct authinfo * ) DECLSPEC_HIDDEN;
void release_host( hostdata_t *host ) DECLSPEC_HIDDEN;
-BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only ) DECLSPEC_HIDDEN; - extern HRESULT WinHttpRequest_create( void ** ) DECLSPEC_HIDDEN; void release_typelib( void ) DECLSPEC_HIDDEN;
diff --git a/media/doc/README.WINE b/media/doc/README.WINE index 6905a44db6..5f30683e42 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -200,7 +200,7 @@ reactos/dll/win32/windowscodecs # Synced to Wine-3.0 reactos/dll/win32/windowscodecsext # Synced to WineStaging-2.9 reactos/dll/win32/winemp3.acm # Synced to Wine-3.0 reactos/dll/win32/wing32 # Synced to WineStaging-2.9 -reactos/dll/win32/winhttp # Synced to WineStaging-2.16 +reactos/dll/win32/winhttp # Synced to Wine-3.0 reactos/dll/win32/wininet # Synced to WineStaging-2.16 reactos/dll/win32/winmm # Forked at Wine-20050628 reactos/dll/win32/winmm/midimap # Forked at Wine-20050628