sync urlmon and winhttp with wine 1.1.23

svn path=/trunk/; revision=41333
This commit is contained in:
Christoph von Wittich 2009-06-07 12:32:05 +00:00
parent 8d470e46d8
commit 12c11159c8
12 changed files with 1604 additions and 280 deletions

View file

@ -608,10 +608,14 @@ static HRESULT WINAPI ProtocolHandler_Start(IInternetProtocol *iface, LPCWSTR sz
static HRESULT WINAPI ProtocolHandler_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
{
BindProtocol *This = PROTOCOLHANDLER_THIS(iface);
HRESULT hres;
TRACE("(%p)->(%p)\n", This, pProtocolData);
return IInternetProtocol_Continue(This->protocol, pProtocolData);
hres = IInternetProtocol_Continue(This->protocol, pProtocolData);
heap_free(pProtocolData);
return hres;
}
static HRESULT WINAPI ProtocolHandler_Abort(IInternetProtocol *iface, HRESULT hrReason,
@ -874,14 +878,14 @@ static ULONG WINAPI BPInternetProtocolSink_Release(IInternetProtocolSink *iface)
typedef struct {
task_header_t header;
PROTOCOLDATA data;
PROTOCOLDATA *data;
} switch_task_t;
static void switch_proc(BindProtocol *bind, task_header_t *t)
{
switch_task_t *task = (switch_task_t*)t;
IInternetProtocol_Continue(bind->protocol_handler, &task->data);
IInternetProtocol_Continue(bind->protocol_handler, task->data);
heap_free(task);
}
@ -890,12 +894,18 @@ static HRESULT WINAPI BPInternetProtocolSink_Switch(IInternetProtocolSink *iface
PROTOCOLDATA *pProtocolData)
{
BindProtocol *This = PROTSINK_THIS(iface);
PROTOCOLDATA *data;
TRACE("(%p)->(%p)\n", This, pProtocolData);
TRACE("flags %x state %x data %p cb %u\n", pProtocolData->grfFlags, pProtocolData->dwState,
pProtocolData->pData, pProtocolData->cbData);
data = heap_alloc(sizeof(PROTOCOLDATA));
if(!data)
return E_OUTOFMEMORY;
memcpy(data, pProtocolData, sizeof(PROTOCOLDATA));
if(!do_direct_notif(This)) {
switch_task_t *task;
@ -903,18 +913,18 @@ static HRESULT WINAPI BPInternetProtocolSink_Switch(IInternetProtocolSink *iface
if(!task)
return E_OUTOFMEMORY;
task->data = *pProtocolData;
task->data = data;
push_task(This, &task->header, switch_proc);
return S_OK;
}
if(!This->protocol_sink) {
IInternetProtocol_Continue(This->protocol_handler, pProtocolData);
IInternetProtocol_Continue(This->protocol_handler, data);
return S_OK;
}
return IInternetProtocolSink_Switch(This->protocol_sink, pProtocolData);
return IInternetProtocolSink_Switch(This->protocol_sink, data);
}
static void report_progress(BindProtocol *This, ULONG status_code, LPCWSTR status_text)

View file

@ -85,7 +85,7 @@ static HRESULT HttpProtocol_open_request(Protocol *prot, LPCWSTR url, DWORD requ
BYTE security_id[512];
DWORD len = 0;
ULONG num = 0;
BOOL res;
BOOL res, b;
HRESULT hres;
static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
@ -209,6 +209,11 @@ static HRESULT HttpProtocol_open_request(Protocol *prot, LPCWSTR url, DWORD requ
optional = (LPWSTR)This->base.bind_info.stgmedData.u.hGlobal;
}
b = TRUE;
res = InternetSetOptionW(This->base.request, INTERNET_OPTION_HTTP_DECODING, &b, sizeof(b));
if(!res)
WARN("InternetSetOption(INTERNET_OPTION_HTTP_DECODING) failed: %08x\n", GetLastError());
res = HttpSendRequestW(This->base.request, This->full_header, lstrlenW(This->full_header),
optional, optional ? This->base.bind_info.cbstgmedData : 0);
if(!res && GetLastError() != ERROR_IO_PENDING) {

View file

@ -36,7 +36,7 @@ LONG URLMON_refCount = 0;
HINSTANCE URLMON_hInstance = 0;
static HMODULE hCabinet = NULL;
static DWORD urlmon_tls;
static DWORD urlmon_tls = TLS_OUT_OF_INDEXES;
static void init_session(BOOL);
@ -56,9 +56,12 @@ tls_data_t *get_tls_data(void)
{
tls_data_t *data;
if(!urlmon_tls) {
if(urlmon_tls == TLS_OUT_OF_INDEXES) {
DWORD tls = TlsAlloc();
tls = InterlockedCompareExchange((LONG*)&urlmon_tls, tls, 0);
if(tls == TLS_OUT_OF_INDEXES)
return NULL;
tls = InterlockedCompareExchange((LONG*)&urlmon_tls, tls, TLS_OUT_OF_INDEXES);
if(tls != urlmon_tls)
TlsFree(tls);
}
@ -83,7 +86,7 @@ static void free_tls_list(void)
{
tls_data_t *data;
if(!urlmon_tls)
if(urlmon_tls == TLS_OUT_OF_INDEXES)
return;
while(!list_empty(&tls_list)) {
@ -99,7 +102,7 @@ static void detach_thread(void)
{
tls_data_t *data;
if(!urlmon_tls)
if(urlmon_tls == TLS_OUT_OF_INDEXES)
return;
data = TlsGetValue(urlmon_tls);

View file

@ -0,0 +1,281 @@
/*
* Copyright 2008 Hans Leidekker for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdarg.h>
#include "wine/debug.h"
#include "wine/list.h"
#include "windef.h"
#include "winbase.h"
#include "winhttp.h"
#include "winhttp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
static domain_t *add_domain( session_t *session, WCHAR *name )
{
domain_t *domain;
if (!(domain = heap_alloc_zero( sizeof(domain_t) ))) return NULL;
list_init( &domain->entry );
list_init( &domain->cookies );
domain->name = name;
list_add_tail( &session->cookie_cache, &domain->entry );
TRACE("%s\n", debugstr_w(domain->name));
return domain;
}
static cookie_t *find_cookie( domain_t *domain, const WCHAR *path, const WCHAR *name )
{
struct list *item;
cookie_t *cookie;
LIST_FOR_EACH( item, &domain->cookies )
{
cookie = LIST_ENTRY( item, cookie_t, entry );
if (!strcmpW( cookie->path, path ) && !strcmpiW( cookie->name, name ))
{
TRACE("found %s=%s\n", debugstr_w(cookie->name), debugstr_w(cookie->value));
return cookie;
}
}
return NULL;
}
static BOOL domain_match( const WCHAR *name, domain_t *domain, BOOL partial )
{
TRACE("comparing %s with %s\n", debugstr_w(name), debugstr_w(domain->name));
if (partial && !strstrW( name, domain->name )) return FALSE;
else if (!partial && strcmpW( name, domain->name )) return FALSE;
return TRUE;
}
static void free_cookie( cookie_t *cookie )
{
heap_free( cookie->name );
heap_free( cookie->value );
heap_free( cookie->path );
heap_free( cookie );
}
static void delete_cookie( cookie_t *cookie )
{
list_remove( &cookie->entry );
free_cookie( cookie );
}
void delete_domain( domain_t *domain )
{
cookie_t *cookie;
struct list *item, *next;
LIST_FOR_EACH_SAFE( item, next, &domain->cookies )
{
cookie = LIST_ENTRY( item, cookie_t, entry );
delete_cookie( cookie );
}
list_remove( &domain->entry );
heap_free( domain->name );
heap_free( domain );
}
static BOOL add_cookie( session_t *session, cookie_t *cookie, WCHAR *domain_name, WCHAR *path )
{
domain_t *domain = NULL;
cookie_t *old_cookie;
struct list *item;
LIST_FOR_EACH( item, &session->cookie_cache )
{
domain = LIST_ENTRY( item, domain_t, entry );
if (domain_match( domain_name, domain, FALSE )) break;
domain = NULL;
}
if (!domain)
{
if (!(domain = add_domain( session, domain_name ))) return FALSE;
}
else if ((old_cookie = find_cookie( domain, path, cookie->name ))) delete_cookie( old_cookie );
cookie->path = path;
list_add_tail( &domain->cookies, &cookie->entry );
TRACE("domain %s path %s <- %s=%s\n", debugstr_w(domain_name), debugstr_w(cookie->path),
debugstr_w(cookie->name), debugstr_w(cookie->value));
return TRUE;
}
static cookie_t *parse_cookie( const WCHAR *string )
{
cookie_t *cookie;
const WCHAR *p;
int len;
if (!(cookie = heap_alloc_zero( sizeof(cookie_t) ))) return NULL;
list_init( &cookie->entry );
if (!(p = strchrW( string, '=' )))
{
WARN("no '=' in %s\n", debugstr_w(string));
return NULL;
}
if (p == string)
{
WARN("empty cookie name in %s\n", debugstr_w(string));
return NULL;
}
len = p - string;
if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) )))
{
heap_free( cookie );
return NULL;
}
memcpy( cookie->name, string, len * sizeof(WCHAR) );
cookie->name[len] = 0;
p++; /* skip '=' */
while (*p == ' ') p++;
len = strlenW( p );
if (!(cookie->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
{
free_cookie( cookie );
return NULL;
}
memcpy( cookie->value, p, len * sizeof(WCHAR) );
cookie->value[len] = 0;
return cookie;
}
BOOL set_cookies( request_t *request, const WCHAR *cookies )
{
static const WCHAR pathW[] = {'p','a','t','h',0};
static const WCHAR domainW[] = {'d','o','m','a','i','n',0};
BOOL ret = FALSE;
WCHAR *buffer, *p, *q, *r;
WCHAR *cookie_domain = NULL, *cookie_path = NULL;
session_t *session = request->connect->session;
cookie_t *cookie;
int len;
len = strlenW( cookies );
if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
strcpyW( buffer, cookies );
p = buffer;
while (*p && *p != ';') p++;
if (*p == ';') *p++ = 0;
if (!(cookie = parse_cookie( buffer )))
{
heap_free( buffer );
return FALSE;
}
if ((q = strstrW( p, domainW ))) /* FIXME: do real attribute parsing */
{
while (*q && *q != '=') q++;
if (!*q) goto end;
r = ++q;
while (*r && *r != ';') r++;
len = r - q;
if (!(cookie_domain = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
memcpy( cookie_domain, q, len * sizeof(WCHAR) );
cookie_domain[len] = 0;
}
if ((q = strstrW( p, pathW )))
{
while (*q && *q != '=') q++;
if (!*q) goto end;
r = ++q;
while (*r && *r != ';') r++;
len = r - q;
if (!(cookie_path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
memcpy( cookie_path, q, len * sizeof(WCHAR) );
cookie_path[len] = 0;
}
if (!cookie_domain && !(cookie_domain = strdupW( request->connect->servername ))) goto end;
if (!cookie_path && !(cookie_path = strdupW( request->path ))) goto end;
if ((p = strrchrW( cookie_path, '/' )) && p != cookie_path) *p = 0;
ret = add_cookie( session, cookie, cookie_domain, cookie_path );
end:
if (!ret)
{
free_cookie( cookie );
heap_free( cookie_domain );
heap_free( cookie_path );
}
heap_free( buffer );
return ret;
}
BOOL add_cookie_headers( request_t *request )
{
struct list *domain_cursor;
session_t *session = request->connect->session;
LIST_FOR_EACH( domain_cursor, &session->cookie_cache )
{
domain_t *domain = LIST_ENTRY( domain_cursor, domain_t, entry );
if (domain_match( request->connect->servername, domain, TRUE ))
{
struct list *cookie_cursor;
TRACE("found domain %s\n", debugstr_w(domain->name));
LIST_FOR_EACH( cookie_cursor, &domain->cookies )
{
cookie_t *cookie = LIST_ENTRY( cookie_cursor, cookie_t, entry );
TRACE("comparing path %s with %s\n", debugstr_w(request->path), debugstr_w(cookie->path));
if (strstrW( request->path, cookie->path ) == request->path)
{
const WCHAR format[] = {'C','o','o','k','i','e',':',' ','%','s','=','%','s',0};
int len;
WCHAR *header;
len = strlenW( cookie->name ) + strlenW( format ) + strlenW( cookie->value );
if (!(header = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
sprintfW( header, format, cookie->name, cookie->value );
TRACE("%s\n", debugstr_w(header));
add_request_headers( request, header, len, WINHTTP_ADDREQ_FLAG_ADD );
heap_free( header );
}
}
}
}
return TRUE;
}

View file

@ -38,8 +38,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
{
switch(fdwReason)
{
case DLL_WINE_PREATTACH:
return FALSE; /* prefer native version */
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstDLL);
break;
@ -84,41 +82,3 @@ HRESULT WINAPI DllUnregisterServer(void)
FIXME("()\n");
return S_OK;
}
#define SCHEME_HTTP 3
#define SCHEME_HTTPS 4
BOOL WINAPI InternetCrackUrlW( LPCWSTR, DWORD, DWORD, LPURL_COMPONENTSW );
BOOL WINAPI InternetCreateUrlW( LPURL_COMPONENTS, DWORD, LPWSTR, LPDWORD );
/***********************************************************************
* WinHttpCrackUrl (winhttp.@)
*/
BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW components )
{
BOOL ret;
TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, components);
if ((ret = InternetCrackUrlW( url, len, flags, components )))
{
/* fix up an incompatibility between wininet and winhttp */
if (components->nScheme == SCHEME_HTTP) components->nScheme = INTERNET_SCHEME_HTTP;
else if (components->nScheme == SCHEME_HTTPS) components->nScheme = INTERNET_SCHEME_HTTPS;
else
{
set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME );
return FALSE;
}
}
return ret;
}
/***********************************************************************
* WinHttpCreateUrl (winhttp.@)
*/
BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS comps, DWORD flags, LPWSTR url, LPDWORD len )
{
TRACE("%p, 0x%08x, %p, %p\n", comps, flags, url, len);
return InternetCreateUrlW( comps, flags, url, len );
}

View file

@ -48,13 +48,14 @@
#include "windef.h"
#include "winbase.h"
#include "winhttp.h"
#include "wincrypt.h"
#include "winhttp_private.h"
/* to avoid conflicts with the Unix socket headers */
#define USE_WS_PREFIX
#include "winsock2.h"
#include "winhttp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
#define DEFAULT_SEND_TIMEOUT 30
@ -83,6 +84,7 @@ static void *libssl_handle;
static void *libcrypto_handle;
static SSL_METHOD *method;
static SSL_CTX *ctx;
#define MAKE_FUNCPTR(f) static typeof(f) * p##f
@ -90,7 +92,6 @@ MAKE_FUNCPTR( SSL_library_init );
MAKE_FUNCPTR( SSL_load_error_strings );
MAKE_FUNCPTR( SSLv23_method );
MAKE_FUNCPTR( SSL_CTX_new );
MAKE_FUNCPTR( SSL_CTX_free );
MAKE_FUNCPTR( SSL_new );
MAKE_FUNCPTR( SSL_free );
MAKE_FUNCPTR( SSL_set_fd );
@ -107,14 +108,15 @@ MAKE_FUNCPTR( SSL_CTX_set_default_verify_paths );
MAKE_FUNCPTR( BIO_new_fp );
MAKE_FUNCPTR( ERR_get_error );
MAKE_FUNCPTR( ERR_error_string );
MAKE_FUNCPTR( i2d_X509 );
#undef MAKE_FUNCPTR
#endif
#if 0
/* translate a unix error code into a winsock error code */
static int sock_get_error( int err )
{
#if !defined(__MINGW32__) && !defined (_MSC_VER)
switch (err)
{
case EINTR: return WSAEINTR;
@ -174,11 +176,9 @@ static int sock_get_error( int err )
#endif
default: errno = err; perror( "sock_set_error" ); return WSAEFAULT;
}
#endif
return err;
}
#else
#define sock_get_error(x) WSAGetLastError()
#endif
BOOL netconn_init( netconn_t *conn, BOOL secure )
{
@ -210,7 +210,6 @@ BOOL netconn_init( netconn_t *conn, BOOL secure )
LOAD_FUNCPTR( SSL_load_error_strings );
LOAD_FUNCPTR( SSLv23_method );
LOAD_FUNCPTR( SSL_CTX_new );
LOAD_FUNCPTR( SSL_CTX_free );
LOAD_FUNCPTR( SSL_new );
LOAD_FUNCPTR( SSL_free );
LOAD_FUNCPTR( SSL_set_fd );
@ -235,6 +234,7 @@ BOOL netconn_init( netconn_t *conn, BOOL secure )
LOAD_FUNCPTR( BIO_new_fp );
LOAD_FUNCPTR( ERR_get_error );
LOAD_FUNCPTR( ERR_error_string );
LOAD_FUNCPTR( i2d_X509 );
#undef LOAD_FUNCPTR
pSSL_library_init();
@ -242,8 +242,6 @@ BOOL netconn_init( netconn_t *conn, BOOL secure )
pBIO_new_fp( stderr, BIO_NOCLOSE );
method = pSSLv23_method();
conn->ssl_ctx = pSSL_CTX_new( method );
#else
WARN("SSL support not compiled in.\n");
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
@ -314,13 +312,14 @@ BOOL netconn_secure_connect( netconn_t *conn )
X509 *cert;
long res;
if (!pSSL_CTX_set_default_verify_paths( conn->ssl_ctx ))
ctx = pSSL_CTX_new( method );
if (!pSSL_CTX_set_default_verify_paths( ctx ))
{
ERR("SSL_CTX_set_default_verify_paths failed: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_OUTOFMEMORY );
return FALSE;
}
if (!(conn->ssl_conn = pSSL_new( conn->ssl_ctx )))
if (!(conn->ssl_conn = pSSL_new( ctx )))
{
ERR("SSL_new failed: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_OUTOFMEMORY );
@ -468,11 +467,7 @@ BOOL netconn_query_data_available( netconn_t *conn, DWORD *available )
return TRUE;
}
#ifdef FIONREAD
if (!(ret = ioctlsocket( conn->socket, FIONREAD, &unread )))
{
TRACE("%d bytes of queued, but unread data\n", unread);
*available += unread;
}
if (!(ret = ioctlsocket( conn->socket, FIONREAD, &unread ))) *available = unread;
#endif
return TRUE;
}
@ -491,8 +486,8 @@ BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen )
#ifdef SONAME_LIBSSL
long timeout;
timeout = pSSL_CTX_get_timeout( conn->ssl_ctx );
pSSL_CTX_set_timeout( conn->ssl_ctx, DEFAULT_RECEIVE_TIMEOUT );
timeout = pSSL_CTX_get_timeout( ctx );
pSSL_CTX_set_timeout( ctx, DEFAULT_RECEIVE_TIMEOUT );
while (recvd < *buflen)
{
@ -509,7 +504,7 @@ BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen )
}
if (buffer[recvd] != '\r') recvd++;
}
pSSL_CTX_set_timeout( conn->ssl_ctx, timeout );
pSSL_CTX_set_timeout( ctx, timeout );
if (ret)
{
buffer[recvd++] = 0;
@ -567,7 +562,7 @@ DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )
tv.tv_sec = value / 1000;
tv.tv_usec = (value % 1000) * 1000;
if ((res = setsockopt( netconn->socket, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, &tv, sizeof(tv) ) == -1))
if ((res = setsockopt( netconn->socket, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, (void*)&tv, sizeof(tv) ) == -1))
{
WARN("setsockopt failed (%s)\n", strerror( errno ));
return sock_get_error( errno );
@ -595,7 +590,7 @@ BOOL netconn_resolve( WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_in *
heap_free( hostname );
if (ret != 0)
{
TRACE("failed to get address of %s (%s)\n", debugstr_a(hostname), gai_strerror(ret));
TRACE("failed to get address of %s (%s)\n", debugstr_w(hostnameW), gai_strerror(ret));
return FALSE;
}
memset( sa, 0, sizeof(struct sockaddr_in) );
@ -611,12 +606,12 @@ BOOL netconn_resolve( WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_in *
heap_free( hostname );
if (!he)
{
TRACE("failed to get address of %s (%d)\n", debugstr_a(hostname), h_errno);
TRACE("failed to get address of %s (%d)\n", debugstr_w(hostnameW), h_errno);
LeaveCriticalSection( &cs_gethostbyname );
return FALSE;
}
memset( sa, 0, sizeof(struct sockaddr_in) );
memcpy( (char *)&sa->sin_addr, he->h_addr, he->h_length );
memcpy( &sa->sin_addr, he->h_addr, he->h_length );
sa->sin_family = he->h_addrtype;
sa->sin_port = htons( port );
@ -624,3 +619,46 @@ BOOL netconn_resolve( WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_in *
#endif
return TRUE;
}
const void *netconn_get_certificate( netconn_t *conn )
{
#ifdef SONAME_LIBSSL
X509 *cert;
unsigned char *buffer, *p;
int len;
BOOL malloc = FALSE;
const CERT_CONTEXT *ret;
if (!conn->secure) return NULL;
if (!(cert = pSSL_get_peer_certificate( conn->ssl_conn ))) return NULL;
p = NULL;
if ((len = pi2d_X509( cert, &p )) < 0) return NULL;
/*
* SSL 0.9.7 and above malloc the buffer if it is null.
* however earlier version do not and so we would need to alloc the buffer.
*
* see the i2d_X509 man page for more details.
*/
if (!p)
{
if (!(buffer = heap_alloc( len ))) return NULL;
p = buffer;
len = pi2d_X509( cert, &p );
}
else
{
buffer = p;
malloc = TRUE;
}
ret = CertCreateCertificateContext( X509_ASN_ENCODING, buffer, len );
if (malloc) free( buffer );
else heap_free( buffer );
return ret;
#else
return NULL;
#endif
}

View file

@ -175,6 +175,22 @@ static const WCHAR *attribute_table[] =
NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
};
static DWORD CALLBACK task_thread( LPVOID param )
{
task_header_t *task = param;
task->proc( task );
release_object( &task->request->hdr );
heap_free( task );
return ERROR_SUCCESS;
}
static BOOL queue_task( task_header_t *task )
{
return QueueUserWorkItem( task_thread, task, WT_EXECUTELONGFUNCTION );
}
static void free_header( header_t *header )
{
heap_free( header->field );
@ -391,13 +407,13 @@ static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DW
return TRUE;
}
static BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags )
BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags )
{
BOOL ret = FALSE;
WCHAR *buffer, *p, *q;
header_t *header;
if (len == ~0UL) len = strlenW( headers );
if (len == ~0u) len = strlenW( headers );
if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
strcpyW( buffer, headers );
@ -467,28 +483,20 @@ static WCHAR *build_request_string( request_t *request )
static const WCHAR crlf[] = {'\r','\n',0};
static const WCHAR colon[] = {':',' ',0};
static const WCHAR twocrlf[] = {'\r','\n','\r','\n',0};
static const WCHAR get[] = {'G','E','T',0};
static const WCHAR slash[] = {'/',0};
static const WCHAR http1_1[] = {'H','T','T','P','/','1','.','1',0};
WCHAR *ret;
const WCHAR **headers, **p;
const WCHAR *verb = get, *path = slash, *version = http1_1;
unsigned int len, i = 0, j;
if (request->verb && request->verb[0]) verb = request->verb;
if (request->path && request->path[0]) path = request->path;
if (request->version && request->version[0]) version = request->version;
/* allocate space for an array of all the string pointers to be added */
len = request->num_headers * 4 + 7;
if (!(headers = heap_alloc( len * sizeof(LPCWSTR) ))) return NULL;
headers[i++] = verb;
headers[i++] = request->verb;
headers[i++] = space;
headers[i++] = path;
headers[i++] = request->path;
headers[i++] = space;
headers[i++] = version;
headers[i++] = request->version;
for (j = 0; j < request->num_headers; j++)
{
@ -561,7 +569,7 @@ static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID
}
else if (buffer)
{
for (p = headers, q = (WCHAR *)buffer; *p; p++, q++)
for (p = headers, q = buffer; *p; p++, q++)
{
if (*p != '\r') *q = *p;
else
@ -571,7 +579,7 @@ static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID
}
}
*q = 0;
TRACE("returning data: %s\n", debugstr_wn((WCHAR *)buffer, len));
TRACE("returning data: %s\n", debugstr_wn(buffer, len));
ret = TRUE;
}
*buflen = len * sizeof(WCHAR);
@ -606,7 +614,7 @@ static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID
}
default:
{
if (attr > sizeof(attribute_table)/sizeof(attribute_table[0]) || !attribute_table[attr])
if (attr >= sizeof(attribute_table)/sizeof(attribute_table[0]) || !attribute_table[attr])
{
FIXME("attribute %u not implemented\n", attr);
return FALSE;
@ -710,13 +718,16 @@ static BOOL open_connection( request_t *request )
connect_t *connect;
char address[32];
WCHAR *addressW;
INTERNET_PORT port;
if (netconn_connected( &request->netconn )) return TRUE;
connect = request->connect;
port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, strlenW(connect->servername) + 1 );
if (!netconn_resolve( connect->servername, connect->serverport, &connect->sockaddr )) return FALSE;
if (!netconn_resolve( connect->servername, port, &connect->sockaddr )) return FALSE;
inet_ntop( connect->sockaddr.sin_family, &connect->sockaddr.sin_addr, address, sizeof(address) );
addressW = strdupAW( address );
@ -759,32 +770,35 @@ void close_connection( request_t *request )
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
}
static BOOL add_host_header( request_t *request, WCHAR *hostname, INTERNET_PORT port, DWORD modifier )
static BOOL add_host_header( request_t *request, DWORD modifier )
{
BOOL ret;
DWORD len;
WCHAR *host;
static const WCHAR fmt[] = {'%','s',':','%','u',0};
connect_t *connect = request->connect;
INTERNET_PORT port;
port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
{
return process_header( request, attr_host, hostname, modifier, TRUE );
return process_header( request, attr_host, connect->hostname, modifier, TRUE );
}
len = strlenW( hostname ) + 7; /* sizeof(":65335") */
len = strlenW( connect->hostname ) + 7; /* sizeof(":65335") */
if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
sprintfW( host, fmt, hostname, port );
sprintfW( host, fmt, connect->hostname, port );
ret = process_header( request, attr_host, host, modifier, TRUE );
heap_free( host );
return ret;
}
static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
DWORD optional_len, DWORD total_len, DWORD_PTR context )
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};
static const WCHAR post[] = {'P','O','S','T',0};
BOOL ret = FALSE;
connect_t *connect = request->connect;
@ -798,9 +812,9 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
if (connect->hostname)
add_host_header( request, connect->hostname, connect->hostport, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
if (total_len || (request->verb && !strcmpW( request->verb, post )))
if (total_len || (request->verb && !strcmpW( request->verb, postW )))
{
WCHAR length[21]; /* decimal long int + null */
sprintfW( length, length_fmt, total_len );
@ -820,6 +834,11 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
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 (!(ret = open_connection( request ))) goto end;
if (!(req = build_request_string( request ))) goto end;
@ -840,10 +859,28 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(DWORD) );
end:
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
else
{
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) );
}
}
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.@)
*/
@ -868,7 +905,25 @@ BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD heade
return FALSE;
}
ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context );
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
send_request_t *s;
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 );
return ret;
@ -889,9 +944,9 @@ static void clear_response_headers( request_t *request )
}
#define MAX_REPLY_LEN 1460
#define INITIAL_HEADER_BUFFER_SIZE 512
#define INITIAL_HEADER_BUFFER_LEN 512
static BOOL receive_response( request_t *request, BOOL clear )
static BOOL read_reply( request_t *request )
{
static const WCHAR crlf[] = {'\r','\n',0};
@ -903,9 +958,6 @@ static BOOL receive_response( request_t *request, BOOL clear )
if (!netconn_connected( &request->netconn )) return FALSE;
/* clear old response headers (eg. from a redirect response) */
if (clear) clear_response_headers( request );
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
received_len = 0;
@ -949,7 +1001,7 @@ static BOOL receive_response( request_t *request, BOOL clear )
heap_free( request->status_text );
request->status_text = status_textW;
len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_SIZE );
len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
memcpy( raw_headers + buflen - 1, crlf, sizeof(crlf) );
@ -1002,6 +1054,7 @@ static BOOL handle_redirect( request_t *request )
connect_t *connect = request->connect;
INTERNET_PORT port;
WCHAR *hostname = NULL, *location = NULL;
int index;
size = 0;
query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
@ -1012,48 +1065,73 @@ static BOOL handle_redirect( request_t *request )
memset( &uc, 0, sizeof(uc) );
uc.dwStructSize = sizeof(uc);
uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0UL;
uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
if (!(ret = WinHttpCrackUrl( location, size / sizeof(WCHAR), 0, &uc ))) goto end;
if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
if (!WinHttpCrackUrl( location, size / sizeof(WCHAR), 0, &uc )) /* assume relative redirect */
{
TRACE("redirect from secure page to non-secure page\n");
request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
WCHAR *path, *p;
len = strlenW( location ) + 1;
if (location[0] != '/') len++;
if (!(p = path = heap_alloc( len * sizeof(WCHAR) ))) goto end;
if (location[0] != '/') *p++ = '/';
strcpyW( p, location );
heap_free( request->path );
request->path = path;
}
else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
else
{
TRACE("redirect from non-secure page to secure page\n");
request->hdr.flags |= WINHTTP_FLAG_SECURE;
if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
{
TRACE("redirect from secure page to non-secure page\n");
request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
}
else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
{
TRACE("redirect from non-secure page to secure page\n");
request->hdr.flags |= WINHTTP_FLAG_SECURE;
}
len = uc.dwHostNameLength;
if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
hostname[len] = 0;
port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
if (strcmpiW( connect->servername, hostname ) || connect->serverport != port)
{
heap_free( connect->hostname );
connect->hostname = hostname;
heap_free( connect->servername );
connect->servername = strdupW( connect->hostname );
connect->serverport = connect->hostport = port;
netconn_close( &request->netconn );
if (!(ret = netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE ))) goto end;
}
if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
if (!(ret = open_connection( request ))) goto end;
heap_free( request->path );
request->path = NULL;
if (uc.dwUrlPathLength)
{
len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
strcpyW( request->path, uc.lpszUrlPath );
}
else request->path = strdupW( slashW );
}
len = uc.dwHostNameLength;
if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
hostname[len] = 0;
port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
if (strcmpiW( connect->servername, hostname ) || connect->serverport != port)
{
heap_free( connect->servername );
connect->servername = hostname;
connect->serverport = connect->hostport = port;
netconn_close( &request->netconn );
if (!(ret = netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE ))) goto end;
}
if (!(ret = add_host_header( request, hostname, port, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
if (!(ret = open_connection( request ))) goto end;
heap_free( request->path );
request->path = NULL;
if (uc.lpszUrlPath)
{
len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
strcpyW( request->path, uc.lpszUrlPath );
}
/* remove content-type/length headers */
if ((index = get_header_index( request, attr_content_type, 0, TRUE )) >= 0) delete_header( request, index );
if ((index = get_header_index( request, attr_content_length, 0, TRUE )) >= 0 ) delete_header( request, index );
/* redirects are always GET requests */
heap_free( request->verb );
request->verb = strdupW( getW );
ret = TRUE;
end:
@ -1062,7 +1140,7 @@ end:
return ret;
}
static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
static BOOL receive_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
{
DWORD to_read;
int bytes_read;
@ -1083,99 +1161,6 @@ static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read
return TRUE;
}
/* read any content returned by the server so that the connection can be reused */
static void drain_content( request_t *request )
{
DWORD bytes_read;
char buffer[2048];
if (request->content_length == ~0UL) return;
for (;;)
{
if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
}
}
/***********************************************************************
* WinHttpReceiveResponse (winhttp.@)
*/
BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
{
BOOL ret = TRUE;
request_t *request;
DWORD size, query, status;
TRACE("%p, %p\n", hrequest, reserved);
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;
}
for (;;)
{
if (!(ret = receive_response( request, TRUE ))) break;
size = sizeof(DWORD);
query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
size = sizeof(DWORD);
query = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER;
if (!query_headers( request, query, NULL, &request->content_length, &size, NULL ))
request->content_length = ~0UL;
if (status == 200) break;
if (status == 301 || status == 302)
{
if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS) break;
drain_content( request );
if (!(ret = handle_redirect( request ))) break;
}
ret = send_request( request, NULL, 0, NULL, 0, 0, 0 );
}
release_object( &request->hdr );
return ret;
}
/***********************************************************************
* WinHttpQueryDataAvailable (winhttp.@)
*/
BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
{
BOOL ret;
DWORD num_bytes;
request_t *request;
TRACE("%p, %p\n", hrequest, available);
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;
}
ret = netconn_query_data_available( &request->netconn, &num_bytes );
if (ret && available) *available = num_bytes;
release_object( &request->hdr );
return ret;
}
static DWORD get_chunk_size( const char *buffer )
{
const char *p;
@ -1191,7 +1176,7 @@ static DWORD get_chunk_size( const char *buffer )
return size;
}
static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
static BOOL receive_data_chunked( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
{
char reply[MAX_REPLY_LEN], *p = buffer;
DWORD buflen, to_read, to_write = size;
@ -1202,7 +1187,7 @@ static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWO
{
if (*read == size) break;
if (request->content_length == ~0UL) /* new chunk */
if (request->content_length == ~0u) /* new chunk */
{
buflen = sizeof(reply);
if (!netconn_get_next_line( &request->netconn, reply, &buflen )) break;
@ -1210,7 +1195,7 @@ static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWO
if (!(request->content_length = get_chunk_size( reply )))
{
/* zero sized chunk marks end of transfer; read any trailing headers and return */
receive_response( request, FALSE );
read_reply( request );
break;
}
}
@ -1236,7 +1221,7 @@ static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWO
if (request->content_read == request->content_length) /* chunk complete */
{
request->content_read = 0;
request->content_length = ~0UL;
request->content_length = ~0u;
buflen = sizeof(reply);
if (!netconn_get_next_line( &request->netconn, reply, &buflen ))
@ -1250,17 +1235,285 @@ static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWO
return TRUE;
}
static void finished_reading( request_t *request )
{
static const WCHAR closeW[] = {'c','l','o','s','e',0};
BOOL close = FALSE;
WCHAR connection[20];
DWORD size = sizeof(connection);
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 );
request->content_length = ~0u;
request->content_read = 0;
}
static BOOL read_data( request_t *request, void *buffer, DWORD to_read, DWORD *read, BOOL async )
{
static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0};
BOOL ret;
WCHAR encoding[20];
DWORD num_bytes, buflen = sizeof(encoding);
if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
!strcmpiW( encoding, chunked ))
{
ret = receive_data_chunked( request, buffer, to_read, &num_bytes, async );
}
else
ret = receive_data( request, buffer, to_read, &num_bytes, async );
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, num_bytes );
else
{
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_READ_DATA;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
if (ret)
{
if (read) *read = num_bytes;
if (!num_bytes) finished_reading( request );
}
return ret;
}
/* read any content returned by the server so that the connection can be reused */
static void drain_content( request_t *request )
{
DWORD bytes_read;
char buffer[2048];
if (!request->content_length) return;
for (;;)
{
if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
}
}
static void record_cookies( request_t *request )
{
unsigned int i;
for (i = 0; i < request->num_headers; i++)
{
header_t *set_cookie = &request->headers[i];
if (!strcmpiW( set_cookie->field, attr_set_cookie ) && !set_cookie->is_request)
{
set_cookies( request, set_cookie->value );
}
}
}
static BOOL receive_response( request_t *request, BOOL async )
{
BOOL ret;
DWORD size, query, status;
for (;;)
{
if (!(ret = read_reply( request ))) break;
size = sizeof(DWORD);
query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
size = sizeof(DWORD);
query = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER;
if (!query_headers( request, query, NULL, &request->content_length, &size, NULL ))
request->content_length = ~0u;
if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
if (status == 301 || status == 302)
{
if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS) break;
drain_content( request );
if (!(ret = handle_redirect( request ))) break;
clear_response_headers( request );
ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */
continue;
}
if (status == 401) FIXME("authentication not supported\n");
break;
}
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
else
{
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_RECEIVE_RESPONSE;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
return ret;
}
static void task_receive_response( task_header_t *task )
{
receive_response_t *r = (receive_response_t *)task;
receive_response( r->hdr.request, TRUE );
}
/***********************************************************************
* WinHttpReceiveResponse (winhttp.@)
*/
BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
{
BOOL ret;
request_t *request;
TRACE("%p, %p\n", hrequest, reserved);
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 (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
receive_response_t *r;
if (!(r = heap_alloc( sizeof(receive_response_t) ))) return FALSE;
r->hdr.request = request;
r->hdr.proc = task_receive_response;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)r );
}
else
ret = receive_response( request, FALSE );
release_object( &request->hdr );
return ret;
}
static BOOL query_data( request_t *request, LPDWORD available, BOOL async )
{
BOOL ret;
DWORD num_bytes;
if ((ret = netconn_query_data_available( &request->netconn, &num_bytes )))
{
if (request->content_read < request->content_length)
{
if (!num_bytes)
{
char buffer[4096];
size_t to_read = min( sizeof(buffer), request->content_length - request->content_read );
ret = netconn_recv( &request->netconn, buffer, to_read, MSG_PEEK, (int *)&num_bytes );
if (ret && !num_bytes) WARN("expected more data to be available\n");
}
}
else if (num_bytes)
{
WARN("extra data available %u\n", num_bytes);
ret = FALSE;
}
}
TRACE("%u bytes available\n", num_bytes);
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &num_bytes, sizeof(DWORD) );
else
{
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_QUERY_DATA_AVAILABLE;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
if (ret && available) *available = num_bytes;
return ret;
}
static void task_query_data( task_header_t *task )
{
query_data_t *q = (query_data_t *)task;
query_data( q->hdr.request, q->available, TRUE );
}
/***********************************************************************
* WinHttpQueryDataAvailable (winhttp.@)
*/
BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
{
BOOL ret;
request_t *request;
TRACE("%p, %p\n", hrequest, available);
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 (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
query_data_t *q;
if (!(q = heap_alloc( sizeof(query_data_t) ))) return FALSE;
q->hdr.request = request;
q->hdr.proc = task_query_data;
q->available = available;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)q );
}
else
ret = query_data( request, available, FALSE );
release_object( &request->hdr );
return ret;
}
static void task_read_data( task_header_t *task )
{
read_data_t *r = (read_data_t *)task;
read_data( r->hdr.request, r->buffer, r->to_read, r->read, TRUE );
}
/***********************************************************************
* WinHttpReadData (winhttp.@)
*/
BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
{
static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0};
BOOL ret;
request_t *request;
WCHAR encoding[20];
DWORD num_bytes, buflen = sizeof(encoding);
TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
@ -1276,19 +1529,55 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, L
return FALSE;
}
if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
!strcmpiW( encoding, chunked ))
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
ret = read_data_chunked( request, buffer, to_read, &num_bytes, request->hdr.flags & WINHTTP_FLAG_ASYNC );
read_data_t *r;
if (!(r = heap_alloc( sizeof(read_data_t) ))) return FALSE;
r->hdr.request = request;
r->hdr.proc = task_read_data;
r->buffer = buffer;
r->to_read = to_read;
r->read = read;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)r );
}
else
ret = read_data( request, buffer, to_read, &num_bytes, request->hdr.flags & WINHTTP_FLAG_ASYNC );
ret = read_data( request, buffer, to_read, read, FALSE );
if (ret && read) *read = num_bytes;
release_object( &request->hdr );
return ret;
}
static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDWORD written, BOOL async )
{
BOOL ret;
int num_bytes;
ret = netconn_send( &request->netconn, buffer, to_write, 0, &num_bytes );
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) );
else
{
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_WRITE_DATA;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
if (ret && written) *written = num_bytes;
return ret;
}
static void task_write_data( task_header_t *task )
{
write_data_t *w = (write_data_t *)task;
write_data( w->hdr.request, w->buffer, w->to_write, w->written, TRUE );
}
/***********************************************************************
* WinHttpWriteData (winhttp.@)
*/
@ -1311,7 +1600,22 @@ BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write
return FALSE;
}
ret = netconn_send( &request->netconn, buffer, to_write, 0, (int *)written );
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
write_data_t *w;
if (!(w = heap_alloc( sizeof(write_data_t) ))) return FALSE;
w->hdr.request = request;
w->hdr.proc = task_write_data;
w->buffer = buffer;
w->to_write = to_write;
w->written = written;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)w );
}
else
ret = write_data( request, buffer, to_write, written, FALSE );
release_object( &request->hdr );
return ret;

View file

@ -25,6 +25,7 @@
#include "windef.h"
#include "winbase.h"
#include "winhttp.h"
#include "wincrypt.h"
#include "winhttp_private.h"
@ -36,11 +37,17 @@ void set_last_error( DWORD error )
SetLastError( error );
}
DWORD get_last_error( void )
{
/* FIXME */
return GetLastError();
}
void send_callback( object_header_t *hdr, DWORD status, LPVOID info, DWORD buflen )
{
TRACE("%p, 0x%08x, %p, %u\n", hdr, status, info, buflen);
if (hdr->notify_mask & status) hdr->callback( hdr->handle, hdr->context, status, info, buflen );
if (hdr->callback && (hdr->notify_mask & status)) hdr->callback( hdr->handle, hdr->context, status, info, buflen );
}
/***********************************************************************
@ -58,9 +65,16 @@ BOOL WINAPI WinHttpCheckPlatform( void )
static void session_destroy( object_header_t *hdr )
{
session_t *session = (session_t *)hdr;
struct list *item, *next;
domain_t *domain;
TRACE("%p\n", session);
LIST_FOR_EACH_SAFE( item, next, &session->cookie_cache )
{
domain = LIST_ENTRY( item, domain_t, entry );
delete_domain( domain );
}
heap_free( session->agent );
heap_free( session->proxy_server );
heap_free( session->proxy_bypass );
@ -69,6 +83,30 @@ static void session_destroy( object_header_t *hdr )
heap_free( session );
}
static BOOL session_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
{
switch (option)
{
case WINHTTP_OPTION_REDIRECT_POLICY:
{
if (!buffer || *buflen < sizeof(DWORD))
{
*buflen = sizeof(DWORD);
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
*(DWORD *)buffer = hdr->redirect_policy;
*buflen = sizeof(DWORD);
return TRUE;
}
default:
FIXME("unimplemented option %u\n", option);
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
{
switch (option)
@ -82,22 +120,33 @@ static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffe
}
case WINHTTP_OPTION_REDIRECT_POLICY:
{
DWORD policy = *(DWORD *)buffer;
DWORD policy;
if (buflen != sizeof(policy))
{
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
policy = *(DWORD *)buffer;
TRACE("0x%x\n", policy);
hdr->redirect_policy = policy;
return TRUE;
}
case WINHTTP_OPTION_DISABLE_FEATURE:
set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
return FALSE;
default:
FIXME("unimplemented option %u\n", option);
return TRUE;
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
static const object_vtbl_t session_vtbl =
{
session_destroy,
NULL,
session_query_option,
session_set_option
};
@ -118,6 +167,8 @@ HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWST
session->hdr.flags = flags;
session->hdr.refs = 1;
session->access = access;
session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
list_init( &session->cookie_cache );
if (agent && !(session->agent = strdupW( agent ))) goto end;
if (proxy && !(session->proxy_server = strdupW( proxy ))) goto end;
@ -202,10 +253,10 @@ HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PO
list_add_head( &session->hdr.children, &connect->hdr.entry );
if (server && !(connect->hostname = strdupW( server ))) goto end;
connect->hostport = port ? port : (connect->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
connect->hostport = port;
if (server && !(connect->servername = strdupW( server ))) goto end;
connect->serverport = port ? port : (connect->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
connect->serverport = port;
if (!(hconnect = alloc_handle( &connect->hdr ))) goto end;
connect->hdr.handle = hconnect;
@ -225,7 +276,7 @@ end:
static void request_destroy( object_header_t *hdr )
{
request_t *request = (request_t *)hdr;
int i;
DWORD i;
TRACE("%p\n", request);
@ -251,15 +302,54 @@ static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buf
{
case WINHTTP_OPTION_SECURITY_FLAGS:
{
DWORD flags = 0;
DWORD flags;
if (!buffer || *buflen < sizeof(flags))
{
*buflen = sizeof(flags);
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
flags = 0;
if (hdr->flags & WINHTTP_FLAG_SECURE) flags |= SECURITY_FLAG_SECURE;
*(DWORD *)buffer = flags;
*buflen = sizeof(flags);
return TRUE;
}
case WINHTTP_OPTION_SERVER_CERT_CONTEXT:
{
const CERT_CONTEXT *cert;
request_t *request = (request_t *)hdr;
if (!buffer || *buflen < sizeof(cert))
{
*buflen = sizeof(cert);
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
if (!(cert = netconn_get_certificate( &request->netconn ))) return FALSE;
*(CERT_CONTEXT **)buffer = (CERT_CONTEXT *)cert;
*buflen = sizeof(cert);
return TRUE;
}
case WINHTTP_OPTION_SECURITY_KEY_BITNESS:
{
if (!buffer || *buflen < sizeof(DWORD))
{
*buflen = sizeof(DWORD);
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
*(DWORD *)buffer = 128; /* FIXME */
*buflen = sizeof(DWORD);
return TRUE;
}
default:
FIXME("unimplemented option %u\n", option);
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
@ -277,30 +367,52 @@ static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffe
}
case WINHTTP_OPTION_DISABLE_FEATURE:
{
DWORD disable = *(DWORD *)buffer;
DWORD disable;
if (buflen != sizeof(DWORD))
{
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
disable = *(DWORD *)buffer;
TRACE("0x%x\n", disable);
hdr->disable_flags &= disable;
hdr->disable_flags |= disable;
return TRUE;
}
case WINHTTP_OPTION_AUTOLOGON_POLICY:
{
DWORD policy = *(DWORD *)buffer;
DWORD policy;
if (buflen != sizeof(DWORD))
{
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
policy = *(DWORD *)buffer;
TRACE("0x%x\n", policy);
hdr->logon_policy = policy;
return TRUE;
}
case WINHTTP_OPTION_REDIRECT_POLICY:
{
DWORD policy = *(DWORD *)buffer;
DWORD policy;
if (buflen != sizeof(DWORD))
{
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
policy = *(DWORD *)buffer;
TRACE("0x%x\n", policy);
hdr->redirect_policy = policy;
return TRUE;
}
default:
FIXME("unimplemented option %u\n", option);
set_last_error( ERROR_INVALID_PARAMETER );
return TRUE;
}
}
@ -355,9 +467,26 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o
if (!netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE )) goto end;
if (verb && !(request->verb = strdupW( verb ))) goto end;
if (object && !(request->path = strdupW( object ))) goto end;
if (version && !(request->version = strdupW( version ))) goto end;
if (!verb || !verb[0]) verb = getW;
if (!(request->verb = strdupW( verb ))) goto end;
if (object)
{
WCHAR *path, *p;
unsigned int len;
len = strlenW( object ) + 1;
if (object[0] != '/') len++;
if (!(p = path = heap_alloc( len * sizeof(WCHAR) ))) goto end;
if (object[0] != '/') *p++ = '/';
strcpyW( p, object );
request->path = path;
}
else if (!(request->path = strdupW( slashW ))) goto end;
if (!version || !version[0]) version = http1_1;
if (!(request->version = strdupW( version ))) goto end;
if (!(hrequest = alloc_handle( &request->hdr ))) goto end;
request->hdr.handle = hrequest;
@ -394,19 +523,36 @@ static BOOL query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPD
{
BOOL ret = FALSE;
if (!buflen)
{
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
switch (option)
{
case WINHTTP_OPTION_CONTEXT_VALUE:
{
if (!buffer || *buflen < sizeof(DWORD_PTR))
{
*buflen = sizeof(DWORD_PTR);
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
*(DWORD_PTR *)buffer = hdr->context;
*buflen = sizeof(DWORD_PTR);
return TRUE;
}
default:
{
if (hdr->vtbl->query_option) ret = hdr->vtbl->query_option( hdr, option, buffer, buflen );
else FIXME("unimplemented option %u\n", option);
}
else
{
FIXME("unimplemented option %u\n", option);
set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
return FALSE;
}
break;
}
return ret;
}
@ -437,18 +583,34 @@ static BOOL set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD
{
BOOL ret = TRUE;
if (!buffer)
{
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
switch (option)
{
case WINHTTP_OPTION_CONTEXT_VALUE:
{
if (buflen != sizeof(DWORD_PTR))
{
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
hdr->context = *(DWORD_PTR *)buffer;
return TRUE;
}
default:
{
if (hdr->vtbl->set_option) ret = hdr->vtbl->set_option( hdr, option, buffer, buflen );
else FIXME("unimplemented option %u\n", option);
}
else
{
FIXME("unimplemented option %u\n", option);
set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
return FALSE;
}
break;
}
return ret;
}

View file

@ -0,0 +1,484 @@
/*
* Copyright 2008 Hans Leidekker for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdarg.h>
#include "wine/debug.h"
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winhttp.h"
#include "shlwapi.h"
#include "winhttp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
static const WCHAR scheme_http[] = {'h','t','t','p',0};
static const WCHAR scheme_https[] = {'h','t','t','p','s',0};
static BOOL set_component( WCHAR **str, DWORD *str_len, WCHAR *value, DWORD len, DWORD flags )
{
if (!*str)
{
if (len && (flags & ICU_DECODE))
{
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
*str = value;
*str_len = len;
}
else
{
if (len > (*str_len) - 1)
{
*str_len = len + 1;
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
memcpy( *str, value, len * sizeof(WCHAR) );
(*str)[len] = 0;
*str_len = len;
}
return TRUE;
}
static BOOL decode_url( LPCWSTR url, LPWSTR buffer, LPDWORD buflen )
{
HRESULT hr = UrlCanonicalizeW( url, buffer, buflen, URL_WININET_COMPATIBILITY | URL_UNESCAPE );
if (hr == E_POINTER) set_last_error( ERROR_INSUFFICIENT_BUFFER );
if (hr == E_INVALIDARG) set_last_error( ERROR_INVALID_PARAMETER );
return (SUCCEEDED(hr)) ? TRUE : FALSE;
}
/***********************************************************************
* WinHttpCrackUrl (winhttp.@)
*/
BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW uc )
{
BOOL ret = FALSE;
WCHAR *p, *q, *r;
WCHAR *url_decoded = NULL;
TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, uc);
if (flags & ICU_ESCAPE) FIXME("flag ICU_ESCAPE not supported\n");
if (!url || !uc || uc->dwStructSize != sizeof(URL_COMPONENTS))
{
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (!len) len = strlenW( url );
if (flags & ICU_DECODE)
{
WCHAR *url_tmp;
DWORD url_len = len + 1;
if (!(url_tmp = HeapAlloc( GetProcessHeap(), 0, url_len * sizeof(WCHAR) )))
{
set_last_error( ERROR_OUTOFMEMORY );
return FALSE;
}
memcpy( url_tmp, url, len * sizeof(WCHAR) );
url_tmp[len] = 0;
if (!(url_decoded = HeapAlloc( GetProcessHeap(), 0, url_len * sizeof(WCHAR) )))
{
HeapFree( GetProcessHeap(), 0, url_tmp );
set_last_error( ERROR_OUTOFMEMORY );
return FALSE;
}
if (decode_url( url_tmp, url_decoded, &url_len ))
{
len = url_len;
url = url_decoded;
}
HeapFree( GetProcessHeap(), 0, url_tmp );
}
if (!(p = strchrW( url, ':' )))
{
set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME );
return FALSE;
}
if (p - url == 4 && !strncmpiW( url, scheme_http, 4 )) uc->nScheme = INTERNET_SCHEME_HTTP;
else if (p - url == 5 && !strncmpiW( url, scheme_https, 5 )) uc->nScheme = INTERNET_SCHEME_HTTPS;
else
{
set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME );
goto exit;
}
if (!(set_component( &uc->lpszScheme, &uc->dwSchemeLength, (WCHAR *)url, p - url, flags ))) goto exit;
p++; /* skip ':' */
if (!p[0] || p[0] != '/' || p[1] != '/') goto exit;
p += 2;
if (!p[0]) goto exit;
if ((q = memchrW( p, '@', len - (p - url) )))
{
if ((r = memchrW( p, ':', q - p )))
{
if (!(set_component( &uc->lpszUserName, &uc->dwUserNameLength, p, r - p, flags ))) goto exit;
r++;
if (!(set_component( &uc->lpszPassword, &uc->dwPasswordLength, r, q - r, flags ))) goto exit;
}
else
{
if (!(set_component( &uc->lpszUserName, &uc->dwUserNameLength, p, q - p, flags ))) goto exit;
if (!(set_component( &uc->lpszPassword, &uc->dwPasswordLength, NULL, 0, flags ))) goto exit;
}
p = q + 1;
}
else
{
if (!(set_component( &uc->lpszUserName, &uc->dwUserNameLength, NULL, 0, flags ))) goto exit;
if (!(set_component( &uc->lpszPassword, &uc->dwPasswordLength, NULL, 0, flags ))) goto exit;
}
if ((q = memchrW( p, '/', len - (p - url) )))
{
if ((r = memchrW( p, ':', q - p )))
{
if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, r - p, flags ))) goto exit;
r++;
uc->nPort = atoiW( r );
}
else
{
if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, q - p, flags ))) goto exit;
if (uc->nScheme == INTERNET_SCHEME_HTTP) uc->nPort = INTERNET_DEFAULT_HTTP_PORT;
if (uc->nScheme == INTERNET_SCHEME_HTTPS) uc->nPort = INTERNET_DEFAULT_HTTPS_PORT;
}
if ((r = memchrW( q, '?', len - (q - url) )))
{
if (!(set_component( &uc->lpszUrlPath, &uc->dwUrlPathLength, q, r - q, flags ))) goto exit;
if (!(set_component( &uc->lpszExtraInfo, &uc->dwExtraInfoLength, r, len - (r - url), flags ))) goto exit;
}
else
{
if (!(set_component( &uc->lpszUrlPath, &uc->dwUrlPathLength, q, len - (q - url), flags ))) goto exit;
if (!(set_component( &uc->lpszExtraInfo, &uc->dwExtraInfoLength, (WCHAR *)url + len, 0, flags ))) goto exit;
}
}
else
{
if ((r = memchrW( p, ':', len - (p - url) )))
{
if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, r - p, flags ))) goto exit;
r++;
uc->nPort = atoiW( r );
}
else
{
if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, len - (p - url), flags ))) goto exit;
if (uc->nScheme == INTERNET_SCHEME_HTTP) uc->nPort = INTERNET_DEFAULT_HTTP_PORT;
if (uc->nScheme == INTERNET_SCHEME_HTTPS) uc->nPort = INTERNET_DEFAULT_HTTPS_PORT;
}
if (!(set_component( &uc->lpszUrlPath, &uc->dwUrlPathLength, (WCHAR *)url + len, 0, flags ))) goto exit;
if (!(set_component( &uc->lpszExtraInfo, &uc->dwExtraInfoLength, (WCHAR *)url + len, 0, flags ))) goto exit;
}
ret = TRUE;
TRACE("scheme(%s) host(%s) port(%d) path(%s) extra(%s)\n",
debugstr_wn( uc->lpszScheme, uc->dwSchemeLength ),
debugstr_wn( uc->lpszHostName, uc->dwHostNameLength ),
uc->nPort,
debugstr_wn( uc->lpszUrlPath, uc->dwUrlPathLength ),
debugstr_wn( uc->lpszExtraInfo, uc->dwExtraInfoLength ));
exit:
HeapFree( GetProcessHeap(), 0, url_decoded );
return ret;
}
static INTERNET_SCHEME get_scheme( const WCHAR *scheme, DWORD len )
{
if (!strncmpW( scheme, scheme_http, len )) return INTERNET_SCHEME_HTTP;
if (!strncmpW( scheme, scheme_https, len )) return INTERNET_SCHEME_HTTPS;
return 0;
}
static const WCHAR *get_scheme_string( INTERNET_SCHEME scheme )
{
if (scheme == INTERNET_SCHEME_HTTP) return scheme_http;
if (scheme == INTERNET_SCHEME_HTTPS) return scheme_https;
return NULL;
}
static BOOL uses_default_port( INTERNET_SCHEME scheme, INTERNET_PORT port )
{
if ((scheme == INTERNET_SCHEME_HTTP) && (port == INTERNET_DEFAULT_HTTP_PORT)) return TRUE;
if ((scheme == INTERNET_SCHEME_HTTPS) && (port == INTERNET_DEFAULT_HTTPS_PORT)) return TRUE;
return FALSE;
}
static BOOL need_escape( WCHAR c )
{
if (isalnumW( c )) return FALSE;
if (c <= 31 || c >= 127) return TRUE;
else
{
switch (c)
{
case ' ':
case '"':
case '#':
case '%':
case '<':
case '>':
case ']':
case '\\':
case '[':
case '^':
case '`':
case '{':
case '|':
case '}':
case '~':
return TRUE;
default:
return FALSE;
}
}
}
static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len )
{
static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
DWORD ret = len;
unsigned int i;
WCHAR *p = dst;
for (i = 0; i < len; i++, p++)
{
if (need_escape( src[i] ))
{
p[0] = '%';
p[1] = hex[(src[i] >> 4) & 0xf];
p[2] = hex[src[i] & 0xf];
ret += 2;
p += 2;
}
else *p = src[i];
}
dst[ret] = 0;
return ret;
}
static DWORD comp_length( DWORD len, DWORD flags, WCHAR *comp )
{
DWORD ret;
unsigned int i;
ret = len ? len : strlenW( comp );
if (!(flags & ICU_ESCAPE)) return ret;
for (i = 0; i < len; i++) if (need_escape( comp[i] )) ret += 2;
return ret;
}
static BOOL calc_length( URL_COMPONENTS *uc, DWORD flags, LPDWORD len )
{
static const WCHAR formatW[] = {'%','d',0};
INTERNET_SCHEME scheme;
*len = 0;
if (uc->lpszScheme)
{
DWORD scheme_len = comp_length( uc->dwSchemeLength, 0, uc->lpszScheme );
*len += scheme_len;
scheme = get_scheme( uc->lpszScheme, scheme_len );
}
else
{
scheme = uc->nScheme;
if (!scheme) scheme = INTERNET_SCHEME_HTTP;
*len += strlenW( get_scheme_string( scheme ) );
}
*len += 1; /* ':' */
if (uc->lpszHostName) *len += 2; /* "//" */
if (uc->lpszUserName)
{
*len += comp_length( uc->dwUserNameLength, 0, uc->lpszUserName );
*len += 1; /* "@" */
}
else
{
if (uc->lpszPassword)
{
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
if (uc->lpszPassword)
{
*len += 1; /* ":" */
*len += comp_length( uc->dwPasswordLength, 0, uc->lpszPassword );
}
if (uc->lpszHostName)
{
*len += comp_length( uc->dwHostNameLength, 0, uc->lpszHostName );
if (!uses_default_port( scheme, uc->nPort ))
{
WCHAR port[sizeof("65535")];
sprintfW( port, formatW, uc->nPort );
*len += strlenW( port );
*len += 1; /* ":" */
}
if (uc->lpszUrlPath && *uc->lpszUrlPath != '/') *len += 1; /* '/' */
}
if (uc->lpszUrlPath) *len += comp_length( uc->dwUrlPathLength, flags, uc->lpszUrlPath );
if (uc->lpszExtraInfo) *len += comp_length( uc->dwExtraInfoLength, flags, uc->lpszExtraInfo );
return TRUE;
}
/***********************************************************************
* WinHttpCreateUrl (winhttp.@)
*/
BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDWORD required )
{
static const WCHAR formatW[] = {'%','d',0};
static const WCHAR twoslashW[] = {'/','/'};
DWORD len;
INTERNET_SCHEME scheme;
TRACE("%p, 0x%08x, %p, %p\n", uc, flags, url, required);
if (!uc || uc->dwStructSize != sizeof(URL_COMPONENTS) || !required)
{
set_last_error( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (!calc_length( uc, flags, &len )) return FALSE;
if (!url || *required < len)
{
*required = len + 1;
set_last_error( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
url[0] = 0;
*required = len;
if (uc->lpszScheme)
{
len = comp_length( uc->dwSchemeLength, 0, uc->lpszScheme );
memcpy( url, uc->lpszScheme, len * sizeof(WCHAR) );
url += len;
scheme = get_scheme( uc->lpszScheme, len );
}
else
{
const WCHAR *schemeW;
scheme = uc->nScheme;
if (!scheme) scheme = INTERNET_SCHEME_HTTP;
schemeW = get_scheme_string( scheme );
len = strlenW( schemeW );
memcpy( url, schemeW, len * sizeof(WCHAR) );
url += len;
}
/* all schemes are followed by at least a colon */
*url = ':';
url++;
if (uc->lpszHostName)
{
memcpy( url, twoslashW, sizeof(twoslashW) );
url += sizeof(twoslashW) / sizeof(twoslashW[0]);
}
if (uc->lpszUserName)
{
len = comp_length( uc->dwUserNameLength, 0, uc->lpszUserName );
memcpy( url, uc->lpszUserName, len * sizeof(WCHAR) );
url += len;
if (uc->lpszPassword)
{
*url = ':';
url++;
len = comp_length( uc->dwPasswordLength, 0, uc->lpszPassword );
memcpy( url, uc->lpszPassword, len * sizeof(WCHAR) );
url += len;
}
*url = '@';
url++;
}
if (uc->lpszHostName)
{
len = comp_length( uc->dwHostNameLength, 0, uc->lpszHostName );
memcpy( url, uc->lpszHostName, len * sizeof(WCHAR) );
url += len;
if (!uses_default_port( scheme, uc->nPort ))
{
WCHAR port[sizeof("65535")];
sprintfW( port, formatW, uc->nPort );
*url = ':';
url++;
len = strlenW( port );
memcpy( url, port, len * sizeof(WCHAR) );
url += len;
}
/* add slash between hostname and path if necessary */
if (uc->lpszUrlPath && *uc->lpszUrlPath != '/')
{
*url = '/';
url++;
}
}
if (uc->lpszUrlPath)
{
len = comp_length( uc->dwUrlPathLength, 0, uc->lpszUrlPath );
if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszUrlPath, len );
else
{
memcpy( url, uc->lpszUrlPath, len * sizeof(WCHAR) );
url += len;
}
}
if (uc->lpszExtraInfo)
{
len = comp_length( uc->dwExtraInfoLength, 0, uc->lpszExtraInfo );
if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszExtraInfo, len );
else
{
memcpy( url, uc->lpszExtraInfo, len * sizeof(WCHAR) );
url += len;
}
}
*url = 0;
return TRUE;
}

View file

@ -7,12 +7,15 @@
<include base="winhttp">.</include>
<include base="ReactOS">include/reactos/wine</include>
<define name="__WINESRC__" />
<file>cookie.c</file>
<file>handle.c</file>
<file>main.c</file>
<file>net.c</file>
<file>request.c</file>
<file>session.c</file>
<file>url.c</file>
<library>wine</library>
<library>shlwapi</library>
<library>wininet</library>
<library>ws2_32</library>
<library>kernel32</library>

View file

@ -26,6 +26,7 @@
#include "wine/list.h"
#include "wine/unicode.h"
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@ -33,14 +34,17 @@
# include <netdb.h>
#endif
#if defined(__MINGW32__) || defined (_MSC_VER)
# include "ws2tcpip.h"
# ifndef MSG_WAITALL
# define MSG_WAITALL 0
# endif
# include <ws2tcpip.h>
#else
# define closesocket close
# define ioctlsocket ioctl
#endif /* __MINGW32__ */
# define closesocket close
# define ioctlsocket ioctl
#endif
static const WCHAR getW[] = {'G','E','T',0};
static const WCHAR postW[] = {'P','O','S','T',0};
static const WCHAR slashW[] = {'/',0};
static const WCHAR http1_0[] = {'H','T','T','P','/','1','.','0',0};
static const WCHAR http1_1[] = {'H','T','T','P','/','1','.','1',0};
typedef struct _object_header_t object_header_t;
@ -69,6 +73,21 @@ struct _object_header_t
struct list children;
};
typedef struct
{
struct list entry;
WCHAR *name;
struct list cookies;
} domain_t;
typedef struct
{
struct list entry;
WCHAR *name;
WCHAR *value;
WCHAR *path;
} cookie_t;
typedef struct
{
object_header_t hdr;
@ -78,6 +97,7 @@ typedef struct
LPWSTR proxy_bypass;
LPWSTR proxy_username;
LPWSTR proxy_password;
struct list cookie_cache;
} session_t;
typedef struct
@ -97,7 +117,6 @@ typedef struct
{
int socket;
BOOL secure; /* SSL active on connection? */
void *ssl_ctx;
void *ssl_conn;
char *peek_msg;
char *peek_msg_mem;
@ -127,6 +146,52 @@ typedef struct
DWORD num_headers;
} request_t;
typedef struct _task_header_t task_header_t;
struct _task_header_t
{
request_t *request;
void (*proc)( task_header_t * );
};
typedef struct
{
task_header_t hdr;
LPWSTR headers;
DWORD headers_len;
LPVOID optional;
DWORD optional_len;
DWORD total_len;
DWORD_PTR context;
} send_request_t;
typedef struct
{
task_header_t hdr;
} receive_response_t;
typedef struct
{
task_header_t hdr;
LPDWORD available;
} query_data_t;
typedef struct
{
task_header_t hdr;
LPVOID buffer;
DWORD to_read;
LPDWORD read;
} read_data_t;
typedef struct
{
task_header_t hdr;
LPCVOID buffer;
DWORD to_write;
LPDWORD written;
} write_data_t;
object_header_t *addref_object( object_header_t * );
object_header_t *grab_object( HINTERNET );
void release_object( object_header_t * );
@ -134,6 +199,7 @@ HINTERNET alloc_handle( object_header_t * );
BOOL free_handle( HINTERNET );
void set_last_error( DWORD );
DWORD get_last_error( void );
void send_callback( object_header_t *, DWORD, LPVOID, DWORD );
void close_connection( request_t * );
@ -148,6 +214,12 @@ BOOL netconn_recv( netconn_t *, void *, size_t, int, int * );
BOOL netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr_in * );
BOOL netconn_secure_connect( netconn_t * );
BOOL netconn_send( netconn_t *, const void *, size_t, int, int * );
const void *netconn_get_certificate( netconn_t * );
BOOL set_cookies( request_t *, const WCHAR * );
BOOL add_cookie_headers( request_t * );
BOOL add_request_headers( request_t *, LPCWSTR, DWORD, DWORD );
void delete_domain( domain_t * );
static inline void *heap_alloc( SIZE_T size )
{

View file

@ -36,6 +36,8 @@ typedef INTERNET_PORT *LPINTERNET_PORT;
#define INTERNET_SCHEME_HTTPS 2
typedef int INTERNET_SCHEME, *LPINTERNET_SCHEME;
#define ICU_ESCAPE 0x80000000
/* flags for WinHttpOpen */
#define WINHTTP_FLAG_ASYNC 0x10000000