diff --git a/reactos/dll/win32/winhttp/handle.c b/reactos/dll/win32/winhttp/handle.c new file mode 100644 index 00000000000..70107275b9c --- /dev/null +++ b/reactos/dll/win32/winhttp/handle.c @@ -0,0 +1,159 @@ +/* + * Copyright 2008 Hans Leidekker for CodeWeavers + * + * Based on the handle implementation from wininet. + * + * 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 "wine/port.h" +#include "wine/debug.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winhttp.h" + +#include "winhttp_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winhttp); + +#define HANDLE_CHUNK_SIZE 0x10 + +static CRITICAL_SECTION handle_cs; +static CRITICAL_SECTION_DEBUG handle_cs_debug = +{ + 0, 0, &handle_cs, + { &handle_cs_debug.ProcessLocksList, &handle_cs_debug.ProcessLocksList }, + 0, 0, { (ULONG_PTR)(__FILE__ ": handle_cs") } +}; +static CRITICAL_SECTION handle_cs = { &handle_cs_debug, -1, 0, 0, 0, 0 }; + +static object_header_t **handles; +static ULONG_PTR next_handle; +static ULONG_PTR max_handles; + +object_header_t *addref_object( object_header_t *hdr ) +{ + ULONG refs = InterlockedIncrement( &hdr->refs ); + TRACE("%p -> refcount = %d\n", hdr, refs); + return hdr; +} + +object_header_t *grab_object( HINTERNET hinternet ) +{ + object_header_t *hdr = NULL; + ULONG_PTR handle = (ULONG_PTR)hinternet; + + EnterCriticalSection( &handle_cs ); + + if ((handle > 0) && (handle <= max_handles) && handles[handle - 1]) + hdr = addref_object( handles[handle - 1] ); + + LeaveCriticalSection( &handle_cs ); + + TRACE("handle 0x%lx -> %p\n", handle, hdr); + return hdr; +} + +void release_object( object_header_t *hdr ) +{ + ULONG refs = InterlockedDecrement( &hdr->refs ); + TRACE("object %p refcount = %d\n", hdr, refs); + if (!refs) + { + if (hdr->type == WINHTTP_HANDLE_TYPE_REQUEST) close_connection( (request_t *)hdr ); + + send_callback( hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, &hdr->handle, sizeof(HINTERNET) ); + + TRACE("destroying object %p\n", hdr); + if (hdr->type != WINHTTP_HANDLE_TYPE_SESSION) list_remove( &hdr->entry ); + hdr->vtbl->destroy( hdr ); + } +} + +HINTERNET alloc_handle( object_header_t *hdr ) +{ + object_header_t **p; + ULONG_PTR handle = 0, num; + + list_init( &hdr->children ); + + EnterCriticalSection( &handle_cs ); + if (!max_handles) + { + num = HANDLE_CHUNK_SIZE; + if (!(p = heap_alloc_zero( sizeof(ULONG_PTR) * num ))) goto end; + handles = p; + max_handles = num; + } + if (max_handles == next_handle) + { + num = max_handles + HANDLE_CHUNK_SIZE; + if (!(p = heap_realloc_zero( handles, sizeof(ULONG_PTR) * num ))) goto end; + handles = p; + max_handles = num; + } + handle = next_handle; + if (handles[handle]) ERR("handle isn't free but should be\n"); + + handles[handle] = addref_object( hdr ); + while (handles[next_handle] && (next_handle < max_handles)) next_handle++; + +end: + LeaveCriticalSection( &handle_cs ); + return hdr->handle = (HINTERNET)(handle + 1); +} + +BOOL free_handle( HINTERNET hinternet ) +{ + BOOL ret = FALSE; + ULONG_PTR handle = (ULONG_PTR)hinternet; + object_header_t *hdr = NULL, *child, *next; + + EnterCriticalSection( &handle_cs ); + + if ((handle > 0) && (handle <= max_handles)) + { + handle--; + if (handles[handle]) + { + hdr = handles[handle]; + TRACE("destroying handle 0x%lx for object %p\n", handle + 1, hdr); + handles[handle] = NULL; + ret = TRUE; + } + } + + LeaveCriticalSection( &handle_cs ); + + if (hdr) + { + LIST_FOR_EACH_ENTRY_SAFE( child, next, &hdr->children, object_header_t, entry ) + { + TRACE("freeing child handle %p for parent handle 0x%lx\n", child->handle, handle + 1); + free_handle( child->handle ); + } + release_object( hdr ); + } + + EnterCriticalSection( &handle_cs ); + if (next_handle > handle && !handles[handle]) next_handle = handle; + LeaveCriticalSection( &handle_cs ); + + return ret; +} diff --git a/reactos/dll/win32/winhttp/inet_ntop.c b/reactos/dll/win32/winhttp/inet_ntop.c new file mode 100644 index 00000000000..f29d46d1625 --- /dev/null +++ b/reactos/dll/win32/winhttp/inet_ntop.c @@ -0,0 +1,192 @@ +/* from NetBSD: inet_ntop.c,v 1.9 2000/01/22 22:19:16 mycroft Exp */ + +/* Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#define ENOSPC 28 +#define EAFNOSUPPORT 52 + +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 +#endif + +#ifndef INT16SZ +#define INT16SZ 2 +#endif + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static const char *inet_ntop4(const u_char *src, char *dst, size_t size); + +#ifdef INET6 +static const char *inet_ntop6(const u_char *src, char *dst, size_t size); +#endif + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char * +inet_ntop(int af, const void *src, char *dst, size_t size) +{ + + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); +#ifdef INET6 + case AF_INET6: + return (inet_ntop6(src, dst, size)); +#endif + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address, more or less like inet_ntoa() + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const u_char *src, char *dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + + if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +#ifdef INET6 +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(const u_char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + u_int words[IN6ADDRSZ / INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%x", words[i])); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} +#endif + diff --git a/reactos/dll/win32/winhttp/main.c b/reactos/dll/win32/winhttp/main.c index 28eb1201e1e..4a4aea8f65a 100644 --- a/reactos/dll/win32/winhttp/main.c +++ b/reactos/dll/win32/winhttp/main.c @@ -27,6 +27,8 @@ #include "wine/debug.h" +#include "winhttp_private.h" + WINE_DEFAULT_DEBUG_CHANNEL(winhttp); /****************************************************************** @@ -83,167 +85,40 @@ HRESULT WINAPI DllUnregisterServer(void) return S_OK; } -/*********************************************************************** - * WinHttpCheckPlatform (winhttp.@) - */ -BOOL WINAPI WinHttpCheckPlatform(void) -{ - FIXME("stub\n"); - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} +#define SCHEME_HTTP 3 +#define SCHEME_HTTPS 4 + +BOOL WINAPI InternetCrackUrlW( LPCWSTR, DWORD, DWORD, LPURL_COMPONENTSW ); +BOOL WINAPI InternetCreateUrlW( LPURL_COMPONENTS, DWORD, LPWSTR, LPDWORD ); /*********************************************************************** - * WinHttpDetectAutoProxyConfigUrl (winhttp.@) + * WinHttpCrackUrl (winhttp.@) */ -BOOL WINAPI WinHttpDetectAutoProxyConfigUrl(DWORD flags, LPWSTR *url) +BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW components ) { - FIXME("(%x %p)\n", flags, url); + BOOL ret; - SetLastError(ERROR_WINHTTP_AUTODETECTION_FAILED); - return FALSE; -} + TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, components); -/*********************************************************************** - * WinHttpGetIEProxyConfigForCurrentUser (winhttp.@) - */ -BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* config) -{ - if(!config) + if ((ret = InternetCrackUrlW( url, len, flags, components ))) { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; + /* 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; + } } - - /* TODO: read from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings */ - FIXME("returning no proxy used\n"); - config->fAutoDetect = FALSE; - config->lpszAutoConfigUrl = NULL; - config->lpszProxy = NULL; - config->lpszProxyBypass = NULL; - - SetLastError(ERROR_SUCCESS); - return TRUE; + return ret; } /*********************************************************************** - * WinHttpOpen (winhttp.@) + * WinHttpCreateUrl (winhttp.@) */ -HINTERNET WINAPI WinHttpOpen(LPCWSTR pwszUserAgent, DWORD dwAccessType, - LPCWSTR pwszProxyName, LPCWSTR pwszProxyByPass, - DWORD dwFlags) +BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS comps, DWORD flags, LPWSTR url, LPDWORD len ) { - FIXME("(%s, %d, %s, %s, 0x%x): stub\n", debugstr_w(pwszUserAgent), - dwAccessType, debugstr_w(pwszProxyName), debugstr_w(pwszProxyByPass), - dwFlags); - - SetLastError(ERROR_NOT_SUPPORTED); - return NULL; -} - -/*********************************************************************** - * WinHttpConnect (winhttp.@) - */ - -HINTERNET WINAPI WinHttpConnect (HINTERNET hSession, LPCWSTR pwszServerName, - INTERNET_PORT nServerPort, DWORD dwReserved) -{ - FIXME("(%s, %d, 0x%x): stub\n", debugstr_w(pwszServerName), nServerPort, dwReserved); - - SetLastError(ERROR_NOT_SUPPORTED); - return NULL; -} - -/*********************************************************************** - * WinHttpOpenRequest (winhttp.@) - */ -HINTERNET WINAPI WinHttpOpenRequest (HINTERNET hConnect, LPCWSTR pwszVerb, LPCWSTR pwszObjectName, - LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR* ppwszAcceptTypes, - DWORD dwFlags) -{ - FIXME("(%s, %s, %s, %s, 0x%x): stub\n", debugstr_w(pwszVerb), debugstr_w(pwszObjectName), - debugstr_w(pwszVersion), debugstr_w(pwszReferrer), dwFlags); - - SetLastError(ERROR_NOT_SUPPORTED); - return NULL; -} - -/*********************************************************************** - * WinHttpSendRequest (winhttp.@) - */ -BOOL WINAPI WinHttpSendRequest (HINTERNET hRequest, LPCWSTR pwszHeaders, DWORD dwHeadersLength, - LPVOID lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, - DWORD_PTR dwContext) -{ - FIXME("(%s, %d, %d, %d): stub\n", debugstr_w(pwszHeaders), dwHeadersLength, dwOptionalLength, dwTotalLength); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -/*********************************************************************** - * WinHttpQueryOption (winhttp.@) - */ -BOOL WINAPI WinHttpQueryOption (HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, LPDWORD lpdwBufferLength) -{ - FIXME("(%d): stub\n", dwOption); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -/*********************************************************************** - * WinHttpQueryDataAvailable (winhttp.@) - */ -BOOL WINAPI WinHttpQueryDataAvailable (HINTERNET hInternet, LPDWORD lpdwNumberOfBytesAvailable) -{ - FIXME("stub\n"); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -/*********************************************************************** - * WinHttpReceiveResponse (winhttp.@) - */ -BOOL WINAPI WinHttpReceiveResponse (HINTERNET hRequest, LPVOID lpReserved) -{ - FIXME("stub\n"); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -/*********************************************************************** - * WinHttpSetOption (winhttp.@) - */ -BOOL WINAPI WinHttpSetOption (HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength) -{ - FIXME("stub\n"); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -/*********************************************************************** - * WinHttpReadData (winhttp.@) - */ -BOOL WINAPI WinHttpReadData (HINTERNET hInternet, LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, - LPDWORD lpdwNumberOfBytesRead) -{ - FIXME("(%d): stub\n", dwNumberOfBytesToRead); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -/*********************************************************************** - * WinHttpReadData (winhttp.@) - */ -BOOL WINAPI WinHttpCloseHandle (HINTERNET hInternet) -{ - FIXME("stub\n"); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; + TRACE("%p, 0x%08x, %p, %p\n", comps, flags, url, len); + return InternetCreateUrlW( comps, flags, url, len ); } diff --git a/reactos/dll/win32/winhttp/net.c b/reactos/dll/win32/winhttp/net.c new file mode 100644 index 00000000000..ff06627765b --- /dev/null +++ b/reactos/dll/win32/winhttp/net.c @@ -0,0 +1,626 @@ +/* + * 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 "wine/port.h" + +#include +#include +#include + +#include +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif +#ifdef HAVE_POLL_H +# include +#endif +#ifdef HAVE_OPENSSL_SSL_H +# include +#undef FAR +#undef DSA +#endif + +#include "wine/debug.h" +#include "wine/library.h" + +#include "windef.h" +#include "winbase.h" +#include "winhttp.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 +#define DEFAULT_RECEIVE_TIMEOUT 30 +#define RESPONSE_TIMEOUT 30 + +#ifndef HAVE_GETADDRINFO + +/* critical section to protect non-reentrant gethostbyname() */ +static CRITICAL_SECTION cs_gethostbyname; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &cs_gethostbyname, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": cs_gethostbyname") } +}; +static CRITICAL_SECTION cs_gethostbyname = { &critsect_debug, -1, 0, 0, 0, 0 }; + +#endif + +#ifdef SONAME_LIBSSL + +#include + +static void *libssl_handle; +static void *libcrypto_handle; + +static SSL_METHOD *method; + +#define MAKE_FUNCPTR(f) static typeof(f) * p##f + +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 ); +MAKE_FUNCPTR( SSL_connect ); +MAKE_FUNCPTR( SSL_shutdown ); +MAKE_FUNCPTR( SSL_write ); +MAKE_FUNCPTR( SSL_read ); +MAKE_FUNCPTR( SSL_get_verify_result ); +MAKE_FUNCPTR( SSL_get_peer_certificate ); +MAKE_FUNCPTR( SSL_CTX_get_timeout ); +MAKE_FUNCPTR( SSL_CTX_set_timeout ); +MAKE_FUNCPTR( SSL_CTX_set_default_verify_paths ); + +MAKE_FUNCPTR( BIO_new_fp ); +MAKE_FUNCPTR( ERR_get_error ); +MAKE_FUNCPTR( ERR_error_string ); +#undef MAKE_FUNCPTR + +#endif + +#if 0 +/* translate a unix error code into a winsock error code */ +static int sock_get_error( int err ) +{ + switch (err) + { + case EINTR: return WSAEINTR; + case EBADF: return WSAEBADF; + case EPERM: + case EACCES: return WSAEACCES; + case EFAULT: return WSAEFAULT; + case EINVAL: return WSAEINVAL; + case EMFILE: return WSAEMFILE; + case EWOULDBLOCK: return WSAEWOULDBLOCK; + case EINPROGRESS: return WSAEINPROGRESS; + case EALREADY: return WSAEALREADY; + case ENOTSOCK: return WSAENOTSOCK; + case EDESTADDRREQ: return WSAEDESTADDRREQ; + case EMSGSIZE: return WSAEMSGSIZE; + case EPROTOTYPE: return WSAEPROTOTYPE; + case ENOPROTOOPT: return WSAENOPROTOOPT; + case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT; + case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT; + case EOPNOTSUPP: return WSAEOPNOTSUPP; + case EPFNOSUPPORT: return WSAEPFNOSUPPORT; + case EAFNOSUPPORT: return WSAEAFNOSUPPORT; + case EADDRINUSE: return WSAEADDRINUSE; + case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL; + case ENETDOWN: return WSAENETDOWN; + case ENETUNREACH: return WSAENETUNREACH; + case ENETRESET: return WSAENETRESET; + case ECONNABORTED: return WSAECONNABORTED; + case EPIPE: + case ECONNRESET: return WSAECONNRESET; + case ENOBUFS: return WSAENOBUFS; + case EISCONN: return WSAEISCONN; + case ENOTCONN: return WSAENOTCONN; + case ESHUTDOWN: return WSAESHUTDOWN; + case ETOOMANYREFS: return WSAETOOMANYREFS; + case ETIMEDOUT: return WSAETIMEDOUT; + case ECONNREFUSED: return WSAECONNREFUSED; + case ELOOP: return WSAELOOP; + case ENAMETOOLONG: return WSAENAMETOOLONG; + case EHOSTDOWN: return WSAEHOSTDOWN; + case EHOSTUNREACH: return WSAEHOSTUNREACH; + case ENOTEMPTY: return WSAENOTEMPTY; +#ifdef EPROCLIM + case EPROCLIM: return WSAEPROCLIM; +#endif +#ifdef EUSERS + case EUSERS: return WSAEUSERS; +#endif +#ifdef EDQUOT + case EDQUOT: return WSAEDQUOT; +#endif +#ifdef ESTALE + case ESTALE: return WSAESTALE; +#endif +#ifdef EREMOTE + case EREMOTE: return WSAEREMOTE; +#endif + default: errno = err; perror( "sock_set_error" ); return WSAEFAULT; + } + return err; +} +#else +#define sock_get_error(x) WSAGetLastError() +#endif + +BOOL netconn_init( netconn_t *conn, BOOL secure ) +{ + conn->socket = -1; + if (!secure) return TRUE; + +#if defined(SONAME_LIBSSL) && defined(SONAME_LIBCRYPTO) + if (libssl_handle) return TRUE; + if (!(libssl_handle = wine_dlopen( SONAME_LIBSSL, RTLD_NOW, NULL, 0 ))) + { + ERR("Trying to use SSL but couldn't load %s. Expect trouble.\n", SONAME_LIBSSL); + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); + return FALSE; + } + if (!(libcrypto_handle = wine_dlopen( SONAME_LIBCRYPTO, RTLD_NOW, NULL, 0 ))) + { + ERR("Trying to use SSL but couldn't load %s. Expect trouble.\n", SONAME_LIBCRYPTO); + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); + return FALSE; + } +#define LOAD_FUNCPTR(x) \ + if (!(p##x = wine_dlsym( libssl_handle, #x, NULL, 0 ))) \ + { \ + ERR("Failed to load symbol %s\n", #x); \ + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); \ + return FALSE; \ + } + LOAD_FUNCPTR( SSL_library_init ); + 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 ); + LOAD_FUNCPTR( SSL_connect ); + LOAD_FUNCPTR( SSL_shutdown ); + LOAD_FUNCPTR( SSL_write ); + LOAD_FUNCPTR( SSL_read ); + LOAD_FUNCPTR( SSL_get_verify_result ); + LOAD_FUNCPTR( SSL_get_peer_certificate ); + LOAD_FUNCPTR( SSL_CTX_get_timeout ); + LOAD_FUNCPTR( SSL_CTX_set_timeout ); + LOAD_FUNCPTR( SSL_CTX_set_default_verify_paths ); +#undef LOAD_FUNCPTR + +#define LOAD_FUNCPTR(x) \ + if (!(p##x = wine_dlsym( libcrypto_handle, #x, NULL, 0 ))) \ + { \ + ERR("Failed to load symbol %s\n", #x); \ + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); \ + return FALSE; \ + } + LOAD_FUNCPTR( BIO_new_fp ); + LOAD_FUNCPTR( ERR_get_error ); + LOAD_FUNCPTR( ERR_error_string ); +#undef LOAD_FUNCPTR + + pSSL_library_init(); + pSSL_load_error_strings(); + 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 ); + return FALSE; +#endif + return TRUE; +} + +BOOL netconn_connected( netconn_t *conn ) +{ + return (conn->socket != -1); +} + +BOOL netconn_create( netconn_t *conn, int domain, int type, int protocol ) +{ + if ((conn->socket = socket( domain, type, protocol )) == -1) + { + WARN("unable to create socket (%s)\n", strerror(errno)); + set_last_error( sock_get_error( errno ) ); + return FALSE; + } + return TRUE; +} + +BOOL netconn_close( netconn_t *conn ) +{ + int res; + +#ifdef SONAME_LIBSSL + if (conn->secure) + { + heap_free( conn->peek_msg_mem ); + conn->peek_msg_mem = NULL; + conn->peek_msg = NULL; + conn->peek_len = 0; + + pSSL_shutdown( conn->ssl_conn ); + pSSL_free( conn->ssl_conn ); + + conn->ssl_conn = NULL; + conn->secure = FALSE; + } +#endif + res = closesocket( conn->socket ); + conn->socket = -1; + if (res == -1) + { + set_last_error( sock_get_error( errno ) ); + return FALSE; + } + return TRUE; +} + +BOOL netconn_connect( netconn_t *conn, const struct sockaddr *sockaddr, unsigned int addr_len ) +{ + if (connect( conn->socket, sockaddr, addr_len ) == -1) + { + WARN("unable to connect to host (%s)\n", strerror(errno)); + set_last_error( sock_get_error( errno ) ); + return FALSE; + } + return TRUE; +} + +BOOL netconn_secure_connect( netconn_t *conn ) +{ +#ifdef SONAME_LIBSSL + X509 *cert; + long res; + + if (!pSSL_CTX_set_default_verify_paths( conn->ssl_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 ))) + { + ERR("SSL_new failed: %s\n", pERR_error_string( pERR_get_error(), 0 )); + set_last_error( ERROR_OUTOFMEMORY ); + goto fail; + } + if (!pSSL_set_fd( conn->ssl_conn, conn->socket )) + { + ERR("SSL_set_fd failed: %s\n", pERR_error_string( pERR_get_error(), 0 )); + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); + goto fail; + } + if (pSSL_connect( conn->ssl_conn ) <= 0) + { + ERR("SSL_connect failed: %s\n", pERR_error_string( pERR_get_error(), 0 )); + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); + goto fail; + } + if (!(cert = pSSL_get_peer_certificate( conn->ssl_conn ))) + { + ERR("No certificate for server: %s\n", pERR_error_string( pERR_get_error(), 0 )); + set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); + goto fail; + } + if ((res = pSSL_get_verify_result( conn->ssl_conn )) != X509_V_OK) + { + /* FIXME: we should set an error and return, but we only print an error at the moment */ + ERR("couldn't verify server certificate (%ld)\n", res); + } + TRACE("established SSL connection\n"); + conn->secure = TRUE; + return TRUE; + +fail: + if (conn->ssl_conn) + { + pSSL_shutdown( conn->ssl_conn ); + pSSL_free( conn->ssl_conn ); + conn->ssl_conn = NULL; + } +#endif + return FALSE; +} + +BOOL netconn_send( netconn_t *conn, const void *msg, size_t len, int flags, int *sent ) +{ + if (!netconn_connected( conn )) return FALSE; + if (conn->secure) + { +#ifdef SONAME_LIBSSL + if (flags) FIXME("SSL_write doesn't support any flags (%08x)\n", flags); + *sent = pSSL_write( conn->ssl_conn, msg, len ); + if (*sent < 1 && len) return FALSE; + return TRUE; +#else + return FALSE; +#endif + } + if ((*sent = send( conn->socket, msg, len, flags )) == -1) + { + set_last_error( sock_get_error( errno ) ); + return FALSE; + } + return TRUE; +} + +BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd ) +{ + *recvd = 0; + if (!netconn_connected( conn )) return FALSE; + if (!len) return TRUE; + + if (conn->secure) + { +#ifdef SONAME_LIBSSL + if (flags & ~(MSG_PEEK | MSG_WAITALL)) + FIXME("SSL_read does not support the following flags: %08x\n", flags); + + /* this ugly hack is all for MSG_PEEK */ + if (flags & MSG_PEEK && !conn->peek_msg) + { + if (!(conn->peek_msg = conn->peek_msg_mem = heap_alloc( len + 1 ))) return FALSE; + } + else if (flags & MSG_PEEK && conn->peek_msg) + { + if (len < conn->peek_len) FIXME("buffer isn't big enough, should we wrap?\n"); + *recvd = min( len, conn->peek_len ); + memcpy( buf, conn->peek_msg, *recvd ); + return TRUE; + } + else if (conn->peek_msg) + { + *recvd = min( len, conn->peek_len ); + memcpy( buf, conn->peek_msg, *recvd ); + conn->peek_len -= *recvd; + conn->peek_msg += *recvd; + + if (conn->peek_len == 0) + { + heap_free( conn->peek_msg_mem ); + conn->peek_msg_mem = NULL; + conn->peek_msg = NULL; + } + /* check if we have enough data from the peek buffer */ + if (!(flags & MSG_WAITALL) || (*recvd == len)) return TRUE; + } + *recvd += pSSL_read( conn->ssl_conn, (char *)buf + *recvd, len - *recvd ); + if (flags & MSG_PEEK) /* must copy into buffer */ + { + conn->peek_len = *recvd; + if (!*recvd) + { + heap_free( conn->peek_msg_mem ); + conn->peek_msg_mem = NULL; + conn->peek_msg = NULL; + } + else memcpy( conn->peek_msg, buf, *recvd ); + } + if (*recvd < 1 && len) return FALSE; + return TRUE; +#else + return FALSE; +#endif + } + if ((*recvd = recv( conn->socket, buf, len, flags )) == -1) + { + set_last_error( sock_get_error( errno ) ); + return FALSE; + } + return TRUE; +} + +BOOL netconn_query_data_available( netconn_t *conn, DWORD *available ) +{ +#ifdef FIONREAD + int ret, unread; +#endif + *available = 0; + if (!netconn_connected( conn )) return FALSE; + + if (conn->secure) + { +#ifdef SONAME_LIBSSL + if (conn->peek_msg) *available = conn->peek_len; +#endif + return TRUE; + } +#ifdef FIONREAD + if (!(ret = ioctlsocket( conn->socket, FIONREAD, &unread ))) + { + TRACE("%d bytes of queued, but unread data\n", unread); + *available += unread; + } +#endif + return TRUE; +} + +BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen ) +{ + struct timeval tv; + fd_set infd; + BOOL ret = FALSE; + DWORD recvd = 0; + + if (!netconn_connected( conn )) return FALSE; + + if (conn->secure) + { +#ifdef SONAME_LIBSSL + long timeout; + + timeout = pSSL_CTX_get_timeout( conn->ssl_ctx ); + pSSL_CTX_set_timeout( conn->ssl_ctx, DEFAULT_RECEIVE_TIMEOUT ); + + while (recvd < *buflen) + { + int dummy; + if (!netconn_recv( conn, &buffer[recvd], 1, 0, &dummy )) + { + set_last_error( ERROR_CONNECTION_ABORTED ); + break; + } + if (buffer[recvd] == '\n') + { + ret = TRUE; + break; + } + if (buffer[recvd] != '\r') recvd++; + } + pSSL_CTX_set_timeout( conn->ssl_ctx, timeout ); + if (ret) + { + buffer[recvd++] = 0; + *buflen = recvd; + TRACE("received line %s\n", debugstr_a(buffer)); + } + return ret; +#else + return FALSE; +#endif + } + + FD_ZERO(&infd); + FD_SET(conn->socket, &infd); + tv.tv_sec=RESPONSE_TIMEOUT; + tv.tv_usec=0; + while (recvd < *buflen) + { + if (select(conn->socket+1,&infd,NULL,NULL,&tv) > 0) + { + int res; + if ((res = recv( conn->socket, &buffer[recvd], 1, 0 )) <= 0) + { + if (res == -1) set_last_error( sock_get_error( errno ) ); + break; + } + if (buffer[recvd] == '\n') + { + ret = TRUE; + break; + } + if (buffer[recvd] != '\r') recvd++; + } + else + { + set_last_error( ERROR_WINHTTP_TIMEOUT ); + break; + } + } + if (ret) + { + buffer[recvd++] = 0; + *buflen = recvd; + TRACE("received line %s\n", debugstr_a(buffer)); + } + return ret; +} + +DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value ) +{ + int res; + struct timeval tv; + + /* value is in milliseconds, convert to struct timeval */ + 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)) + { + WARN("setsockopt failed (%s)\n", strerror( errno )); + return sock_get_error( errno ); + } + return ERROR_SUCCESS; +} + +BOOL netconn_resolve( WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_in *sa ) +{ + char *hostname; +#ifdef HAVE_GETADDRINFO + struct addrinfo *res, hints; + int ret; +#else + struct hostent *he; +#endif + + if (!(hostname = strdupWA( hostnameW ))) return FALSE; + +#ifdef HAVE_GETADDRINFO + memset( &hints, 0, sizeof(struct addrinfo) ); + hints.ai_family = AF_INET; + + ret = getaddrinfo( hostname, NULL, &hints, &res ); + heap_free( hostname ); + if (ret != 0) + { + TRACE("failed to get address of %s (%s)\n", debugstr_a(hostname), gai_strerror(ret)); + return FALSE; + } + memset( sa, 0, sizeof(struct sockaddr_in) ); + memcpy( &sa->sin_addr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, sizeof(struct in_addr) ); + sa->sin_family = res->ai_family; + sa->sin_port = htons( port ); + + freeaddrinfo( res ); +#else + EnterCriticalSection( &cs_gethostbyname ); + + he = gethostbyname( hostname ); + heap_free( hostname ); + if (!he) + { + TRACE("failed to get address of %s (%d)\n", debugstr_a(hostname), 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 ); + sa->sin_family = he->h_addrtype; + sa->sin_port = htons( port ); + + LeaveCriticalSection( &cs_gethostbyname ); +#endif + return TRUE; +} diff --git a/reactos/dll/win32/winhttp/request.c b/reactos/dll/win32/winhttp/request.c new file mode 100644 index 00000000000..e791bb3e86d --- /dev/null +++ b/reactos/dll/win32/winhttp/request.c @@ -0,0 +1,1546 @@ +/* + * 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 "wine/port.h" +#include "wine/debug.h" + +#include +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +#include "windef.h" +#include "winbase.h" +#include "winhttp.h" + +#include "winhttp_private.h" + +#include "inet_ntop.c" + +WINE_DEFAULT_DEBUG_CHANNEL(winhttp); + +static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0}; +static const WCHAR attr_accept_charset[] = {'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0}; +static const WCHAR attr_accept_encoding[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0}; +static const WCHAR attr_accept_language[] = {'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0}; +static const WCHAR attr_accept_ranges[] = {'A','c','c','e','p','t','-','R','a','n','g','e','s',0}; +static const WCHAR attr_age[] = {'A','g','e',0}; +static const WCHAR attr_allow[] = {'A','l','l','o','w',0}; +static const WCHAR attr_authorization[] = {'A','u','t','h','o','r','i','z','a','t','i','o','n',0}; +static const WCHAR attr_cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',0}; +static const WCHAR attr_connection[] = {'C','o','n','n','e','c','t','i','o','n',0}; +static const WCHAR attr_content_base[] = {'C','o','n','t','e','n','t','-','B','a','s','e',0}; +static const WCHAR attr_content_encoding[] = {'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0}; +static const WCHAR attr_content_id[] = {'C','o','n','t','e','n','t','-','I','D',0}; +static const WCHAR attr_content_language[] = {'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0}; +static const WCHAR attr_content_length[] = {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0}; +static const WCHAR attr_content_location[] = {'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0}; +static const WCHAR attr_content_md5[] = {'C','o','n','t','e','n','t','-','M','D','5',0}; +static const WCHAR attr_content_range[] = {'C','o','n','t','e','n','t','-','R','a','n','g','e',0}; +static const WCHAR attr_content_transfer_encoding[] = {'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0}; +static const WCHAR attr_content_type[] = {'C','o','n','t','e','n','t','-','T','y','p','e',0}; +static const WCHAR attr_cookie[] = {'C','o','o','k','i','e',0}; +static const WCHAR attr_date[] = {'D','a','t','e',0}; +static const WCHAR attr_from[] = {'F','r','o','m',0}; +static const WCHAR attr_etag[] = {'E','T','a','g',0}; +static const WCHAR attr_expect[] = {'E','x','p','e','c','t',0}; +static const WCHAR attr_expires[] = {'E','x','p','i','r','e','s',0}; +static const WCHAR attr_host[] = {'H','o','s','t',0}; +static const WCHAR attr_if_match[] = {'I','f','-','M','a','t','c','h',0}; +static const WCHAR attr_if_modified_since[] = {'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0}; +static const WCHAR attr_if_none_match[] = {'I','f','-','N','o','n','e','-','M','a','t','c','h',0}; +static const WCHAR attr_if_range[] = {'I','f','-','R','a','n','g','e',0}; +static const WCHAR attr_if_unmodified_since[] = {'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0}; +static const WCHAR attr_last_modified[] = {'L','a','s','t','-','M','o','d','i','f','i','e','d',0}; +static const WCHAR attr_location[] = {'L','o','c','a','t','i','o','n',0}; +static const WCHAR attr_max_forwards[] = {'M','a','x','-','F','o','r','w','a','r','d','s',0}; +static const WCHAR attr_mime_version[] = {'M','i','m','e','-','V','e','r','s','i','o','n',0}; +static const WCHAR attr_pragma[] = {'P','r','a','g','m','a',0}; +static const WCHAR attr_proxy_authenticate[] = {'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0}; +static const WCHAR attr_proxy_authorization[] = {'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0}; +static const WCHAR attr_proxy_connection[] = {'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0}; +static const WCHAR attr_public[] = {'P','u','b','l','i','c',0}; +static const WCHAR attr_range[] = {'R','a','n','g','e',0}; +static const WCHAR attr_referer[] = {'R','e','f','e','r','e','r',0}; +static const WCHAR attr_retry_after[] = {'R','e','t','r','y','-','A','f','t','e','r',0}; +static const WCHAR attr_server[] = {'S','e','r','v','e','r',0}; +static const WCHAR attr_set_cookie[] = {'S','e','t','-','C','o','o','k','i','e',0}; +static const WCHAR attr_status[] = {'S','t','a','t','u','s',0}; +static const WCHAR attr_transfer_encoding[] = {'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0}; +static const WCHAR attr_unless_modified_since[] = {'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0}; +static const WCHAR attr_upgrade[] = {'U','p','g','r','a','d','e',0}; +static const WCHAR attr_uri[] = {'U','R','I',0}; +static const WCHAR attr_user_agent[] = {'U','s','e','r','-','A','g','e','n','t',0}; +static const WCHAR attr_vary[] = {'V','a','r','y',0}; +static const WCHAR attr_via[] = {'V','i','a',0}; +static const WCHAR attr_warning[] = {'W','a','r','n','i','n','g',0}; +static const WCHAR attr_www_authenticate[] = {'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0}; + +static const WCHAR *attribute_table[] = +{ + attr_mime_version, /* WINHTTP_QUERY_MIME_VERSION = 0 */ + attr_content_type, /* WINHTTP_QUERY_CONTENT_TYPE = 1 */ + attr_content_transfer_encoding, /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */ + attr_content_id, /* WINHTTP_QUERY_CONTENT_ID = 3 */ + NULL, /* WINHTTP_QUERY_CONTENT_DESCRIPTION = 4 */ + attr_content_length, /* WINHTTP_QUERY_CONTENT_LENGTH = 5 */ + attr_content_language, /* WINHTTP_QUERY_CONTENT_LANGUAGE = 6 */ + attr_allow, /* WINHTTP_QUERY_ALLOW = 7 */ + attr_public, /* WINHTTP_QUERY_PUBLIC = 8 */ + attr_date, /* WINHTTP_QUERY_DATE = 9 */ + attr_expires, /* WINHTTP_QUERY_EXPIRES = 10 */ + attr_last_modified, /* WINHTTP_QUERY_LAST_MODIFIEDcw = 11 */ + NULL, /* WINHTTP_QUERY_MESSAGE_ID = 12 */ + attr_uri, /* WINHTTP_QUERY_URI = 13 */ + attr_from, /* WINHTTP_QUERY_DERIVED_FROM = 14 */ + NULL, /* WINHTTP_QUERY_COST = 15 */ + NULL, /* WINHTTP_QUERY_LINK = 16 */ + attr_pragma, /* WINHTTP_QUERY_PRAGMA = 17 */ + NULL, /* WINHTTP_QUERY_VERSION = 18 */ + attr_status, /* WINHTTP_QUERY_STATUS_CODE = 19 */ + NULL, /* WINHTTP_QUERY_STATUS_TEXT = 20 */ + NULL, /* WINHTTP_QUERY_RAW_HEADERS = 21 */ + NULL, /* WINHTTP_QUERY_RAW_HEADERS_CRLF = 22 */ + attr_connection, /* WINHTTP_QUERY_CONNECTION = 23 */ + attr_accept, /* WINHTTP_QUERY_ACCEPT = 24 */ + attr_accept_charset, /* WINHTTP_QUERY_ACCEPT_CHARSET = 25 */ + attr_accept_encoding, /* WINHTTP_QUERY_ACCEPT_ENCODING = 26 */ + attr_accept_language, /* WINHTTP_QUERY_ACCEPT_LANGUAGE = 27 */ + attr_authorization, /* WINHTTP_QUERY_AUTHORIZATION = 28 */ + attr_content_encoding, /* WINHTTP_QUERY_CONTENT_ENCODING = 29 */ + NULL, /* WINHTTP_QUERY_FORWARDED = 30 */ + NULL, /* WINHTTP_QUERY_FROM = 31 */ + attr_if_modified_since, /* WINHTTP_QUERY_IF_MODIFIED_SINCE = 32 */ + attr_location, /* WINHTTP_QUERY_LOCATION = 33 */ + NULL, /* WINHTTP_QUERY_ORIG_URI = 34 */ + attr_referer, /* WINHTTP_QUERY_REFERER = 35 */ + attr_retry_after, /* WINHTTP_QUERY_RETRY_AFTER = 36 */ + attr_server, /* WINHTTP_QUERY_SERVER = 37 */ + NULL, /* WINHTTP_TITLE = 38 */ + attr_user_agent, /* WINHTTP_QUERY_USER_AGENT = 39 */ + attr_www_authenticate, /* WINHTTP_QUERY_WWW_AUTHENTICATE = 40 */ + attr_proxy_authenticate, /* WINHTTP_QUERY_PROXY_AUTHENTICATE = 41 */ + attr_accept_ranges, /* WINHTTP_QUERY_ACCEPT_RANGES = 42 */ + attr_set_cookie, /* WINHTTP_QUERY_SET_COOKIE = 43 */ + attr_cookie, /* WINHTTP_QUERY_COOKIE = 44 */ + NULL, /* WINHTTP_QUERY_REQUEST_METHOD = 45 */ + NULL, /* WINHTTP_QUERY_REFRESH = 46 */ + NULL, /* WINHTTP_QUERY_CONTENT_DISPOSITION = 47 */ + attr_age, /* WINHTTP_QUERY_AGE = 48 */ + attr_cache_control, /* WINHTTP_QUERY_CACHE_CONTROL = 49 */ + attr_content_base, /* WINHTTP_QUERY_CONTENT_BASE = 50 */ + attr_content_location, /* WINHTTP_QUERY_CONTENT_LOCATION = 51 */ + attr_content_md5, /* WINHTTP_QUERY_CONTENT_MD5 = 52 */ + attr_content_range, /* WINHTTP_QUERY_CONTENT_RANGE = 53 */ + attr_etag, /* WINHTTP_QUERY_ETAG = 54 */ + attr_host, /* WINHTTP_QUERY_HOST = 55 */ + attr_if_match, /* WINHTTP_QUERY_IF_MATCH = 56 */ + attr_if_none_match, /* WINHTTP_QUERY_IF_NONE_MATCH = 57 */ + attr_if_range, /* WINHTTP_QUERY_IF_RANGE = 58 */ + attr_if_unmodified_since, /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */ + attr_max_forwards, /* WINHTTP_QUERY_MAX_FORWARDS = 60 */ + attr_proxy_authorization, /* WINHTTP_QUERY_PROXY_AUTHORIZATION = 61 */ + attr_range, /* WINHTTP_QUERY_RANGE = 62 */ + attr_transfer_encoding, /* WINHTTP_QUERY_TRANSFER_ENCODING = 63 */ + attr_upgrade, /* WINHTTP_QUERY_UPGRADE = 64 */ + attr_vary, /* WINHTTP_QUERY_VARY = 65 */ + attr_via, /* WINHTTP_QUERY_VIA = 66 */ + attr_warning, /* WINHTTP_QUERY_WARNING = 67 */ + attr_expect, /* WINHTTP_QUERY_EXPECT = 68 */ + attr_proxy_connection, /* WINHTTP_QUERY_PROXY_CONNECTION = 69 */ + attr_unless_modified_since, /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */ + NULL, /* WINHTTP_QUERY_PROXY_SUPPORT = 75 */ + NULL, /* WINHTTP_QUERY_AUTHENTICATION_INFO = 76 */ + NULL, /* WINHTTP_QUERY_PASSPORT_URLS = 77 */ + NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */ +}; + +static void free_header( header_t *header ) +{ + heap_free( header->field ); + heap_free( header->value ); + heap_free( header ); +} + +static BOOL valid_token_char( WCHAR c ) +{ + if (c < 32 || c == 127) return FALSE; + switch (c) + { + case '(': case ')': + case '<': case '>': + case '@': case ',': + case ';': case ':': + case '\\': case '\"': + case '/': case '[': + case ']': case '?': + case '=': case '{': + case '}': case ' ': + case '\t': + return FALSE; + default: + return TRUE; + } +} + +static header_t *parse_header( LPCWSTR string ) +{ + const WCHAR *p, *q; + header_t *header; + int len; + + p = string; + if (!(q = strchrW( p, ':' ))) + { + WARN("no ':' in line %s\n", debugstr_w(string)); + return NULL; + } + if (q == string) + { + WARN("empty field name in line %s\n", debugstr_w(string)); + return NULL; + } + while (*p != ':') + { + if (!valid_token_char( *p )) + { + WARN("invalid character in field name %s\n", debugstr_w(string)); + return NULL; + } + p++; + } + len = q - string; + if (!(header = heap_alloc_zero( sizeof(header_t) ))) return NULL; + if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + heap_free( header ); + return NULL; + } + memcpy( header->field, string, len * sizeof(WCHAR) ); + header->field[len] = 0; + + q++; /* skip past colon */ + while (*q == ' ') q++; + if (!*q) + { + WARN("no value in line %s\n", debugstr_w(string)); + return header; + } + len = strlenW( q ); + if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + free_header( header ); + return NULL; + } + memcpy( header->value, q, len * sizeof(WCHAR) ); + header->value[len] = 0; + + return header; +} + +static int get_header_index( request_t *request, LPCWSTR field, int requested_index, BOOL request_only ) +{ + int index; + + TRACE("%s\n", debugstr_w(field)); + + for (index = 0; index < request->num_headers; index++) + { + if (strcmpiW( request->headers[index].field, field )) continue; + if (request_only && !request->headers[index].is_request) continue; + if (!request_only && request->headers[index].is_request) continue; + + if (!requested_index) break; + requested_index--; + } + if (index >= request->num_headers) index = -1; + TRACE("returning %d\n", index); + return index; +} + +static BOOL insert_header( request_t *request, header_t *header ) +{ + DWORD count; + header_t *hdrs; + + count = request->num_headers + 1; + if (count > 1) + hdrs = heap_realloc_zero( request->headers, sizeof(header_t) * count ); + else + hdrs = heap_alloc_zero( sizeof(header_t) * count ); + + if (hdrs) + { + request->headers = hdrs; + request->headers[count - 1].field = strdupW( header->field ); + request->headers[count - 1].value = strdupW( header->value ); + request->headers[count - 1].is_request = header->is_request; + request->num_headers++; + return TRUE; + } + return FALSE; +} + +static BOOL delete_header( request_t *request, DWORD index ) +{ + if (!request->num_headers) return FALSE; + if (index >= request->num_headers) return FALSE; + request->num_headers--; + + heap_free( request->headers[index].field ); + heap_free( request->headers[index].value ); + + memmove( &request->headers[index], &request->headers[index + 1], (request->num_headers - index) * sizeof(header_t) ); + memset( &request->headers[request->num_headers], 0, sizeof(header_t) ); + return TRUE; +} + +static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only ) +{ + int index; + header_t *header; + + TRACE("%s: %s 0x%08x\n", debugstr_w(field), debugstr_w(value), flags); + + /* replace wins out over add */ + if (flags & WINHTTP_ADDREQ_FLAG_REPLACE) flags &= ~WINHTTP_ADDREQ_FLAG_ADD; + + if (flags & WINHTTP_ADDREQ_FLAG_ADD) index = -1; + else + index = get_header_index( request, field, 0, request_only ); + + if (index >= 0) + { + if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return FALSE; + header = &request->headers[index]; + } + else if (value) + { + header_t hdr; + + hdr.field = (LPWSTR)field; + hdr.value = (LPWSTR)value; + hdr.is_request = request_only; + + return insert_header( request, &hdr ); + } + /* no value to delete */ + else return TRUE; + + if (flags & WINHTTP_ADDREQ_FLAG_REPLACE) + { + delete_header( request, index ); + if (value) + { + header_t hdr; + + hdr.field = (LPWSTR)field; + hdr.value = (LPWSTR)value; + hdr.is_request = request_only; + + return insert_header( request, &hdr ); + } + return TRUE; + } + else if (flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) + { + WCHAR sep, *tmp; + int len, orig_len, value_len; + + orig_len = strlenW( header->value ); + value_len = strlenW( value ); + + if (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) sep = ','; + else sep = ';'; + + len = orig_len + value_len + 2; + if ((tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) ))) + { + header->value = tmp; + + header->value[orig_len] = sep; + orig_len++; + header->value[orig_len] = ' '; + orig_len++; + + memcpy( &header->value[orig_len], value, value_len * sizeof(WCHAR) ); + header->value[len] = 0; + return TRUE; + } + } + return TRUE; +} + +static 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 (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; + strcpyW( buffer, headers ); + + p = buffer; + do + { + q = p; + while (*q) + { + if (q[0] == '\r' && q[1] == '\n') break; + q++; + } + if (!*p) break; + if (*q == '\r') + { + *q = 0; + q += 2; /* jump over \r\n */ + } + if ((header = parse_header( p ))) + { + ret = process_header( request, header->field, header->value, flags, TRUE ); + free_header( header ); + } + p = q; + } while (ret); + + heap_free( buffer ); + return ret; +} + +/*********************************************************************** + * WinHttpAddRequestHeaders (winhttp.@) + */ +BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags ) +{ + BOOL ret; + request_t *request; + + TRACE("%p, %s, 0x%x, 0x%08x\n", hrequest, debugstr_w(headers), len, flags); + + if (!headers) + { + set_last_error( ERROR_INVALID_PARAMETER ); + 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) + { + release_object( &request->hdr ); + set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); + return FALSE; + } + + ret = add_request_headers( request, headers, len, flags ); + + release_object( &request->hdr ); + return ret; +} + +static WCHAR *build_request_string( request_t *request ) +{ + static const WCHAR space[] = {' ',0}; + 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++] = space; + headers[i++] = path; + headers[i++] = space; + headers[i++] = version; + + for (j = 0; j < request->num_headers; j++) + { + if (request->headers[j].is_request) + { + headers[i++] = crlf; + headers[i++] = request->headers[j].field; + headers[i++] = colon; + headers[i++] = request->headers[j].value; + + TRACE("adding header %s (%s)\n", debugstr_w(request->headers[j].field), + debugstr_w(request->headers[j].value)); + } + } + headers[i++] = twocrlf; + headers[i] = NULL; + + len = 0; + for (p = headers; *p; p++) len += strlenW( *p ); + len++; + + if (!(ret = heap_alloc( len * sizeof(WCHAR) ))) + { + heap_free( headers ); + return NULL; + } + *ret = 0; + for (p = headers; *p; p++) strcatW( ret, *p ); + + heap_free( headers ); + return ret; +} + +#define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER) + +static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index ) +{ + header_t *header = NULL; + BOOL request_only, ret = FALSE; + int requested_index, header_index = -1; + DWORD attr, len; + + request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS; + requested_index = index ? *index : 0; + + attr = level & ~QUERY_MODIFIER_MASK; + switch (attr) + { + case WINHTTP_QUERY_CUSTOM: + { + header_index = get_header_index( request, name, requested_index, request_only ); + break; + } + case WINHTTP_QUERY_RAW_HEADERS: + { + WCHAR *headers, *p, *q; + + if (request_only) + headers = build_request_string( request ); + else + headers = request->raw_headers; + + if (!(p = headers)) return FALSE; + for (len = 0; *p; p++) if (*p != '\r') len++; + + if ((len + 1) * sizeof(WCHAR) > *buflen || !buffer) + { + len++; + set_last_error( ERROR_INSUFFICIENT_BUFFER ); + } + else if (buffer) + { + for (p = headers, q = (WCHAR *)buffer; *p; p++, q++) + { + if (*p != '\r') *q = *p; + else + { + *q = 0; + p++; /* skip '\n' */ + } + } + *q = 0; + TRACE("returning data: %s\n", debugstr_wn((WCHAR *)buffer, len)); + ret = TRUE; + } + *buflen = len * sizeof(WCHAR); + if (request_only) heap_free( headers ); + return ret; + } + case WINHTTP_QUERY_RAW_HEADERS_CRLF: + { + WCHAR *headers; + + if (request_only) + headers = build_request_string( request ); + else + headers = request->raw_headers; + + if (!headers) return FALSE; + len = strlenW( headers ) * sizeof(WCHAR); + if (len + sizeof(WCHAR) > *buflen || !buffer) + { + len += sizeof(WCHAR); + set_last_error( ERROR_INSUFFICIENT_BUFFER ); + } + else if (buffer) + { + memcpy( buffer, headers, len + sizeof(WCHAR) ); + TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR))); + ret = TRUE; + } + *buflen = len; + if (request_only) heap_free( headers ); + return ret; + } + default: + { + if (attr > sizeof(attribute_table)/sizeof(attribute_table[0]) || !attribute_table[attr]) + { + FIXME("attribute %u not implemented\n", attr); + return FALSE; + } + TRACE("attribute %s\n", debugstr_w(attribute_table[attr])); + header_index = get_header_index( request, attribute_table[attr], requested_index, request_only ); + } + } + + if (header_index >= 0) + { + header = &request->headers[header_index]; + } + if (!header || (request_only && !header->is_request)) + { + set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND ); + return FALSE; + } + if (index) *index += 1; + if (level & WINHTTP_QUERY_FLAG_NUMBER) + { + int *number = buffer; + if (sizeof(int) > *buflen) + { + set_last_error( ERROR_INSUFFICIENT_BUFFER ); + } + else if (number) + { + *number = atoiW( header->value ); + TRACE("returning number: %d\n", *number); + ret = TRUE; + } + *buflen = sizeof(int); + } + else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME) + { + SYSTEMTIME *st = buffer; + if (sizeof(SYSTEMTIME) > *buflen) + { + set_last_error( ERROR_INSUFFICIENT_BUFFER ); + } + else if (st && (ret = WinHttpTimeToSystemTime( header->value, st ))) + { + TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n", + st->wYear, st->wMonth, st->wDay, st->wDayOfWeek, + st->wHour, st->wMinute, st->wSecond, st->wMilliseconds); + } + *buflen = sizeof(SYSTEMTIME); + } + else if (header->value) + { + WCHAR *string = buffer; + DWORD len = (strlenW( header->value ) + 1) * sizeof(WCHAR); + if (len > *buflen) + { + set_last_error( ERROR_INSUFFICIENT_BUFFER ); + *buflen = len; + return FALSE; + } + else if (string) + { + strcpyW( string, header->value ); + TRACE("returning string: %s\n", debugstr_w(string)); + ret = TRUE; + } + *buflen = len - sizeof(WCHAR); + } + return ret; +} + +/*********************************************************************** + * WinHttpQueryHeaders (winhttp.@) + */ +BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index ) +{ + BOOL ret; + request_t *request; + + TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index); + + 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 = query_headers( request, level, name, buffer, buflen, index ); + + release_object( &request->hdr ); + return ret; +} + +static BOOL open_connection( request_t *request ) +{ + connect_t *connect; + char address[32]; + WCHAR *addressW; + + if (netconn_connected( &request->netconn )) return TRUE; + connect = request->connect; + + 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; + inet_ntop( connect->sockaddr.sin_family, &connect->sockaddr.sin_addr, address, sizeof(address) ); + addressW = strdupAW( address ); + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, strlenW(addressW) + 1 ); + + TRACE("connecting to %s:%u\n", address, ntohs(connect->sockaddr.sin_port)); + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 ); + + if (!netconn_create( &request->netconn, connect->sockaddr.sin_family, SOCK_STREAM, 0 )) + { + heap_free( addressW ); + return FALSE; + } + if (!netconn_connect( &request->netconn, (struct sockaddr *)&connect->sockaddr, sizeof(struct sockaddr_in) )) + { + netconn_close( &request->netconn ); + heap_free( addressW ); + return FALSE; + } + if (request->hdr.flags & WINHTTP_FLAG_SECURE && !netconn_secure_connect( &request->netconn )) + { + netconn_close( &request->netconn ); + heap_free( addressW ); + return FALSE; + } + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 ); + + heap_free( addressW ); + return TRUE; +} + +void close_connection( request_t *request ) +{ + if (!netconn_connected( &request->netconn )) return; + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 ); + netconn_close( &request->netconn ); + 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 ) +{ + BOOL ret; + DWORD len; + WCHAR *host; + static const WCHAR fmt[] = {'%','s',':','%','u',0}; + + if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT) + { + return process_header( request, attr_host, hostname, modifier, TRUE ); + } + len = strlenW( hostname ) + 7; /* sizeof(":65335") */ + if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE; + sprintfW( host, fmt, 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 ) +{ + 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; + session_t *session = connect->session; + WCHAR *req = NULL; + char *req_ascii; + int bytes_sent; + DWORD len; + + if (session->agent) + 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 ); + + if (total_len || (request->verb && !strcmpW( request->verb, post ))) + { + 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 (!(ret = open_connection( request ))) goto end; + if (!(req = build_request_string( request ))) goto end; + + if (!(req_ascii = strdupWA( req ))) goto end; + TRACE("full request: %s\n", debugstr_a(req_ascii)); + len = strlen(req_ascii); + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 ); + + ret = netconn_send( &request->netconn, req_ascii, len, 0, &bytes_sent ); + heap_free( req_ascii ); + if (!ret) goto end; + + if (optional_len && !netconn_send( &request->netconn, optional, optional_len, 0, &bytes_sent )) goto end; + len += optional_len; + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(DWORD) ); + +end: + heap_free( req ); + return ret; +} + +/*********************************************************************** + * 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; + + TRACE("%p, %s, 0x%x, %u, %u, %lx\n", + hrequest, debugstr_w(headers), headers_len, optional_len, total_len, context); + + 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 = send_request( request, headers, headers_len, optional, optional_len, total_len, context ); + + release_object( &request->hdr ); + return ret; +} + +static void clear_response_headers( request_t *request ) +{ + 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--; + } +} + +#define MAX_REPLY_LEN 1460 +#define INITIAL_HEADER_BUFFER_SIZE 512 + +static BOOL receive_response( request_t *request, BOOL clear ) +{ + static const WCHAR crlf[] = {'\r','\n',0}; + + char buffer[MAX_REPLY_LEN]; + DWORD buflen, len, offset, received_len, crlf_len = 2; /* strlenW(crlf) */ + char *status_code, *status_text; + WCHAR *versionW, *status_textW, *raw_headers; + WCHAR status_codeW[4]; /* sizeof("nnn") */ + + 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; + do + { + buflen = MAX_REPLY_LEN; + if (!netconn_get_next_line( &request->netconn, buffer, &buflen )) return FALSE; + received_len += buflen; + + /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */ + if (!(status_code = strchr( buffer, ' ' ))) return FALSE; + status_code++; + if (!(status_text = strchr( status_code, ' ' ))) return FALSE; + if ((len = status_text - status_code) != sizeof("nnn") - 1) return FALSE; + status_text++; + + TRACE("version [%s] status code [%s] status text [%s]\n", + debugstr_an(buffer, status_code - buffer - 1), + debugstr_an(status_code, len), + debugstr_a(status_text)); + + } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */ + + /* we rely on the fact that the protocol is ascii */ + MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len ); + status_codeW[len] = 0; + if (!(process_header( request, attr_status, status_codeW, WINHTTP_ADDREQ_FLAG_REPLACE, FALSE ))) return FALSE; + + len = status_code - buffer; + if (!(versionW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE; + MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 ); + versionW[len - 1] = 0; + + heap_free( request->version ); + request->version = versionW; + + len = buflen - (status_text - buffer); + if (!(status_textW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE; + MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len ); + + heap_free( request->status_text ); + request->status_text = status_textW; + + len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_SIZE ); + 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) ); + + heap_free( request->raw_headers ); + request->raw_headers = raw_headers; + + offset = buflen + crlf_len - 1; + for (;;) + { + header_t *header; + + buflen = MAX_REPLY_LEN; + if (!netconn_get_next_line( &request->netconn, buffer, &buflen )) goto end; + received_len += buflen; + if (!*buffer) break; + + while (len - offset < buflen + crlf_len) + { + WCHAR *tmp; + len *= 2; + if (!(tmp = heap_realloc( raw_headers, len * sizeof(WCHAR) ))) return FALSE; + request->raw_headers = raw_headers = tmp; + } + MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen ); + + if (!(header = parse_header( raw_headers + offset ))) break; + if (!(process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE ))) + { + free_header( header ); + break; + } + free_header( header ); + memcpy( raw_headers + offset + buflen - 1, crlf, sizeof(crlf) ); + offset += buflen + crlf_len - 1; + } + + TRACE("raw headers: %s\n", debugstr_w(raw_headers)); + +end: + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &received_len, sizeof(DWORD) ); + return TRUE; +} + +static BOOL handle_redirect( request_t *request ) +{ + BOOL ret = FALSE; + DWORD size, len; + URL_COMPONENTS uc; + connect_t *connect = request->connect; + INTERNET_PORT port; + WCHAR *hostname = NULL, *location = NULL; + + size = 0; + query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL ); + if (!(location = heap_alloc( size ))) return FALSE; + if (!query_headers( request, WINHTTP_QUERY_LOCATION, NULL, location, &size, NULL )) goto end; + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, size / sizeof(WCHAR) + 1 ); + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0UL; + + if (!(ret = WinHttpCrackUrl( location, size / sizeof(WCHAR), 0, &uc ))) goto end; + + 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->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 ); + } + + ret = TRUE; + +end: + if (!ret) heap_free( hostname ); + heap_free( location ); + return ret; +} + +static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) +{ + DWORD to_read; + int bytes_read; + + to_read = min( size, request->content_length - request->content_read ); + if (!netconn_recv( &request->netconn, buffer, to_read, async ? 0 : MSG_WAITALL, &bytes_read )) + { + if (bytes_read != to_read) + { + ERR("not all data received %d/%d\n", bytes_read, to_read); + } + /* always return success, even if the network layer returns an error */ + *read = 0; + return TRUE; + } + request->content_read += bytes_read; + *read = bytes_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; + DWORD size = 0; + + for (p = buffer; *p; p++) + { + if (*p >= '0' && *p <= '9') size = size * 16 + *p - '0'; + else if (*p >= 'a' && *p <= 'f') size = size * 16 + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') size = size * 16 + *p - 'A' + 10; + else if (*p == ';') break; + } + return size; +} + +static BOOL read_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; + int bytes_read; + + *read = 0; + for (;;) + { + if (*read == size) break; + + if (request->content_length == ~0UL) /* new chunk */ + { + buflen = sizeof(reply); + if (!netconn_get_next_line( &request->netconn, reply, &buflen )) break; + + 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 ); + break; + } + } + to_read = min( to_write, request->content_length - request->content_read ); + + if (!netconn_recv( &request->netconn, p, to_read, async ? 0 : MSG_WAITALL, &bytes_read )) + { + if (bytes_read != to_read) + { + ERR("Not all data received %d/%d\n", bytes_read, to_read); + } + /* always return success, even if the network layer returns an error */ + *read = 0; + break; + } + if (!bytes_read) break; + + request->content_read += bytes_read; + to_write -= bytes_read; + *read += bytes_read; + p += bytes_read; + + if (request->content_read == request->content_length) /* chunk complete */ + { + request->content_read = 0; + request->content_length = ~0UL; + + buflen = sizeof(reply); + if (!netconn_get_next_line( &request->netconn, reply, &buflen )) + { + ERR("Malformed chunk\n"); + *read = 0; + break; + } + } + } + return 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); + + 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 (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) && + !strcmpiW( encoding, chunked )) + { + ret = read_data_chunked( request, buffer, to_read, &num_bytes, request->hdr.flags & WINHTTP_FLAG_ASYNC ); + } + else + ret = read_data( request, buffer, to_read, &num_bytes, request->hdr.flags & WINHTTP_FLAG_ASYNC ); + + if (ret && read) *read = num_bytes; + release_object( &request->hdr ); + return ret; +} + +/*********************************************************************** + * WinHttpWriteData (winhttp.@) + */ +BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written ) +{ + BOOL ret; + request_t *request; + + TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written); + + 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_send( &request->netconn, buffer, to_write, 0, (int *)written ); + + release_object( &request->hdr ); + return ret; +} + +#define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0])) + +static DWORD auth_scheme_from_header( WCHAR *header ) +{ + static const WCHAR basic[] = {'B','a','s','i','c'}; + static const WCHAR ntlm[] = {'N','T','L','M'}; + static const WCHAR passport[] = {'P','a','s','s','p','o','r','t'}; + static const WCHAR digest[] = {'D','i','g','e','s','t'}; + static const WCHAR negotiate[] = {'N','e','g','o','t','i','a','t','e'}; + + if (!strncmpiW( header, basic, ARRAYSIZE(basic) ) && + (header[ARRAYSIZE(basic)] == ' ' || !header[ARRAYSIZE(basic)])) return WINHTTP_AUTH_SCHEME_BASIC; + + if (!strncmpiW( header, ntlm, ARRAYSIZE(ntlm) ) && + (header[ARRAYSIZE(ntlm)] == ' ' || !header[ARRAYSIZE(ntlm)])) return WINHTTP_AUTH_SCHEME_NTLM; + + if (!strncmpiW( header, passport, ARRAYSIZE(passport) ) && + (header[ARRAYSIZE(passport)] == ' ' || !header[ARRAYSIZE(passport)])) return WINHTTP_AUTH_SCHEME_PASSPORT; + + if (!strncmpiW( header, digest, ARRAYSIZE(digest) ) && + (header[ARRAYSIZE(digest)] == ' ' || !header[ARRAYSIZE(digest)])) return WINHTTP_AUTH_SCHEME_DIGEST; + + if (!strncmpiW( header, negotiate, ARRAYSIZE(negotiate) ) && + (header[ARRAYSIZE(negotiate)] == ' ' || !header[ARRAYSIZE(negotiate)])) return WINHTTP_AUTH_SCHEME_NEGOTIATE; + + return 0; +} + +static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD supported, LPDWORD first ) +{ + DWORD index = 0; + BOOL ret = FALSE; + + for (;;) + { + WCHAR *buffer; + DWORD size, scheme; + + size = 0; + query_headers( request, level, NULL, NULL, &size, &index ); + if (GetLastError() != 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 ); + if (index == 1) *first = scheme; + *supported |= scheme; + + heap_free( buffer ); + ret = TRUE; + } + return ret; +} + +/*********************************************************************** + * WinHttpQueryAuthSchemes (winhttp.@) + */ +BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target ) +{ + BOOL ret = FALSE; + request_t *request; + + TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target); + + 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 (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; + } + + release_object( &request->hdr ); + return ret; +} + +static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 ) +{ + UINT n = 0, x; + static const char base64enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + while (len > 0) + { + /* 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) + { + base64[n++] = base64enc[x]; + base64[n++] = '='; + break; + } + base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)]; + + /* last 6 bits, all from bin [2] */ + base64[n++] = base64enc[bin[2] & 0x3f]; + bin += 3; + len -= 3; + } + base64[n] = 0; + return n; +} + +static BOOL set_credentials( request_t *request, DWORD target, DWORD scheme, LPCWSTR username, LPCWSTR password ) +{ + static const WCHAR basic[] = {'B','a','s','i','c',' ',0}; + + const WCHAR *auth_scheme, *auth_target; + WCHAR *auth_header; + DWORD len, auth_data_len; + char *auth_data; + BOOL ret; + + switch (target) + { + case WINHTTP_AUTH_TARGET_SERVER: auth_target = attr_authorization; break; + case WINHTTP_AUTH_TARGET_PROXY: auth_target = attr_proxy_authorization; break; + default: + WARN("unknown target %x\n", target); + return FALSE; + } + switch (scheme) + { + case WINHTTP_AUTH_SCHEME_BASIC: + { + int userlen = WideCharToMultiByte( CP_UTF8, 0, username, strlenW( username ), NULL, 0, NULL, NULL ); + int passlen = WideCharToMultiByte( CP_UTF8, 0, password, strlenW( password ), NULL, 0, NULL, NULL ); + + TRACE("basic authentication\n"); + + auth_scheme = basic; + auth_data_len = userlen + 1 + passlen; + if (!(auth_data = heap_alloc( auth_data_len ))) return FALSE; + + WideCharToMultiByte( CP_UTF8, 0, username, -1, auth_data, userlen, NULL, NULL ); + auth_data[userlen] = ':'; + WideCharToMultiByte( CP_UTF8, 0, password, -1, auth_data + userlen + 1, passlen, NULL, NULL ); + break; + } + case WINHTTP_AUTH_SCHEME_NTLM: + case WINHTTP_AUTH_SCHEME_PASSPORT: + case WINHTTP_AUTH_SCHEME_DIGEST: + case WINHTTP_AUTH_SCHEME_NEGOTIATE: + FIXME("unimplemented authentication scheme %x\n", scheme); + return FALSE; + default: + WARN("unknown authentication scheme %x\n", scheme); + return FALSE; + } + + len = strlenW( auth_scheme ) + ((auth_data_len + 2) * 4) / 3; + if (!(auth_header = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + heap_free( auth_data ); + return FALSE; + } + strcpyW( auth_header, auth_scheme ); + encode_base64( auth_data, auth_data_len, auth_header + strlenW( auth_header ) ); + + ret = process_header( request, auth_target, auth_header, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE ); + + heap_free( auth_data ); + heap_free( auth_header ); + return ret; +} + +/*********************************************************************** + * WinHttpSetCredentials (winhttp.@) + */ +BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, LPCWSTR username, + LPCWSTR password, LPVOID params ) +{ + BOOL ret; + request_t *request; + + TRACE("%p, %x, 0x%08x, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params); + + 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 = set_credentials( request, target, scheme, username, password ); + + release_object( &request->hdr ); + return ret; +} diff --git a/reactos/dll/win32/winhttp/session.c b/reactos/dll/win32/winhttp/session.c new file mode 100644 index 00000000000..8725f7e7688 --- /dev/null +++ b/reactos/dll/win32/winhttp/session.c @@ -0,0 +1,693 @@ +/* + * 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 "wine/port.h" +#include "wine/debug.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winhttp.h" + +#include "winhttp_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winhttp); + +void set_last_error( DWORD error ) +{ + /* FIXME */ + SetLastError( error ); +} + +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 ); +} + +/*********************************************************************** + * WinHttpCheckPlatform (winhttp.@) + */ +BOOL WINAPI WinHttpCheckPlatform( void ) +{ + TRACE("\n"); + return TRUE; +} + +/*********************************************************************** + * session_destroy (internal) + */ +static void session_destroy( object_header_t *hdr ) +{ + session_t *session = (session_t *)hdr; + + TRACE("%p\n", session); + + heap_free( session->agent ); + heap_free( session->proxy_server ); + heap_free( session->proxy_bypass ); + heap_free( session->proxy_username ); + heap_free( session->proxy_password ); + heap_free( session ); +} + +static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen ) +{ + switch (option) + { + case WINHTTP_OPTION_PROXY: + { + WINHTTP_PROXY_INFO *pi = buffer; + + FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass)); + return TRUE; + } + case WINHTTP_OPTION_REDIRECT_POLICY: + { + DWORD policy = *(DWORD *)buffer; + + TRACE("0x%x\n", policy); + hdr->redirect_policy = policy; + return TRUE; + } + default: + FIXME("unimplemented option %u\n", option); + return TRUE; + } +} + +static const object_vtbl_t session_vtbl = +{ + session_destroy, + NULL, + session_set_option +}; + +/*********************************************************************** + * WinHttpOpen (winhttp.@) + */ +HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWSTR bypass, DWORD flags ) +{ + session_t *session; + HINTERNET handle = NULL; + + TRACE("%s, %u, %s, %s, 0x%08x\n", debugstr_w(agent), access, debugstr_w(proxy), debugstr_w(bypass), flags); + + if (!(session = heap_alloc_zero( sizeof(session_t) ))) return NULL; + + session->hdr.type = WINHTTP_HANDLE_TYPE_SESSION; + session->hdr.vtbl = &session_vtbl; + session->hdr.flags = flags; + session->hdr.refs = 1; + session->access = access; + + if (agent && !(session->agent = strdupW( agent ))) goto end; + if (proxy && !(session->proxy_server = strdupW( proxy ))) goto end; + if (bypass && !(session->proxy_bypass = strdupW( bypass ))) goto end; + + if (!(handle = alloc_handle( &session->hdr ))) goto end; + session->hdr.handle = handle; + +end: + release_object( &session->hdr ); + TRACE("returning %p\n", handle); + return handle; +} + +/*********************************************************************** + * connect_destroy (internal) + */ +static void connect_destroy( object_header_t *hdr ) +{ + connect_t *connect = (connect_t *)hdr; + + TRACE("%p\n", connect); + + release_object( &connect->session->hdr ); + + heap_free( connect->hostname ); + heap_free( connect->servername ); + heap_free( connect->username ); + heap_free( connect->password ); + heap_free( connect ); +} + +static const object_vtbl_t connect_vtbl = +{ + connect_destroy, + NULL, + NULL +}; + +/*********************************************************************** + * WinHttpConnect (winhttp.@) + */ +HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PORT port, DWORD reserved ) +{ + connect_t *connect; + session_t *session; + HINTERNET hconnect = NULL; + + TRACE("%p, %s, %u, %x\n", hsession, debugstr_w(server), port, reserved); + + if (!server) + { + set_last_error( ERROR_INVALID_PARAMETER ); + return NULL; + } + if (!(session = (session_t *)grab_object( hsession ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return NULL; + } + if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION) + { + release_object( &session->hdr ); + set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); + return NULL; + } + if (!(connect = heap_alloc_zero( sizeof(connect_t) ))) + { + release_object( &session->hdr ); + return NULL; + } + connect->hdr.type = WINHTTP_HANDLE_TYPE_CONNECT; + connect->hdr.vtbl = &connect_vtbl; + connect->hdr.refs = 1; + connect->hdr.flags = session->hdr.flags; + connect->hdr.callback = session->hdr.callback; + connect->hdr.notify_mask = session->hdr.notify_mask; + connect->hdr.context = session->hdr.context; + + addref_object( &session->hdr ); + connect->session = session; + 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); + + if (server && !(connect->servername = strdupW( server ))) goto end; + connect->serverport = port ? port : (connect->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80); + + if (!(hconnect = alloc_handle( &connect->hdr ))) goto end; + connect->hdr.handle = hconnect; + + send_callback( &session->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hconnect, sizeof(hconnect) ); + +end: + release_object( &connect->hdr ); + + TRACE("returning %p\n", hconnect); + return hconnect; +} + +/*********************************************************************** + * request_destroy (internal) + */ +static void request_destroy( object_header_t *hdr ) +{ + request_t *request = (request_t *)hdr; + int i; + + TRACE("%p\n", request); + + release_object( &request->connect->hdr ); + + heap_free( request->verb ); + heap_free( request->path ); + heap_free( request->version ); + heap_free( request->raw_headers ); + heap_free( request->status_text ); + for (i = 0; i < request->num_headers; i++) + { + heap_free( request->headers[i].field ); + heap_free( request->headers[i].value ); + } + heap_free( request->headers ); + heap_free( request ); +} + +static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen ) +{ + switch (option) + { + case WINHTTP_OPTION_SECURITY_FLAGS: + { + DWORD flags = 0; + + if (hdr->flags & WINHTTP_FLAG_SECURE) flags |= SECURITY_FLAG_SECURE; + *(DWORD *)buffer = flags; + *buflen = sizeof(DWORD); + return TRUE; + } + default: + FIXME("unimplemented option %u\n", option); + return FALSE; + } +} + +static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen ) +{ + switch (option) + { + case WINHTTP_OPTION_PROXY: + { + WINHTTP_PROXY_INFO *pi = buffer; + + FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass)); + return TRUE; + } + case WINHTTP_OPTION_DISABLE_FEATURE: + { + DWORD disable = *(DWORD *)buffer; + + TRACE("0x%x\n", disable); + hdr->disable_flags &= disable; + return TRUE; + } + case WINHTTP_OPTION_AUTOLOGON_POLICY: + { + DWORD policy = *(DWORD *)buffer; + + TRACE("0x%x\n", policy); + hdr->logon_policy = policy; + return TRUE; + } + case WINHTTP_OPTION_REDIRECT_POLICY: + { + DWORD policy = *(DWORD *)buffer; + + TRACE("0x%x\n", policy); + hdr->redirect_policy = policy; + return TRUE; + } + default: + FIXME("unimplemented option %u\n", option); + return TRUE; + } +} + +static const object_vtbl_t request_vtbl = +{ + request_destroy, + request_query_option, + request_set_option +}; + +/*********************************************************************** + * WinHttpOpenRequest (winhttp.@) + */ +HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR object, LPCWSTR version, + LPCWSTR referrer, LPCWSTR *types, DWORD flags ) +{ + request_t *request; + connect_t *connect; + HINTERNET hrequest = NULL; + + TRACE("%p, %s, %s, %s, %s, %p, 0x%08x\n", hconnect, debugstr_w(verb), debugstr_w(object), + debugstr_w(version), debugstr_w(referrer), types, flags); + + if (!(connect = (connect_t *)grab_object( hconnect ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return NULL; + } + if (connect->hdr.type != WINHTTP_HANDLE_TYPE_CONNECT) + { + release_object( &connect->hdr ); + set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); + return NULL; + } + if (!(request = heap_alloc_zero( sizeof(request_t) ))) + { + release_object( &connect->hdr ); + return NULL; + } + request->hdr.type = WINHTTP_HANDLE_TYPE_REQUEST; + request->hdr.vtbl = &request_vtbl; + request->hdr.refs = 1; + request->hdr.flags = flags; + request->hdr.callback = connect->hdr.callback; + request->hdr.notify_mask = connect->hdr.notify_mask; + request->hdr.context = connect->hdr.context; + + addref_object( &connect->hdr ); + request->connect = connect; + list_add_head( &connect->hdr.children, &request->hdr.entry ); + + 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 (!(hrequest = alloc_handle( &request->hdr ))) goto end; + request->hdr.handle = hrequest; + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hrequest, sizeof(hrequest) ); + +end: + release_object( &request->hdr ); + + TRACE("returning %p\n", hrequest); + return hrequest; +} + +/*********************************************************************** + * WinHttpCloseHandle (winhttp.@) + */ +BOOL WINAPI WinHttpCloseHandle( HINTERNET handle ) +{ + object_header_t *hdr; + + TRACE("%p\n", handle); + + if (!(hdr = grab_object( handle ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return FALSE; + } + release_object( hdr ); + free_handle( handle ); + return TRUE; +} + +static BOOL query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen ) +{ + BOOL ret = FALSE; + + switch (option) + { + case WINHTTP_OPTION_CONTEXT_VALUE: + { + *(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); + } + } + return ret; +} + +/*********************************************************************** + * WinHttpQueryOption (winhttp.@) + */ +BOOL WINAPI WinHttpQueryOption( HINTERNET handle, DWORD option, LPVOID buffer, LPDWORD buflen ) +{ + BOOL ret = FALSE; + object_header_t *hdr; + + TRACE("%p, %u, %p, %p\n", handle, option, buffer, buflen); + + if (!(hdr = grab_object( handle ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return FALSE; + } + + ret = query_option( hdr, option, buffer, buflen ); + + release_object( hdr ); + return ret; +} + +static BOOL set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen ) +{ + BOOL ret = TRUE; + + switch (option) + { + case WINHTTP_OPTION_CONTEXT_VALUE: + { + 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); + } + } + return ret; +} + +/*********************************************************************** + * WinHttpSetOption (winhttp.@) + */ +BOOL WINAPI WinHttpSetOption( HINTERNET handle, DWORD option, LPVOID buffer, DWORD buflen ) +{ + BOOL ret = FALSE; + object_header_t *hdr; + + TRACE("%p, %u, %p, %u\n", handle, option, buffer, buflen); + + if (!(hdr = grab_object( handle ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return FALSE; + } + + ret = set_option( hdr, option, buffer, buflen ); + + release_object( hdr ); + return ret; +} + +/*********************************************************************** + * WinHttpDetectAutoProxyConfigUrl (winhttp.@) + */ +BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, LPWSTR *url ) +{ + FIXME("0x%08x, %p\n", flags, url); + + set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); + return FALSE; +} + +/*********************************************************************** + * WinHttpGetDefaultProxyConfiguration (winhttp.@) + */ +BOOL WINAPI WinHttpGetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) +{ + FIXME("%p\n", info); + + info->dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; + info->lpszProxy = NULL; + info->lpszProxyBypass = NULL; + + return TRUE; +} + +/*********************************************************************** + * WinHttpGetIEProxyConfigForCurrentUser (winhttp.@) + */ +BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *config ) +{ + TRACE("%p\n", config); + + if (!config) + { + set_last_error( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + /* FIXME: read from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings */ + + FIXME("returning no proxy used\n"); + config->fAutoDetect = FALSE; + config->lpszAutoConfigUrl = NULL; + config->lpszProxy = NULL; + config->lpszProxyBypass = NULL; + + return TRUE; +} + +/*********************************************************************** + * WinHttpGetProxyForUrl (winhttp.@) + */ +BOOL WINAPI WinHttpGetProxyForUrl( HINTERNET hsession, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options, + WINHTTP_PROXY_INFO *info ) +{ + FIXME("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info); + + set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR ); + return FALSE; +} + +/*********************************************************************** + * WinHttpSetDefaultProxyConfiguration (winhttp.@) + */ +BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info ) +{ + FIXME("%p [%u, %s, %s]\n", info, info->dwAccessType, debugstr_w(info->lpszProxy), + debugstr_w(info->lpszProxyBypass)); + return TRUE; +} + +/*********************************************************************** + * WinHttpSetStatusCallback (winhttp.@) + */ +WINHTTP_STATUS_CALLBACK WINAPI WinHttpSetStatusCallback( HINTERNET handle, WINHTTP_STATUS_CALLBACK callback, + DWORD flags, DWORD_PTR reserved ) +{ + object_header_t *hdr; + WINHTTP_STATUS_CALLBACK ret; + + TRACE("%p, %p, 0x%08x, 0x%lx\n", handle, callback, flags, reserved); + + if (!(hdr = grab_object( handle ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return WINHTTP_INVALID_STATUS_CALLBACK; + } + ret = hdr->callback; + hdr->callback = callback; + hdr->notify_mask = flags; + + release_object( hdr ); + return ret; +} + +/*********************************************************************** + * WinHttpSetTimeouts (winhttp.@) + */ +BOOL WINAPI WinHttpSetTimeouts( HINTERNET handle, int resolve, int connect, int send, int receive ) +{ + FIXME("%p, %d, %d, %d, %d\n", handle, resolve, connect, send, receive); + return TRUE; +} + +static const WCHAR wkday[7][4] = + {{'S','u','n', 0}, {'M','o','n', 0}, {'T','u','e', 0}, {'W','e','d', 0}, + {'T','h','u', 0}, {'F','r','i', 0}, {'S','a','t', 0}}; +static const WCHAR month[12][4] = + {{'J','a','n', 0}, {'F','e','b', 0}, {'M','a','r', 0}, {'A','p','r', 0}, + {'M','a','y', 0}, {'J','u','n', 0}, {'J','u','l', 0}, {'A','u','g', 0}, + {'S','e','p', 0}, {'O','c','t', 0}, {'N','o','v', 0}, {'D','e','c', 0}}; + +/*********************************************************************** + * WinHttpTimeFromSystemTime (WININET.@) + */ +BOOL WINAPI WinHttpTimeFromSystemTime( const SYSTEMTIME *time, LPWSTR string ) +{ + static const WCHAR format[] = + {'%','s',',',' ','%','0','2','d',' ','%','s',' ','%','4','d',' ','%','0', + '2','d',':','%','0','2','d',':','%','0','2','d',' ','G','M','T', 0}; + + TRACE("%p, %p\n", time, string); + + if (!time || !string) return FALSE; + + sprintfW( string, format, + wkday[time->wDayOfWeek], + time->wDay, + month[time->wMonth - 1], + time->wYear, + time->wHour, + time->wMinute, + time->wSecond ); + + return TRUE; +} + +/*********************************************************************** + * WinHttpTimeToSystemTime (WININET.@) + */ +BOOL WINAPI WinHttpTimeToSystemTime( LPCWSTR string, SYSTEMTIME *time ) +{ + unsigned int i; + const WCHAR *s = string; + WCHAR *end; + + TRACE("%s, %p\n", debugstr_w(string), time); + + if (!string || !time) return FALSE; + + /* Windows does this too */ + GetSystemTime( time ); + + /* Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into + * a SYSTEMTIME structure. + */ + + while (*s && !isalphaW( *s )) s++; + if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE; + time->wDayOfWeek = 7; + + for (i = 0; i < 7; i++) + { + if (toupperW( wkday[i][0] ) == toupperW( s[0] ) && + toupperW( wkday[i][1] ) == toupperW( s[1] ) && + toupperW( wkday[i][2] ) == toupperW( s[2] ) ) + { + time->wDayOfWeek = i; + break; + } + } + + if (time->wDayOfWeek > 6) return TRUE; + while (*s && !isdigitW( *s )) s++; + time->wDay = strtolW( s, &end, 10 ); + s = end; + + while (*s && !isalphaW( *s )) s++; + if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE; + time->wMonth = 0; + + for (i = 0; i < 12; i++) + { + if (toupperW( month[i][0]) == toupperW( s[0] ) && + toupperW( month[i][1]) == toupperW( s[1] ) && + toupperW( month[i][2]) == toupperW( s[2] ) ) + { + time->wMonth = i + 1; + break; + } + } + if (time->wMonth == 0) return TRUE; + + while (*s && !isdigitW( *s )) s++; + if (*s == '\0') return TRUE; + time->wYear = strtolW( s, &end, 10 ); + s = end; + + while (*s && !isdigitW( *s )) s++; + if (*s == '\0') return TRUE; + time->wHour = strtolW( s, &end, 10 ); + s = end; + + while (*s && !isdigitW( *s )) s++; + if (*s == '\0') return TRUE; + time->wMinute = strtolW( s, &end, 10 ); + s = end; + + while (*s && !isdigitW( *s )) s++; + if (*s == '\0') return TRUE; + time->wSecond = strtolW( s, &end, 10 ); + s = end; + + time->wMilliseconds = 0; + return TRUE; +} diff --git a/reactos/dll/win32/winhttp/winhttp.rbuild b/reactos/dll/win32/winhttp/winhttp.rbuild index 2cdc8d1efd7..5841090544c 100644 --- a/reactos/dll/win32/winhttp/winhttp.rbuild +++ b/reactos/dll/win32/winhttp/winhttp.rbuild @@ -9,9 +9,15 @@ 0x600 0x600 + handle.c main.c + net.c + request.c + session.c winhttp.spec wine + wininet + ws2_32 kernel32 ntdll diff --git a/reactos/dll/win32/winhttp/winhttp.spec b/reactos/dll/win32/winhttp/winhttp.spec index 1017c71397f..15c560a65a3 100644 --- a/reactos/dll/win32/winhttp/winhttp.spec +++ b/reactos/dll/win32/winhttp/winhttp.spec @@ -2,30 +2,30 @@ @ stdcall -private DllGetClassObject(ptr ptr ptr) @ stdcall -private DllRegisterServer() @ stdcall -private DllUnregisterServer() -@ stub WinHttpAddRequestHeaders +@ stdcall WinHttpAddRequestHeaders(ptr wstr long long) @ stdcall WinHttpCheckPlatform() @ stdcall WinHttpCloseHandle(ptr) @ stdcall WinHttpConnect(ptr wstr long long) -@ stub WinHttpCrackUrl -@ stub WinHttpCreateUrl +@ stdcall WinHttpCrackUrl(wstr long long ptr) +@ stdcall WinHttpCreateUrl(ptr long ptr ptr) @ stdcall WinHttpDetectAutoProxyConfigUrl(long ptr) -@ stub WinHttpGetDefaultProxyConfiguration +@ stdcall WinHttpGetDefaultProxyConfiguration(ptr) @ stdcall WinHttpGetIEProxyConfigForCurrentUser(ptr) -@ stub WinHttpGetProxyForUrl +@ stdcall WinHttpGetProxyForUrl(ptr wstr ptr ptr) @ stdcall WinHttpOpen(wstr long wstr wstr long) @ stdcall WinHttpOpenRequest(ptr wstr wstr wstr wstr ptr long) -@ stub WinHttpQueryAuthSchemes +@ stdcall WinHttpQueryAuthSchemes(ptr ptr ptr ptr) @ stdcall WinHttpQueryDataAvailable(ptr ptr) -@ stub WinHttpQueryHeaders +@ stdcall WinHttpQueryHeaders(ptr long wstr ptr ptr ptr) @ stdcall WinHttpQueryOption(ptr long ptr ptr) @ stdcall WinHttpReadData(ptr ptr long ptr) @ stdcall WinHttpReceiveResponse(ptr ptr) @ stdcall WinHttpSendRequest(ptr wstr long ptr long long ptr) -@ stub WinHttpSetCredentials -@ stub WinHttpSetDefaultProxyConfiguration +@ stdcall WinHttpSetCredentials(ptr long long wstr ptr ptr) +@ stdcall WinHttpSetDefaultProxyConfiguration(ptr) @ stdcall WinHttpSetOption(ptr long ptr long) -@ stub WinHttpSetStatusCallback -@ stub WinHttpSetTimeouts -@ stub WinHttpTimeFromSystemTime -@ stub WinHttpTimeToSystemTime -@ stub WinHttpWriteData +@ stdcall WinHttpSetStatusCallback(ptr ptr long ptr) +@ stdcall WinHttpSetTimeouts(ptr long long long long) +@ stdcall WinHttpTimeFromSystemTime(ptr ptr) +@ stdcall WinHttpTimeToSystemTime(wstr ptr) +@ stdcall WinHttpWriteData(ptr ptr long ptr) diff --git a/reactos/dll/win32/winhttp/winhttp_private.h b/reactos/dll/win32/winhttp/winhttp_private.h new file mode 100644 index 00000000000..8e1003028e6 --- /dev/null +++ b/reactos/dll/win32/winhttp/winhttp_private.h @@ -0,0 +1,211 @@ +/* + * 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 + */ + +#ifndef _WINE_WINHTTP_PRIVATE_H_ +#define _WINE_WINHTTP_PRIVATE_H_ + +#ifndef __WINE_CONFIG_H +# error You must include config.h to use this header +#endif + +#include "wine/list.h" +#include "wine/unicode.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#if defined(__MINGW32__) || defined (_MSC_VER) +# include "ws2tcpip.h" +# ifndef MSG_WAITALL +# define MSG_WAITALL 0 +# endif +#else +# define closesocket close +# define ioctlsocket ioctl +#endif /* __MINGW32__ */ + +typedef struct _object_header_t object_header_t; + +typedef struct +{ + void (*destroy)( object_header_t * ); + BOOL (*query_option)( object_header_t *, DWORD, void *, DWORD * ); + BOOL (*set_option)( object_header_t *, DWORD, void *, DWORD ); +} object_vtbl_t; + +struct _object_header_t +{ + DWORD type; + HINTERNET handle; + const object_vtbl_t *vtbl; + DWORD flags; + DWORD disable_flags; + DWORD logon_policy; + DWORD redirect_policy; + DWORD error; + DWORD_PTR context; + LONG refs; + WINHTTP_STATUS_CALLBACK callback; + DWORD notify_mask; + struct list entry; + struct list children; +}; + +typedef struct +{ + object_header_t hdr; + LPWSTR agent; + DWORD access; + LPWSTR proxy_server; + LPWSTR proxy_bypass; + LPWSTR proxy_username; + LPWSTR proxy_password; +} session_t; + +typedef struct +{ + object_header_t hdr; + session_t *session; + LPWSTR hostname; /* final destination of the request */ + LPWSTR servername; /* name of the server we directly connect to */ + LPWSTR username; + LPWSTR password; + INTERNET_PORT hostport; + INTERNET_PORT serverport; + struct sockaddr_in sockaddr; +} connect_t; + +typedef struct +{ + int socket; + BOOL secure; /* SSL active on connection? */ + void *ssl_ctx; + void *ssl_conn; + char *peek_msg; + char *peek_msg_mem; + size_t peek_len; +} netconn_t; + +typedef struct +{ + LPWSTR field; + LPWSTR value; + BOOL is_request; /* part of request headers? */ +} header_t; + +typedef struct +{ + object_header_t hdr; + connect_t *connect; + LPWSTR verb; + LPWSTR path; + LPWSTR version; + LPWSTR raw_headers; + netconn_t netconn; + LPWSTR status_text; + DWORD content_length; /* total number of bytes to be read (per chunk) */ + DWORD content_read; /* bytes read so far */ + header_t *headers; + DWORD num_headers; +} request_t; + +object_header_t *addref_object( object_header_t * ); +object_header_t *grab_object( HINTERNET ); +void release_object( object_header_t * ); +HINTERNET alloc_handle( object_header_t * ); +BOOL free_handle( HINTERNET ); + +void set_last_error( DWORD ); +void send_callback( object_header_t *, DWORD, LPVOID, DWORD ); +void close_connection( request_t * ); + +BOOL netconn_close( netconn_t * ); +BOOL netconn_connect( netconn_t *, const struct sockaddr *, unsigned int ); +BOOL netconn_connected( netconn_t * ); +BOOL netconn_create( netconn_t *, int, int, int ); +BOOL netconn_get_next_line( netconn_t *, char *, DWORD * ); +BOOL netconn_init( netconn_t *, BOOL ); +BOOL netconn_query_data_available( netconn_t *, DWORD * ); +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 * ); + +static inline void *heap_alloc( SIZE_T size ) +{ + return HeapAlloc( GetProcessHeap(), 0, size ); +} + +static inline void *heap_alloc_zero( SIZE_T size ) +{ + return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); +} + +static inline void *heap_realloc( LPVOID mem, SIZE_T size ) +{ + return HeapReAlloc( GetProcessHeap(), 0, mem, size ); +} + +static inline void *heap_realloc_zero( LPVOID mem, SIZE_T size ) +{ + return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, size ); +} + +static inline BOOL heap_free( LPVOID mem ) +{ + return HeapFree( GetProcessHeap(), 0, mem ); +} + +static inline WCHAR *strdupW( const WCHAR *src ) +{ + WCHAR *dst; + + if (!src) return NULL; + dst = heap_alloc( (strlenW( src ) + 1) * sizeof(WCHAR) ); + if (dst) strcpyW( dst, src ); + return dst; +} + +static inline WCHAR *strdupAW( const char *src ) +{ + WCHAR *dst = NULL; + if (src) + { + DWORD len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 ); + if ((dst = heap_alloc( len * sizeof(WCHAR) ))) + MultiByteToWideChar( CP_ACP, 0, src, -1, dst, len ); + } + return dst; +} + +static inline char *strdupWA( const WCHAR *src ) +{ + char *dst = NULL; + if (src) + { + int len = WideCharToMultiByte( CP_ACP, 0, src, -1, NULL, 0, NULL, NULL ); + if ((dst = heap_alloc( len ))) + WideCharToMultiByte( CP_ACP, 0, src, -1, dst, len, NULL, NULL ); + } + return dst; +} + +#endif /* _WINE_WINHTTP_PRIVATE_H_ */ diff --git a/reactos/dll/win32/winhttp/winhttp_ros.diff b/reactos/dll/win32/winhttp/winhttp_ros.diff new file mode 100644 index 00000000000..f6693910389 --- /dev/null +++ b/reactos/dll/win32/winhttp/winhttp_ros.diff @@ -0,0 +1,103 @@ +--- net.c Wed Sep 03 14:46:10 2008 ++++ net.c Sun Sep 07 10:54:26 2008 +@@ -59,6 +59,7 @@ + + #define DEFAULT_SEND_TIMEOUT 30 + #define DEFAULT_RECEIVE_TIMEOUT 30 ++#define RESPONSE_TIMEOUT 30 + + #ifndef HAVE_GETADDRINFO + +@@ -110,6 +111,7 @@ + + #endif + ++#if 0 + /* translate a unix error code into a winsock error code */ + static int sock_get_error( int err ) + { +@@ -174,6 +176,9 @@ + } + return err; + } ++#else ++#define sock_get_error(x) WSAGetLastError() ++#endif + + BOOL netconn_init( netconn_t *conn, BOOL secure ) + { +@@ -282,7 +287,7 @@ + conn->secure = FALSE; + } + #endif +- res = close( conn->socket ); ++ res = closesocket( conn->socket ); + conn->socket = -1; + if (res == -1) + { +@@ -463,7 +468,7 @@ + return TRUE; + } + #ifdef FIONREAD +- if (!(ret = ioctl( conn->socket, FIONREAD, &unread ))) ++ if (!(ret = ioctlsocket( conn->socket, FIONREAD, &unread ))) + { + TRACE("%d bytes of queued, but unread data\n", unread); + *available += unread; +@@ -474,7 +479,8 @@ + + BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen ) + { +- struct pollfd pfd; ++ struct timeval tv; ++ fd_set infd; + BOOL ret = FALSE; + DWORD recvd = 0; + +@@ -516,11 +522,13 @@ + #endif + } + +- pfd.fd = conn->socket; +- pfd.events = POLLIN; ++ FD_ZERO(&infd); ++ FD_SET(conn->socket, &infd); ++ tv.tv_sec=RESPONSE_TIMEOUT; ++ tv.tv_usec=0; + while (recvd < *buflen) + { +- if (poll( &pfd, 1, DEFAULT_RECEIVE_TIMEOUT * 1000 ) > 0) ++ if (select(conn->socket+1,&infd,NULL,NULL,&tv) > 0) + { + int res; + if ((res = recv( conn->socket, &buffer[recvd], 1, 0 )) <= 0) +--- request.c Fri Sep 05 17:34:17 2008 ++++ request.c Sun Sep 07 10:55:25 2008 +@@ -34,6 +34,8 @@ + + #include "winhttp_private.h" + ++#include "inet_ntop.c" ++ + WINE_DEFAULT_DEBUG_CHANNEL(winhttp); + + static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0}; +--- winhttp_private.h Thu Sep 04 15:27:29 2008 ++++ winhttp_private.h Sun Sep 07 10:46:54 2008 +@@ -33,8 +33,14 @@ + # include + #endif + #if defined(__MINGW32__) || defined (_MSC_VER) +-# include +-#endif ++# include "ws2tcpip.h" ++# ifndef MSG_WAITALL ++# define MSG_WAITALL 0 ++# endif ++#else ++# define closesocket close ++# define ioctlsocket ioctl ++#endif /* __MINGW32__ */ + + typedef struct _object_header_t object_header_t; +