diff --git a/dlls/winhttp/Makefile.in b/dlls/winhttp/Makefile.in index c65adbd5ee7..02cd48cf955 100644 --- a/dlls/winhttp/Makefile.in +++ b/dlls/winhttp/Makefile.in @@ -9,6 +9,7 @@ IMPORTS = wininet kernel32 C_SRCS = \ handle.c \ main.c \ + request.c \ session.c @MAKE_DLL_RULES@ diff --git a/dlls/winhttp/main.c b/dlls/winhttp/main.c index c503e230136..b9911bb3ca3 100644 --- a/dlls/winhttp/main.c +++ b/dlls/winhttp/main.c @@ -176,18 +176,6 @@ BOOL WINAPI WinHttpQueryHeaders (HINTERNET hRequest, DWORD dwInfoLevel, LPCWSTR return FALSE; } -/*********************************************************************** - * WinHttpAddRequestHeaders (winhttp.@) - */ -BOOL WINAPI WinHttpAddRequestHeaders (HINTERNET hRequest, LPCWSTR pwszHeaders, - DWORD dwHeadersLength, DWORD dwModifiers) -{ - FIXME("(%s, %d, %d): stub\n", debugstr_w(pwszHeaders), dwHeadersLength, dwModifiers); - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - BOOL WINAPI InternetCrackUrlW( LPCWSTR, DWORD, DWORD, LPURL_COMPONENTSW ); BOOL WINAPI InternetCreateUrlW( LPURL_COMPONENTS, DWORD, LPWSTR, LPDWORD ); diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c new file mode 100644 index 00000000000..6e34e7bc5cb --- /dev/null +++ b/dlls/winhttp/request.c @@ -0,0 +1,320 @@ +/* + * 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 "winnls.h" +#include "winhttp.h" + +#include "winhttp_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winhttp); + +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; + + TRACE("inserting %s: %s\n", debugstr_w(header->field), debugstr_w(header->value)); + + 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; +} diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 39f9be1f8d0..01ab33daa08 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -31,7 +31,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(winhttp); -static void set_last_error( DWORD error ) +void set_last_error( DWORD error ) { /* FIXME */ SetLastError( error ); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 395d66f3b14..c4d58bad439 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -243,7 +243,7 @@ static void test_WinHttpAddHeaders(void) test_header_name, buffer, &len, &index); ok(ret == FALSE, "WinHttpQueryHeaders unexpectedly succeeded, found 'Warning' header."); ret = WinHttpAddRequestHeaders(request, test_headers[0], -1L, WINHTTP_ADDREQ_FLAG_ADD); - todo_wine ok(ret == TRUE, "WinHttpAddRequestHeader failed to add new header, got %d with error %u.\n", ret, GetLastError()); + ok(ret == TRUE, "WinHttpAddRequestHeader failed to add new header, got %d with error %u.\n", ret, GetLastError()); index = 0; len = sizeof(buffer); @@ -337,7 +337,7 @@ static void test_WinHttpAddHeaders(void) /* tests for more indices */ ret = WinHttpAddRequestHeaders(request, test_headers[1], -1L, WINHTTP_ADDREQ_FLAG_ADD); - todo_wine ok(ret == TRUE, "WinHttpAddRequestHeaders failed to add duplicate header: %d\n", ret); + ok(ret == TRUE, "WinHttpAddRequestHeaders failed to add duplicate header: %d\n", ret); index = 0; len = sizeof(buffer); @@ -362,7 +362,7 @@ static void test_WinHttpAddHeaders(void) } ret = WinHttpAddRequestHeaders(request, test_headers[2], -1L, WINHTTP_ADDREQ_FLAG_REPLACE); - todo_wine ok(ret == TRUE, "WinHttpAddRequestHeaders failed to add duplicate header.\n"); + ok(ret == TRUE, "WinHttpAddRequestHeaders failed to add duplicate header.\n"); index = 0; len = sizeof(buffer); @@ -418,7 +418,7 @@ static void test_WinHttpAddHeaders(void) /* coalesce flag */ ret = WinHttpAddRequestHeaders(request, test_headers[3], -1L, WINHTTP_ADDREQ_FLAG_COALESCE); - todo_wine ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_COALESCE.\n"); + ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_COALESCE.\n"); index = 0; len = sizeof(buffer); @@ -448,7 +448,7 @@ static void test_WinHttpAddHeaders(void) /* coalesce with comma flag */ ret = WinHttpAddRequestHeaders(request, test_headers[4], -1L, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA); - todo_wine ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA.\n"); + ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA.\n"); index = 0; len = sizeof(buffer); @@ -479,7 +479,7 @@ static void test_WinHttpAddHeaders(void) /* coalesce with semicolon flag */ ret = WinHttpAddRequestHeaders(request, test_headers[5], -1L, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON); - todo_wine ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON.\n"); + ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON.\n"); index = 0; len = sizeof(buffer); @@ -509,8 +509,7 @@ static void test_WinHttpAddHeaders(void) /* add and replace flags */ ret = WinHttpAddRequestHeaders(request, test_headers[3], -1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE); - todo_wine ok(ret == TRUE, - "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE.\n"); + ok(ret == TRUE, "WinHttpAddRequestHeaders failed with flag WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE.\n"); index = 0; len = sizeof(buffer); diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index c13a63a036b..549d9cd2a04 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -90,8 +90,7 @@ typedef struct { LPWSTR field; LPWSTR value; - WORD flags; - WORD count; + BOOL is_request; /* part of request headers? */ } header_t; typedef struct @@ -116,6 +115,7 @@ 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 ); static inline void *heap_alloc( SIZE_T size ) @@ -128,6 +128,11 @@ 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 );