* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include "winhttp_private.h"
+#define COBJMACROS
+#include "config.h"
+#include "wine/port.h"
+#include <stdarg.h>
+#include <assert.h>
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
-#include <assert.h>
-#include <winuser.h>
-#include <httprequest.h>
-#include <httprequestid.h>
+#include "windef.h"
+#include "winbase.h"
+#include "ole2.h"
+#include "initguid.h"
+#include "httprequest.h"
+#include "httprequestid.h"
+#include "schannel.h"
+#include "winhttp.h"
+
+#include "winhttp_private.h"
+
+#include "wine/debug.h"
+WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
+
+#ifdef __REACTOS__
#include "inet_ntop.c"
+#endif
#define DEFAULT_KEEP_ALIVE_TIMEOUT 30000
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)
{
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)
+ /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
+ if (len == 1)
{
- 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++;
- }
- }
+ 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;
-
- if (netconn_is_alive( netconn )) break;
- TRACE("connection %p no longer alive, closing\n", netconn);
- netconn_close( netconn );
- netconn = NULL;
- }
-
- if (!connect->resolved && netconn)
- {
- connect->sockaddr = netconn->sockaddr;
- connect->resolved = TRUE;
- }
-
- if (!connect->resolved)
- {
- len = strlenW( host->hostname ) + 1;
- send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
-
- if (!netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout ))
+ else
{
- release_host( host );
- return FALSE;
+ if (!has_auth_value) return FALSE;
+ username = request->connect->username;
+ password = request->connect->password;
}
- connect->resolved = TRUE;
+ break;
- if (!(addressW = addr_to_str( &connect->sockaddr )))
- {
- release_host( host );
+ 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;
}
- len = strlenW( addressW ) + 1;
- send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
+ else
+ {
+ username = request->connect->session->proxy_username;
+ password = request->connect->session->proxy_password;
+ }
+ break;
+
+ default:
+ WARN("unknown target %x\n", target);
+ return FALSE;
}
+ authinfo = *auth_ptr;
- if (!netconn)
+ switch (scheme)
{
- if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
- {
- release_host( host );
- return FALSE;
- }
+ case SCHEME_BASIC:
+ {
+ int userlen, passlen;
- TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
+ if (!username || !password) return FALSE;
+ if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
- send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
+ 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 = 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)
+ 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:
+ {
+ 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)
{
- if (connect->session->proxy_server &&
- strcmpiW( connect->hostname, connect->servername ))
+ TimeStamp exp;
+ SEC_WINNT_AUTH_IDENTITY_W id;
+ WCHAR *domain, *user;
+
+ if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
+
+ first = TRUE;
+ domain = (WCHAR *)username;
+ user = strchrW( username, '\\' );
+
+ 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;
+
+ for( t = list; *t ; t++ )
+ strcatW( str, *t );
+
+ return str;
}
-static void clear_response_headers( request_t *request )
+static LPWSTR build_header_request_string( request_t *request, LPCWSTR verb,
+ LPCWSTR path, LPCWSTR version )
{
- unsigned int i;
-
- 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--;
- }
-}
-
-/* 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;
-}
+ 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};
-
- BOOL close = FALSE;
- WCHAR connection[20];
- DWORD size = sizeof(connection);
+ 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 };
- if (!request->netconn) return;
+static struct list connection_pool = LIST_INIT( connection_pool );
- 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;
- }
+void release_host( hostdata_t *host )
+{
+ LONG ref;
- cache_connection( request->netconn );
- request->netconn = NULL;
-}
+ EnterCriticalSection( &connection_pool_cs );
+ if (!(ref = --host->ref)) list_remove( &host->entry );
+ LeaveCriticalSection( &connection_pool_cs );
+ if (ref) return;
-/* 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;
+ assert( list_empty( &host->connections ) );
+ heap_free( host->hostname );
+ heap_free( host );
}
-/* check if we have reached the end of the data to read */
-static BOOL end_of_read_data( request_t *request )
-{
- 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 connection_collector_running;
-static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
+static DWORD WINAPI connection_collector(void *arg)
{
- int count, bytes_read = 0;
-
- if (end_of_read_data( request )) goto done;
+ unsigned int remaining_connections;
+ netconn_t *netconn, *next_netconn;
+ hostdata_t *host, *next_host;
+ ULONGLONG now;
- while (size)
+ do
{
- if (!(count = get_available_data( request )))
+ /* FIXME: Use more sophisticated method */
+ Sleep(5000);
+ remaining_connections = 0;
+ now = GetTickCount64();
+
+ EnterCriticalSection(&connection_pool_cs);
+
+ 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;
+ 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++;
+ }
+ }
}
- 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 );
-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 ))
- {
- TRACE("failed to add request headers\n");
- return FALSE;
- }
- if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request ))
+ 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)
{
- 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 );
- }
- else
- ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, 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 );
+ }
- release_object( &request->hdr );
- if (ret) set_last_error( ERROR_SUCCESS );
- return ret;
-}
+ if (!netconn)
+ {
+ if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
+ {
+ release_host( host );
+ return FALSE;
+ }
-#undef ARRAYSIZE
-#define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0]))
+ TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
-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};
+ send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
-static const struct
-{
- const WCHAR *str;
- unsigned int len;
- DWORD scheme;
+ 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)];
+ request->read_pos = request->read_size = 0; /* discard everything */
+ if (!read_more_data( request, -1, notify )) return FALSE;
+ } while (request->read_size);
+ return TRUE;
+}
- /* last 6 bits, all from bin [2] */
- base64[n++] = base64enc[bin[2] & 0x3f];
- bin += 3;
- len -= 3;
- }
- base64[n] = 0;
- return n;
-}
-
-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;
- 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;
+ 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;
}