include: Use nested namespaces in windows.system.userprofile.idl.
[wine.git] / dlls / winhttp / request.c
blobaed26d98c07017bf44e3724d89c3e695ca016fce
1 /*
2 * Copyright 2004 Mike McCormack for CodeWeavers
3 * Copyright 2006 Rob Shearman for CodeWeavers
4 * Copyright 2008, 2011 Hans Leidekker for CodeWeavers
5 * Copyright 2009 Juan Lang
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <wchar.h>
26 #define COBJMACROS
27 #include "windef.h"
28 #include "winbase.h"
29 #include "ws2tcpip.h"
30 #include "ole2.h"
31 #include "initguid.h"
32 #include "httprequest.h"
33 #include "httprequestid.h"
34 #include "schannel.h"
35 #include "winhttp.h"
36 #include "ntsecapi.h"
37 #include "winternl.h"
39 #include "wine/debug.h"
40 #include "winhttp_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
44 #define DEFAULT_KEEP_ALIVE_TIMEOUT 30000
46 static const WCHAR *attribute_table[] =
48 L"Mime-Version", /* WINHTTP_QUERY_MIME_VERSION = 0 */
49 L"Content-Type" , /* WINHTTP_QUERY_CONTENT_TYPE = 1 */
50 L"Content-Transfer-Encoding", /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
51 L"Content-ID", /* WINHTTP_QUERY_CONTENT_ID = 3 */
52 NULL, /* WINHTTP_QUERY_CONTENT_DESCRIPTION = 4 */
53 L"Content-Length", /* WINHTTP_QUERY_CONTENT_LENGTH = 5 */
54 L"Content-Language", /* WINHTTP_QUERY_CONTENT_LANGUAGE = 6 */
55 L"Allow", /* WINHTTP_QUERY_ALLOW = 7 */
56 L"Public", /* WINHTTP_QUERY_PUBLIC = 8 */
57 L"Date", /* WINHTTP_QUERY_DATE = 9 */
58 L"Expires", /* WINHTTP_QUERY_EXPIRES = 10 */
59 L"Last-Modified", /* WINHTTP_QUERY_LAST_MODIFIEDcw = 11 */
60 NULL, /* WINHTTP_QUERY_MESSAGE_ID = 12 */
61 L"URI", /* WINHTTP_QUERY_URI = 13 */
62 L"From", /* WINHTTP_QUERY_DERIVED_FROM = 14 */
63 NULL, /* WINHTTP_QUERY_COST = 15 */
64 NULL, /* WINHTTP_QUERY_LINK = 16 */
65 L"Pragma", /* WINHTTP_QUERY_PRAGMA = 17 */
66 NULL, /* WINHTTP_QUERY_VERSION = 18 */
67 L"Status", /* WINHTTP_QUERY_STATUS_CODE = 19 */
68 NULL, /* WINHTTP_QUERY_STATUS_TEXT = 20 */
69 NULL, /* WINHTTP_QUERY_RAW_HEADERS = 21 */
70 NULL, /* WINHTTP_QUERY_RAW_HEADERS_CRLF = 22 */
71 L"Connection", /* WINHTTP_QUERY_CONNECTION = 23 */
72 L"Accept", /* WINHTTP_QUERY_ACCEPT = 24 */
73 L"Accept-Charset", /* WINHTTP_QUERY_ACCEPT_CHARSET = 25 */
74 L"Accept-Encoding", /* WINHTTP_QUERY_ACCEPT_ENCODING = 26 */
75 L"Accept-Language", /* WINHTTP_QUERY_ACCEPT_LANGUAGE = 27 */
76 L"Authorization", /* WINHTTP_QUERY_AUTHORIZATION = 28 */
77 L"Content-Encoding", /* WINHTTP_QUERY_CONTENT_ENCODING = 29 */
78 NULL, /* WINHTTP_QUERY_FORWARDED = 30 */
79 NULL, /* WINHTTP_QUERY_FROM = 31 */
80 L"If-Modified-Since", /* WINHTTP_QUERY_IF_MODIFIED_SINCE = 32 */
81 L"Location", /* WINHTTP_QUERY_LOCATION = 33 */
82 NULL, /* WINHTTP_QUERY_ORIG_URI = 34 */
83 L"Referer", /* WINHTTP_QUERY_REFERER = 35 */
84 L"Retry-After", /* WINHTTP_QUERY_RETRY_AFTER = 36 */
85 L"Server", /* WINHTTP_QUERY_SERVER = 37 */
86 NULL, /* WINHTTP_TITLE = 38 */
87 L"User-Agent", /* WINHTTP_QUERY_USER_AGENT = 39 */
88 L"WWW-Authenticate", /* WINHTTP_QUERY_WWW_AUTHENTICATE = 40 */
89 L"Proxy-Authenticate", /* WINHTTP_QUERY_PROXY_AUTHENTICATE = 41 */
90 L"Accept-Ranges", /* WINHTTP_QUERY_ACCEPT_RANGES = 42 */
91 L"Set-Cookie", /* WINHTTP_QUERY_SET_COOKIE = 43 */
92 L"Cookie", /* WINHTTP_QUERY_COOKIE = 44 */
93 NULL, /* WINHTTP_QUERY_REQUEST_METHOD = 45 */
94 NULL, /* WINHTTP_QUERY_REFRESH = 46 */
95 NULL, /* WINHTTP_QUERY_CONTENT_DISPOSITION = 47 */
96 L"Age", /* WINHTTP_QUERY_AGE = 48 */
97 L"Cache-Control", /* WINHTTP_QUERY_CACHE_CONTROL = 49 */
98 L"Content-Base", /* WINHTTP_QUERY_CONTENT_BASE = 50 */
99 L"Content-Location", /* WINHTTP_QUERY_CONTENT_LOCATION = 51 */
100 L"Content-MD5", /* WINHTTP_QUERY_CONTENT_MD5 = 52 */
101 L"Content-Range", /* WINHTTP_QUERY_CONTENT_RANGE = 53 */
102 L"ETag", /* WINHTTP_QUERY_ETAG = 54 */
103 L"Host", /* WINHTTP_QUERY_HOST = 55 */
104 L"If-Match", /* WINHTTP_QUERY_IF_MATCH = 56 */
105 L"If-None-Match", /* WINHTTP_QUERY_IF_NONE_MATCH = 57 */
106 L"If-Range", /* WINHTTP_QUERY_IF_RANGE = 58 */
107 L"If-Unmodified-Since", /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
108 L"Max-Forwards", /* WINHTTP_QUERY_MAX_FORWARDS = 60 */
109 L"Proxy-Authorization", /* WINHTTP_QUERY_PROXY_AUTHORIZATION = 61 */
110 L"Range", /* WINHTTP_QUERY_RANGE = 62 */
111 L"Transfer-Encoding", /* WINHTTP_QUERY_TRANSFER_ENCODING = 63 */
112 L"Upgrade", /* WINHTTP_QUERY_UPGRADE = 64 */
113 L"Vary", /* WINHTTP_QUERY_VARY = 65 */
114 L"Via", /* WINHTTP_QUERY_VIA = 66 */
115 L"Warning", /* WINHTTP_QUERY_WARNING = 67 */
116 L"Expect", /* WINHTTP_QUERY_EXPECT = 68 */
117 L"Proxy-Connection", /* WINHTTP_QUERY_PROXY_CONNECTION = 69 */
118 L"Unless-Modified-Since", /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
119 NULL, /* WINHTTP_QUERY_PROXY_SUPPORT = 75 */
120 NULL, /* WINHTTP_QUERY_AUTHENTICATION_INFO = 76 */
121 NULL, /* WINHTTP_QUERY_PASSPORT_URLS = 77 */
122 NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
125 void init_queue( struct queue *queue )
127 InitializeSRWLock( &queue->lock );
128 list_init( &queue->queued_tasks );
129 queue->callback_running = FALSE;
132 void stop_queue( struct queue *queue )
134 assert( list_empty( &queue->queued_tasks ));
135 TRACE("stopped %p\n", queue);
138 static void addref_task( struct task_header *task )
140 InterlockedIncrement( &task->refs );
143 static void release_task( struct task_header *task )
145 if (!InterlockedDecrement( &task->refs ))
146 free( task );
149 static struct task_header *get_next_task( struct queue *queue, struct task_header *prev_task )
151 struct task_header *task;
152 struct list *entry;
154 AcquireSRWLockExclusive( &queue->lock );
155 assert( queue->callback_running );
156 if (prev_task)
158 list_remove( &prev_task->entry );
159 release_task( prev_task );
161 if ((entry = list_head( &queue->queued_tasks )))
163 task = LIST_ENTRY( entry, struct task_header, entry );
164 addref_task( task );
166 else
168 task = NULL;
169 queue->callback_running = FALSE;
171 ReleaseSRWLockExclusive( &queue->lock );
172 return task;
175 static void CALLBACK task_callback( TP_CALLBACK_INSTANCE *instance, void *ctx )
177 struct task_header *task, *next_task;
178 struct queue *queue = ctx;
180 TRACE( "instance %p.\n", instance );
182 task = get_next_task( queue, NULL );
183 while (task)
185 task->callback( task, FALSE );
186 /* Queue object may be freed by release_object() unless there is another task referencing it. */
187 next_task = get_next_task( queue, task );
188 release_object( task->obj );
189 release_task( task );
190 task = next_task;
192 TRACE( "instance %p exiting.\n", instance );
195 static DWORD queue_task( struct queue *queue, TASK_CALLBACK task, struct task_header *task_hdr,
196 struct object_header *obj )
198 BOOL callback_running;
200 TRACE("queueing %p in %p\n", task_hdr, queue);
201 task_hdr->callback = task;
202 task_hdr->completion_sent = 0;
203 task_hdr->refs = 1;
204 task_hdr->obj = obj;
205 addref_object( obj );
207 AcquireSRWLockExclusive( &queue->lock );
208 list_add_tail( &queue->queued_tasks, &task_hdr->entry );
209 if (!(callback_running = queue->callback_running))
211 if ((queue->callback_running = TrySubmitThreadpoolCallback( task_callback, queue, NULL )))
212 callback_running = TRUE;
213 else
214 list_remove( &task_hdr->entry );
216 ReleaseSRWLockExclusive( &queue->lock );
218 if (!callback_running)
220 release_object( obj );
221 ERR( "Submiting threadpool callback failed, err %lu.\n", GetLastError() );
222 return ERROR_OUTOFMEMORY;
225 return ERROR_SUCCESS;
228 static BOOL task_needs_completion( struct task_header *task_hdr )
230 return !InterlockedExchange( &task_hdr->completion_sent, 1 );
233 static BOOL cancel_queue( struct queue *queue )
235 struct task_header *task_hdr, *found;
236 BOOL cancelled = FALSE;
238 while (1)
240 AcquireSRWLockExclusive( &queue->lock );
241 found = NULL;
242 LIST_FOR_EACH_ENTRY( task_hdr, &queue->queued_tasks, struct task_header, entry )
244 if (task_needs_completion( task_hdr ))
246 found = task_hdr;
247 addref_task( found );
248 break;
251 ReleaseSRWLockExclusive( &queue->lock );
252 if (!found) break;
253 cancelled = TRUE;
254 found->callback( found, TRUE );
255 release_task( found );
257 return cancelled;
260 static void free_header( struct header *header )
262 free( header->field );
263 free( header->value );
264 free( header );
267 static BOOL valid_token_char( WCHAR c )
269 if (c < 32 || c == 127) return FALSE;
270 switch (c)
272 case '(': case ')':
273 case '<': case '>':
274 case '@': case ',':
275 case ';': case ':':
276 case '\\': case '\"':
277 case '/': case '[':
278 case ']': case '?':
279 case '=': case '{':
280 case '}': case ' ':
281 case '\t':
282 return FALSE;
283 default:
284 return TRUE;
288 static struct header *parse_header( const WCHAR *string, size_t string_len )
290 const WCHAR *p, *q;
291 struct header *header;
292 int len;
294 p = string;
295 if (!(q = wcschr( p, ':' )))
297 WARN("no ':' in line %s\n", debugstr_w(string));
298 return NULL;
300 if (q == string)
302 WARN("empty field name in line %s\n", debugstr_w(string));
303 return NULL;
305 while (*p != ':')
307 if (!valid_token_char( *p ))
309 WARN("invalid character in field name %s\n", debugstr_w(string));
310 return NULL;
312 p++;
314 len = q - string;
315 if (!(header = calloc( 1, sizeof(*header) ))) return NULL;
316 if (!(header->field = malloc( (len + 1) * sizeof(WCHAR) )))
318 free( header );
319 return NULL;
321 memcpy( header->field, string, len * sizeof(WCHAR) );
322 header->field[len] = 0;
324 q++; /* skip past colon */
325 while (*q == ' ') q++;
326 len = (string + string_len) - q;
328 if (!(header->value = malloc( (len + 1) * sizeof(WCHAR) )))
330 free_header( header );
331 return NULL;
333 memcpy( header->value, q, len * sizeof(WCHAR) );
334 header->value[len] = 0;
336 return header;
339 static int get_header_index( struct request *request, const WCHAR *field, int requested_index, BOOL request_only )
341 int index;
343 TRACE("%s\n", debugstr_w(field));
345 for (index = 0; index < request->num_headers; index++)
347 if (wcsicmp( request->headers[index].field, field )) continue;
348 if (request_only && !request->headers[index].is_request) continue;
349 if (!request_only && request->headers[index].is_request) continue;
351 if (!requested_index) break;
352 requested_index--;
354 if (index >= request->num_headers) index = -1;
355 TRACE("returning %d\n", index);
356 return index;
359 static DWORD insert_header( struct request *request, struct header *header )
361 DWORD count = request->num_headers + 1;
362 struct header *hdrs;
364 if (request->headers)
366 if ((hdrs = realloc( request->headers, sizeof(*header) * count )))
367 memset( &hdrs[count - 1], 0, sizeof(*header) );
369 else hdrs = calloc( 1, sizeof(*header) );
370 if (!hdrs) return ERROR_OUTOFMEMORY;
372 request->headers = hdrs;
373 request->headers[count - 1].field = wcsdup( header->field );
374 request->headers[count - 1].value = wcsdup( header->value );
375 request->headers[count - 1].is_request = header->is_request;
376 request->num_headers = count;
377 return ERROR_SUCCESS;
380 static void delete_header( struct request *request, DWORD index )
382 if (!request->num_headers || index >= request->num_headers) return;
383 request->num_headers--;
385 free( request->headers[index].field );
386 free( request->headers[index].value );
388 memmove( &request->headers[index], &request->headers[index + 1],
389 (request->num_headers - index) * sizeof(struct header) );
390 memset( &request->headers[request->num_headers], 0, sizeof(struct header) );
393 DWORD process_header( struct request *request, const WCHAR *field, const WCHAR *value, DWORD flags, BOOL request_only )
395 int index;
396 struct header hdr;
398 TRACE( "%s: %s %#lx\n", debugstr_w(field), debugstr_w(value), flags );
400 if ((index = get_header_index( request, field, 0, request_only )) >= 0)
402 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return ERROR_WINHTTP_HEADER_ALREADY_EXISTS;
405 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
407 if (index >= 0)
409 delete_header( request, index );
410 if (!value || !value[0]) return ERROR_SUCCESS;
412 else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
414 hdr.field = (LPWSTR)field;
415 hdr.value = (LPWSTR)value;
416 hdr.is_request = request_only;
417 return insert_header( request, &hdr );
419 else if (value)
422 if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
423 index >= 0)
425 WCHAR *tmp;
426 int len, len_orig, len_value;
427 struct header *header = &request->headers[index];
429 len_orig = lstrlenW( header->value );
430 len_value = lstrlenW( value );
432 len = len_orig + len_value + 2;
433 if (!(tmp = realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
434 header->value = tmp;
435 header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
436 header->value[len_orig++] = ' ';
438 memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
439 header->value[len] = 0;
440 return ERROR_SUCCESS;
442 else
444 hdr.field = (LPWSTR)field;
445 hdr.value = (LPWSTR)value;
446 hdr.is_request = request_only;
447 return insert_header( request, &hdr );
451 return ERROR_SUCCESS;
454 DWORD add_request_headers( struct request *request, const WCHAR *headers, DWORD len, DWORD flags )
456 DWORD ret = ERROR_WINHTTP_INVALID_HEADER;
457 struct header *header;
458 const WCHAR *p, *q;
460 if (len == ~0u) len = lstrlenW( headers );
461 if (!len) return ERROR_SUCCESS;
463 p = headers;
466 const WCHAR *end;
468 if (p >= headers + len) break;
470 for (q = p; q < headers + len && *q != '\r' && *q != '\n'; ++q)
472 end = q;
473 while (*q == '\r' || *q == '\n')
474 ++q;
476 if ((header = parse_header( p, end - p )))
478 ret = process_header( request, header->field, header->value, flags, TRUE );
479 free_header( header );
481 p = q;
482 } while (!ret);
484 return ret;
487 /***********************************************************************
488 * WinHttpAddRequestHeaders (winhttp.@)
490 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, const WCHAR *headers, DWORD len, DWORD flags )
492 DWORD ret;
493 struct request *request;
495 TRACE( "%p, %s, %lu, %#lx\n", hrequest, debugstr_wn(headers, len), len, flags );
497 if (!headers || !len)
499 SetLastError( ERROR_INVALID_PARAMETER );
500 return FALSE;
502 if (!(request = (struct request *)grab_object( hrequest )))
504 SetLastError( ERROR_INVALID_HANDLE );
505 return FALSE;
507 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
509 release_object( &request->hdr );
510 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
511 return FALSE;
514 ret = add_request_headers( request, headers, len, flags );
516 release_object( &request->hdr );
517 SetLastError( ret );
518 return !ret;
521 static WCHAR *build_absolute_request_path( struct request *request, const WCHAR **path )
523 const WCHAR *scheme;
524 WCHAR *ret;
525 int len, offset;
527 scheme = (request->netconn ? request->netconn->secure : (request->hdr.flags & WINHTTP_FLAG_SECURE)) ? L"https" : L"http";
529 len = lstrlenW( scheme ) + lstrlenW( request->connect->hostname ) + 4; /* '://' + nul */
530 if (request->connect->hostport) len += 6; /* ':' between host and port, up to 5 for port */
532 len += lstrlenW( request->path );
533 if ((ret = malloc( len * sizeof(WCHAR) )))
535 offset = swprintf( ret, len, L"%s://%s", scheme, request->connect->hostname );
536 if (request->connect->hostport)
538 offset += swprintf( ret + offset, len - offset, L":%u", request->connect->hostport );
540 lstrcpyW( ret + offset, request->path );
541 if (path) *path = ret + offset;
544 return ret;
547 static WCHAR *build_request_string( struct request *request )
549 WCHAR *path, *ret;
550 unsigned int i, len;
552 if (!wcsicmp( request->connect->hostname, request->connect->servername )) path = request->path;
553 else if (!(path = build_absolute_request_path( request, NULL ))) return NULL;
555 len = lstrlenW( request->verb ) + 1 /* ' ' */;
556 len += lstrlenW( path ) + 1 /* ' ' */;
557 len += lstrlenW( request->version );
559 for (i = 0; i < request->num_headers; i++)
561 if (request->headers[i].is_request)
562 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
564 len += 4; /* '\r\n\r\n' */
566 if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
568 lstrcpyW( ret, request->verb );
569 lstrcatW( ret, L" " );
570 lstrcatW( ret, path );
571 lstrcatW( ret, L" " );
572 lstrcatW( ret, request->version );
574 for (i = 0; i < request->num_headers; i++)
576 if (request->headers[i].is_request)
578 lstrcatW( ret, L"\r\n" );
579 lstrcatW( ret, request->headers[i].field );
580 lstrcatW( ret, L": " );
581 lstrcatW( ret, request->headers[i].value );
584 lstrcatW( ret, L"\r\n\r\n" );
587 if (path != request->path) free( path );
588 return ret;
591 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
593 static DWORD query_headers( struct request *request, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
594 DWORD *index )
596 struct header *header = NULL;
597 BOOL request_only;
598 int requested_index, header_index = -1;
599 DWORD attr, len, ret = ERROR_WINHTTP_HEADER_NOT_FOUND;
601 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
602 requested_index = index ? *index : 0;
604 attr = level & ~QUERY_MODIFIER_MASK;
605 switch (attr)
607 case WINHTTP_QUERY_CUSTOM:
609 header_index = get_header_index( request, name, requested_index, request_only );
610 break;
612 case WINHTTP_QUERY_RAW_HEADERS:
614 WCHAR *headers, *p, *q;
616 if (request_only)
617 headers = build_request_string( request );
618 else
619 headers = request->raw_headers;
621 if (!(p = headers)) return ERROR_OUTOFMEMORY;
622 for (len = 0; *p; p++) if (*p != '\r') len++;
624 if (!buffer || len * sizeof(WCHAR) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
625 else
627 for (p = headers, q = buffer; *p; p++, q++)
629 if (*p != '\r') *q = *p;
630 else
632 *q = 0;
633 p++; /* skip '\n' */
636 TRACE("returning data: %s\n", debugstr_wn(buffer, len));
637 if (len) len--;
638 ret = ERROR_SUCCESS;
640 *buflen = len * sizeof(WCHAR);
641 if (request_only) free( headers );
642 return ret;
644 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
646 WCHAR *headers;
648 if (request_only)
649 headers = build_request_string( request );
650 else
651 headers = request->raw_headers;
653 if (!headers) return ERROR_OUTOFMEMORY;
654 len = lstrlenW( headers ) * sizeof(WCHAR);
655 if (!buffer || len + sizeof(WCHAR) > *buflen)
657 len += sizeof(WCHAR);
658 ret = ERROR_INSUFFICIENT_BUFFER;
660 else
662 memcpy( buffer, headers, len + sizeof(WCHAR) );
663 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
664 ret = ERROR_SUCCESS;
666 *buflen = len;
667 if (request_only) free( headers );
668 return ret;
670 case WINHTTP_QUERY_VERSION:
671 len = lstrlenW( request->version ) * sizeof(WCHAR);
672 if (!buffer || len + sizeof(WCHAR) > *buflen)
674 len += sizeof(WCHAR);
675 ret = ERROR_INSUFFICIENT_BUFFER;
677 else
679 lstrcpyW( buffer, request->version );
680 TRACE("returning string: %s\n", debugstr_w(buffer));
681 ret = ERROR_SUCCESS;
683 *buflen = len;
684 return ret;
686 case WINHTTP_QUERY_STATUS_TEXT:
687 len = lstrlenW( request->status_text ) * sizeof(WCHAR);
688 if (!buffer || len + sizeof(WCHAR) > *buflen)
690 len += sizeof(WCHAR);
691 ret = ERROR_INSUFFICIENT_BUFFER;
693 else
695 lstrcpyW( buffer, request->status_text );
696 TRACE("returning string: %s\n", debugstr_w(buffer));
697 ret = ERROR_SUCCESS;
699 *buflen = len;
700 return ret;
702 case WINHTTP_QUERY_REQUEST_METHOD:
703 len = lstrlenW( request->verb ) * sizeof(WCHAR);
704 if (!buffer || len + sizeof(WCHAR) > *buflen)
706 len += sizeof(WCHAR);
707 ret = ERROR_INSUFFICIENT_BUFFER;
709 else
711 lstrcpyW( buffer, request->verb );
712 TRACE("returning string: %s\n", debugstr_w(buffer));
713 ret = ERROR_SUCCESS;
715 *buflen = len;
716 return ret;
718 default:
719 if (attr >= ARRAY_SIZE(attribute_table)) return ERROR_INVALID_PARAMETER;
720 if (!attribute_table[attr])
722 FIXME( "attribute %lu not implemented\n", attr );
723 return ERROR_WINHTTP_HEADER_NOT_FOUND;
725 TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
726 header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
727 break;
730 if (header_index >= 0)
732 header = &request->headers[header_index];
734 if (!header || (request_only && !header->is_request)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
735 if (level & WINHTTP_QUERY_FLAG_NUMBER)
737 if (!buffer || sizeof(DWORD) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
738 else
740 DWORD *number = buffer;
741 *number = wcstoul( header->value, NULL, 10 );
742 TRACE("returning number: %lu\n", *number);
743 ret = ERROR_SUCCESS;
745 *buflen = sizeof(DWORD);
747 else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
749 SYSTEMTIME *st = buffer;
750 if (!buffer || sizeof(SYSTEMTIME) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
751 else if (WinHttpTimeToSystemTime( header->value, st ))
753 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
754 st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
755 st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
756 ret = ERROR_SUCCESS;
758 *buflen = sizeof(SYSTEMTIME);
760 else if (header->value)
762 len = lstrlenW( header->value ) * sizeof(WCHAR);
763 if (!buffer || len + sizeof(WCHAR) > *buflen)
765 len += sizeof(WCHAR);
766 ret = ERROR_INSUFFICIENT_BUFFER;
768 else
770 lstrcpyW( buffer, header->value );
771 TRACE("returning string: %s\n", debugstr_w(buffer));
772 ret = ERROR_SUCCESS;
774 *buflen = len;
776 if (!ret && index) *index += 1;
777 return ret;
780 /***********************************************************************
781 * WinHttpQueryHeaders (winhttp.@)
783 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
784 DWORD *index )
786 DWORD ret;
787 struct request *request;
789 TRACE( "%p, %#lx, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index );
791 if (!(request = (struct request *)grab_object( hrequest )))
793 SetLastError( ERROR_INVALID_HANDLE );
794 return FALSE;
796 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
798 release_object( &request->hdr );
799 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
800 return FALSE;
802 if (request->state < REQUEST_RESPONSE_STATE_RESPONSE_RECEIVED && !(level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS)
803 && ((level & ~QUERY_MODIFIER_MASK) != WINHTTP_QUERY_REQUEST_METHOD))
805 release_object( &request->hdr );
806 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_STATE );
807 return FALSE;
810 ret = query_headers( request, level, name, buffer, buflen, index );
812 release_object( &request->hdr );
813 SetLastError( ret );
814 return !ret;
817 static const struct
819 const WCHAR *str;
820 unsigned int len;
821 DWORD scheme;
823 auth_schemes[] =
825 { L"Basic", ARRAY_SIZE(L"Basic") - 1, WINHTTP_AUTH_SCHEME_BASIC },
826 { L"NTLM", ARRAY_SIZE(L"NTLM") - 1, WINHTTP_AUTH_SCHEME_NTLM },
827 { L"Passport", ARRAY_SIZE(L"Passport") - 1, WINHTTP_AUTH_SCHEME_PASSPORT },
828 { L"Digest", ARRAY_SIZE(L"Digest") - 1, WINHTTP_AUTH_SCHEME_DIGEST },
829 { L"Negotiate", ARRAY_SIZE(L"Negotiate") - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
832 static enum auth_scheme scheme_from_flag( DWORD flag )
834 int i;
836 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++) if (flag == auth_schemes[i].scheme) return i;
837 return SCHEME_INVALID;
840 static DWORD auth_scheme_from_header( const WCHAR *header )
842 unsigned int i;
844 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
846 if (!wcsnicmp( header, auth_schemes[i].str, auth_schemes[i].len ) &&
847 (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
849 return 0;
852 static DWORD query_auth_schemes( struct request *request, DWORD level, DWORD *supported, DWORD *first )
854 DWORD ret, index = 0, supported_schemes = 0, first_scheme = 0;
856 for (;;)
858 WCHAR *buffer;
859 DWORD size, scheme;
861 size = 0;
862 ret = query_headers( request, level, NULL, NULL, &size, &index );
863 if (ret != ERROR_INSUFFICIENT_BUFFER)
865 if (index) ret = ERROR_SUCCESS;
866 break;
869 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
870 if ((ret = query_headers( request, level, NULL, buffer, &size, &index )))
872 free( buffer );
873 return ret;
875 scheme = auth_scheme_from_header( buffer );
876 free( buffer );
877 if (!scheme) continue;
879 if (!first_scheme) first_scheme = scheme;
880 supported_schemes |= scheme;
883 if (!ret)
885 *supported = supported_schemes;
886 *first = first_scheme;
888 return ret;
891 /***********************************************************************
892 * WinHttpQueryAuthSchemes (winhttp.@)
894 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
896 DWORD ret;
897 struct request *request;
899 TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
901 if (!(request = (struct request *)grab_object( hrequest )))
903 SetLastError( ERROR_INVALID_HANDLE );
904 return FALSE;
906 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
908 release_object( &request->hdr );
909 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
910 return FALSE;
912 if (!supported || !first || !target)
914 release_object( &request->hdr );
915 SetLastError( ERROR_INVALID_PARAMETER );
916 return FALSE;
920 if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first )))
922 *target = WINHTTP_AUTH_TARGET_SERVER;
924 else if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first )))
926 *target = WINHTTP_AUTH_TARGET_PROXY;
928 else ret = ERROR_INVALID_OPERATION;
930 release_object( &request->hdr );
931 SetLastError( ret );
932 return !ret;
935 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
937 UINT n = 0, x;
938 static const char base64enc[] =
939 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
941 while (len > 0)
943 /* first 6 bits, all from bin[0] */
944 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
945 x = (bin[0] & 3) << 4;
947 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
948 if (len == 1)
950 base64[n++] = base64enc[x];
951 base64[n++] = '=';
952 base64[n++] = '=';
953 break;
955 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
956 x = (bin[1] & 0x0f) << 2;
958 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
959 if (len == 2)
961 base64[n++] = base64enc[x];
962 base64[n++] = '=';
963 break;
965 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
967 /* last 6 bits, all from bin [2] */
968 base64[n++] = base64enc[bin[2] & 0x3f];
969 bin += 3;
970 len -= 3;
972 base64[n] = 0;
973 return n;
976 static inline char decode_char( WCHAR c )
978 if (c >= 'A' && c <= 'Z') return c - 'A';
979 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
980 if (c >= '0' && c <= '9') return c - '0' + 52;
981 if (c == '+') return 62;
982 if (c == '/') return 63;
983 return 64;
986 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
988 unsigned int i = 0;
989 char c0, c1, c2, c3;
990 const WCHAR *p = base64;
992 while (len > 4)
994 if ((c0 = decode_char( p[0] )) > 63) return 0;
995 if ((c1 = decode_char( p[1] )) > 63) return 0;
996 if ((c2 = decode_char( p[2] )) > 63) return 0;
997 if ((c3 = decode_char( p[3] )) > 63) return 0;
999 if (buf)
1001 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1002 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1003 buf[i + 2] = (c2 << 6) | c3;
1005 len -= 4;
1006 i += 3;
1007 p += 4;
1009 if (p[2] == '=')
1011 if ((c0 = decode_char( p[0] )) > 63) return 0;
1012 if ((c1 = decode_char( p[1] )) > 63) return 0;
1014 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
1015 i++;
1017 else if (p[3] == '=')
1019 if ((c0 = decode_char( p[0] )) > 63) return 0;
1020 if ((c1 = decode_char( p[1] )) > 63) return 0;
1021 if ((c2 = decode_char( p[2] )) > 63) return 0;
1023 if (buf)
1025 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1026 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1028 i += 2;
1030 else
1032 if ((c0 = decode_char( p[0] )) > 63) return 0;
1033 if ((c1 = decode_char( p[1] )) > 63) return 0;
1034 if ((c2 = decode_char( p[2] )) > 63) return 0;
1035 if ((c3 = decode_char( p[3] )) > 63) return 0;
1037 if (buf)
1039 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1040 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1041 buf[i + 2] = (c2 << 6) | c3;
1043 i += 3;
1045 return i;
1048 static struct authinfo *alloc_authinfo(void)
1050 struct authinfo *ret;
1052 if (!(ret = malloc( sizeof(*ret) ))) return NULL;
1054 SecInvalidateHandle( &ret->cred );
1055 SecInvalidateHandle( &ret->ctx );
1056 memset( &ret->exp, 0, sizeof(ret->exp) );
1057 ret->scheme = 0;
1058 ret->attr = 0;
1059 ret->max_token = 0;
1060 ret->data = NULL;
1061 ret->data_len = 0;
1062 ret->finished = FALSE;
1063 return ret;
1066 void destroy_authinfo( struct authinfo *authinfo )
1068 if (!authinfo) return;
1070 if (SecIsValidHandle( &authinfo->ctx ))
1071 DeleteSecurityContext( &authinfo->ctx );
1072 if (SecIsValidHandle( &authinfo->cred ))
1073 FreeCredentialsHandle( &authinfo->cred );
1075 free( authinfo->data );
1076 free( authinfo );
1079 static BOOL get_authvalue( struct request *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
1081 DWORD size, index = 0;
1082 for (;;)
1084 size = len;
1085 if (query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
1086 if (auth_scheme_from_header( buffer ) == scheme) break;
1088 return TRUE;
1091 static BOOL do_authorization( struct request *request, DWORD target, DWORD scheme_flag )
1093 struct authinfo *authinfo, **auth_ptr;
1094 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1095 const WCHAR *auth_target, *username, *password;
1096 WCHAR auth_value[2048], *auth_reply;
1097 DWORD len = sizeof(auth_value), len_scheme, flags;
1098 BOOL ret, has_auth_value;
1100 if (scheme == SCHEME_INVALID) return FALSE;
1102 switch (target)
1104 case WINHTTP_AUTH_TARGET_SERVER:
1105 has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len );
1106 auth_ptr = &request->authinfo;
1107 auth_target = L"Authorization";
1108 if (request->creds[TARGET_SERVER][scheme].username)
1110 if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE;
1111 username = request->creds[TARGET_SERVER][scheme].username;
1112 password = request->creds[TARGET_SERVER][scheme].password;
1114 else
1116 if (!has_auth_value) return FALSE;
1117 username = request->connect->username;
1118 password = request->connect->password;
1120 break;
1122 case WINHTTP_AUTH_TARGET_PROXY:
1123 if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1124 return FALSE;
1125 auth_ptr = &request->proxy_authinfo;
1126 auth_target = L"Proxy-Authorization";
1127 if (request->creds[TARGET_PROXY][scheme].username)
1129 username = request->creds[TARGET_PROXY][scheme].username;
1130 password = request->creds[TARGET_PROXY][scheme].password;
1132 else
1134 username = request->connect->session->proxy_username;
1135 password = request->connect->session->proxy_password;
1137 break;
1139 default:
1140 WARN( "unknown target %#lx\n", target );
1141 return FALSE;
1143 authinfo = *auth_ptr;
1145 switch (scheme)
1147 case SCHEME_BASIC:
1149 int userlen, passlen;
1151 if (!username || !password) return FALSE;
1152 if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1154 userlen = WideCharToMultiByte( CP_UTF8, 0, username, lstrlenW( username ), NULL, 0, NULL, NULL );
1155 passlen = WideCharToMultiByte( CP_UTF8, 0, password, lstrlenW( password ), NULL, 0, NULL, NULL );
1157 authinfo->data_len = userlen + 1 + passlen;
1158 if (!(authinfo->data = malloc( authinfo->data_len ))) return FALSE;
1160 WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1161 authinfo->data[userlen] = ':';
1162 WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1164 authinfo->scheme = SCHEME_BASIC;
1165 authinfo->finished = TRUE;
1166 break;
1168 case SCHEME_NTLM:
1169 case SCHEME_NEGOTIATE:
1171 SECURITY_STATUS status;
1172 SecBufferDesc out_desc, in_desc;
1173 SecBuffer out, in;
1174 ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1175 const WCHAR *p;
1176 BOOL first = FALSE;
1178 if (!authinfo)
1180 TimeStamp exp;
1181 SEC_WINNT_AUTH_IDENTITY_W id;
1182 WCHAR *domain, *user;
1184 if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1186 first = TRUE;
1187 domain = (WCHAR *)username;
1188 user = wcschr( username, '\\' );
1190 if (user) user++;
1191 else
1193 user = (WCHAR *)username;
1194 domain = NULL;
1196 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1197 id.User = user;
1198 id.UserLength = lstrlenW( user );
1199 id.Domain = domain;
1200 id.DomainLength = domain ? user - domain - 1 : 0;
1201 id.Password = (WCHAR *)password;
1202 id.PasswordLength = lstrlenW( password );
1204 status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1205 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1206 &authinfo->cred, &exp );
1207 if (status == SEC_E_OK)
1209 PSecPkgInfoW info;
1210 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1211 if (status == SEC_E_OK)
1213 authinfo->max_token = info->cbMaxToken;
1214 FreeContextBuffer( info );
1217 if (status != SEC_E_OK)
1219 WARN( "AcquireCredentialsHandleW for scheme %s failed with error %#lx\n",
1220 debugstr_w(auth_schemes[scheme].str), status );
1221 free( authinfo );
1222 return FALSE;
1224 authinfo->scheme = scheme;
1226 else if (authinfo->finished) return FALSE;
1228 if ((lstrlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1229 wcsnicmp( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1231 ERR("authentication scheme changed from %s to %s\n",
1232 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1233 destroy_authinfo( authinfo );
1234 *auth_ptr = NULL;
1235 return FALSE;
1237 in.BufferType = SECBUFFER_TOKEN;
1238 in.cbBuffer = 0;
1239 in.pvBuffer = NULL;
1241 in_desc.ulVersion = 0;
1242 in_desc.cBuffers = 1;
1243 in_desc.pBuffers = &in;
1245 p = auth_value + auth_schemes[scheme].len;
1246 if (*p == ' ')
1248 int len = lstrlenW( ++p );
1249 in.cbBuffer = decode_base64( p, len, NULL );
1250 if (!(in.pvBuffer = malloc( in.cbBuffer ))) {
1251 destroy_authinfo( authinfo );
1252 *auth_ptr = NULL;
1253 return FALSE;
1255 decode_base64( p, len, in.pvBuffer );
1257 out.BufferType = SECBUFFER_TOKEN;
1258 out.cbBuffer = authinfo->max_token;
1259 if (!(out.pvBuffer = malloc( authinfo->max_token )))
1261 free( in.pvBuffer );
1262 destroy_authinfo( authinfo );
1263 *auth_ptr = NULL;
1264 return FALSE;
1266 out_desc.ulVersion = 0;
1267 out_desc.cBuffers = 1;
1268 out_desc.pBuffers = &out;
1270 status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1271 first ? request->connect->servername : NULL, flags, 0,
1272 SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1273 &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1274 free( in.pvBuffer );
1275 if (status == SEC_E_OK)
1277 free( authinfo->data );
1278 authinfo->data = out.pvBuffer;
1279 authinfo->data_len = out.cbBuffer;
1280 authinfo->finished = TRUE;
1281 TRACE("sending last auth packet\n");
1283 else if (status == SEC_I_CONTINUE_NEEDED)
1285 free( authinfo->data );
1286 authinfo->data = out.pvBuffer;
1287 authinfo->data_len = out.cbBuffer;
1288 TRACE("sending next auth packet\n");
1290 else
1292 ERR( "InitializeSecurityContextW failed with error %#lx\n", status );
1293 free( out.pvBuffer );
1294 destroy_authinfo( authinfo );
1295 *auth_ptr = NULL;
1296 return FALSE;
1298 break;
1300 default:
1301 ERR("invalid scheme %u\n", scheme);
1302 return FALSE;
1304 *auth_ptr = authinfo;
1306 len_scheme = auth_schemes[authinfo->scheme].len;
1307 len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1308 if (!(auth_reply = malloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1310 memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1311 auth_reply[len_scheme] = ' ';
1312 encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1314 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1315 ret = !process_header( request, auth_target, auth_reply, flags, TRUE );
1316 free( auth_reply );
1317 return ret;
1320 static WCHAR *build_proxy_connect_string( struct request *request )
1322 WCHAR *ret, *host;
1323 unsigned int i;
1324 int len = lstrlenW( request->connect->hostname ) + 7;
1326 if (!(host = malloc( len * sizeof(WCHAR) ))) return NULL;
1327 len = swprintf( host, len, L"%s:%u", request->connect->hostname, request->connect->hostport );
1329 len += ARRAY_SIZE(L"CONNECT");
1330 len += ARRAY_SIZE(L"HTTP/1.1");
1332 for (i = 0; i < request->num_headers; i++)
1334 if (request->headers[i].is_request)
1335 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
1337 len += 4; /* '\r\n\r\n' */
1339 if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
1341 lstrcpyW( ret, L"CONNECT" );
1342 lstrcatW( ret, L" " );
1343 lstrcatW( ret, host );
1344 lstrcatW( ret, L" " );
1345 lstrcatW( ret, L"HTTP/1.1" );
1347 for (i = 0; i < request->num_headers; i++)
1349 if (request->headers[i].is_request)
1351 lstrcatW( ret, L"\r\n" );
1352 lstrcatW( ret, request->headers[i].field );
1353 lstrcatW( ret, L": " );
1354 lstrcatW( ret, request->headers[i].value );
1357 lstrcatW( ret, L"\r\n\r\n" );
1360 free( host );
1361 return ret;
1364 static DWORD read_reply( struct request *request );
1366 static DWORD secure_proxy_connect( struct request *request )
1368 WCHAR *str;
1369 char *strA;
1370 int len, bytes_sent;
1371 DWORD ret;
1373 if (!(str = build_proxy_connect_string( request ))) return ERROR_OUTOFMEMORY;
1374 strA = strdupWA( str );
1375 free( str );
1376 if (!strA) return ERROR_OUTOFMEMORY;
1378 len = strlen( strA );
1379 ret = netconn_send( request->netconn, strA, len, &bytes_sent, NULL );
1380 free( strA );
1381 if (!ret) ret = read_reply( request );
1383 return ret;
1386 static WCHAR *addr_to_str( struct sockaddr_storage *addr )
1388 char buf[INET6_ADDRSTRLEN];
1389 void *src;
1391 switch (addr->ss_family)
1393 case AF_INET:
1394 src = &((struct sockaddr_in *)addr)->sin_addr;
1395 break;
1396 case AF_INET6:
1397 src = &((struct sockaddr_in6 *)addr)->sin6_addr;
1398 break;
1399 default:
1400 WARN("unsupported address family %d\n", addr->ss_family);
1401 return NULL;
1403 if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL;
1404 return strdupAW( buf );
1407 static CRITICAL_SECTION connection_pool_cs;
1408 static CRITICAL_SECTION_DEBUG connection_pool_debug =
1410 0, 0, &connection_pool_cs,
1411 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
1412 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
1414 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
1416 static struct list connection_pool = LIST_INIT( connection_pool );
1418 void release_host( struct hostdata *host )
1420 LONG ref;
1422 EnterCriticalSection( &connection_pool_cs );
1423 if (!(ref = --host->ref)) list_remove( &host->entry );
1424 LeaveCriticalSection( &connection_pool_cs );
1425 if (ref) return;
1427 assert( list_empty( &host->connections ) );
1428 free( host->hostname );
1429 free( host );
1432 static BOOL connection_collector_running;
1434 static void CALLBACK connection_collector( TP_CALLBACK_INSTANCE *instance, void *ctx )
1436 unsigned int remaining_connections;
1437 struct netconn *netconn, *next_netconn;
1438 struct hostdata *host, *next_host;
1439 ULONGLONG now;
1443 /* FIXME: Use more sophisticated method */
1444 Sleep(5000);
1445 remaining_connections = 0;
1446 now = GetTickCount64();
1448 EnterCriticalSection(&connection_pool_cs);
1450 LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, struct hostdata, entry)
1452 LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, struct netconn, entry)
1454 if (netconn->keep_until < now)
1456 TRACE("freeing %p\n", netconn);
1457 list_remove(&netconn->entry);
1458 netconn_release(netconn);
1460 else remaining_connections++;
1464 if (!remaining_connections) connection_collector_running = FALSE;
1466 LeaveCriticalSection(&connection_pool_cs);
1467 } while(remaining_connections);
1469 FreeLibraryWhenCallbackReturns( instance, winhttp_instance );
1472 static void cache_connection( struct netconn *netconn )
1474 TRACE( "caching connection %p\n", netconn );
1476 EnterCriticalSection( &connection_pool_cs );
1478 netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT;
1479 list_add_head( &netconn->host->connections, &netconn->entry );
1481 if (!connection_collector_running)
1483 HMODULE module;
1485 GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)winhttp_instance, &module );
1487 if (TrySubmitThreadpoolCallback( connection_collector, NULL, NULL )) connection_collector_running = TRUE;
1488 else FreeLibrary( winhttp_instance );
1491 LeaveCriticalSection( &connection_pool_cs );
1494 static DWORD map_secure_protocols( DWORD mask )
1496 DWORD ret = 0;
1497 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT;
1498 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT;
1499 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT;
1500 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT;
1501 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT;
1502 return ret;
1505 static DWORD ensure_cred_handle( struct request *request )
1507 SECURITY_STATUS status = SEC_E_OK;
1509 if (request->cred_handle_initialized) return ERROR_SUCCESS;
1511 if (!request->cred_handle_initialized)
1513 SCHANNEL_CRED cred;
1514 memset( &cred, 0, sizeof(cred) );
1515 cred.dwVersion = SCHANNEL_CRED_VERSION;
1516 cred.grbitEnabledProtocols = map_secure_protocols( request->connect->session->secure_protocols );
1517 if (request->client_cert)
1519 cred.paCred = &request->client_cert;
1520 cred.cCreds = 1;
1522 status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
1523 &cred, NULL, NULL, &request->cred_handle, NULL );
1524 if (status == SEC_E_OK)
1525 request->cred_handle_initialized = TRUE;
1528 if (status != SEC_E_OK)
1530 WARN( "AcquireCredentialsHandleW failed: %#lx\n", status );
1531 return status;
1533 return ERROR_SUCCESS;
1536 static DWORD open_connection( struct request *request )
1538 BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
1539 struct hostdata *host = NULL, *iter;
1540 struct netconn *netconn = NULL;
1541 struct connect *connect;
1542 WCHAR *addressW = NULL;
1543 INTERNET_PORT port;
1544 DWORD ret, len;
1546 if (request->netconn) goto done;
1548 connect = request->connect;
1549 port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1551 EnterCriticalSection( &connection_pool_cs );
1553 LIST_FOR_EACH_ENTRY( iter, &connection_pool, struct hostdata, entry )
1555 if (iter->port == port && !wcscmp( connect->servername, iter->hostname ) && !is_secure == !iter->secure)
1557 host = iter;
1558 host->ref++;
1559 break;
1563 if (!host)
1565 if ((host = malloc( sizeof(*host) )))
1567 host->ref = 1;
1568 host->secure = is_secure;
1569 host->port = port;
1570 list_init( &host->connections );
1571 if ((host->hostname = wcsdup( connect->servername )))
1573 list_add_head( &connection_pool, &host->entry );
1575 else
1577 free( host );
1578 host = NULL;
1583 LeaveCriticalSection( &connection_pool_cs );
1585 if (!host) return ERROR_OUTOFMEMORY;
1587 for (;;)
1589 EnterCriticalSection( &connection_pool_cs );
1590 if (!list_empty( &host->connections ))
1592 netconn = LIST_ENTRY( list_head( &host->connections ), struct netconn, entry );
1593 list_remove( &netconn->entry );
1595 LeaveCriticalSection( &connection_pool_cs );
1596 if (!netconn) break;
1598 if (netconn_is_alive( netconn )) break;
1599 TRACE("connection %p no longer alive, closing\n", netconn);
1600 netconn_release( netconn );
1601 netconn = NULL;
1604 if (!connect->resolved && netconn)
1606 connect->sockaddr = netconn->sockaddr;
1607 connect->resolved = TRUE;
1610 if (!connect->resolved)
1612 len = lstrlenW( host->hostname ) + 1;
1613 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
1615 if ((ret = netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout )))
1617 release_host( host );
1618 return ret;
1620 connect->resolved = TRUE;
1622 if (!(addressW = addr_to_str( &connect->sockaddr )))
1624 release_host( host );
1625 return ERROR_OUTOFMEMORY;
1627 len = lstrlenW( addressW ) + 1;
1628 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1631 if (!netconn)
1633 if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
1635 release_host( host );
1636 return ERROR_OUTOFMEMORY;
1639 TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1641 len = lstrlenW( addressW ) + 1;
1642 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, len );
1644 if ((ret = netconn_create( host, &connect->sockaddr, request->connect_timeout, &netconn )))
1646 free( addressW );
1647 release_host( host );
1648 return ret;
1650 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1651 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1653 request->netconn = netconn;
1655 if (is_secure)
1657 if (connect->session->proxy_server && wcsicmp( connect->hostname, connect->servername ))
1659 if ((ret = secure_proxy_connect( request )))
1661 request->netconn = NULL;
1662 free( addressW );
1663 netconn_release( netconn );
1664 return ret;
1668 CertFreeCertificateContext( request->server_cert );
1669 request->server_cert = NULL;
1671 if ((ret = ensure_cred_handle( request )) ||
1672 (ret = netconn_secure_connect( netconn, connect->hostname, request->security_flags,
1673 &request->cred_handle, request->check_revocation )))
1675 request->netconn = NULL;
1676 free( addressW );
1677 netconn_release( netconn );
1678 return ret;
1682 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, lstrlenW(addressW) + 1 );
1684 else
1686 TRACE("using connection %p\n", netconn);
1688 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1689 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1690 request->netconn = netconn;
1693 if (netconn->secure && !(request->server_cert = netconn_get_certificate( netconn )))
1695 free( addressW );
1696 netconn_release( netconn );
1697 return ERROR_WINHTTP_SECURE_FAILURE;
1700 done:
1701 request->read_pos = request->read_size = 0;
1702 request->read_chunked = FALSE;
1703 request->read_chunked_size = ~0u;
1704 request->read_chunked_eof = FALSE;
1705 free( addressW );
1706 return ERROR_SUCCESS;
1709 void close_connection( struct request *request )
1711 if (!request->netconn) return;
1713 netconn_release( request->netconn );
1714 request->netconn = NULL;
1717 static DWORD add_host_header( struct request *request, DWORD modifier )
1719 DWORD ret, len;
1720 WCHAR *host;
1721 struct connect *connect = request->connect;
1722 INTERNET_PORT port;
1724 port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1726 if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1728 return process_header( request, L"Host", connect->hostname, modifier, TRUE );
1730 len = lstrlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1731 if (!(host = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
1732 swprintf( host, len, L"%s:%u", connect->hostname, port );
1733 ret = process_header( request, L"Host", host, modifier, TRUE );
1734 free( host );
1735 return ret;
1738 static void clear_response_headers( struct request *request )
1740 unsigned int i;
1742 for (i = 0; i < request->num_headers; i++)
1744 if (!request->headers[i].field) continue;
1745 if (!request->headers[i].value) continue;
1746 if (request->headers[i].is_request) continue;
1747 delete_header( request, i );
1748 i--;
1752 /* remove some amount of data from the read buffer */
1753 static void remove_data( struct request *request, int count )
1755 if (!(request->read_size -= count)) request->read_pos = 0;
1756 else request->read_pos += count;
1759 /* read some more data into the read buffer */
1760 static DWORD read_more_data( struct request *request, int maxlen, BOOL notify )
1762 int len;
1763 DWORD ret;
1765 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1767 if (request->read_size && request->read_pos)
1769 /* move existing data to the start of the buffer */
1770 memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1771 request->read_pos = 0;
1773 if (maxlen == -1) maxlen = sizeof(request->read_buf);
1775 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1777 ret = netconn_recv( request->netconn, request->read_buf + request->read_size,
1778 maxlen - request->read_size, 0, &len );
1780 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1781 request->read_reply_len += len;
1783 request->read_size += len;
1784 return ret;
1787 /* discard data contents until we reach end of line */
1788 static DWORD discard_eol( struct request *request, BOOL notify )
1790 DWORD ret;
1793 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1794 if (eol)
1796 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1797 break;
1799 request->read_pos = request->read_size = 0; /* discard everything */
1800 if ((ret = read_more_data( request, -1, notify ))) return ret;
1801 } while (request->read_size);
1802 return ERROR_SUCCESS;
1805 static void update_value_from_digit( DWORD *value, char ch )
1807 if (ch >= '0' && ch <= '9') *value = *value * 16 + ch - '0';
1808 else if (ch >= 'a' && ch <= 'f') *value = *value * 16 + ch - 'a' + 10;
1809 else if (ch >= 'A' && ch <= 'F') *value = *value * 16 + ch - 'A' + 10;
1812 /* read chunk size if already in the read buffer */
1813 static BOOL get_chunk_size( struct request *request )
1815 DWORD chunk_size;
1816 char *p, *eol;
1818 if (request->read_chunked_size != ~0ul) return TRUE;
1820 eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1821 if (!eol) return FALSE;
1823 chunk_size = 0;
1824 for (p = request->read_buf + request->read_pos; p != eol; ++p)
1826 if (*p == ';' || *p == '\r') break;
1827 update_value_from_digit( &chunk_size, *p );
1830 request->read_chunked_size = chunk_size;
1831 if (!chunk_size) request->read_chunked_eof = TRUE;
1833 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1834 return TRUE;
1837 /* read the size of the next chunk */
1838 static DWORD start_next_chunk( struct request *request, BOOL notify )
1840 DWORD ret, chunk_size = 0;
1842 assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
1844 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1846 /* read terminator for the previous chunk */
1847 if (!request->read_chunked_size && (ret = discard_eol( request, notify ))) return ret;
1849 for (;;)
1851 while (request->read_size)
1853 char ch = request->read_buf[request->read_pos];
1855 if (ch == ';' || ch == '\r' || ch == '\n')
1857 TRACE( "reading %lu byte chunk\n", chunk_size );
1859 if (request->content_length == ~0u) request->content_length = chunk_size;
1860 else request->content_length += chunk_size;
1862 request->read_chunked_size = chunk_size;
1863 if (!chunk_size) request->read_chunked_eof = TRUE;
1865 return discard_eol( request, notify );
1867 update_value_from_digit( &chunk_size, ch );
1868 remove_data( request, 1 );
1870 if ((ret = read_more_data( request, -1, notify ))) return ret;
1871 if (!request->read_size)
1873 request->content_length = request->content_read = 0;
1874 request->read_chunked_size = 0;
1875 return ERROR_SUCCESS;
1880 static DWORD refill_buffer( struct request *request, BOOL notify )
1882 int len = sizeof(request->read_buf);
1883 DWORD ret;
1885 if (request->read_chunked)
1887 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1888 if (request->read_chunked_size == ~0u || !request->read_chunked_size)
1890 if ((ret = start_next_chunk( request, notify ))) return ret;
1892 len = min( len, request->read_chunked_size );
1894 else if (request->content_length != ~0u)
1896 len = min( len, request->content_length - request->content_read );
1899 if (len <= request->read_size) return ERROR_SUCCESS;
1900 if ((ret = read_more_data( request, len, notify ))) return ret;
1901 if (!request->read_size) request->content_length = request->content_read = 0;
1902 return ERROR_SUCCESS;
1905 static void finished_reading( struct request *request )
1907 BOOL close = FALSE, notify;
1908 WCHAR connection[20];
1909 DWORD size = sizeof(connection);
1911 if (!request->netconn) return;
1913 if (request->netconn->socket == -1) close = TRUE;
1914 else if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
1915 else if (!query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
1916 !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
1918 if (!wcsicmp( connection, L"close" )) close = TRUE;
1920 else if (!wcscmp( request->version, L"HTTP/1.0" )) close = TRUE;
1922 if (close)
1924 size = sizeof(connection);
1925 notify = (!query_headers( request, WINHTTP_QUERY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
1926 NULL, connection, &size, NULL )
1927 || !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
1928 NULL, connection, &size, NULL ))
1929 && !wcsicmp( connection, L"close" );
1931 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1932 netconn_release( request->netconn );
1933 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1935 else
1936 cache_connection( request->netconn );
1937 request->netconn = NULL;
1940 /* return the size of data available to be read immediately */
1941 static DWORD get_available_data( struct request *request )
1943 if (request->read_chunked)
1945 if (!get_chunk_size( request )) return 0;
1946 return min( request->read_chunked_size, request->read_size );
1948 return request->read_size;
1951 /* check if we have reached the end of the data to read */
1952 static BOOL end_of_read_data( struct request *request )
1954 if (!request->content_length) return TRUE;
1955 if (request->read_chunked) return request->read_chunked_eof;
1956 if (request->content_length == ~0u) return FALSE;
1957 return (request->content_length == request->content_read);
1960 static DWORD read_data( struct request *request, void *buffer, DWORD size, DWORD *read, BOOL async )
1962 int count, bytes_read = 0;
1963 DWORD ret = ERROR_SUCCESS;
1965 if (request->read_chunked && request->read_chunked_size == ~0u
1966 && (ret = start_next_chunk( request, async ))) goto done;
1968 if (end_of_read_data( request )) goto done;
1970 while (size)
1972 if (!(count = get_available_data( request )))
1974 if ((ret = refill_buffer( request, async ))) goto done;
1975 if (!(count = get_available_data( request ))) goto done;
1977 count = min( count, size );
1978 memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
1979 remove_data( request, count );
1980 if (request->read_chunked) request->read_chunked_size -= count;
1981 size -= count;
1982 bytes_read += count;
1983 request->content_read += count;
1984 if (end_of_read_data( request )) goto done;
1986 if (request->read_chunked && !request->read_chunked_size) ret = refill_buffer( request, async );
1988 done:
1989 TRACE( "retrieved %u bytes (%lu/%lu)\n", bytes_read, request->content_read, request->content_length );
1990 if (end_of_read_data( request )) finished_reading( request );
1991 if (async)
1993 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
1994 else
1996 WINHTTP_ASYNC_RESULT result;
1997 result.dwResult = API_READ_DATA;
1998 result.dwError = ret;
1999 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2003 if (!ret && read) *read = bytes_read;
2004 return ret;
2007 /* read any content returned by the server so that the connection can be reused */
2008 static void drain_content( struct request *request )
2010 DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read;
2011 char buffer[2048];
2013 refill_buffer( request, FALSE );
2014 for (;;)
2016 if (request->read_chunked) size = sizeof(buffer);
2017 else size = min( sizeof(buffer), bytes_left - bytes_total );
2019 if (read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return;
2020 bytes_total += bytes_read;
2024 enum escape_flags
2026 ESCAPE_FLAG_NON_PRINTABLE = 0x01,
2027 ESCAPE_FLAG_SPACE = 0x02,
2028 ESCAPE_FLAG_PERCENT = 0x04,
2029 ESCAPE_FLAG_UNSAFE = 0x08,
2030 ESCAPE_FLAG_DEL = 0x10,
2031 ESCAPE_FLAG_8BIT = 0x20,
2032 ESCAPE_FLAG_STRIP_CRLF = 0x40,
2035 #define ESCAPE_MASK_DEFAULT (ESCAPE_FLAG_NON_PRINTABLE | ESCAPE_FLAG_SPACE | ESCAPE_FLAG_UNSAFE |\
2036 ESCAPE_FLAG_DEL | ESCAPE_FLAG_8BIT)
2037 #define ESCAPE_MASK_PERCENT (ESCAPE_FLAG_PERCENT | ESCAPE_MASK_DEFAULT)
2038 #define ESCAPE_MASK_DISABLE (ESCAPE_FLAG_SPACE | ESCAPE_FLAG_8BIT | ESCAPE_FLAG_STRIP_CRLF)
2040 static inline BOOL need_escape( char ch, enum escape_flags flags )
2042 static const char unsafe[] = "\"#<>[\\]^`{|}";
2043 const char *ptr = unsafe;
2045 if ((flags & ESCAPE_FLAG_SPACE) && ch == ' ') return TRUE;
2046 if ((flags & ESCAPE_FLAG_PERCENT) && ch == '%') return TRUE;
2047 if ((flags & ESCAPE_FLAG_NON_PRINTABLE) && ch < 0x20) return TRUE;
2048 if ((flags & ESCAPE_FLAG_DEL) && ch == 0x7f) return TRUE;
2049 if ((flags & ESCAPE_FLAG_8BIT) && (ch & 0x80)) return TRUE;
2050 if ((flags & ESCAPE_FLAG_UNSAFE)) while (*ptr) { if (ch == *ptr++) return TRUE; }
2051 return FALSE;
2054 static DWORD escape_string( const char *src, DWORD len, char *dst, enum escape_flags flags )
2056 static const char hex[] = "0123456789ABCDEF";
2057 DWORD i, ret = len;
2058 char *ptr = dst;
2060 for (i = 0; i < len; i++)
2062 if ((flags & ESCAPE_FLAG_STRIP_CRLF) && (src[i] == '\r' || src[i] == '\n'))
2064 ret--;
2065 continue;
2067 if (need_escape( src[i], flags ))
2069 if (dst)
2071 ptr[0] = '%';
2072 ptr[1] = hex[(src[i] >> 4) & 0xf];
2073 ptr[2] = hex[src[i] & 0xf];
2074 ptr += 3;
2076 ret += 2;
2078 else if (dst) *ptr++ = src[i];
2081 if (dst) dst[ret] = 0;
2082 return ret;
2085 static DWORD str_to_wire( const WCHAR *src, int src_len, char *dst, enum escape_flags flags )
2087 DWORD len;
2088 char *utf8;
2090 if (src_len < 0) src_len = lstrlenW( src );
2091 len = WideCharToMultiByte( CP_UTF8, 0, src, src_len, NULL, 0, NULL, NULL );
2092 if (!(utf8 = malloc( len ))) return 0;
2094 WideCharToMultiByte( CP_UTF8, 0, src, -1, utf8, len, NULL, NULL );
2095 len = escape_string( utf8, len, dst, flags );
2096 free( utf8 );
2098 return len;
2101 static char *build_wire_path( struct request *request, DWORD *ret_len )
2103 WCHAR *full_path;
2104 const WCHAR *start, *path, *query = NULL;
2105 DWORD len, len_path = 0, len_query = 0;
2106 enum escape_flags path_flags, query_flags;
2107 char *ret;
2109 if (!wcsicmp( request->connect->hostname, request->connect->servername )) start = full_path = request->path;
2110 else if (!(full_path = build_absolute_request_path( request, &start ))) return NULL;
2112 len = lstrlenW( full_path );
2113 if ((path = wcschr( start, '/' )))
2115 len_path = lstrlenW( path );
2116 if ((query = wcschr( path, '?' )))
2118 len_query = lstrlenW( query );
2119 len_path -= len_query;
2123 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_MASK_DISABLE;
2124 else if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_MASK_PERCENT;
2125 else path_flags = ESCAPE_MASK_DEFAULT;
2127 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_MASK_DISABLE;
2128 else query_flags = path_flags;
2130 *ret_len = str_to_wire( full_path, len - len_path - len_query, NULL, 0 );
2131 if (path) *ret_len += str_to_wire( path, len_path, NULL, path_flags );
2132 if (query) *ret_len += str_to_wire( query, len_query, NULL, query_flags );
2134 if ((ret = malloc( *ret_len + 1 )))
2136 len = str_to_wire( full_path, len - len_path - len_query, ret, 0 );
2137 if (path) len += str_to_wire( path, len_path, ret + len, path_flags );
2138 if (query) str_to_wire( query, len_query, ret + len, query_flags );
2141 if (full_path != request->path) free( full_path );
2142 return ret;
2145 static char *build_wire_request( struct request *request, DWORD *len )
2147 char *path, *ptr, *ret;
2148 DWORD i, len_path;
2150 if (!(path = build_wire_path( request, &len_path ))) return NULL;
2152 *len = str_to_wire( request->verb, -1, NULL, 0 ) + 1; /* ' ' */
2153 *len += len_path + 1; /* ' ' */
2154 *len += str_to_wire( request->version, -1, NULL, 0 );
2156 for (i = 0; i < request->num_headers; i++)
2158 if (request->headers[i].is_request)
2160 *len += str_to_wire( request->headers[i].field, -1, NULL, 0 ) + 2; /* ': ' */
2161 *len += str_to_wire( request->headers[i].value, -1, NULL, 0 ) + 2; /* '\r\n' */
2164 *len += 4; /* '\r\n\r\n' */
2166 if ((ret = ptr = malloc( *len + 1 )))
2168 ptr += str_to_wire( request->verb, -1, ptr, 0 );
2169 *ptr++ = ' ';
2170 memcpy( ptr, path, len_path );
2171 ptr += len_path;
2172 *ptr++ = ' ';
2173 ptr += str_to_wire( request->version, -1, ptr, 0 );
2175 for (i = 0; i < request->num_headers; i++)
2177 if (request->headers[i].is_request)
2179 *ptr++ = '\r';
2180 *ptr++ = '\n';
2181 ptr += str_to_wire( request->headers[i].field, -1, ptr, 0 );
2182 *ptr++ = ':';
2183 *ptr++ = ' ';
2184 ptr += str_to_wire( request->headers[i].value, -1, ptr, 0 );
2187 memcpy( ptr, "\r\n\r\n", sizeof("\r\n\r\n") );
2190 free( path );
2191 return ret;
2194 static WCHAR *create_websocket_key(void)
2196 WCHAR *ret;
2197 char buf[16];
2198 DWORD base64_len = ((sizeof(buf) + 2) * 4) / 3;
2199 if (!RtlGenRandom( buf, sizeof(buf) )) return NULL;
2200 if ((ret = malloc( (base64_len + 1) * sizeof(WCHAR) ))) encode_base64( buf, sizeof(buf), ret );
2201 return ret;
2204 static DWORD add_websocket_key_header( struct request *request )
2206 WCHAR *key = create_websocket_key();
2207 if (!key) return ERROR_OUTOFMEMORY;
2208 process_header( request, L"Sec-WebSocket-Key", key, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE );
2209 free( key );
2210 return ERROR_SUCCESS;
2213 static DWORD send_request( struct request *request, const WCHAR *headers, DWORD headers_len, void *optional,
2214 DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
2216 struct connect *connect = request->connect;
2217 struct session *session = connect->session;
2218 DWORD ret, len, buflen, content_length;
2219 char *wire_req;
2220 int bytes_sent;
2222 TRACE( "request state %d.\n", request->state );
2224 request->read_reply_status = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2225 request->read_reply_len = 0;
2226 request->state = REQUEST_RESPONSE_STATE_NONE;
2228 if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE
2229 && request->websocket_set_send_buffer_size < MIN_WEBSOCKET_SEND_BUFFER_SIZE)
2231 WARN( "Invalid send buffer size %u.\n", request->websocket_set_send_buffer_size );
2232 ret = ERROR_NOT_ENOUGH_MEMORY;
2233 goto end;
2236 drain_content( request );
2237 clear_response_headers( request );
2239 if (session->agent)
2240 process_header( request, L"User-Agent", session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2242 if (connect->hostname)
2243 add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
2245 if (request->creds[TARGET_SERVER][SCHEME_BASIC].username)
2246 do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC );
2248 if (total_len || (request->verb && (!wcscmp( request->verb, L"POST" ) || !wcscmp( request->verb, L"PUT" ))))
2250 WCHAR length[21]; /* decimal long int + null */
2251 swprintf( length, ARRAY_SIZE(length), L"%ld", total_len );
2252 process_header( request, L"Content-Length", length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2254 if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE)
2256 request->websocket_send_buffer_size = request->websocket_set_send_buffer_size;
2257 process_header( request, L"Upgrade", L"websocket", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2258 process_header( request, L"Connection", L"Upgrade", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2259 process_header( request, L"Sec-WebSocket-Version", L"13", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2260 if ((ret = add_websocket_key_header( request ))) return ret;
2262 else if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
2264 process_header( request, L"Connection", L"Keep-Alive", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2266 if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
2268 process_header( request, L"Pragma", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2269 process_header( request, L"Cache-Control", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2271 if (headers && (ret = add_request_headers( request, headers, headers_len,
2272 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE )))
2274 TRACE( "failed to add request headers: %lu\n", ret );
2275 return ret;
2277 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && (ret = add_cookie_headers( request )))
2279 WARN( "failed to add cookie headers: %lu\n", ret );
2280 return ret;
2283 if (context) request->hdr.context = context;
2285 if ((ret = open_connection( request ))) goto end;
2286 if (!(wire_req = build_wire_request( request, &len )))
2288 ret = ERROR_OUTOFMEMORY;
2289 goto end;
2291 TRACE("full request: %s\n", debugstr_a(wire_req));
2293 request->state = REQUEST_RESPONSE_STATE_SENDING_REQUEST;
2294 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
2296 ret = netconn_send( request->netconn, wire_req, len, &bytes_sent, NULL );
2297 free( wire_req );
2298 if (ret) goto end;
2300 if (optional_len)
2302 if ((ret = netconn_send( request->netconn, optional, optional_len, &bytes_sent, NULL ))) goto end;
2303 request->optional = optional;
2304 request->optional_len = optional_len;
2305 len += optional_len;
2307 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
2309 buflen = sizeof(content_length);
2310 if (query_headers( request, WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_CONTENT_LENGTH
2311 | WINHTTP_QUERY_FLAG_NUMBER, NULL, &content_length, &buflen, NULL ))
2312 content_length = total_len;
2314 if (content_length <= optional_len)
2316 netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout );
2317 request->read_reply_status = read_reply( request );
2318 if (request->state == REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED)
2319 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REPLY_RECEIVED;
2320 else
2321 request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED;
2323 else
2325 if (request->state == REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED)
2326 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT;
2327 else
2328 request->state = REQUEST_RESPONSE_STATE_REQUEST_SENT;
2331 end:
2332 if (async)
2334 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
2335 else
2337 WINHTTP_ASYNC_RESULT result;
2338 result.dwResult = API_SEND_REQUEST;
2339 result.dwError = ret;
2340 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2343 return ret;
2346 static void task_send_request( void *ctx, BOOL abort )
2348 struct send_request *s = ctx;
2349 struct request *request = (struct request *)s->task_hdr.obj;
2351 if (abort) return;
2353 TRACE( "running %p\n", ctx );
2354 send_request( request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
2356 free( s->headers );
2359 /***********************************************************************
2360 * WinHttpSendRequest (winhttp.@)
2362 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, const WCHAR *headers, DWORD headers_len,
2363 void *optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
2365 DWORD ret;
2366 struct request *request;
2368 TRACE( "%p, %s, %lu, %p, %lu, %lu, %Ix\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional,
2369 optional_len, total_len, context );
2371 if (!(request = (struct request *)grab_object( hrequest )))
2373 SetLastError( ERROR_INVALID_HANDLE );
2374 return FALSE;
2376 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2378 release_object( &request->hdr );
2379 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2380 return FALSE;
2383 if (headers && !headers_len) headers_len = lstrlenW( headers );
2385 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2387 struct send_request *s;
2389 if (!(s = malloc( sizeof(*s) )))
2391 release_object( &request->hdr );
2392 SetLastError( ERROR_OUTOFMEMORY );
2393 return FALSE;
2395 s->headers = wcsdup( headers );
2396 s->headers_len = headers_len;
2397 s->optional = optional;
2398 s->optional_len = optional_len;
2399 s->total_len = total_len;
2400 s->context = context;
2402 if ((ret = queue_task( &request->queue, task_send_request, &s->task_hdr, &request->hdr )))
2404 free( s->headers );
2405 free( s );
2408 else ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
2410 release_object( &request->hdr );
2411 SetLastError( ret );
2412 return !ret;
2415 static DWORD set_credentials( struct request *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
2416 const WCHAR *password )
2418 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
2420 if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
2422 return ERROR_INVALID_PARAMETER;
2424 switch (target)
2426 case WINHTTP_AUTH_TARGET_SERVER:
2428 free( request->creds[TARGET_SERVER][scheme].username );
2429 if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
2430 else if (!(request->creds[TARGET_SERVER][scheme].username = wcsdup( username ))) return ERROR_OUTOFMEMORY;
2432 free( request->creds[TARGET_SERVER][scheme].password );
2433 if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
2434 else if (!(request->creds[TARGET_SERVER][scheme].password = wcsdup( password ))) return ERROR_OUTOFMEMORY;
2435 break;
2437 case WINHTTP_AUTH_TARGET_PROXY:
2439 free( request->creds[TARGET_PROXY][scheme].username );
2440 if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
2441 else if (!(request->creds[TARGET_PROXY][scheme].username = wcsdup( username ))) return ERROR_OUTOFMEMORY;
2443 free( request->creds[TARGET_PROXY][scheme].password );
2444 if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
2445 else if (!(request->creds[TARGET_PROXY][scheme].password = wcsdup( password ))) return ERROR_OUTOFMEMORY;
2446 break;
2448 default:
2449 WARN( "unknown target %lu\n", target );
2450 return ERROR_INVALID_PARAMETER;
2452 return ERROR_SUCCESS;
2455 /***********************************************************************
2456 * WinHttpSetCredentials (winhttp.@)
2458 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, const WCHAR *username,
2459 const WCHAR *password, void *params )
2461 DWORD ret;
2462 struct request *request;
2464 TRACE( "%p, %lu, %#lx, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params );
2466 if (!(request = (struct request *)grab_object( hrequest )))
2468 SetLastError( ERROR_INVALID_HANDLE );
2469 return FALSE;
2471 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2473 release_object( &request->hdr );
2474 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2475 return FALSE;
2478 ret = set_credentials( request, target, scheme, username, password );
2480 release_object( &request->hdr );
2481 SetLastError( ret );
2482 return !ret;
2485 static DWORD handle_authorization( struct request *request, DWORD status )
2487 DWORD ret, i, schemes, first, level, target;
2489 switch (status)
2491 case HTTP_STATUS_DENIED:
2492 target = WINHTTP_AUTH_TARGET_SERVER;
2493 level = WINHTTP_QUERY_WWW_AUTHENTICATE;
2494 break;
2496 case HTTP_STATUS_PROXY_AUTH_REQ:
2497 target = WINHTTP_AUTH_TARGET_PROXY;
2498 level = WINHTTP_QUERY_PROXY_AUTHENTICATE;
2499 break;
2501 default:
2502 ERR( "unhandled status %lu\n", status );
2503 return ERROR_WINHTTP_INTERNAL_ERROR;
2506 if ((ret = query_auth_schemes( request, level, &schemes, &first ))) return ret;
2507 if (do_authorization( request, target, first )) return ERROR_SUCCESS;
2509 schemes &= ~first;
2510 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
2512 if (!(schemes & auth_schemes[i].scheme)) continue;
2513 if (do_authorization( request, target, auth_schemes[i].scheme )) return ERROR_SUCCESS;
2515 return ERROR_WINHTTP_LOGIN_FAILURE;
2518 /* set the request content length based on the headers */
2519 static void set_content_length( struct request *request, DWORD status )
2521 WCHAR encoding[20];
2522 DWORD buflen = sizeof(request->content_length);
2524 if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED ||
2525 status == HTTP_STATUS_SWITCH_PROTOCOLS || !wcscmp( request->verb, L"HEAD" ))
2527 request->content_length = 0;
2529 else
2531 if (query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
2532 NULL, &request->content_length, &buflen, NULL ))
2533 request->content_length = ~0u;
2535 buflen = sizeof(encoding);
2536 if (!query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
2537 !wcsicmp( encoding, L"chunked" ))
2539 request->content_length = ~0u;
2540 request->read_chunked = TRUE;
2541 request->read_chunked_size = ~0u;
2542 request->read_chunked_eof = FALSE;
2545 request->content_read = 0;
2548 static DWORD read_line( struct request *request, char *buffer, DWORD *len )
2550 int count, bytes_read, pos = 0;
2551 DWORD ret;
2553 for (;;)
2555 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
2556 if (eol)
2558 count = eol - (request->read_buf + request->read_pos);
2559 bytes_read = count + 1;
2561 else count = bytes_read = request->read_size;
2563 count = min( count, *len - pos );
2564 memcpy( buffer + pos, request->read_buf + request->read_pos, count );
2565 pos += count;
2566 remove_data( request, bytes_read );
2567 if (eol) break;
2569 if ((ret = read_more_data( request, -1, FALSE ))) return ret;
2570 if (!request->read_size)
2572 *len = 0;
2573 TRACE("returning empty string\n");
2574 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2577 if (pos < *len)
2579 if (pos && buffer[pos - 1] == '\r') pos--;
2580 *len = pos + 1;
2582 buffer[*len - 1] = 0;
2583 TRACE("returning %s\n", debugstr_a(buffer));
2584 return ERROR_SUCCESS;
2587 #define MAX_REPLY_LEN 1460
2588 #define INITIAL_HEADER_BUFFER_LEN 512
2590 static DWORD read_reply( struct request *request )
2592 char buffer[MAX_REPLY_LEN];
2593 DWORD ret, buflen, len, offset, crlf_len = 2; /* lstrlenW(crlf) */
2594 char *status_code, *status_text;
2595 WCHAR *versionW, *status_textW, *raw_headers;
2596 WCHAR status_codeW[4]; /* sizeof("nnn") */
2598 if (!request->netconn) return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2602 buflen = MAX_REPLY_LEN;
2603 if ((ret = read_line( request, buffer, &buflen ))) return ret;
2605 /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2606 if (!(status_code = strchr( buffer, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2607 status_code++;
2608 if (!(status_text = strchr( status_code, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2609 if ((len = status_text - status_code) != sizeof("nnn") - 1) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2610 status_text++;
2612 TRACE("version [%s] status code [%s] status text [%s]\n",
2613 debugstr_an(buffer, status_code - buffer - 1),
2614 debugstr_an(status_code, len),
2615 debugstr_a(status_text));
2617 } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2619 /* we rely on the fact that the protocol is ascii */
2620 MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2621 status_codeW[len] = 0;
2622 if ((ret = process_header( request, L"Status", status_codeW,
2623 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE ))) return ret;
2625 len = status_code - buffer;
2626 if (!(versionW = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2627 MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2628 versionW[len - 1] = 0;
2630 free( request->version );
2631 request->version = versionW;
2633 len = buflen - (status_text - buffer);
2634 if (!(status_textW = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2635 MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2637 free( request->status_text );
2638 request->status_text = status_textW;
2640 len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2641 if (!(raw_headers = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2642 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2643 memcpy( raw_headers + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2645 free( request->raw_headers );
2646 request->raw_headers = raw_headers;
2648 offset = buflen + crlf_len - 1;
2649 for (;;)
2651 struct header *header;
2652 int lenW;
2654 buflen = MAX_REPLY_LEN;
2655 if (read_line( request, buffer, &buflen )) return ERROR_SUCCESS;
2656 if (!*buffer) buflen = 1;
2658 while (len - offset < buflen + crlf_len)
2660 WCHAR *tmp;
2661 len *= 2;
2662 if (!(tmp = realloc( raw_headers, len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2663 request->raw_headers = raw_headers = tmp;
2665 if (!*buffer)
2667 memcpy( raw_headers + offset, L"\r\n", sizeof(L"\r\n") );
2668 break;
2670 lenW = MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2672 if (!(header = parse_header( raw_headers + offset, lenW - 1 ))) break;
2673 if ((ret = process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2675 free_header( header );
2676 break;
2678 free_header( header );
2679 memcpy( raw_headers + offset + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2680 offset += buflen + crlf_len - 1;
2683 TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2684 return ret;
2687 static void record_cookies( struct request *request )
2689 unsigned int i;
2691 for (i = 0; i < request->num_headers; i++)
2693 struct header *set_cookie = &request->headers[i];
2694 if (!wcsicmp( set_cookie->field, L"Set-Cookie" ) && !set_cookie->is_request)
2696 set_cookies( request, set_cookie->value );
2701 static DWORD get_redirect_url( struct request *request, WCHAR **ret_url, DWORD *ret_len )
2703 DWORD size, ret;
2704 WCHAR *url;
2706 ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2707 if (ret != ERROR_INSUFFICIENT_BUFFER) return ret;
2708 if (!(url = malloc( size ))) return ERROR_OUTOFMEMORY;
2709 if ((ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, url, &size, NULL )))
2711 free( url );
2712 return ret;
2714 *ret_url = url;
2715 *ret_len = size / sizeof(WCHAR);
2716 return ERROR_SUCCESS;
2719 static DWORD handle_redirect( struct request *request, DWORD status )
2721 DWORD ret, len, len_loc = 0;
2722 URL_COMPONENTS uc;
2723 struct connect *connect = request->connect;
2724 INTERNET_PORT port;
2725 WCHAR *hostname = NULL, *location = NULL;
2727 if ((ret = get_redirect_url( request, &location, &len_loc ))) return ret;
2729 memset( &uc, 0, sizeof(uc) );
2730 uc.dwStructSize = sizeof(uc);
2731 uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2733 if (!WinHttpCrackUrl( location, len_loc, 0, &uc )) /* assume relative redirect */
2735 WCHAR *path, *p;
2737 ret = ERROR_OUTOFMEMORY;
2738 if (location[0] == '/')
2740 if (!(path = malloc( (len_loc + 1) * sizeof(WCHAR) ))) goto end;
2741 memcpy( path, location, len_loc * sizeof(WCHAR) );
2742 path[len_loc] = 0;
2744 else
2746 if ((p = wcsrchr( request->path, '/' ))) *p = 0;
2747 len = lstrlenW( request->path ) + 1 + len_loc;
2748 if (!(path = malloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2749 lstrcpyW( path, request->path );
2750 lstrcatW( path, L"/" );
2751 memcpy( path + lstrlenW(path), location, len_loc * sizeof(WCHAR) );
2752 path[len_loc] = 0;
2754 free( request->path );
2755 request->path = path;
2756 ret = ERROR_SUCCESS;
2758 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2760 else
2762 if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2764 if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP)
2766 ret = ERROR_WINHTTP_REDIRECT_FAILED;
2767 goto end;
2769 TRACE("redirect from secure page to non-secure page\n");
2770 request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2772 else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2774 TRACE("redirect from non-secure page to secure page\n");
2775 request->hdr.flags |= WINHTTP_FLAG_SECURE;
2778 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2780 len = uc.dwHostNameLength;
2781 if (!(hostname = malloc( (len + 1) * sizeof(WCHAR) )))
2783 ret = ERROR_OUTOFMEMORY;
2784 goto end;
2786 memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2787 hostname[len] = 0;
2789 port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2790 if (wcsicmp( connect->hostname, hostname ) || connect->serverport != port)
2792 free( connect->hostname );
2793 connect->hostname = hostname;
2794 connect->hostport = port;
2795 if (!set_server_for_hostname( connect, hostname, port ))
2797 ret = ERROR_OUTOFMEMORY;
2798 goto end;
2801 netconn_release( request->netconn );
2802 request->netconn = NULL;
2803 request->content_length = request->content_read = 0;
2804 request->read_pos = request->read_size = 0;
2805 request->read_chunked = request->read_chunked_eof = FALSE;
2807 else free( hostname );
2809 if ((ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2811 free( request->path );
2812 request->path = NULL;
2813 if (uc.dwUrlPathLength)
2815 len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2816 if (!(request->path = malloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2817 memcpy( request->path, uc.lpszUrlPath, (len + 1) * sizeof(WCHAR) );
2818 request->path[len] = 0;
2820 else request->path = wcsdup( L"/" );
2823 if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !wcscmp( request->verb, L"POST" ))
2825 free( request->verb );
2826 request->verb = wcsdup( L"GET" );
2827 request->optional = NULL;
2828 request->optional_len = 0;
2831 end:
2832 free( location );
2833 return ret;
2836 static BOOL is_passport_request( struct request *request )
2838 static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t','1','.','4'};
2839 WCHAR buf[1024];
2840 DWORD len = ARRAY_SIZE(buf);
2842 if (!(request->connect->session->passport_flags & WINHTTP_ENABLE_PASSPORT_AUTH) ||
2843 query_headers( request, WINHTTP_QUERY_WWW_AUTHENTICATE, NULL, buf, &len, NULL )) return FALSE;
2845 if (!wcsnicmp( buf, passportW, ARRAY_SIZE(passportW) ) &&
2846 (buf[ARRAY_SIZE(passportW)] == ' ' || !buf[ARRAY_SIZE(passportW)])) return TRUE;
2848 return FALSE;
2851 static DWORD handle_passport_redirect( struct request *request )
2853 DWORD ret, flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
2854 int i, len = lstrlenW( request->raw_headers );
2855 WCHAR *p = request->raw_headers;
2857 if ((ret = process_header( request, L"Status", L"401", flags, FALSE ))) return ret;
2859 for (i = 0; i < len; i++)
2861 if (i <= len - 3 && p[i] == '3' && p[i + 1] == '0' && p[i + 2] == '2')
2863 p[i] = '4';
2864 p[i + 2] = '1';
2865 break;
2868 return ERROR_SUCCESS;
2871 static void task_receive_response( void *ctx, BOOL abort );
2873 static DWORD queue_receive_response( struct request *request )
2875 struct receive_response *r;
2876 DWORD ret;
2878 if (!(r = malloc( sizeof(*r) ))) return ERROR_OUTOFMEMORY;
2879 if ((ret = queue_task( &request->queue, task_receive_response, &r->task_hdr, &request->hdr )))
2880 free( r );
2881 return ret;
2884 static DWORD receive_response( struct request *request )
2886 BOOL async_mode = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC;
2887 DWORD ret, size, query, status;
2889 TRACE( "request state %d.\n", request->state );
2891 switch (request->state)
2893 case REQUEST_RESPONSE_RECURSIVE_REQUEST:
2894 TRACE( "Sending request.\n" );
2895 if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) goto done;
2896 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2897 break;
2899 case REQUEST_RESPONSE_STATE_SENDING_REQUEST:
2900 if (!async_mode)
2902 ret = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2903 goto done;
2905 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2906 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED;
2907 return queue_receive_response( request );
2910 case REQUEST_RESPONSE_STATE_REQUEST_SENT:
2911 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2912 if (async_mode)
2914 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT;
2915 return queue_receive_response( request );
2917 /* fallthrough */
2918 case REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT:
2919 netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout );
2920 request->read_reply_status = read_reply( request );
2921 request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED;
2922 break;
2924 case REQUEST_RESPONSE_STATE_REPLY_RECEIVED:
2925 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2926 break;
2928 case REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REPLY_RECEIVED:
2929 request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED;
2930 break;
2932 default:
2933 ret = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2934 goto done;
2937 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED,
2938 &request->read_reply_len, sizeof(request->read_reply_len) );
2939 if ((ret = request->read_reply_status)) goto done;
2941 size = sizeof(DWORD);
2942 query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2943 if ((ret = query_headers( request, query, NULL, &status, &size, NULL ))) goto done;
2945 set_content_length( request, status );
2947 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2949 if (status == HTTP_STATUS_REDIRECT && is_passport_request( request ))
2951 ret = handle_passport_redirect( request );
2952 goto done;
2954 if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2956 if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2957 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) goto done;
2959 if (++request->redirect_count > request->max_redirects)
2961 ret = ERROR_WINHTTP_REDIRECT_FAILED;
2962 goto done;
2965 if ((ret = handle_redirect( request, status ))) goto done;
2967 else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2969 if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) goto done;
2971 if (handle_authorization( request, status )) goto done;
2973 else goto done;
2975 request->state = REQUEST_RESPONSE_RECURSIVE_REQUEST;
2976 return async_mode ? queue_receive_response( request ) : receive_response( request );
2978 done:
2979 if (!ret)
2981 request->state = REQUEST_RESPONSE_STATE_RESPONSE_RECEIVED;
2982 if (request->netconn) netconn_set_timeout( request->netconn, FALSE, request->receive_timeout );
2984 if (async_mode)
2986 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
2987 else
2989 WINHTTP_ASYNC_RESULT result;
2990 result.dwResult = API_RECEIVE_RESPONSE;
2991 result.dwError = ret;
2992 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2994 return ERROR_SUCCESS;
2996 return ret;
2999 static void task_receive_response( void *ctx, BOOL abort )
3001 struct receive_response *r = ctx;
3002 struct request *request = (struct request *)r->task_hdr.obj;
3004 if (abort) return;
3006 TRACE("running %p\n", ctx);
3007 receive_response( request );
3010 /***********************************************************************
3011 * WinHttpReceiveResponse (winhttp.@)
3013 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
3015 DWORD ret;
3016 struct request *request;
3018 TRACE("%p, %p\n", hrequest, reserved);
3020 if (!(request = (struct request *)grab_object( hrequest )))
3022 SetLastError( ERROR_INVALID_HANDLE );
3023 return FALSE;
3025 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3027 release_object( &request->hdr );
3028 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3029 return FALSE;
3032 ret = receive_response( request );
3034 release_object( &request->hdr );
3035 SetLastError( ret );
3036 return !ret;
3039 static DWORD query_data_ready( struct request *request )
3041 DWORD count;
3043 count = get_available_data( request );
3044 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
3046 return count;
3049 static BOOL skip_async_queue( struct request *request )
3051 return request->hdr.recursion_count < 3 && (end_of_read_data( request ) || query_data_ready( request ));
3054 static DWORD query_data_available( struct request *request, DWORD *available, BOOL async )
3056 DWORD ret = ERROR_SUCCESS, count = 0;
3058 if (end_of_read_data( request )) goto done;
3060 if (!(count = query_data_ready( request )))
3062 if ((ret = refill_buffer( request, async ))) goto done;
3063 count = query_data_ready( request );
3066 done:
3067 TRACE( "%lu bytes available\n", count );
3068 if (async)
3070 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
3071 else
3073 WINHTTP_ASYNC_RESULT result;
3074 result.dwResult = API_QUERY_DATA_AVAILABLE;
3075 result.dwError = ret;
3076 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3080 if (!ret && available) *available = count;
3081 return ret;
3084 static void task_query_data_available( void *ctx, BOOL abort )
3086 struct query_data *q = ctx;
3087 struct request *request = (struct request *)q->task_hdr.obj;
3089 if (abort) return;
3091 TRACE("running %p\n", ctx);
3092 query_data_available( request, q->available, TRUE );
3095 /***********************************************************************
3096 * WinHttpQueryDataAvailable (winhttp.@)
3098 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
3100 DWORD ret;
3101 struct request *request;
3102 BOOL async;
3104 TRACE("%p, %p\n", hrequest, available);
3106 if (!(request = (struct request *)grab_object( hrequest )))
3108 SetLastError( ERROR_INVALID_HANDLE );
3109 return FALSE;
3111 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3113 release_object( &request->hdr );
3114 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3115 return FALSE;
3118 if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request ))
3120 struct query_data *q;
3122 if (!(q = malloc( sizeof(*q) )))
3124 release_object( &request->hdr );
3125 SetLastError( ERROR_OUTOFMEMORY );
3126 return FALSE;
3129 q->available = available;
3131 if ((ret = queue_task( &request->queue, task_query_data_available, &q->task_hdr, &request->hdr )))
3132 free( q );
3133 else
3134 ret = ERROR_IO_PENDING;
3136 else ret = query_data_available( request, available, async );
3138 release_object( &request->hdr );
3139 SetLastError( ret );
3140 return !ret || ret == ERROR_IO_PENDING;
3143 static void task_read_data( void *ctx, BOOL abort )
3145 struct read_data *r = ctx;
3146 struct request *request = (struct request *)r->task_hdr.obj;
3148 if (abort) return;
3150 TRACE("running %p\n", ctx);
3151 read_data( request, r->buffer, r->to_read, r->read, TRUE );
3154 /***********************************************************************
3155 * WinHttpReadData (winhttp.@)
3157 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DWORD *read )
3159 DWORD ret;
3160 struct request *request;
3161 BOOL async;
3163 TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read );
3165 if (!(request = (struct request *)grab_object( hrequest )))
3167 SetLastError( ERROR_INVALID_HANDLE );
3168 return FALSE;
3170 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3172 release_object( &request->hdr );
3173 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3174 return FALSE;
3177 if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request ))
3179 struct read_data *r;
3181 if (!(r = malloc( sizeof(*r) )))
3183 release_object( &request->hdr );
3184 SetLastError( ERROR_OUTOFMEMORY );
3185 return FALSE;
3187 r->buffer = buffer;
3188 r->to_read = to_read;
3189 r->read = read;
3191 if ((ret = queue_task( &request->queue, task_read_data, &r->task_hdr, &request->hdr )))
3192 free( r );
3193 else
3194 ret = ERROR_IO_PENDING;
3196 else ret = read_data( request, buffer, to_read, read, async );
3198 release_object( &request->hdr );
3199 SetLastError( ret );
3200 return !ret || ret == ERROR_IO_PENDING;
3203 static DWORD write_data( struct request *request, const void *buffer, DWORD to_write, DWORD *written, BOOL async )
3205 DWORD ret;
3206 int num_bytes;
3208 ret = netconn_send( request->netconn, buffer, to_write, &num_bytes, NULL );
3210 if (async)
3212 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) );
3213 else
3215 WINHTTP_ASYNC_RESULT result;
3216 result.dwResult = API_WRITE_DATA;
3217 result.dwError = ret;
3218 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3221 if (!ret && written) *written = num_bytes;
3222 return ret;
3225 static void task_write_data( void *ctx, BOOL abort )
3227 struct write_data *w = ctx;
3228 struct request *request = (struct request *)w->task_hdr.obj;
3230 if (abort) return;
3232 TRACE("running %p\n", ctx);
3233 write_data( request, w->buffer, w->to_write, w->written, TRUE );
3236 /***********************************************************************
3237 * WinHttpWriteData (winhttp.@)
3239 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, const void *buffer, DWORD to_write, DWORD *written )
3241 DWORD ret;
3242 struct request *request;
3244 TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_write, written );
3246 if (!(request = (struct request *)grab_object( hrequest )))
3248 SetLastError( ERROR_INVALID_HANDLE );
3249 return FALSE;
3251 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3253 release_object( &request->hdr );
3254 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3255 return FALSE;
3258 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3260 struct write_data *w;
3262 if (!(w = malloc( sizeof(*w) )))
3264 release_object( &request->hdr );
3265 SetLastError( ERROR_OUTOFMEMORY );
3266 return FALSE;
3268 w->buffer = buffer;
3269 w->to_write = to_write;
3270 w->written = written;
3272 if ((ret = queue_task( &request->queue, task_write_data, &w->task_hdr, &request->hdr )))
3273 free( w );
3275 else ret = write_data( request, buffer, to_write, written, FALSE );
3277 release_object( &request->hdr );
3278 SetLastError( ret );
3279 return !ret;
3282 static void socket_handle_closing( struct object_header *hdr )
3284 struct socket *socket = (struct socket *)hdr;
3285 BOOL pending_tasks;
3287 pending_tasks = cancel_queue( &socket->send_q );
3288 pending_tasks = cancel_queue( &socket->recv_q ) || pending_tasks;
3290 if (pending_tasks)
3291 netconn_cancel_io( socket->netconn );
3294 static BOOL socket_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
3296 switch (option)
3298 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
3299 SetLastError( ERROR_INVALID_PARAMETER );
3300 return FALSE;
3303 FIXME( "unimplemented option %lu\n", option );
3304 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3305 return FALSE;
3308 static void socket_destroy( struct object_header *hdr )
3310 struct socket *socket = (struct socket *)hdr;
3312 TRACE("%p\n", socket);
3314 stop_queue( &socket->send_q );
3315 stop_queue( &socket->recv_q );
3317 netconn_release( socket->netconn );
3318 free( socket->read_buffer );
3319 free( socket->send_frame_buffer );
3320 free( socket );
3323 static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
3325 struct socket *socket = (struct socket *)hdr;
3327 switch (option)
3329 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
3331 DWORD interval;
3333 if (buflen != sizeof(DWORD) || (interval = *(DWORD *)buffer) < 15000)
3335 WARN( "Invalid parameters for WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL.\n" );
3336 SetLastError( ERROR_INVALID_PARAMETER );
3337 return FALSE;
3339 socket->keepalive_interval = interval;
3340 netconn_set_timeout( socket->netconn, FALSE, socket->keepalive_interval );
3341 SetLastError( ERROR_SUCCESS );
3342 TRACE( "WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL %lu.\n", interval);
3343 return TRUE;
3347 FIXME( "unimplemented option %lu\n", option );
3348 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3349 return FALSE;
3352 static const struct object_vtbl socket_vtbl =
3354 socket_handle_closing,
3355 socket_destroy,
3356 socket_query_option,
3357 socket_set_option,
3360 HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR context )
3362 struct socket *socket;
3363 struct request *request;
3364 HINTERNET hsocket = NULL;
3366 TRACE( "%p, %Ix\n", hrequest, context );
3368 if (!(request = (struct request *)grab_object( hrequest )))
3370 SetLastError( ERROR_INVALID_HANDLE );
3371 return NULL;
3373 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3375 release_object( &request->hdr );
3376 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3377 return NULL;
3379 if (!(socket = calloc( 1, sizeof(*socket) )))
3381 release_object( &request->hdr );
3382 return NULL;
3384 socket->hdr.type = WINHTTP_HANDLE_TYPE_SOCKET;
3385 socket->hdr.vtbl = &socket_vtbl;
3386 socket->hdr.refs = 1;
3387 socket->hdr.callback = request->hdr.callback;
3388 socket->hdr.notify_mask = request->hdr.notify_mask;
3389 socket->hdr.context = context;
3390 socket->hdr.flags = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC;
3391 socket->keepalive_interval = 30000;
3392 socket->send_buffer_size = request->websocket_send_buffer_size;
3393 if (request->read_size)
3395 if (!(socket->read_buffer = malloc( request->read_size )))
3397 ERR( "No memory.\n" );
3398 free( socket );
3399 release_object( &request->hdr );
3400 return NULL;
3402 socket->bytes_in_read_buffer = request->read_size;
3403 memcpy( socket->read_buffer, request->read_buf + request->read_pos, request->read_size );
3404 request->read_pos = request->read_size = 0;
3406 InitializeSRWLock( &socket->send_lock );
3407 init_queue( &socket->send_q );
3408 init_queue( &socket->recv_q );
3409 netconn_addref( request->netconn );
3410 socket->netconn = request->netconn;
3412 netconn_set_timeout( socket->netconn, FALSE, socket->keepalive_interval );
3414 if ((hsocket = alloc_handle( &socket->hdr )))
3416 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hsocket, sizeof(hsocket) );
3419 release_object( &socket->hdr );
3420 release_object( &request->hdr );
3421 TRACE("returning %p\n", hsocket);
3422 if (hsocket) SetLastError( ERROR_SUCCESS );
3423 return hsocket;
3426 static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, WSAOVERLAPPED *ovr )
3428 int count;
3429 DWORD err;
3430 err = netconn_send( socket->netconn, bytes, len, &count, ovr );
3431 if (sent) *sent = count;
3432 if (err) return err;
3433 return (count == len || (ovr && count)) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
3436 #define FIN_BIT (1 << 7)
3437 #define MASK_BIT (1 << 7)
3438 #define RESERVED_BIT (7 << 4)
3439 #define CONTROL_BIT (1 << 3)
3441 static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHORT status, const char *buf,
3442 DWORD buflen, BOOL final, WSAOVERLAPPED *ovr )
3444 DWORD i, offset = 2, len = buflen, buffer_size, ret = 0;
3445 int sent_size;
3446 char hdr[14];
3447 char *ptr;
3449 TRACE( "sending %02x frame, len %lu\n", opcode, len );
3451 if (opcode == SOCKET_OPCODE_CLOSE) len += sizeof(status);
3453 hdr[0] = final ? (char)FIN_BIT : 0;
3454 hdr[0] |= opcode;
3455 hdr[1] = (char)MASK_BIT;
3456 if (len < 126) hdr[1] |= len;
3457 else if (len < 65536)
3459 hdr[1] |= 126;
3460 hdr[2] = len >> 8;
3461 hdr[3] = len & 0xff;
3462 offset += 2;
3464 else
3466 hdr[1] |= 127;
3467 hdr[2] = hdr[3] = hdr[4] = hdr[5] = 0;
3468 hdr[6] = len >> 24;
3469 hdr[7] = (len >> 16) & 0xff;
3470 hdr[8] = (len >> 8) & 0xff;
3471 hdr[9] = len & 0xff;
3472 offset += 8;
3475 buffer_size = len + offset + 4;
3476 assert( buffer_size - len < socket->send_buffer_size );
3477 if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < socket->send_buffer_size)
3479 DWORD new_size;
3480 void *new;
3482 new_size = min( buffer_size, socket->send_buffer_size );
3483 if (!(new = realloc( socket->send_frame_buffer, new_size )))
3485 ERR( "out of memory, buffer_size %lu\n", buffer_size);
3486 return ERROR_OUTOFMEMORY;
3488 socket->send_frame_buffer = new;
3489 socket->send_frame_buffer_size = new_size;
3491 ptr = socket->send_frame_buffer;
3493 memcpy(ptr, hdr, offset);
3494 ptr += offset;
3496 RtlGenRandom( socket->mask, 4 );
3497 memcpy( ptr, socket->mask, 4 );
3498 ptr += 4;
3499 socket->mask_index = 0;
3501 if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */
3503 *ptr++ = (status >> 8) ^ socket->mask[socket->mask_index++ % 4];
3504 *ptr++ = (status & 0xff) ^ socket->mask[socket->mask_index++ % 4];
3507 offset = ptr - socket->send_frame_buffer;
3508 socket->send_remaining_size = offset + buflen;
3509 socket->client_buffer_offset = 0;
3510 while (socket->send_remaining_size)
3512 len = min( buflen, socket->send_buffer_size - offset );
3513 for (i = 0; i < len; ++i)
3515 socket->send_frame_buffer[offset++] = buf[socket->client_buffer_offset++]
3516 ^ socket->mask[socket->mask_index++ % 4];
3519 sent_size = 0;
3520 ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr );
3521 socket->send_remaining_size -= sent_size;
3522 if (ret)
3524 if (ovr && ret == WSA_IO_PENDING)
3526 memmove( socket->send_frame_buffer, socket->send_frame_buffer + sent_size, offset - sent_size );
3527 socket->bytes_in_send_frame_buffer = offset - sent_size;
3529 return ret;
3531 assert( sent_size == offset );
3532 offset = 0;
3533 buflen -= len;
3535 return ERROR_SUCCESS;
3538 static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf )
3540 DWORD ret, len, i;
3542 if (!netconn_wait_overlapped_result( socket->netconn, ovr, &len ))
3543 return WSAGetLastError();
3545 if (socket->bytes_in_send_frame_buffer)
3547 ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL );
3548 if (ret) return ret;
3551 assert( socket->bytes_in_send_frame_buffer <= socket->send_remaining_size );
3552 socket->send_remaining_size -= socket->bytes_in_send_frame_buffer;
3554 while (socket->send_remaining_size)
3556 len = min( socket->send_remaining_size, socket->send_buffer_size );
3557 for (i = 0; i < len; ++i)
3559 socket->send_frame_buffer[i] = buf[socket->client_buffer_offset++]
3560 ^ socket->mask[socket->mask_index++ % 4];
3562 ret = send_bytes( socket, socket->send_frame_buffer, len, NULL, NULL );
3563 if (ret) return ret;
3564 socket->send_remaining_size -= len;
3566 return ERROR_SUCCESS;
3569 static void send_io_complete( struct object_header *hdr )
3571 LONG count = InterlockedDecrement( &hdr->pending_sends );
3572 assert( count >= 0 );
3575 /* returns FALSE if sending callback should be omitted. */
3576 static void receive_io_complete( struct socket *socket )
3578 LONG count = InterlockedDecrement( &socket->hdr.pending_receives );
3579 assert( count >= 0 );
3582 static BOOL socket_can_send( struct socket *socket )
3584 return socket->state == SOCKET_STATE_OPEN && !socket->close_frame_received;
3587 static BOOL socket_can_receive( struct socket *socket )
3589 return socket->state <= SOCKET_STATE_SHUTDOWN && !socket->close_frame_received;
3592 static BOOL validate_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type, enum fragment_type current_fragment )
3594 switch (current_fragment)
3596 case SOCKET_FRAGMENT_NONE:
3597 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3598 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
3599 || type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3600 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3601 case SOCKET_FRAGMENT_BINARY:
3602 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3603 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3604 case SOCKET_FRAGMENT_UTF8:
3605 return type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3606 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3608 assert( 0 );
3609 return FALSE;
3612 static enum socket_opcode map_buffer_type( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
3614 switch (type)
3616 case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
3617 if (socket->sending_fragment_type)
3619 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3620 return SOCKET_OPCODE_CONTINUE;
3622 return SOCKET_OPCODE_TEXT;
3624 case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
3625 if (socket->sending_fragment_type)
3627 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3628 return SOCKET_OPCODE_CONTINUE;
3630 return SOCKET_OPCODE_BINARY;
3632 case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
3633 if (!socket->sending_fragment_type)
3635 socket->sending_fragment_type = SOCKET_FRAGMENT_UTF8;
3636 return SOCKET_OPCODE_TEXT;
3638 return SOCKET_OPCODE_CONTINUE;
3640 case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
3641 if (!socket->sending_fragment_type)
3643 socket->sending_fragment_type = SOCKET_FRAGMENT_BINARY;
3644 return SOCKET_OPCODE_BINARY;
3646 return SOCKET_OPCODE_CONTINUE;
3648 case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
3649 return SOCKET_OPCODE_CLOSE;
3651 default:
3652 FIXME("buffer type %u not supported\n", type);
3653 return SOCKET_OPCODE_INVALID;
3657 static void socket_send_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
3659 if (!ret)
3661 WINHTTP_WEB_SOCKET_STATUS status;
3662 status.dwBytesTransferred = len;
3663 status.eBufferType = type;
3664 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) );
3666 else
3668 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3669 result.AsyncResult.dwResult = API_WRITE_DATA;
3670 result.AsyncResult.dwError = ret;
3671 result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION;
3672 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3676 static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len,
3677 WSAOVERLAPPED *ovr )
3679 enum socket_opcode opcode = map_buffer_type( socket, type );
3680 BOOL final = (type != WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE &&
3681 type != WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
3683 return send_frame( socket, opcode, 0, buf, len, final, ovr );
3686 static void task_socket_send( void *ctx, BOOL abort )
3688 struct socket_send *s = ctx;
3689 struct socket *socket = (struct socket *)s->task_hdr.obj;
3690 DWORD ret;
3692 if (abort) return;
3694 TRACE("running %p\n", ctx);
3696 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->buf );
3697 else ret = socket_send( socket, s->type, s->buf, s->len, NULL );
3699 send_io_complete( &socket->hdr );
3700 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3701 socket_send_complete( socket, ret, s->type, s->len );
3704 DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len )
3706 struct socket *socket;
3707 DWORD ret = 0;
3709 TRACE( "%p, %u, %p, %lu\n", hsocket, type, buf, len );
3711 if (len && !buf) return ERROR_INVALID_PARAMETER;
3713 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3714 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3716 release_object( &socket->hdr );
3717 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3719 if (!socket_can_send( socket ))
3721 release_object( &socket->hdr );
3722 return ERROR_INVALID_OPERATION;
3725 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
3727 BOOL async_send, complete_async = FALSE;
3728 struct socket_send *s;
3730 if (InterlockedCompareExchange( &socket->pending_noncontrol_send, 1, 0 ))
3732 WARN( "Previous send is still queued.\n" );
3733 release_object( &socket->hdr );
3734 return ERROR_INVALID_OPERATION;
3736 if (!validate_buffer_type( type, socket->sending_fragment_type ))
3738 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3739 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3740 release_object( &socket->hdr );
3741 return ERROR_INVALID_PARAMETER;
3744 if (!(s = malloc( sizeof(*s) )))
3746 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3747 release_object( &socket->hdr );
3748 return ERROR_OUTOFMEMORY;
3751 AcquireSRWLockExclusive( &socket->send_lock );
3752 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
3753 if (!async_send)
3755 memset( &s->ovr, 0, sizeof(s->ovr) );
3756 if ((ret = socket_send( socket, type, buf, len, &s->ovr )) == WSA_IO_PENDING)
3758 async_send = TRUE;
3759 complete_async = TRUE;
3763 if (async_send)
3765 s->complete_async = complete_async;
3766 TRACE("queueing, complete_async %#x.\n", complete_async);
3767 s->type = type;
3768 s->buf = buf;
3769 s->len = len;
3771 if ((ret = queue_task( &socket->send_q, task_socket_send, &s->task_hdr, &socket->hdr )))
3772 free( s );
3774 if (!async_send || ret)
3776 InterlockedDecrement( &socket->hdr.pending_sends );
3777 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3779 ReleaseSRWLockExclusive( &socket->send_lock );
3780 if (!async_send)
3782 TRACE("sent sync.\n");
3783 free( s );
3784 socket_send_complete( socket, ret, type, len );
3785 ret = ERROR_SUCCESS;
3788 else
3790 if (validate_buffer_type( type, socket->sending_fragment_type ))
3792 ret = socket_send( socket, type, buf, len, NULL );
3794 else
3796 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3797 ret = ERROR_INVALID_PARAMETER;
3801 release_object( &socket->hdr );
3802 return ret;
3805 static DWORD receive_bytes( struct socket *socket, char *buf, DWORD len, DWORD *ret_len, BOOL read_full_buffer )
3807 DWORD err, size = 0, needed = len;
3808 char *ptr = buf;
3809 int received;
3811 if (socket->bytes_in_read_buffer)
3813 size = min( needed, socket->bytes_in_read_buffer );
3814 memcpy( ptr, socket->read_buffer, size );
3815 memmove( socket->read_buffer, socket->read_buffer + size, socket->bytes_in_read_buffer - size );
3816 socket->bytes_in_read_buffer -= size;
3817 needed -= size;
3818 ptr += size;
3820 while (size != len)
3822 if ((err = netconn_recv( socket->netconn, ptr, needed, 0, &received ))) return err;
3823 if (!received) break;
3824 size += received;
3825 if (!read_full_buffer) break;
3826 needed -= received;
3827 ptr += received;
3829 *ret_len = size;
3830 if (size != len && (read_full_buffer || !size)) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3831 return ERROR_SUCCESS;
3834 static BOOL is_supported_opcode( enum socket_opcode opcode )
3836 switch (opcode)
3838 case SOCKET_OPCODE_CONTINUE:
3839 case SOCKET_OPCODE_TEXT:
3840 case SOCKET_OPCODE_BINARY:
3841 case SOCKET_OPCODE_CLOSE:
3842 case SOCKET_OPCODE_PING:
3843 case SOCKET_OPCODE_PONG:
3844 return TRUE;
3845 default:
3846 FIXME( "opcode %02x not handled\n", opcode );
3847 return FALSE;
3851 static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_opcode *opcode, BOOL *final )
3853 DWORD ret, len, count;
3854 char hdr[2];
3856 if ((ret = receive_bytes( socket, hdr, sizeof(hdr), &count, TRUE ))) return ret;
3857 if ((hdr[0] & RESERVED_BIT) || (hdr[1] & MASK_BIT) || !is_supported_opcode( hdr[0] & 0xf ))
3859 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3861 *opcode = hdr[0] & 0xf;
3862 *final = hdr[0] & FIN_BIT;
3863 TRACE("received %02x frame, final %#x\n", *opcode, *final);
3865 len = hdr[1] & ~MASK_BIT;
3866 if (len == 126)
3868 USHORT len16;
3869 if ((ret = receive_bytes( socket, (char *)&len16, sizeof(len16), &count, TRUE ))) return ret;
3870 len = RtlUshortByteSwap( len16 );
3872 else if (len == 127)
3874 ULONGLONG len64;
3875 if ((ret = receive_bytes( socket, (char *)&len64, sizeof(len64), &count, TRUE ))) return ret;
3876 if ((len64 = RtlUlonglongByteSwap( len64 )) > ~0u) return ERROR_NOT_SUPPORTED;
3877 len = len64;
3880 *ret_len = len;
3881 return ERROR_SUCCESS;
3884 static void task_socket_send_pong( void *ctx, BOOL abort )
3886 struct socket_send *s = ctx;
3887 struct socket *socket = (struct socket *)s->task_hdr.obj;
3889 if (abort) return;
3891 TRACE("running %p\n", ctx);
3893 if (s->complete_async) complete_send_frame( socket, &s->ovr, NULL );
3894 else send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3896 send_io_complete( &socket->hdr );
3899 static DWORD socket_send_pong( struct socket *socket )
3901 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
3903 BOOL async_send, complete_async = FALSE;
3904 struct socket_send *s;
3905 DWORD ret = 0;
3907 if (!(s = malloc( sizeof(*s) ))) return ERROR_OUTOFMEMORY;
3909 AcquireSRWLockExclusive( &socket->send_lock );
3910 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1;
3911 if (!async_send)
3913 memset( &s->ovr, 0, sizeof(s->ovr) );
3914 if ((ret = send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, &s->ovr )) == WSA_IO_PENDING)
3916 async_send = TRUE;
3917 complete_async = TRUE;
3921 if (async_send)
3923 s->complete_async = complete_async;
3924 if ((ret = queue_task( &socket->send_q, task_socket_send_pong, &s->task_hdr, &socket->hdr )))
3926 InterlockedDecrement( &socket->hdr.pending_sends );
3927 free( s );
3930 else
3932 InterlockedDecrement( &socket->hdr.pending_sends );
3933 free( s );
3935 ReleaseSRWLockExclusive( &socket->send_lock );
3936 return ret;
3938 return send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3941 static DWORD socket_drain( struct socket *socket )
3943 DWORD ret, count;
3945 while (socket->read_size)
3947 char buf[1024];
3948 if ((ret = receive_bytes( socket, buf, min(socket->read_size, sizeof(buf)), &count, TRUE ))) return ret;
3949 socket->read_size -= count;
3951 return ERROR_SUCCESS;
3954 static DWORD receive_close_status( struct socket *socket, DWORD len )
3956 DWORD reason_len, ret;
3958 socket->close_frame_received = TRUE;
3959 if ((len && (len < sizeof(socket->status) || len > sizeof(socket->status) + sizeof(socket->reason))))
3960 return (socket->close_frame_receive_err = ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
3962 if (!len) return (socket->close_frame_receive_err = ERROR_SUCCESS);
3964 reason_len = len - sizeof(socket->status);
3965 if ((ret = receive_bytes( socket, (char *)&socket->status, sizeof(socket->status), &len, TRUE )))
3966 return (socket->close_frame_receive_err = ret);
3967 socket->status = RtlUshortByteSwap( socket->status );
3968 return (socket->close_frame_receive_err
3969 = receive_bytes( socket, socket->reason, reason_len, &socket->reason_len, TRUE ));
3972 static DWORD handle_control_frame( struct socket *socket )
3974 DWORD ret;
3976 TRACE( "opcode %u.\n", socket->opcode );
3978 switch (socket->opcode)
3980 case SOCKET_OPCODE_PING:
3981 return socket_send_pong( socket );
3983 case SOCKET_OPCODE_PONG:
3984 return socket_drain( socket );
3986 case SOCKET_OPCODE_CLOSE:
3987 if (socket->state < SOCKET_STATE_SHUTDOWN)
3988 WARN( "SOCKET_OPCODE_CLOSE received, socket->state %u.\n", socket->state );
3989 if (socket->close_frame_received)
3991 FIXME( "Close frame already received.\n" );
3992 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3995 ret = receive_close_status( socket, socket->read_size );
3996 socket->read_size = 0;
3997 return ret;
3999 default:
4000 ERR("unhandled control opcode %02x\n", socket->opcode);
4001 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
4004 return ERROR_SUCCESS;
4007 static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( struct socket *socket, enum socket_opcode opcode, BOOL fragment )
4009 enum fragment_type frag_type = socket->receiving_fragment_type;
4011 switch (opcode)
4013 case SOCKET_OPCODE_TEXT:
4014 if (frag_type && frag_type != SOCKET_FRAGMENT_UTF8)
4015 FIXME( "Received SOCKET_OPCODE_TEXT with prev fragment %u.\n", frag_type );
4016 if (fragment)
4018 socket->receiving_fragment_type = SOCKET_FRAGMENT_UTF8;
4019 return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
4021 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
4022 return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
4024 case SOCKET_OPCODE_BINARY:
4025 if (frag_type && frag_type != SOCKET_FRAGMENT_BINARY)
4026 FIXME( "Received SOCKET_OPCODE_BINARY with prev fragment %u.\n", frag_type );
4027 if (fragment)
4029 socket->receiving_fragment_type = SOCKET_FRAGMENT_BINARY;
4030 return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
4032 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
4033 return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
4035 case SOCKET_OPCODE_CONTINUE:
4036 if (!frag_type)
4038 FIXME( "Received SOCKET_OPCODE_CONTINUE without starting fragment.\n" );
4039 return ~0u;
4041 if (fragment)
4043 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
4044 : WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
4046 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
4047 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
4048 : WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
4050 case SOCKET_OPCODE_CLOSE:
4051 return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
4053 default:
4054 FIXME("opcode %02x not handled\n", opcode);
4055 return ~0u;
4059 static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len,
4060 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
4062 BOOL final = socket->last_receive_final;
4063 DWORD count, ret = ERROR_SUCCESS;
4065 if (!socket->read_size)
4067 for (;;)
4069 if (!(ret = receive_frame( socket, &socket->read_size, &socket->opcode, &final )))
4071 if (!(socket->opcode & CONTROL_BIT) || (ret = handle_control_frame( socket ))
4072 || socket->opcode == SOCKET_OPCODE_CLOSE) break;
4074 else if (ret == WSAETIMEDOUT) ret = socket_send_pong( socket );
4075 if (ret) break;
4078 if (!ret)
4080 socket->last_receive_final = final;
4081 ret = receive_bytes( socket, buf, min(len, socket->read_size), &count, FALSE );
4083 if (!ret)
4085 if (count < socket->read_size)
4086 WARN("Short read.\n");
4088 socket->read_size -= count;
4089 *ret_len = count;
4090 *ret_type = map_opcode( socket, socket->opcode, !final || socket->read_size != 0 );
4091 TRACE( "len %lu, *ret_len %lu, *ret_type %u.\n", len, *ret_len, *ret_type );
4092 if (*ret_type == ~0u)
4094 FIXME( "Unexpected opcode %u.\n", socket->opcode );
4095 socket->read_size = 0;
4096 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
4099 return ret;
4102 static void socket_receive_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
4104 if (!ret)
4106 WINHTTP_WEB_SOCKET_STATUS status;
4107 status.dwBytesTransferred = len;
4108 status.eBufferType = type;
4109 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
4111 else
4113 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4114 result.AsyncResult.dwResult = 0;
4115 result.AsyncResult.dwError = ret;
4116 result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
4117 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4121 static void task_socket_receive( void *ctx, BOOL abort )
4123 struct socket_receive *r = ctx;
4124 struct socket *socket = (struct socket *)r->task_hdr.obj;
4125 DWORD ret, count;
4126 WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
4128 if (abort)
4130 socket_receive_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED, 0, 0 );
4131 return;
4134 TRACE("running %p\n", ctx);
4135 ret = socket_receive( socket, r->buf, r->len, &count, &type );
4136 receive_io_complete( socket );
4137 if (task_needs_completion( &r->task_hdr ))
4138 socket_receive_complete( socket, ret, type, count );
4141 DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *ret_len,
4142 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
4144 struct socket *socket;
4145 DWORD ret;
4147 TRACE( "%p, %p, %lu, %p, %p\n", hsocket, buf, len, ret_len, ret_type );
4149 if (!buf || !len) return ERROR_INVALID_PARAMETER;
4151 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4152 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4154 release_object( &socket->hdr );
4155 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4157 if (!socket_can_receive( socket ))
4159 release_object( &socket->hdr );
4160 return ERROR_INVALID_OPERATION;
4163 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4165 struct socket_receive *r;
4167 if (InterlockedIncrement( &socket->hdr.pending_receives ) > 1)
4169 InterlockedDecrement( &socket->hdr.pending_receives );
4170 WARN( "Attempt to queue receive while another is pending.\n" );
4171 release_object( &socket->hdr );
4172 return ERROR_INVALID_OPERATION;
4175 if (!(r = malloc( sizeof(*r) )))
4177 InterlockedDecrement( &socket->hdr.pending_receives );
4178 release_object( &socket->hdr );
4179 return ERROR_OUTOFMEMORY;
4181 r->buf = buf;
4182 r->len = len;
4184 if ((ret = queue_task( &socket->recv_q, task_socket_receive, &r->task_hdr, &socket->hdr )))
4186 InterlockedDecrement( &socket->hdr.pending_receives );
4187 free( r );
4190 else ret = socket_receive( socket, buf, len, ret_len, ret_type );
4192 release_object( &socket->hdr );
4193 return ret;
4196 static void socket_shutdown_complete( struct socket *socket, DWORD ret )
4198 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 );
4199 else
4201 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4202 result.AsyncResult.dwResult = API_WRITE_DATA;
4203 result.AsyncResult.dwError = ret;
4204 result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION;
4205 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4209 static void task_socket_shutdown( void *ctx, BOOL abort )
4211 struct socket_shutdown *s = ctx;
4212 struct socket *socket = (struct socket *)s->task_hdr.obj;
4213 DWORD ret;
4215 if (abort) return;
4217 TRACE("running %p\n", ctx);
4219 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->reason );
4220 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, s->status, s->reason, s->len, TRUE, NULL );
4222 send_io_complete( &socket->hdr );
4223 if (s->send_callback) socket_shutdown_complete( socket, ret );
4226 static DWORD send_socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len,
4227 BOOL send_callback)
4229 DWORD ret;
4231 if (socket->state < SOCKET_STATE_SHUTDOWN) socket->state = SOCKET_STATE_SHUTDOWN;
4233 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4235 BOOL async_send, complete_async = FALSE;
4236 struct socket_shutdown *s;
4238 if (!(s = malloc( sizeof(*s) ))) return FALSE;
4240 AcquireSRWLockExclusive( &socket->send_lock );
4241 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
4242 if (!async_send)
4244 memset( &s->ovr, 0, sizeof(s->ovr) );
4245 if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, &s->ovr )) == WSA_IO_PENDING)
4247 async_send = TRUE;
4248 complete_async = TRUE;
4252 if (async_send)
4254 s->complete_async = complete_async;
4255 s->status = status;
4256 memcpy( s->reason, reason, len );
4257 s->len = len;
4258 s->send_callback = send_callback;
4260 if ((ret = queue_task( &socket->send_q, task_socket_shutdown, &s->task_hdr, &socket->hdr )))
4262 InterlockedDecrement( &socket->hdr.pending_sends );
4263 free( s );
4266 else InterlockedDecrement( &socket->hdr.pending_sends );
4267 ReleaseSRWLockExclusive( &socket->send_lock );
4268 if (!async_send)
4270 free( s );
4271 if (send_callback)
4273 socket_shutdown_complete( socket, ret );
4274 ret = ERROR_SUCCESS;
4278 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL );
4280 return ret;
4283 DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4285 struct socket *socket;
4286 DWORD ret;
4288 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4290 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4292 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4293 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4295 release_object( &socket->hdr );
4296 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4298 if (socket->state >= SOCKET_STATE_SHUTDOWN)
4300 release_object( &socket->hdr );
4301 return ERROR_INVALID_OPERATION;
4304 ret = send_socket_shutdown( socket, status, reason, len, TRUE );
4305 release_object( &socket->hdr );
4306 return ret;
4309 static DWORD socket_close( struct socket *socket )
4311 BOOL final = FALSE;
4312 DWORD ret, count;
4314 if (socket->close_frame_received) return socket->close_frame_receive_err;
4316 if ((ret = socket_drain( socket ))) return ret;
4318 while (1)
4320 if ((ret = receive_frame( socket, &count, &socket->opcode, &final ))) return ret;
4321 if (socket->opcode == SOCKET_OPCODE_CLOSE) break;
4323 socket->read_size = count;
4324 if ((ret = socket_drain( socket ))) return ret;
4326 if (!final)
4327 FIXME( "Received close opcode without FIN bit.\n" );
4329 return receive_close_status( socket, count );
4332 static void socket_close_complete( struct socket *socket, DWORD ret )
4334 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NULL, 0 );
4335 else
4337 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4338 result.AsyncResult.dwResult = API_READ_DATA; /* FIXME */
4339 result.AsyncResult.dwError = ret;
4340 result.Operation = WINHTTP_WEB_SOCKET_CLOSE_OPERATION;
4341 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4345 static void task_socket_close( void *ctx, BOOL abort )
4347 struct socket_shutdown *s = ctx;
4348 struct socket *socket = (struct socket *)s->task_hdr.obj;
4349 DWORD ret;
4351 if (abort)
4353 socket_close_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED );
4354 return;
4357 TRACE("running %p\n", ctx);
4359 ret = socket_close( socket );
4360 receive_io_complete( socket );
4361 if (task_needs_completion( &s->task_hdr ))
4362 socket_close_complete( socket, ret );
4365 DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4367 enum socket_state prev_state;
4368 LONG pending_receives = 0;
4369 struct socket *socket;
4370 DWORD ret;
4372 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4374 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4376 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4377 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4379 release_object( &socket->hdr );
4380 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4382 if (socket->state >= SOCKET_STATE_CLOSED)
4384 release_object( &socket->hdr );
4385 return ERROR_INVALID_OPERATION;
4388 prev_state = socket->state;
4389 socket->state = SOCKET_STATE_CLOSED;
4391 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4393 pending_receives = InterlockedIncrement( &socket->hdr.pending_receives );
4394 cancel_queue( &socket->recv_q );
4397 if (prev_state < SOCKET_STATE_SHUTDOWN && (ret = send_socket_shutdown( socket, status, reason, len, FALSE )))
4398 goto done;
4400 if (pending_receives == 1 && socket->close_frame_received)
4402 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4403 socket_close_complete( socket, socket->close_frame_receive_err );
4404 goto done;
4407 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4409 struct socket_shutdown *s;
4411 if (!(s = calloc( 1, sizeof(*s) )))
4413 ret = ERROR_OUTOFMEMORY;
4414 goto done;
4416 if ((ret = queue_task( &socket->recv_q, task_socket_close, &s->task_hdr, &socket->hdr )))
4418 InterlockedDecrement( &socket->hdr.pending_receives );
4419 free( s );
4422 else ret = socket_close( socket );
4424 done:
4425 release_object( &socket->hdr );
4426 return ret;
4429 DWORD WINAPI WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket, USHORT *status, void *reason, DWORD len,
4430 DWORD *ret_len )
4432 struct socket *socket;
4433 DWORD ret;
4435 TRACE( "%p, %p, %p, %lu, %p\n", hsocket, status, reason, len, ret_len );
4437 if (!status || (len && !reason) || !ret_len) return ERROR_INVALID_PARAMETER;
4439 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4440 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4442 release_object( &socket->hdr );
4443 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4446 if (!socket->close_frame_received || socket->close_frame_receive_err)
4448 ret = socket->close_frame_received ? socket->close_frame_receive_err : ERROR_INVALID_OPERATION;
4449 release_object( &socket->hdr );
4450 return ret;
4452 *status = socket->status;
4453 *ret_len = socket->reason_len;
4454 if (socket->reason_len > len) ret = ERROR_INSUFFICIENT_BUFFER;
4455 else
4457 memcpy( reason, socket->reason, socket->reason_len );
4458 ret = ERROR_SUCCESS;
4461 release_object( &socket->hdr );
4462 return ret;
4465 enum request_state
4467 REQUEST_STATE_INITIALIZED,
4468 REQUEST_STATE_CANCELLED,
4469 REQUEST_STATE_OPEN,
4470 REQUEST_STATE_SENT,
4471 REQUEST_STATE_RESPONSE_RECEIVED
4474 struct winhttp_request
4476 IWinHttpRequest IWinHttpRequest_iface;
4477 LONG refs;
4478 CRITICAL_SECTION cs;
4479 enum request_state state;
4480 HINTERNET hsession;
4481 HINTERNET hconnect;
4482 HINTERNET hrequest;
4483 VARIANT data;
4484 WCHAR *verb;
4485 HANDLE done;
4486 HANDLE wait;
4487 HANDLE cancel;
4488 BOOL proc_running;
4489 char *buffer;
4490 DWORD offset;
4491 DWORD bytes_available;
4492 DWORD bytes_read;
4493 DWORD error;
4494 DWORD logon_policy;
4495 DWORD disable_feature;
4496 LONG resolve_timeout;
4497 LONG connect_timeout;
4498 LONG send_timeout;
4499 LONG receive_timeout;
4500 WINHTTP_PROXY_INFO proxy;
4501 BOOL async;
4502 UINT url_codepage;
4505 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
4507 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
4510 static ULONG WINAPI winhttp_request_AddRef(
4511 IWinHttpRequest *iface )
4513 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4514 return InterlockedIncrement( &request->refs );
4517 /* critical section must be held */
4518 static void cancel_request( struct winhttp_request *request )
4520 if (request->state <= REQUEST_STATE_CANCELLED) return;
4522 if (request->proc_running)
4524 SetEvent( request->cancel );
4525 LeaveCriticalSection( &request->cs );
4527 WaitForSingleObject( request->done, INFINITE );
4529 EnterCriticalSection( &request->cs );
4531 request->state = REQUEST_STATE_CANCELLED;
4534 /* critical section must be held */
4535 static void free_request( struct winhttp_request *request )
4537 if (request->state < REQUEST_STATE_INITIALIZED) return;
4538 WinHttpCloseHandle( request->hrequest );
4539 WinHttpCloseHandle( request->hconnect );
4540 WinHttpCloseHandle( request->hsession );
4541 CloseHandle( request->done );
4542 CloseHandle( request->wait );
4543 CloseHandle( request->cancel );
4544 free( request->proxy.lpszProxy );
4545 free( request->proxy.lpszProxyBypass );
4546 free( request->buffer );
4547 free( request->verb );
4548 VariantClear( &request->data );
4551 static ULONG WINAPI winhttp_request_Release(
4552 IWinHttpRequest *iface )
4554 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4555 LONG refs = InterlockedDecrement( &request->refs );
4556 if (!refs)
4558 TRACE("destroying %p\n", request);
4560 EnterCriticalSection( &request->cs );
4561 cancel_request( request );
4562 free_request( request );
4563 LeaveCriticalSection( &request->cs );
4564 request->cs.DebugInfo->Spare[0] = 0;
4565 DeleteCriticalSection( &request->cs );
4566 free( request );
4568 return refs;
4571 static HRESULT WINAPI winhttp_request_QueryInterface(
4572 IWinHttpRequest *iface,
4573 REFIID riid,
4574 void **obj )
4576 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4578 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
4580 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
4581 IsEqualGUID( riid, &IID_IDispatch ) ||
4582 IsEqualGUID( riid, &IID_IUnknown ))
4584 *obj = iface;
4586 else
4588 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4589 return E_NOINTERFACE;
4591 IWinHttpRequest_AddRef( iface );
4592 return S_OK;
4595 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
4596 IWinHttpRequest *iface,
4597 UINT *count )
4599 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4601 TRACE("%p, %p\n", request, count);
4602 *count = 1;
4603 return S_OK;
4606 enum type_id
4608 IWinHttpRequest_tid,
4609 last_tid
4612 static ITypeLib *winhttp_typelib;
4613 static ITypeInfo *winhttp_typeinfo[last_tid];
4615 static REFIID winhttp_tid_id[] =
4617 &IID_IWinHttpRequest
4620 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
4622 HRESULT hr;
4624 if (!winhttp_typelib)
4626 ITypeLib *typelib;
4628 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
4629 if (FAILED(hr))
4631 ERR( "LoadRegTypeLib failed: %#lx\n", hr );
4632 return hr;
4634 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
4635 ITypeLib_Release( typelib );
4637 if (!winhttp_typeinfo[tid])
4639 ITypeInfo *typeinfo;
4641 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
4642 if (FAILED(hr))
4644 ERR( "GetTypeInfoOfGuid(%s) failed: %#lx\n", debugstr_guid(winhttp_tid_id[tid]), hr );
4645 return hr;
4647 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
4648 ITypeInfo_Release( typeinfo );
4650 *ret = winhttp_typeinfo[tid];
4651 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
4652 return S_OK;
4655 void release_typelib(void)
4657 unsigned i;
4659 for (i = 0; i < ARRAY_SIZE(winhttp_typeinfo); i++)
4660 if (winhttp_typeinfo[i])
4661 ITypeInfo_Release(winhttp_typeinfo[i]);
4663 if (winhttp_typelib)
4664 ITypeLib_Release(winhttp_typelib);
4667 static HRESULT WINAPI winhttp_request_GetTypeInfo(
4668 IWinHttpRequest *iface,
4669 UINT index,
4670 LCID lcid,
4671 ITypeInfo **info )
4673 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4674 TRACE( "%p, %u, %lu, %p\n", request, index, lcid, info );
4676 return get_typeinfo( IWinHttpRequest_tid, info );
4679 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
4680 IWinHttpRequest *iface,
4681 REFIID riid,
4682 LPOLESTR *names,
4683 UINT count,
4684 LCID lcid,
4685 DISPID *dispid )
4687 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4688 ITypeInfo *typeinfo;
4689 HRESULT hr;
4691 TRACE( "%p, %s, %p, %u, %lu, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid );
4693 if (!names || !count || !dispid) return E_INVALIDARG;
4695 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4696 if (SUCCEEDED(hr))
4698 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
4699 ITypeInfo_Release( typeinfo );
4701 return hr;
4704 static HRESULT WINAPI winhttp_request_Invoke(
4705 IWinHttpRequest *iface,
4706 DISPID member,
4707 REFIID riid,
4708 LCID lcid,
4709 WORD flags,
4710 DISPPARAMS *params,
4711 VARIANT *result,
4712 EXCEPINFO *excep_info,
4713 UINT *arg_err )
4715 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4716 ITypeInfo *typeinfo;
4717 HRESULT hr;
4719 TRACE( "%p, %ld, %s, %lu, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
4720 lcid, flags, params, result, excep_info, arg_err );
4722 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
4724 if (member == DISPID_HTTPREQUEST_OPTION)
4726 VARIANT ret_value, option;
4727 UINT err_pos;
4729 if (!result) result = &ret_value;
4730 if (!arg_err) arg_err = &err_pos;
4732 VariantInit( &option );
4733 VariantInit( result );
4735 if (!flags) return S_OK;
4737 if (flags == DISPATCH_PROPERTYPUT)
4739 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4740 if (FAILED(hr)) return hr;
4742 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
4743 if (FAILED(hr))
4744 WARN( "put_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4745 return hr;
4747 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
4749 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4750 if (FAILED(hr)) return hr;
4752 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
4753 if (FAILED(hr))
4754 WARN( "get_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4755 return hr;
4758 FIXME("unsupported flags %x\n", flags);
4759 return E_NOTIMPL;
4762 /* fallback to standard implementation */
4764 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4765 if (SUCCEEDED(hr))
4767 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
4768 params, result, excep_info, arg_err );
4769 ITypeInfo_Release( typeinfo );
4771 return hr;
4774 static HRESULT WINAPI winhttp_request_SetProxy(
4775 IWinHttpRequest *iface,
4776 HTTPREQUEST_PROXY_SETTING proxy_setting,
4777 VARIANT proxy_server,
4778 VARIANT bypass_list )
4780 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4781 DWORD err = ERROR_SUCCESS;
4783 TRACE( "%p, %lu, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
4784 debugstr_variant(&bypass_list) );
4786 EnterCriticalSection( &request->cs );
4787 switch (proxy_setting)
4789 case HTTPREQUEST_PROXYSETTING_DEFAULT:
4790 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
4791 free( request->proxy.lpszProxy );
4792 free( request->proxy.lpszProxyBypass );
4793 request->proxy.lpszProxy = NULL;
4794 request->proxy.lpszProxyBypass = NULL;
4795 break;
4797 case HTTPREQUEST_PROXYSETTING_DIRECT:
4798 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
4799 free( request->proxy.lpszProxy );
4800 free( request->proxy.lpszProxyBypass );
4801 request->proxy.lpszProxy = NULL;
4802 request->proxy.lpszProxyBypass = NULL;
4803 break;
4805 case HTTPREQUEST_PROXYSETTING_PROXY:
4806 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
4807 if (V_VT( &proxy_server ) == VT_BSTR)
4809 free( request->proxy.lpszProxy );
4810 request->proxy.lpszProxy = wcsdup( V_BSTR( &proxy_server ) );
4812 if (V_VT( &bypass_list ) == VT_BSTR)
4814 free( request->proxy.lpszProxyBypass );
4815 request->proxy.lpszProxyBypass = wcsdup( V_BSTR( &bypass_list ) );
4817 break;
4819 default:
4820 err = ERROR_INVALID_PARAMETER;
4821 break;
4823 LeaveCriticalSection( &request->cs );
4824 return HRESULT_FROM_WIN32( err );
4827 static HRESULT WINAPI winhttp_request_SetCredentials(
4828 IWinHttpRequest *iface,
4829 BSTR username,
4830 BSTR password,
4831 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
4833 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4834 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
4835 DWORD err = ERROR_SUCCESS;
4837 TRACE( "%p, %s, %p, %#lx\n", request, debugstr_w(username), password, flags );
4839 EnterCriticalSection( &request->cs );
4840 if (request->state < REQUEST_STATE_OPEN)
4842 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4843 goto done;
4845 switch (flags)
4847 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
4848 target = WINHTTP_AUTH_TARGET_SERVER;
4849 break;
4850 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
4851 target = WINHTTP_AUTH_TARGET_PROXY;
4852 break;
4853 default:
4854 err = ERROR_INVALID_PARAMETER;
4855 goto done;
4857 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
4859 err = GetLastError();
4861 done:
4862 LeaveCriticalSection( &request->cs );
4863 return HRESULT_FROM_WIN32( err );
4866 static void initialize_request( struct winhttp_request *request )
4868 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
4869 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
4870 request->done = CreateEventW( NULL, FALSE, FALSE, NULL );
4871 request->connect_timeout = 60000;
4872 request->send_timeout = 30000;
4873 request->receive_timeout = 30000;
4874 request->url_codepage = CP_UTF8;
4875 VariantInit( &request->data );
4876 request->state = REQUEST_STATE_INITIALIZED;
4879 static void reset_request( struct winhttp_request *request )
4881 cancel_request( request );
4882 WinHttpCloseHandle( request->hrequest );
4883 request->hrequest = NULL;
4884 WinHttpCloseHandle( request->hconnect );
4885 request->hconnect = NULL;
4886 free( request->buffer );
4887 request->buffer = NULL;
4888 free( request->verb );
4889 request->verb = NULL;
4890 request->offset = 0;
4891 request->bytes_available = 0;
4892 request->bytes_read = 0;
4893 request->error = ERROR_SUCCESS;
4894 request->logon_policy = 0;
4895 request->disable_feature = 0;
4896 request->async = FALSE;
4897 request->connect_timeout = 60000;
4898 request->send_timeout = 30000;
4899 request->receive_timeout = 30000;
4900 request->url_codepage = CP_UTF8;
4901 free( request->proxy.lpszProxy );
4902 request->proxy.lpszProxy = NULL;
4903 free( request->proxy.lpszProxyBypass );
4904 request->proxy.lpszProxyBypass = NULL;
4905 VariantClear( &request->data );
4906 request->state = REQUEST_STATE_INITIALIZED;
4909 static HRESULT WINAPI winhttp_request_Open(
4910 IWinHttpRequest *iface,
4911 BSTR method,
4912 BSTR url,
4913 VARIANT async )
4915 static const WCHAR httpsW[] = {'h','t','t','p','s'};
4916 static const WCHAR *acceptW[] = {L"*/*", NULL};
4917 static const WCHAR user_agentW[] = L"Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)";
4918 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4919 URL_COMPONENTS uc;
4920 WCHAR *hostname, *path = NULL, *verb = NULL;
4921 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
4923 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
4924 debugstr_variant(&async));
4926 if (!method || !url) return E_INVALIDARG;
4928 memset( &uc, 0, sizeof(uc) );
4929 uc.dwStructSize = sizeof(uc);
4930 uc.dwSchemeLength = ~0u;
4931 uc.dwHostNameLength = ~0u;
4932 uc.dwUrlPathLength = ~0u;
4933 uc.dwExtraInfoLength = ~0u;
4934 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( GetLastError() );
4936 EnterCriticalSection( &request->cs );
4937 reset_request( request );
4939 if (!(hostname = malloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
4940 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
4941 hostname[uc.dwHostNameLength] = 0;
4943 if (!(path = malloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
4944 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
4945 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
4947 if (!(verb = wcsdup( method ))) goto error;
4948 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
4949 else request->async = FALSE;
4951 if (!request->hsession)
4953 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
4954 WINHTTP_FLAG_ASYNC )))
4956 err = GetLastError();
4957 goto error;
4959 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4961 WinHttpCloseHandle( request->hsession );
4962 request->hsession = NULL;
4963 err = GetLastError();
4964 goto error;
4967 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4969 err = GetLastError();
4970 goto error;
4973 len = ARRAY_SIZE( httpsW );
4974 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
4976 flags |= WINHTTP_FLAG_SECURE;
4978 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
4980 err = GetLastError();
4981 goto error;
4983 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
4985 request->state = REQUEST_STATE_OPEN;
4986 request->verb = verb;
4987 free( hostname );
4988 free( path );
4989 LeaveCriticalSection( &request->cs );
4990 return S_OK;
4992 error:
4993 WinHttpCloseHandle( request->hconnect );
4994 request->hconnect = NULL;
4995 free( hostname );
4996 free( path );
4997 free( verb );
4998 LeaveCriticalSection( &request->cs );
4999 return HRESULT_FROM_WIN32( err );
5002 static HRESULT WINAPI winhttp_request_SetRequestHeader(
5003 IWinHttpRequest *iface,
5004 BSTR header,
5005 BSTR value )
5007 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5008 DWORD len, err = ERROR_SUCCESS;
5009 WCHAR *str;
5011 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
5013 if (!header) return E_INVALIDARG;
5015 EnterCriticalSection( &request->cs );
5016 if (request->state < REQUEST_STATE_OPEN)
5018 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
5019 goto done;
5021 if (request->state >= REQUEST_STATE_SENT)
5023 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
5024 goto done;
5026 len = lstrlenW( header ) + 4;
5027 if (value) len += lstrlenW( value );
5028 if (!(str = malloc( (len + 1) * sizeof(WCHAR) )))
5030 err = ERROR_OUTOFMEMORY;
5031 goto done;
5033 swprintf( str, len + 1, L"%s: %s\r\n", header, value ? value : L"" );
5034 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
5035 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
5037 err = GetLastError();
5039 free( str );
5041 done:
5042 LeaveCriticalSection( &request->cs );
5043 return HRESULT_FROM_WIN32( err );
5046 static HRESULT WINAPI winhttp_request_GetResponseHeader(
5047 IWinHttpRequest *iface,
5048 BSTR header,
5049 BSTR *value )
5051 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5052 DWORD size, err = ERROR_SUCCESS;
5054 TRACE("%p, %p\n", request, header);
5056 EnterCriticalSection( &request->cs );
5057 if (request->state < REQUEST_STATE_SENT)
5059 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5060 goto done;
5062 if (!header || !value)
5064 err = ERROR_INVALID_PARAMETER;
5065 goto done;
5067 size = 0;
5068 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
5070 err = GetLastError();
5071 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5073 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
5075 err = ERROR_OUTOFMEMORY;
5076 goto done;
5078 err = ERROR_SUCCESS;
5079 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
5081 err = GetLastError();
5082 SysFreeString( *value );
5084 done:
5085 LeaveCriticalSection( &request->cs );
5086 return HRESULT_FROM_WIN32( err );
5089 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
5090 IWinHttpRequest *iface,
5091 BSTR *headers )
5093 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5094 DWORD size, err = ERROR_SUCCESS;
5096 TRACE("%p, %p\n", request, headers);
5098 if (!headers) return E_INVALIDARG;
5100 EnterCriticalSection( &request->cs );
5101 if (request->state < REQUEST_STATE_SENT)
5103 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5104 goto done;
5106 size = 0;
5107 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
5109 err = GetLastError();
5110 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5112 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
5114 err = ERROR_OUTOFMEMORY;
5115 goto done;
5117 err = ERROR_SUCCESS;
5118 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
5120 err = GetLastError();
5121 SysFreeString( *headers );
5123 done:
5124 LeaveCriticalSection( &request->cs );
5125 return HRESULT_FROM_WIN32( err );
5128 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
5130 struct winhttp_request *request = (struct winhttp_request *)context;
5132 switch (status)
5134 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
5135 request->bytes_available = *(DWORD *)buffer;
5136 request->error = ERROR_SUCCESS;
5137 break;
5138 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
5139 request->bytes_read = size;
5140 request->error = ERROR_SUCCESS;
5141 break;
5142 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
5144 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
5145 request->error = result->dwError;
5146 break;
5148 default:
5149 request->error = ERROR_SUCCESS;
5150 break;
5152 SetEvent( request->wait );
5155 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
5157 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
5158 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
5161 static DWORD wait_for_completion( struct winhttp_request *request )
5163 HANDLE handles[2] = { request->wait, request->cancel };
5164 DWORD ret;
5166 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
5168 case WAIT_OBJECT_0:
5169 ret = request->error;
5170 break;
5171 case WAIT_OBJECT_0 + 1:
5172 ret = request->error = ERROR_CANCELLED;
5173 SetEvent( request->done );
5174 break;
5175 default:
5176 ret = request->error = GetLastError();
5177 break;
5179 return ret;
5182 static HRESULT request_receive( struct winhttp_request *request )
5184 DWORD err, size, buflen = 4096;
5186 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
5187 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
5189 return HRESULT_FROM_WIN32( GetLastError() );
5191 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
5192 if (!wcscmp( request->verb, L"HEAD" ))
5194 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5195 return S_OK;
5197 if (!(request->buffer = malloc( buflen ))) return E_OUTOFMEMORY;
5198 request->buffer[0] = 0;
5199 size = 0;
5202 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
5203 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
5205 err = GetLastError();
5206 goto error;
5208 if ((err = wait_for_completion( request ))) goto error;
5209 if (!request->bytes_available) break;
5210 size += request->bytes_available;
5211 if (buflen < size)
5213 char *tmp;
5214 while (buflen < size) buflen *= 2;
5215 if (!(tmp = realloc( request->buffer, buflen )))
5217 err = ERROR_OUTOFMEMORY;
5218 goto error;
5220 request->buffer = tmp;
5222 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
5223 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
5224 request->bytes_available, &request->bytes_read ))
5226 err = GetLastError();
5227 goto error;
5229 if ((err = wait_for_completion( request ))) goto error;
5230 request->offset += request->bytes_read;
5231 } while (request->bytes_read);
5233 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5234 return S_OK;
5236 error:
5237 free( request->buffer );
5238 request->buffer = NULL;
5239 return HRESULT_FROM_WIN32( err );
5242 static DWORD request_set_parameters( struct winhttp_request *request )
5244 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
5245 sizeof(request->proxy) )) return GetLastError();
5247 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
5248 sizeof(request->logon_policy) )) return GetLastError();
5250 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
5251 sizeof(request->disable_feature) )) return GetLastError();
5253 if (!WinHttpSetTimeouts( request->hrequest,
5254 request->resolve_timeout,
5255 request->connect_timeout,
5256 request->send_timeout,
5257 request->receive_timeout )) return GetLastError();
5258 return ERROR_SUCCESS;
5261 static void request_set_utf8_content_type( struct winhttp_request *request )
5263 WCHAR headerW[64];
5264 int len;
5266 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"text/plain" );
5267 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
5269 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"charset=utf-8" );
5270 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
5273 static HRESULT request_send( struct winhttp_request *request )
5275 SAFEARRAY *sa = NULL;
5276 VARIANT data;
5277 char *ptr = NULL;
5278 LONG size = 0;
5279 HRESULT hr;
5280 DWORD err;
5282 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
5283 if (wcscmp( request->verb, L"GET" ))
5285 VariantInit( &data );
5286 if (V_VT( &request->data ) == VT_BSTR)
5288 UINT cp = CP_ACP;
5289 const WCHAR *str = V_BSTR( &request->data );
5290 int i, len = lstrlenW( str );
5292 for (i = 0; i < len; i++)
5294 if (str[i] > 127)
5296 cp = CP_UTF8;
5297 break;
5300 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
5301 if (!(ptr = malloc( size ))) return E_OUTOFMEMORY;
5302 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
5303 if (cp == CP_UTF8) request_set_utf8_content_type( request );
5305 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
5307 sa = V_ARRAY( &data );
5308 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
5309 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
5311 SafeArrayUnaccessData( sa );
5312 return hr;
5314 size++;
5317 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE );
5318 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
5320 err = GetLastError();
5321 goto error;
5323 if ((err = wait_for_completion( request ))) goto error;
5324 if (sa) SafeArrayUnaccessData( sa );
5325 else free( ptr );
5326 request->state = REQUEST_STATE_SENT;
5327 return S_OK;
5329 error:
5330 if (sa) SafeArrayUnaccessData( sa );
5331 else free( ptr );
5332 return HRESULT_FROM_WIN32( err );
5335 static void CALLBACK send_and_receive_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
5337 struct winhttp_request *request = (struct winhttp_request *)ctx;
5338 if (request_send( request ) == S_OK) request_receive( request );
5339 SetEvent( request->done );
5342 /* critical section must be held */
5343 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
5345 HANDLE done = request->done;
5346 DWORD err, ret;
5348 LeaveCriticalSection( &request->cs );
5349 while ((err = MsgWaitForMultipleObjects( 1, &done, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
5351 MSG msg;
5352 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
5354 TranslateMessage( &msg );
5355 DispatchMessageW( &msg );
5358 switch (err)
5360 case WAIT_OBJECT_0:
5361 ret = request->error;
5362 break;
5363 case WAIT_TIMEOUT:
5364 ret = ERROR_TIMEOUT;
5365 break;
5366 default:
5367 ret = GetLastError();
5368 break;
5370 EnterCriticalSection( &request->cs );
5371 if (err == WAIT_OBJECT_0) request->proc_running = FALSE;
5372 return ret;
5375 static HRESULT WINAPI winhttp_request_Send(
5376 IWinHttpRequest *iface,
5377 VARIANT body )
5379 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5380 HRESULT hr;
5382 TRACE("%p, %s\n", request, debugstr_variant(&body));
5384 EnterCriticalSection( &request->cs );
5385 if (request->state < REQUEST_STATE_OPEN)
5387 LeaveCriticalSection( &request->cs );
5388 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
5390 if (request->state >= REQUEST_STATE_SENT)
5392 LeaveCriticalSection( &request->cs );
5393 return S_OK;
5395 VariantClear( &request->data );
5396 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
5398 LeaveCriticalSection( &request->cs );
5399 return hr;
5401 if (!TrySubmitThreadpoolCallback( send_and_receive_proc, request, NULL ))
5403 LeaveCriticalSection( &request->cs );
5404 return HRESULT_FROM_WIN32( GetLastError() );
5406 request->proc_running = TRUE;
5407 if (!request->async)
5409 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
5411 LeaveCriticalSection( &request->cs );
5412 return hr;
5415 static HRESULT WINAPI winhttp_request_get_Status(
5416 IWinHttpRequest *iface,
5417 LONG *status )
5419 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5420 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
5422 TRACE("%p, %p\n", request, status);
5424 if (!status) return E_INVALIDARG;
5426 EnterCriticalSection( &request->cs );
5427 if (request->state < REQUEST_STATE_SENT)
5429 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5430 goto done;
5432 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
5433 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
5435 err = GetLastError();
5436 goto done;
5438 *status = status_code;
5440 done:
5441 LeaveCriticalSection( &request->cs );
5442 return HRESULT_FROM_WIN32( err );
5445 static HRESULT WINAPI winhttp_request_get_StatusText(
5446 IWinHttpRequest *iface,
5447 BSTR *status )
5449 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5450 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
5452 TRACE("%p, %p\n", request, status);
5454 if (!status) return E_INVALIDARG;
5456 EnterCriticalSection( &request->cs );
5457 if (request->state < REQUEST_STATE_SENT)
5459 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5460 goto done;
5462 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
5464 err = GetLastError();
5465 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5467 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
5469 err = ERROR_OUTOFMEMORY;
5470 goto done;
5472 index = 0;
5473 err = ERROR_SUCCESS;
5474 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
5476 err = GetLastError();
5477 SysFreeString( *status );
5479 done:
5480 LeaveCriticalSection( &request->cs );
5481 return HRESULT_FROM_WIN32( err );
5484 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
5486 WCHAR *buffer, *p;
5487 DWORD size;
5489 *codepage = CP_ACP;
5490 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
5491 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5493 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
5494 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
5496 free( buffer );
5497 return GetLastError();
5499 if ((p = wcsstr( buffer, L"charset" )))
5501 p += lstrlenW( L"charset" );
5502 while (*p == ' ') p++;
5503 if (*p++ == '=')
5505 while (*p == ' ') p++;
5506 if (!wcsicmp( p, L"utf-8" )) *codepage = CP_UTF8;
5509 free( buffer );
5511 return ERROR_SUCCESS;
5514 static HRESULT WINAPI winhttp_request_get_ResponseText(
5515 IWinHttpRequest *iface,
5516 BSTR *body )
5518 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5519 UINT codepage;
5520 DWORD err = ERROR_SUCCESS;
5521 int len;
5523 TRACE("%p, %p\n", request, body);
5525 if (!body) return E_INVALIDARG;
5527 EnterCriticalSection( &request->cs );
5528 if (request->state < REQUEST_STATE_SENT)
5530 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5531 goto done;
5533 if ((err = request_get_codepage( request, &codepage ))) goto done;
5534 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
5535 if (!(*body = SysAllocStringLen( NULL, len )))
5537 err = ERROR_OUTOFMEMORY;
5538 goto done;
5540 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
5541 (*body)[len] = 0;
5543 done:
5544 LeaveCriticalSection( &request->cs );
5545 return HRESULT_FROM_WIN32( err );
5548 static HRESULT WINAPI winhttp_request_get_ResponseBody(
5549 IWinHttpRequest *iface,
5550 VARIANT *body )
5552 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5553 SAFEARRAY *sa;
5554 HRESULT hr;
5555 DWORD err = ERROR_SUCCESS;
5556 char *ptr;
5558 TRACE("%p, %p\n", request, body);
5560 if (!body) return E_INVALIDARG;
5562 EnterCriticalSection( &request->cs );
5563 if (request->state < REQUEST_STATE_SENT)
5565 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5566 goto done;
5568 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
5570 err = ERROR_OUTOFMEMORY;
5571 goto done;
5573 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
5575 SafeArrayDestroy( sa );
5576 LeaveCriticalSection( &request->cs );
5577 return hr;
5579 memcpy( ptr, request->buffer, request->offset );
5580 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
5582 SafeArrayDestroy( sa );
5583 LeaveCriticalSection( &request->cs );
5584 return hr;
5586 V_VT( body ) = VT_ARRAY|VT_UI1;
5587 V_ARRAY( body ) = sa;
5589 done:
5590 LeaveCriticalSection( &request->cs );
5591 return HRESULT_FROM_WIN32( err );
5594 struct stream
5596 IStream IStream_iface;
5597 LONG refs;
5598 char *data;
5599 ULARGE_INTEGER pos, size;
5602 static inline struct stream *impl_from_IStream( IStream *iface )
5604 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
5607 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
5609 struct stream *stream = impl_from_IStream( iface );
5611 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
5613 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
5615 *obj = iface;
5617 else
5619 FIXME("interface %s not implemented\n", debugstr_guid(riid));
5620 return E_NOINTERFACE;
5622 IStream_AddRef( iface );
5623 return S_OK;
5626 static ULONG WINAPI stream_AddRef( IStream *iface )
5628 struct stream *stream = impl_from_IStream( iface );
5629 return InterlockedIncrement( &stream->refs );
5632 static ULONG WINAPI stream_Release( IStream *iface )
5634 struct stream *stream = impl_from_IStream( iface );
5635 LONG refs = InterlockedDecrement( &stream->refs );
5636 if (!refs)
5638 free( stream->data );
5639 free( stream );
5641 return refs;
5644 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
5646 struct stream *stream = impl_from_IStream( iface );
5647 ULONG size;
5649 if (stream->pos.QuadPart >= stream->size.QuadPart)
5651 *read = 0;
5652 return S_FALSE;
5655 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
5656 memcpy( buf, stream->data + stream->pos.QuadPart, size );
5657 stream->pos.QuadPart += size;
5658 *read = size;
5660 return S_OK;
5663 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
5665 FIXME("\n");
5666 return E_NOTIMPL;
5669 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
5671 struct stream *stream = impl_from_IStream( iface );
5673 if (origin == STREAM_SEEK_SET)
5674 stream->pos.QuadPart = move.QuadPart;
5675 else if (origin == STREAM_SEEK_CUR)
5676 stream->pos.QuadPart += move.QuadPart;
5677 else if (origin == STREAM_SEEK_END)
5678 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
5680 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
5681 return S_OK;
5684 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
5686 FIXME("\n");
5687 return E_NOTIMPL;
5690 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
5691 ULARGE_INTEGER *written )
5693 FIXME("\n");
5694 return E_NOTIMPL;
5697 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
5699 FIXME("\n");
5700 return E_NOTIMPL;
5703 static HRESULT WINAPI stream_Revert( IStream *iface )
5705 FIXME("\n");
5706 return E_NOTIMPL;
5709 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5711 FIXME("\n");
5712 return E_NOTIMPL;
5715 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5717 FIXME("\n");
5718 return E_NOTIMPL;
5721 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
5723 FIXME("\n");
5724 return E_NOTIMPL;
5727 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
5729 FIXME("\n");
5730 return E_NOTIMPL;
5733 static const IStreamVtbl stream_vtbl =
5735 stream_QueryInterface,
5736 stream_AddRef,
5737 stream_Release,
5738 stream_Read,
5739 stream_Write,
5740 stream_Seek,
5741 stream_SetSize,
5742 stream_CopyTo,
5743 stream_Commit,
5744 stream_Revert,
5745 stream_LockRegion,
5746 stream_UnlockRegion,
5747 stream_Stat,
5748 stream_Clone
5751 static HRESULT WINAPI winhttp_request_get_ResponseStream(
5752 IWinHttpRequest *iface,
5753 VARIANT *body )
5755 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5756 DWORD err = ERROR_SUCCESS;
5757 struct stream *stream;
5759 TRACE("%p, %p\n", request, body);
5761 if (!body) return E_INVALIDARG;
5763 EnterCriticalSection( &request->cs );
5764 if (request->state < REQUEST_STATE_SENT)
5766 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5767 goto done;
5769 if (!(stream = malloc( sizeof(*stream) )))
5771 err = ERROR_OUTOFMEMORY;
5772 goto done;
5774 stream->IStream_iface.lpVtbl = &stream_vtbl;
5775 stream->refs = 1;
5776 if (!(stream->data = malloc( request->offset )))
5778 free( stream );
5779 err = ERROR_OUTOFMEMORY;
5780 goto done;
5782 memcpy( stream->data, request->buffer, request->offset );
5783 stream->pos.QuadPart = 0;
5784 stream->size.QuadPart = request->offset;
5785 V_VT( body ) = VT_UNKNOWN;
5786 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
5788 done:
5789 LeaveCriticalSection( &request->cs );
5790 return HRESULT_FROM_WIN32( err );
5793 static HRESULT WINAPI winhttp_request_get_Option(
5794 IWinHttpRequest *iface,
5795 WinHttpRequestOption option,
5796 VARIANT *value )
5798 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5799 HRESULT hr = S_OK;
5801 TRACE("%p, %u, %p\n", request, option, value);
5803 EnterCriticalSection( &request->cs );
5804 switch (option)
5806 case WinHttpRequestOption_URLCodePage:
5807 V_VT( value ) = VT_I4;
5808 V_I4( value ) = request->url_codepage;
5809 break;
5810 default:
5811 FIXME("unimplemented option %u\n", option);
5812 hr = E_NOTIMPL;
5813 break;
5815 LeaveCriticalSection( &request->cs );
5816 return hr;
5819 static HRESULT WINAPI winhttp_request_put_Option(
5820 IWinHttpRequest *iface,
5821 WinHttpRequestOption option,
5822 VARIANT value )
5824 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5825 HRESULT hr = S_OK;
5827 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
5829 EnterCriticalSection( &request->cs );
5830 switch (option)
5832 case WinHttpRequestOption_EnableRedirects:
5834 if (V_BOOL( &value ))
5835 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
5836 else
5837 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
5838 break;
5840 case WinHttpRequestOption_URLCodePage:
5842 VARIANT cp;
5843 VariantInit( &cp );
5844 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
5845 if (SUCCEEDED( hr ))
5847 request->url_codepage = V_UI4( &cp );
5848 TRACE("URL codepage: %u\n", request->url_codepage);
5850 else if (V_VT( &value ) == VT_BSTR && !wcsicmp( V_BSTR( &value ), L"utf-8" ))
5852 TRACE("URL codepage: UTF-8\n");
5853 request->url_codepage = CP_UTF8;
5854 hr = S_OK;
5856 else
5857 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
5858 break;
5860 default:
5861 FIXME("unimplemented option %u\n", option);
5862 hr = E_NOTIMPL;
5863 break;
5865 LeaveCriticalSection( &request->cs );
5866 return hr;
5869 static HRESULT WINAPI winhttp_request_WaitForResponse(
5870 IWinHttpRequest *iface,
5871 VARIANT timeout,
5872 VARIANT_BOOL *succeeded )
5874 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5875 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
5877 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
5879 EnterCriticalSection( &request->cs );
5880 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
5882 LeaveCriticalSection( &request->cs );
5883 return S_OK;
5885 switch ((err = request_wait( request, msecs )))
5887 case ERROR_TIMEOUT:
5888 if (succeeded) *succeeded = VARIANT_FALSE;
5889 err = ERROR_SUCCESS;
5890 break;
5892 default:
5893 if (succeeded) *succeeded = VARIANT_TRUE;
5894 break;
5896 LeaveCriticalSection( &request->cs );
5897 return HRESULT_FROM_WIN32( err );
5900 static HRESULT WINAPI winhttp_request_Abort(
5901 IWinHttpRequest *iface )
5903 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5905 TRACE("%p\n", request);
5907 EnterCriticalSection( &request->cs );
5908 cancel_request( request );
5909 LeaveCriticalSection( &request->cs );
5910 return S_OK;
5913 static HRESULT WINAPI winhttp_request_SetTimeouts(
5914 IWinHttpRequest *iface,
5915 LONG resolve_timeout,
5916 LONG connect_timeout,
5917 LONG send_timeout,
5918 LONG receive_timeout )
5920 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5922 TRACE( "%p, %ld, %ld, %ld, %ld\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout );
5924 EnterCriticalSection( &request->cs );
5925 request->resolve_timeout = resolve_timeout;
5926 request->connect_timeout = connect_timeout;
5927 request->send_timeout = send_timeout;
5928 request->receive_timeout = receive_timeout;
5929 LeaveCriticalSection( &request->cs );
5930 return S_OK;
5933 static HRESULT WINAPI winhttp_request_SetClientCertificate(
5934 IWinHttpRequest *iface,
5935 BSTR certificate )
5937 FIXME("\n");
5938 return E_NOTIMPL;
5941 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
5942 IWinHttpRequest *iface,
5943 WinHttpRequestAutoLogonPolicy policy )
5945 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5946 HRESULT hr = S_OK;
5948 TRACE("%p, %u\n", request, policy );
5950 EnterCriticalSection( &request->cs );
5951 switch (policy)
5953 case AutoLogonPolicy_Always:
5954 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
5955 break;
5956 case AutoLogonPolicy_OnlyIfBypassProxy:
5957 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
5958 break;
5959 case AutoLogonPolicy_Never:
5960 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
5961 break;
5962 default: hr = E_INVALIDARG;
5963 break;
5965 LeaveCriticalSection( &request->cs );
5966 return hr;
5969 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
5971 winhttp_request_QueryInterface,
5972 winhttp_request_AddRef,
5973 winhttp_request_Release,
5974 winhttp_request_GetTypeInfoCount,
5975 winhttp_request_GetTypeInfo,
5976 winhttp_request_GetIDsOfNames,
5977 winhttp_request_Invoke,
5978 winhttp_request_SetProxy,
5979 winhttp_request_SetCredentials,
5980 winhttp_request_Open,
5981 winhttp_request_SetRequestHeader,
5982 winhttp_request_GetResponseHeader,
5983 winhttp_request_GetAllResponseHeaders,
5984 winhttp_request_Send,
5985 winhttp_request_get_Status,
5986 winhttp_request_get_StatusText,
5987 winhttp_request_get_ResponseText,
5988 winhttp_request_get_ResponseBody,
5989 winhttp_request_get_ResponseStream,
5990 winhttp_request_get_Option,
5991 winhttp_request_put_Option,
5992 winhttp_request_WaitForResponse,
5993 winhttp_request_Abort,
5994 winhttp_request_SetTimeouts,
5995 winhttp_request_SetClientCertificate,
5996 winhttp_request_SetAutoLogonPolicy
5999 HRESULT WinHttpRequest_create( void **obj )
6001 struct winhttp_request *request;
6003 TRACE("%p\n", obj);
6005 if (!(request = calloc( 1, sizeof(*request) ))) return E_OUTOFMEMORY;
6006 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
6007 request->refs = 1;
6008 InitializeCriticalSection( &request->cs );
6009 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
6010 initialize_request( request );
6012 *obj = &request->IWinHttpRequest_iface;
6013 TRACE("returning iface %p\n", *obj);
6014 return S_OK;