riched20: Merge the richole object with the text services object.
[wine.git] / dlls / winhttp / request.c
blob2ff211255eefa2fee444f89f3e4537889560a8ad
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 static DWORD start_queue( struct queue *queue )
127 if (queue->pool) return ERROR_SUCCESS;
129 if (!(queue->pool = CreateThreadpool( NULL ))) return GetLastError();
130 SetThreadpoolThreadMinimum( queue->pool, 1 );
131 SetThreadpoolThreadMaximum( queue->pool, 1 );
133 memset( &queue->env, 0, sizeof(queue->env) );
134 queue->env.Version = 1;
135 queue->env.Pool = queue->pool;
137 TRACE("started %p\n", queue);
138 return ERROR_SUCCESS;
141 void stop_queue( struct queue *queue )
143 if (!queue->pool) return;
144 CloseThreadpool( queue->pool );
145 queue->pool = NULL;
146 TRACE("stopped %p\n", queue);
149 static DWORD queue_task( struct queue *queue, PTP_WORK_CALLBACK task, void *ctx )
151 TP_WORK *work;
152 DWORD ret;
154 if ((ret = start_queue( queue ))) return ret;
156 if (!(work = CreateThreadpoolWork( task, ctx, &queue->env ))) return GetLastError();
157 TRACE("queueing %p in %p\n", work, queue);
158 SubmitThreadpoolWork( work );
159 CloseThreadpoolWork( work );
161 return ERROR_SUCCESS;
164 static void free_header( struct header *header )
166 heap_free( header->field );
167 heap_free( header->value );
168 heap_free( header );
171 static BOOL valid_token_char( WCHAR c )
173 if (c < 32 || c == 127) return FALSE;
174 switch (c)
176 case '(': case ')':
177 case '<': case '>':
178 case '@': case ',':
179 case ';': case ':':
180 case '\\': case '\"':
181 case '/': case '[':
182 case ']': case '?':
183 case '=': case '{':
184 case '}': case ' ':
185 case '\t':
186 return FALSE;
187 default:
188 return TRUE;
192 static struct header *parse_header( const WCHAR *string )
194 const WCHAR *p, *q;
195 struct header *header;
196 int len;
198 p = string;
199 if (!(q = wcschr( p, ':' )))
201 WARN("no ':' in line %s\n", debugstr_w(string));
202 return NULL;
204 if (q == string)
206 WARN("empty field name in line %s\n", debugstr_w(string));
207 return NULL;
209 while (*p != ':')
211 if (!valid_token_char( *p ))
213 WARN("invalid character in field name %s\n", debugstr_w(string));
214 return NULL;
216 p++;
218 len = q - string;
219 if (!(header = heap_alloc_zero( sizeof(struct header) ))) return NULL;
220 if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) )))
222 heap_free( header );
223 return NULL;
225 memcpy( header->field, string, len * sizeof(WCHAR) );
226 header->field[len] = 0;
228 q++; /* skip past colon */
229 while (*q == ' ') q++;
230 len = lstrlenW( q );
232 if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
234 free_header( header );
235 return NULL;
237 memcpy( header->value, q, len * sizeof(WCHAR) );
238 header->value[len] = 0;
240 return header;
243 static int get_header_index( struct request *request, const WCHAR *field, int requested_index, BOOL request_only )
245 int index;
247 TRACE("%s\n", debugstr_w(field));
249 for (index = 0; index < request->num_headers; index++)
251 if (wcsicmp( request->headers[index].field, field )) continue;
252 if (request_only && !request->headers[index].is_request) continue;
253 if (!request_only && request->headers[index].is_request) continue;
255 if (!requested_index) break;
256 requested_index--;
258 if (index >= request->num_headers) index = -1;
259 TRACE("returning %d\n", index);
260 return index;
263 static DWORD insert_header( struct request *request, struct header *header )
265 DWORD count = request->num_headers + 1;
266 struct header *hdrs;
268 if (request->headers)
269 hdrs = heap_realloc_zero( request->headers, sizeof(struct header) * count );
270 else
271 hdrs = heap_alloc_zero( sizeof(struct header) );
272 if (!hdrs) return ERROR_OUTOFMEMORY;
274 request->headers = hdrs;
275 request->headers[count - 1].field = strdupW( header->field );
276 request->headers[count - 1].value = strdupW( header->value );
277 request->headers[count - 1].is_request = header->is_request;
278 request->num_headers = count;
279 return ERROR_SUCCESS;
282 static void delete_header( struct request *request, DWORD index )
284 if (!request->num_headers || index >= request->num_headers) return;
285 request->num_headers--;
287 heap_free( request->headers[index].field );
288 heap_free( request->headers[index].value );
290 memmove( &request->headers[index], &request->headers[index + 1],
291 (request->num_headers - index) * sizeof(struct header) );
292 memset( &request->headers[request->num_headers], 0, sizeof(struct header) );
295 DWORD process_header( struct request *request, const WCHAR *field, const WCHAR *value, DWORD flags, BOOL request_only )
297 int index;
298 struct header hdr;
300 TRACE("%s: %s 0x%08x\n", debugstr_w(field), debugstr_w(value), flags);
302 if ((index = get_header_index( request, field, 0, request_only )) >= 0)
304 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return ERROR_WINHTTP_HEADER_ALREADY_EXISTS;
307 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
309 if (index >= 0)
311 delete_header( request, index );
312 if (!value || !value[0]) return ERROR_SUCCESS;
314 else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
316 hdr.field = (LPWSTR)field;
317 hdr.value = (LPWSTR)value;
318 hdr.is_request = request_only;
319 return insert_header( request, &hdr );
321 else if (value)
324 if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
325 index >= 0)
327 WCHAR *tmp;
328 int len, len_orig, len_value;
329 struct header *header = &request->headers[index];
331 len_orig = lstrlenW( header->value );
332 len_value = lstrlenW( value );
334 len = len_orig + len_value + 2;
335 if (!(tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
336 header->value = tmp;
337 header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
338 header->value[len_orig++] = ' ';
340 memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
341 header->value[len] = 0;
342 return ERROR_SUCCESS;
344 else
346 hdr.field = (LPWSTR)field;
347 hdr.value = (LPWSTR)value;
348 hdr.is_request = request_only;
349 return insert_header( request, &hdr );
353 return ERROR_SUCCESS;
356 DWORD add_request_headers( struct request *request, const WCHAR *headers, DWORD len, DWORD flags )
358 DWORD ret = ERROR_WINHTTP_INVALID_HEADER;
359 WCHAR *buffer, *p, *q;
360 struct header *header;
362 if (len == ~0u) len = lstrlenW( headers );
363 if (!len) return ERROR_SUCCESS;
364 if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
365 memcpy( buffer, headers, len * sizeof(WCHAR) );
366 buffer[len] = 0;
368 p = buffer;
371 q = p;
372 while (*q)
374 if (q[0] == '\n' && q[1] == '\r')
376 q[0] = '\r';
377 q[1] = '\n';
379 if (q[0] == '\r' && q[1] == '\n') break;
380 q++;
382 if (!*p) break;
383 if (*q == '\r')
385 *q = 0;
386 q += 2; /* jump over \r\n */
388 if ((header = parse_header( p )))
390 ret = process_header( request, header->field, header->value, flags, TRUE );
391 free_header( header );
393 p = q;
394 } while (!ret);
396 heap_free( buffer );
397 return ret;
400 /***********************************************************************
401 * WinHttpAddRequestHeaders (winhttp.@)
403 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags )
405 DWORD ret;
406 struct request *request;
408 TRACE("%p, %s, %u, 0x%08x\n", hrequest, debugstr_wn(headers, len), len, flags);
410 if (!headers || !len)
412 SetLastError( ERROR_INVALID_PARAMETER );
413 return FALSE;
415 if (!(request = (struct request *)grab_object( hrequest )))
417 SetLastError( ERROR_INVALID_HANDLE );
418 return FALSE;
420 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
422 release_object( &request->hdr );
423 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
424 return FALSE;
427 ret = add_request_headers( request, headers, len, flags );
429 release_object( &request->hdr );
430 SetLastError( ret );
431 return !ret;
434 static WCHAR *build_absolute_request_path( struct request *request, const WCHAR **path )
436 const WCHAR *scheme;
437 WCHAR *ret;
438 int len, offset;
440 scheme = (request->netconn ? request->netconn->secure : (request->hdr.flags & WINHTTP_FLAG_SECURE)) ? L"https" : L"http";
442 len = lstrlenW( scheme ) + lstrlenW( request->connect->hostname ) + 4; /* '://' + nul */
443 if (request->connect->hostport) len += 6; /* ':' between host and port, up to 5 for port */
445 len += lstrlenW( request->path );
446 if ((ret = heap_alloc( len * sizeof(WCHAR) )))
448 offset = swprintf( ret, len, L"%s://%s", scheme, request->connect->hostname );
449 if (request->connect->hostport)
451 offset += swprintf( ret + offset, len - offset, L":%u", request->connect->hostport );
453 lstrcpyW( ret + offset, request->path );
454 if (path) *path = ret + offset;
457 return ret;
460 static WCHAR *build_request_string( struct request *request )
462 WCHAR *path, *ret;
463 unsigned int i, len;
465 if (!wcsicmp( request->connect->hostname, request->connect->servername )) path = request->path;
466 else if (!(path = build_absolute_request_path( request, NULL ))) return NULL;
468 len = lstrlenW( request->verb ) + 1 /* ' ' */;
469 len += lstrlenW( path ) + 1 /* ' ' */;
470 len += lstrlenW( request->version );
472 for (i = 0; i < request->num_headers; i++)
474 if (request->headers[i].is_request)
475 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
477 len += 4; /* '\r\n\r\n' */
479 if ((ret = heap_alloc( (len + 1) * sizeof(WCHAR) )))
481 lstrcpyW( ret, request->verb );
482 lstrcatW( ret, L" " );
483 lstrcatW( ret, path );
484 lstrcatW( ret, L" " );
485 lstrcatW( ret, request->version );
487 for (i = 0; i < request->num_headers; i++)
489 if (request->headers[i].is_request)
491 lstrcatW( ret, L"\r\n" );
492 lstrcatW( ret, request->headers[i].field );
493 lstrcatW( ret, L": " );
494 lstrcatW( ret, request->headers[i].value );
497 lstrcatW( ret, L"\r\n\r\n" );
500 if (path != request->path) heap_free( path );
501 return ret;
504 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
506 static DWORD query_headers( struct request *request, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
507 DWORD *index )
509 struct header *header = NULL;
510 BOOL request_only;
511 int requested_index, header_index = -1;
512 DWORD attr, len, ret = ERROR_WINHTTP_HEADER_NOT_FOUND;
514 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
515 requested_index = index ? *index : 0;
517 attr = level & ~QUERY_MODIFIER_MASK;
518 switch (attr)
520 case WINHTTP_QUERY_CUSTOM:
522 header_index = get_header_index( request, name, requested_index, request_only );
523 break;
525 case WINHTTP_QUERY_RAW_HEADERS:
527 WCHAR *headers, *p, *q;
529 if (request_only)
530 headers = build_request_string( request );
531 else
532 headers = request->raw_headers;
534 if (!(p = headers)) return ERROR_OUTOFMEMORY;
535 for (len = 0; *p; p++) if (*p != '\r') len++;
537 if (!buffer || len * sizeof(WCHAR) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
538 else
540 for (p = headers, q = buffer; *p; p++, q++)
542 if (*p != '\r') *q = *p;
543 else
545 *q = 0;
546 p++; /* skip '\n' */
549 TRACE("returning data: %s\n", debugstr_wn(buffer, len));
550 if (len) len--;
551 ret = ERROR_SUCCESS;
553 *buflen = len * sizeof(WCHAR);
554 if (request_only) heap_free( headers );
555 return ret;
557 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
559 WCHAR *headers;
561 if (request_only)
562 headers = build_request_string( request );
563 else
564 headers = request->raw_headers;
566 if (!headers) return ERROR_OUTOFMEMORY;
567 len = lstrlenW( headers ) * sizeof(WCHAR);
568 if (!buffer || len + sizeof(WCHAR) > *buflen)
570 len += sizeof(WCHAR);
571 ret = ERROR_INSUFFICIENT_BUFFER;
573 else
575 memcpy( buffer, headers, len + sizeof(WCHAR) );
576 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
577 ret = ERROR_SUCCESS;
579 *buflen = len;
580 if (request_only) heap_free( headers );
581 return ret;
583 case WINHTTP_QUERY_VERSION:
584 len = lstrlenW( request->version ) * sizeof(WCHAR);
585 if (!buffer || len + sizeof(WCHAR) > *buflen)
587 len += sizeof(WCHAR);
588 ret = ERROR_INSUFFICIENT_BUFFER;
590 else
592 lstrcpyW( buffer, request->version );
593 TRACE("returning string: %s\n", debugstr_w(buffer));
594 ret = ERROR_SUCCESS;
596 *buflen = len;
597 return ret;
599 case WINHTTP_QUERY_STATUS_TEXT:
600 len = lstrlenW( request->status_text ) * sizeof(WCHAR);
601 if (!buffer || len + sizeof(WCHAR) > *buflen)
603 len += sizeof(WCHAR);
604 ret = ERROR_INSUFFICIENT_BUFFER;
606 else
608 lstrcpyW( buffer, request->status_text );
609 TRACE("returning string: %s\n", debugstr_w(buffer));
610 ret = ERROR_SUCCESS;
612 *buflen = len;
613 return ret;
615 case WINHTTP_QUERY_REQUEST_METHOD:
616 len = lstrlenW( request->verb ) * sizeof(WCHAR);
617 if (!buffer || len + sizeof(WCHAR) > *buflen)
619 len += sizeof(WCHAR);
620 ret = ERROR_INSUFFICIENT_BUFFER;
622 else
624 lstrcpyW( buffer, request->verb );
625 TRACE("returning string: %s\n", debugstr_w(buffer));
626 ret = ERROR_SUCCESS;
628 *buflen = len;
629 return ret;
631 default:
632 if (attr >= ARRAY_SIZE(attribute_table)) return ERROR_INVALID_PARAMETER;
633 if (!attribute_table[attr])
635 FIXME("attribute %u not implemented\n", attr);
636 return ERROR_WINHTTP_HEADER_NOT_FOUND;
638 TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
639 header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
640 break;
643 if (header_index >= 0)
645 header = &request->headers[header_index];
647 if (!header || (request_only && !header->is_request)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
648 if (level & WINHTTP_QUERY_FLAG_NUMBER)
650 if (!buffer || sizeof(int) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
651 else
653 int *number = buffer;
654 *number = wcstol( header->value, NULL, 10 );
655 TRACE("returning number: %d\n", *number);
656 ret = ERROR_SUCCESS;
658 *buflen = sizeof(int);
660 else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
662 SYSTEMTIME *st = buffer;
663 if (!buffer || sizeof(SYSTEMTIME) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
664 else if (WinHttpTimeToSystemTime( header->value, st ))
666 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
667 st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
668 st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
669 ret = ERROR_SUCCESS;
671 *buflen = sizeof(SYSTEMTIME);
673 else if (header->value)
675 len = lstrlenW( header->value ) * sizeof(WCHAR);
676 if (!buffer || len + sizeof(WCHAR) > *buflen)
678 len += sizeof(WCHAR);
679 ret = ERROR_INSUFFICIENT_BUFFER;
681 else
683 lstrcpyW( buffer, header->value );
684 TRACE("returning string: %s\n", debugstr_w(buffer));
685 ret = ERROR_SUCCESS;
687 *buflen = len;
689 if (!ret && index) *index += 1;
690 return ret;
693 /***********************************************************************
694 * WinHttpQueryHeaders (winhttp.@)
696 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
698 DWORD ret;
699 struct request *request;
701 TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index);
703 if (!(request = (struct request *)grab_object( hrequest )))
705 SetLastError( ERROR_INVALID_HANDLE );
706 return FALSE;
708 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
710 release_object( &request->hdr );
711 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
712 return FALSE;
715 ret = query_headers( request, level, name, buffer, buflen, index );
717 release_object( &request->hdr );
718 SetLastError( ret );
719 return !ret;
722 static const struct
724 const WCHAR *str;
725 unsigned int len;
726 DWORD scheme;
728 auth_schemes[] =
730 { L"Basic", ARRAY_SIZE(L"Basic") - 1, WINHTTP_AUTH_SCHEME_BASIC },
731 { L"NTLM", ARRAY_SIZE(L"NTLM") - 1, WINHTTP_AUTH_SCHEME_NTLM },
732 { L"Passport", ARRAY_SIZE(L"Passport") - 1, WINHTTP_AUTH_SCHEME_PASSPORT },
733 { L"Digest", ARRAY_SIZE(L"Digest") - 1, WINHTTP_AUTH_SCHEME_DIGEST },
734 { L"Negotiate", ARRAY_SIZE(L"Negotiate") - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
737 static enum auth_scheme scheme_from_flag( DWORD flag )
739 int i;
741 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++) if (flag == auth_schemes[i].scheme) return i;
742 return SCHEME_INVALID;
745 static DWORD auth_scheme_from_header( const WCHAR *header )
747 unsigned int i;
749 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
751 if (!wcsnicmp( header, auth_schemes[i].str, auth_schemes[i].len ) &&
752 (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
754 return 0;
757 static DWORD query_auth_schemes( struct request *request, DWORD level, DWORD *supported, DWORD *first )
759 DWORD ret, index = 0, supported_schemes = 0, first_scheme = 0;
761 for (;;)
763 WCHAR *buffer;
764 DWORD size, scheme;
766 size = 0;
767 ret = query_headers( request, level, NULL, NULL, &size, &index );
768 if (ret != ERROR_INSUFFICIENT_BUFFER)
770 if (index) ret = ERROR_SUCCESS;
771 break;
774 if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY;
775 if ((ret = query_headers( request, level, NULL, buffer, &size, &index )))
777 heap_free( buffer );
778 return ret;
780 scheme = auth_scheme_from_header( buffer );
781 heap_free( buffer );
782 if (!scheme) continue;
784 if (!first_scheme) first_scheme = scheme;
785 supported_schemes |= scheme;
788 if (!ret)
790 *supported = supported_schemes;
791 *first = first_scheme;
793 return ret;
796 /***********************************************************************
797 * WinHttpQueryAuthSchemes (winhttp.@)
799 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
801 DWORD ret;
802 struct request *request;
804 TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
806 if (!(request = (struct request *)grab_object( hrequest )))
808 SetLastError( ERROR_INVALID_HANDLE );
809 return FALSE;
811 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
813 release_object( &request->hdr );
814 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
815 return FALSE;
817 if (!supported || !first || !target)
819 release_object( &request->hdr );
820 SetLastError( ERROR_INVALID_PARAMETER );
821 return FALSE;
825 if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first )))
827 *target = WINHTTP_AUTH_TARGET_SERVER;
829 else if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first )))
831 *target = WINHTTP_AUTH_TARGET_PROXY;
833 else ret = ERROR_INVALID_OPERATION;
835 release_object( &request->hdr );
836 SetLastError( ret );
837 return !ret;
840 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
842 UINT n = 0, x;
843 static const char base64enc[] =
844 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
846 while (len > 0)
848 /* first 6 bits, all from bin[0] */
849 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
850 x = (bin[0] & 3) << 4;
852 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
853 if (len == 1)
855 base64[n++] = base64enc[x];
856 base64[n++] = '=';
857 base64[n++] = '=';
858 break;
860 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
861 x = (bin[1] & 0x0f) << 2;
863 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
864 if (len == 2)
866 base64[n++] = base64enc[x];
867 base64[n++] = '=';
868 break;
870 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
872 /* last 6 bits, all from bin [2] */
873 base64[n++] = base64enc[bin[2] & 0x3f];
874 bin += 3;
875 len -= 3;
877 base64[n] = 0;
878 return n;
881 static inline char decode_char( WCHAR c )
883 if (c >= 'A' && c <= 'Z') return c - 'A';
884 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
885 if (c >= '0' && c <= '9') return c - '0' + 52;
886 if (c == '+') return 62;
887 if (c == '/') return 63;
888 return 64;
891 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
893 unsigned int i = 0;
894 char c0, c1, c2, c3;
895 const WCHAR *p = base64;
897 while (len > 4)
899 if ((c0 = decode_char( p[0] )) > 63) return 0;
900 if ((c1 = decode_char( p[1] )) > 63) return 0;
901 if ((c2 = decode_char( p[2] )) > 63) return 0;
902 if ((c3 = decode_char( p[3] )) > 63) return 0;
904 if (buf)
906 buf[i + 0] = (c0 << 2) | (c1 >> 4);
907 buf[i + 1] = (c1 << 4) | (c2 >> 2);
908 buf[i + 2] = (c2 << 6) | c3;
910 len -= 4;
911 i += 3;
912 p += 4;
914 if (p[2] == '=')
916 if ((c0 = decode_char( p[0] )) > 63) return 0;
917 if ((c1 = decode_char( p[1] )) > 63) return 0;
919 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
920 i++;
922 else if (p[3] == '=')
924 if ((c0 = decode_char( p[0] )) > 63) return 0;
925 if ((c1 = decode_char( p[1] )) > 63) return 0;
926 if ((c2 = decode_char( p[2] )) > 63) return 0;
928 if (buf)
930 buf[i + 0] = (c0 << 2) | (c1 >> 4);
931 buf[i + 1] = (c1 << 4) | (c2 >> 2);
933 i += 2;
935 else
937 if ((c0 = decode_char( p[0] )) > 63) return 0;
938 if ((c1 = decode_char( p[1] )) > 63) return 0;
939 if ((c2 = decode_char( p[2] )) > 63) return 0;
940 if ((c3 = decode_char( p[3] )) > 63) return 0;
942 if (buf)
944 buf[i + 0] = (c0 << 2) | (c1 >> 4);
945 buf[i + 1] = (c1 << 4) | (c2 >> 2);
946 buf[i + 2] = (c2 << 6) | c3;
948 i += 3;
950 return i;
953 static struct authinfo *alloc_authinfo(void)
955 struct authinfo *ret;
957 if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
959 SecInvalidateHandle( &ret->cred );
960 SecInvalidateHandle( &ret->ctx );
961 memset( &ret->exp, 0, sizeof(ret->exp) );
962 ret->scheme = 0;
963 ret->attr = 0;
964 ret->max_token = 0;
965 ret->data = NULL;
966 ret->data_len = 0;
967 ret->finished = FALSE;
968 return ret;
971 void destroy_authinfo( struct authinfo *authinfo )
973 if (!authinfo) return;
975 if (SecIsValidHandle( &authinfo->ctx ))
976 DeleteSecurityContext( &authinfo->ctx );
977 if (SecIsValidHandle( &authinfo->cred ))
978 FreeCredentialsHandle( &authinfo->cred );
980 heap_free( authinfo->data );
981 heap_free( authinfo );
984 static BOOL get_authvalue( struct request *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
986 DWORD size, index = 0;
987 for (;;)
989 size = len;
990 if (query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
991 if (auth_scheme_from_header( buffer ) == scheme) break;
993 return TRUE;
996 static BOOL do_authorization( struct request *request, DWORD target, DWORD scheme_flag )
998 struct authinfo *authinfo, **auth_ptr;
999 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1000 const WCHAR *auth_target, *username, *password;
1001 WCHAR auth_value[2048], *auth_reply;
1002 DWORD len = sizeof(auth_value), len_scheme, flags;
1003 BOOL ret, has_auth_value;
1005 if (scheme == SCHEME_INVALID) return FALSE;
1007 switch (target)
1009 case WINHTTP_AUTH_TARGET_SERVER:
1010 has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len );
1011 auth_ptr = &request->authinfo;
1012 auth_target = L"Authorization";
1013 if (request->creds[TARGET_SERVER][scheme].username)
1015 if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE;
1016 username = request->creds[TARGET_SERVER][scheme].username;
1017 password = request->creds[TARGET_SERVER][scheme].password;
1019 else
1021 if (!has_auth_value) return FALSE;
1022 username = request->connect->username;
1023 password = request->connect->password;
1025 break;
1027 case WINHTTP_AUTH_TARGET_PROXY:
1028 if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1029 return FALSE;
1030 auth_ptr = &request->proxy_authinfo;
1031 auth_target = L"Proxy-Authorization";
1032 if (request->creds[TARGET_PROXY][scheme].username)
1034 username = request->creds[TARGET_PROXY][scheme].username;
1035 password = request->creds[TARGET_PROXY][scheme].password;
1037 else
1039 username = request->connect->session->proxy_username;
1040 password = request->connect->session->proxy_password;
1042 break;
1044 default:
1045 WARN("unknown target %x\n", target);
1046 return FALSE;
1048 authinfo = *auth_ptr;
1050 switch (scheme)
1052 case SCHEME_BASIC:
1054 int userlen, passlen;
1056 if (!username || !password) return FALSE;
1057 if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1059 userlen = WideCharToMultiByte( CP_UTF8, 0, username, lstrlenW( username ), NULL, 0, NULL, NULL );
1060 passlen = WideCharToMultiByte( CP_UTF8, 0, password, lstrlenW( password ), NULL, 0, NULL, NULL );
1062 authinfo->data_len = userlen + 1 + passlen;
1063 if (!(authinfo->data = heap_alloc( authinfo->data_len ))) return FALSE;
1065 WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1066 authinfo->data[userlen] = ':';
1067 WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1069 authinfo->scheme = SCHEME_BASIC;
1070 authinfo->finished = TRUE;
1071 break;
1073 case SCHEME_NTLM:
1074 case SCHEME_NEGOTIATE:
1076 SECURITY_STATUS status;
1077 SecBufferDesc out_desc, in_desc;
1078 SecBuffer out, in;
1079 ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1080 const WCHAR *p;
1081 BOOL first = FALSE;
1083 if (!authinfo)
1085 TimeStamp exp;
1086 SEC_WINNT_AUTH_IDENTITY_W id;
1087 WCHAR *domain, *user;
1089 if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1091 first = TRUE;
1092 domain = (WCHAR *)username;
1093 user = wcschr( username, '\\' );
1095 if (user) user++;
1096 else
1098 user = (WCHAR *)username;
1099 domain = NULL;
1101 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1102 id.User = user;
1103 id.UserLength = lstrlenW( user );
1104 id.Domain = domain;
1105 id.DomainLength = domain ? user - domain - 1 : 0;
1106 id.Password = (WCHAR *)password;
1107 id.PasswordLength = lstrlenW( password );
1109 status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1110 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1111 &authinfo->cred, &exp );
1112 if (status == SEC_E_OK)
1114 PSecPkgInfoW info;
1115 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1116 if (status == SEC_E_OK)
1118 authinfo->max_token = info->cbMaxToken;
1119 FreeContextBuffer( info );
1122 if (status != SEC_E_OK)
1124 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1125 debugstr_w(auth_schemes[scheme].str), status);
1126 heap_free( authinfo );
1127 return FALSE;
1129 authinfo->scheme = scheme;
1131 else if (authinfo->finished) return FALSE;
1133 if ((lstrlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1134 wcsnicmp( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1136 ERR("authentication scheme changed from %s to %s\n",
1137 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1138 destroy_authinfo( authinfo );
1139 *auth_ptr = NULL;
1140 return FALSE;
1142 in.BufferType = SECBUFFER_TOKEN;
1143 in.cbBuffer = 0;
1144 in.pvBuffer = NULL;
1146 in_desc.ulVersion = 0;
1147 in_desc.cBuffers = 1;
1148 in_desc.pBuffers = &in;
1150 p = auth_value + auth_schemes[scheme].len;
1151 if (*p == ' ')
1153 int len = lstrlenW( ++p );
1154 in.cbBuffer = decode_base64( p, len, NULL );
1155 if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) {
1156 destroy_authinfo( authinfo );
1157 *auth_ptr = NULL;
1158 return FALSE;
1160 decode_base64( p, len, in.pvBuffer );
1162 out.BufferType = SECBUFFER_TOKEN;
1163 out.cbBuffer = authinfo->max_token;
1164 if (!(out.pvBuffer = heap_alloc( authinfo->max_token )))
1166 heap_free( in.pvBuffer );
1167 destroy_authinfo( authinfo );
1168 *auth_ptr = NULL;
1169 return FALSE;
1171 out_desc.ulVersion = 0;
1172 out_desc.cBuffers = 1;
1173 out_desc.pBuffers = &out;
1175 status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1176 first ? request->connect->servername : NULL, flags, 0,
1177 SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1178 &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1179 heap_free( in.pvBuffer );
1180 if (status == SEC_E_OK)
1182 heap_free( authinfo->data );
1183 authinfo->data = out.pvBuffer;
1184 authinfo->data_len = out.cbBuffer;
1185 authinfo->finished = TRUE;
1186 TRACE("sending last auth packet\n");
1188 else if (status == SEC_I_CONTINUE_NEEDED)
1190 heap_free( authinfo->data );
1191 authinfo->data = out.pvBuffer;
1192 authinfo->data_len = out.cbBuffer;
1193 TRACE("sending next auth packet\n");
1195 else
1197 ERR("InitializeSecurityContextW failed with error 0x%08x\n", status);
1198 heap_free( out.pvBuffer );
1199 destroy_authinfo( authinfo );
1200 *auth_ptr = NULL;
1201 return FALSE;
1203 break;
1205 default:
1206 ERR("invalid scheme %u\n", scheme);
1207 return FALSE;
1209 *auth_ptr = authinfo;
1211 len_scheme = auth_schemes[authinfo->scheme].len;
1212 len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1213 if (!(auth_reply = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1215 memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1216 auth_reply[len_scheme] = ' ';
1217 encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1219 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1220 ret = !process_header( request, auth_target, auth_reply, flags, TRUE );
1221 heap_free( auth_reply );
1222 return ret;
1225 static WCHAR *build_proxy_connect_string( struct request *request )
1227 WCHAR *ret, *host;
1228 unsigned int i;
1229 int len = lstrlenW( request->connect->hostname ) + 7;
1231 if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return NULL;
1232 len = swprintf( host, len, L"%s:%u", request->connect->hostname, request->connect->hostport );
1234 len += ARRAY_SIZE(L"CONNECT");
1235 len += ARRAY_SIZE(L"HTTP/1.1");
1237 for (i = 0; i < request->num_headers; i++)
1239 if (request->headers[i].is_request)
1240 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
1242 len += 4; /* '\r\n\r\n' */
1244 if ((ret = heap_alloc( (len + 1) * sizeof(WCHAR) )))
1246 lstrcpyW( ret, L"CONNECT" );
1247 lstrcatW( ret, L" " );
1248 lstrcatW( ret, host );
1249 lstrcatW( ret, L" " );
1250 lstrcatW( ret, L"HTTP/1.1" );
1252 for (i = 0; i < request->num_headers; i++)
1254 if (request->headers[i].is_request)
1256 lstrcatW( ret, L"\r\n" );
1257 lstrcatW( ret, request->headers[i].field );
1258 lstrcatW( ret, L": " );
1259 lstrcatW( ret, request->headers[i].value );
1262 lstrcatW( ret, L"\r\n\r\n" );
1265 heap_free( host );
1266 return ret;
1269 static DWORD read_reply( struct request *request );
1271 static DWORD secure_proxy_connect( struct request *request )
1273 WCHAR *str;
1274 char *strA;
1275 int len, bytes_sent;
1276 DWORD ret;
1278 if (!(str = build_proxy_connect_string( request ))) return ERROR_OUTOFMEMORY;
1279 strA = strdupWA( str );
1280 heap_free( str );
1281 if (!strA) return ERROR_OUTOFMEMORY;
1283 len = strlen( strA );
1284 ret = netconn_send( request->netconn, strA, len, &bytes_sent );
1285 heap_free( strA );
1286 if (!ret) ret = read_reply( request );
1288 return ret;
1291 static WCHAR *addr_to_str( struct sockaddr_storage *addr )
1293 char buf[INET6_ADDRSTRLEN];
1294 void *src;
1296 switch (addr->ss_family)
1298 case AF_INET:
1299 src = &((struct sockaddr_in *)addr)->sin_addr;
1300 break;
1301 case AF_INET6:
1302 src = &((struct sockaddr_in6 *)addr)->sin6_addr;
1303 break;
1304 default:
1305 WARN("unsupported address family %d\n", addr->ss_family);
1306 return NULL;
1308 if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL;
1309 return strdupAW( buf );
1312 static CRITICAL_SECTION connection_pool_cs;
1313 static CRITICAL_SECTION_DEBUG connection_pool_debug =
1315 0, 0, &connection_pool_cs,
1316 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
1317 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
1319 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
1321 static struct list connection_pool = LIST_INIT( connection_pool );
1323 void release_host( struct hostdata *host )
1325 LONG ref;
1327 EnterCriticalSection( &connection_pool_cs );
1328 if (!(ref = --host->ref)) list_remove( &host->entry );
1329 LeaveCriticalSection( &connection_pool_cs );
1330 if (ref) return;
1332 assert( list_empty( &host->connections ) );
1333 heap_free( host->hostname );
1334 heap_free( host );
1337 static BOOL connection_collector_running;
1339 static void CALLBACK connection_collector( TP_CALLBACK_INSTANCE *instance, void *ctx )
1341 unsigned int remaining_connections;
1342 struct netconn *netconn, *next_netconn;
1343 struct hostdata *host, *next_host;
1344 ULONGLONG now;
1348 /* FIXME: Use more sophisticated method */
1349 Sleep(5000);
1350 remaining_connections = 0;
1351 now = GetTickCount64();
1353 EnterCriticalSection(&connection_pool_cs);
1355 LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, struct hostdata, entry)
1357 LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, struct netconn, entry)
1359 if (netconn->keep_until < now)
1361 TRACE("freeing %p\n", netconn);
1362 list_remove(&netconn->entry);
1363 netconn_close(netconn);
1365 else remaining_connections++;
1369 if (!remaining_connections) connection_collector_running = FALSE;
1371 LeaveCriticalSection(&connection_pool_cs);
1372 } while(remaining_connections);
1374 FreeLibraryWhenCallbackReturns( instance, winhttp_instance );
1377 static void cache_connection( struct netconn *netconn )
1379 TRACE( "caching connection %p\n", netconn );
1381 EnterCriticalSection( &connection_pool_cs );
1383 netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT;
1384 list_add_head( &netconn->host->connections, &netconn->entry );
1386 if (!connection_collector_running)
1388 HMODULE module;
1390 GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)winhttp_instance, &module );
1392 if (TrySubmitThreadpoolCallback( connection_collector, NULL, NULL )) connection_collector_running = TRUE;
1393 else FreeLibrary( winhttp_instance );
1396 LeaveCriticalSection( &connection_pool_cs );
1399 static DWORD map_secure_protocols( DWORD mask )
1401 DWORD ret = 0;
1402 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT;
1403 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT;
1404 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT;
1405 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT;
1406 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT;
1407 return ret;
1410 static DWORD ensure_cred_handle( struct request *request )
1412 SECURITY_STATUS status = SEC_E_OK;
1414 if (request->cred_handle_initialized) return ERROR_SUCCESS;
1416 if (!request->cred_handle_initialized)
1418 SCHANNEL_CRED cred;
1419 memset( &cred, 0, sizeof(cred) );
1420 cred.dwVersion = SCHANNEL_CRED_VERSION;
1421 cred.grbitEnabledProtocols = map_secure_protocols( request->connect->session->secure_protocols );
1422 if (request->client_cert)
1424 cred.paCred = &request->client_cert;
1425 cred.cCreds = 1;
1427 status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
1428 &cred, NULL, NULL, &request->cred_handle, NULL );
1429 if (status == SEC_E_OK)
1430 request->cred_handle_initialized = TRUE;
1433 if (status != SEC_E_OK)
1435 WARN( "AcquireCredentialsHandleW failed: 0x%08x\n", status );
1436 return status;
1438 return ERROR_SUCCESS;
1441 static DWORD open_connection( struct request *request )
1443 BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
1444 struct hostdata *host = NULL, *iter;
1445 struct netconn *netconn = NULL;
1446 struct connect *connect;
1447 WCHAR *addressW = NULL;
1448 INTERNET_PORT port;
1449 DWORD ret, len;
1451 if (request->netconn) goto done;
1453 connect = request->connect;
1454 port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1456 EnterCriticalSection( &connection_pool_cs );
1458 LIST_FOR_EACH_ENTRY( iter, &connection_pool, struct hostdata, entry )
1460 if (iter->port == port && !wcscmp( connect->servername, iter->hostname ) && !is_secure == !iter->secure)
1462 host = iter;
1463 host->ref++;
1464 break;
1468 if (!host)
1470 if ((host = heap_alloc( sizeof(*host) )))
1472 host->ref = 1;
1473 host->secure = is_secure;
1474 host->port = port;
1475 list_init( &host->connections );
1476 if ((host->hostname = strdupW( connect->servername )))
1478 list_add_head( &connection_pool, &host->entry );
1480 else
1482 heap_free( host );
1483 host = NULL;
1488 LeaveCriticalSection( &connection_pool_cs );
1490 if (!host) return ERROR_OUTOFMEMORY;
1492 for (;;)
1494 EnterCriticalSection( &connection_pool_cs );
1495 if (!list_empty( &host->connections ))
1497 netconn = LIST_ENTRY( list_head( &host->connections ), struct netconn, entry );
1498 list_remove( &netconn->entry );
1500 LeaveCriticalSection( &connection_pool_cs );
1501 if (!netconn) break;
1503 if (netconn_is_alive( netconn )) break;
1504 TRACE("connection %p no longer alive, closing\n", netconn);
1505 netconn_close( netconn );
1506 netconn = NULL;
1509 if (!connect->resolved && netconn)
1511 connect->sockaddr = netconn->sockaddr;
1512 connect->resolved = TRUE;
1515 if (!connect->resolved)
1517 len = lstrlenW( host->hostname ) + 1;
1518 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
1520 if ((ret = netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout )))
1522 release_host( host );
1523 return ret;
1525 connect->resolved = TRUE;
1527 if (!(addressW = addr_to_str( &connect->sockaddr )))
1529 release_host( host );
1530 return ERROR_OUTOFMEMORY;
1532 len = lstrlenW( addressW ) + 1;
1533 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1536 if (!netconn)
1538 if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
1540 release_host( host );
1541 return ERROR_OUTOFMEMORY;
1544 TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1546 len = lstrlenW( addressW ) + 1;
1547 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, len );
1549 if ((ret = netconn_create( host, &connect->sockaddr, request->connect_timeout, &netconn )))
1551 heap_free( addressW );
1552 release_host( host );
1553 return ret;
1555 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1556 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1558 request->netconn = netconn;
1560 if (is_secure)
1562 if (connect->session->proxy_server && wcsicmp( connect->hostname, connect->servername ))
1564 if ((ret = secure_proxy_connect( request )))
1566 request->netconn = NULL;
1567 heap_free( addressW );
1568 netconn_close( netconn );
1569 return ret;
1573 CertFreeCertificateContext( request->server_cert );
1574 request->server_cert = NULL;
1576 if ((ret = ensure_cred_handle( request )) ||
1577 (ret = netconn_secure_connect( netconn, connect->hostname, request->security_flags,
1578 &request->cred_handle, request->check_revocation )))
1580 request->netconn = NULL;
1581 heap_free( addressW );
1582 netconn_close( netconn );
1583 return ret;
1587 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, lstrlenW(addressW) + 1 );
1589 else
1591 TRACE("using connection %p\n", netconn);
1593 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1594 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1595 request->netconn = netconn;
1598 if (netconn->secure && !(request->server_cert = netconn_get_certificate( netconn )))
1600 heap_free( addressW );
1601 netconn_close( netconn );
1602 return ERROR_WINHTTP_SECURE_FAILURE;
1605 done:
1606 request->read_pos = request->read_size = 0;
1607 request->read_chunked = FALSE;
1608 request->read_chunked_size = ~0u;
1609 request->read_chunked_eof = FALSE;
1610 heap_free( addressW );
1611 return ERROR_SUCCESS;
1614 void close_connection( struct request *request )
1616 if (!request->netconn) return;
1618 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1619 netconn_close( request->netconn );
1620 request->netconn = NULL;
1621 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1624 static DWORD add_host_header( struct request *request, DWORD modifier )
1626 DWORD ret, len;
1627 WCHAR *host;
1628 struct connect *connect = request->connect;
1629 INTERNET_PORT port;
1631 port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1633 if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1635 return process_header( request, L"Host", connect->hostname, modifier, TRUE );
1637 len = lstrlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1638 if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
1639 swprintf( host, len, L"%s:%u", connect->hostname, port );
1640 ret = process_header( request, L"Host", host, modifier, TRUE );
1641 heap_free( host );
1642 return ret;
1645 static void clear_response_headers( struct request *request )
1647 unsigned int i;
1649 for (i = 0; i < request->num_headers; i++)
1651 if (!request->headers[i].field) continue;
1652 if (!request->headers[i].value) continue;
1653 if (request->headers[i].is_request) continue;
1654 delete_header( request, i );
1655 i--;
1659 /* remove some amount of data from the read buffer */
1660 static void remove_data( struct request *request, int count )
1662 if (!(request->read_size -= count)) request->read_pos = 0;
1663 else request->read_pos += count;
1666 /* read some more data into the read buffer */
1667 static DWORD read_more_data( struct request *request, int maxlen, BOOL notify )
1669 int len;
1670 DWORD ret;
1672 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1674 if (request->read_size && request->read_pos)
1676 /* move existing data to the start of the buffer */
1677 memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1678 request->read_pos = 0;
1680 if (maxlen == -1) maxlen = sizeof(request->read_buf);
1682 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1684 ret = netconn_recv( request->netconn, request->read_buf + request->read_size,
1685 maxlen - request->read_size, 0, &len );
1687 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1689 request->read_size += len;
1690 return ret;
1693 /* discard data contents until we reach end of line */
1694 static DWORD discard_eol( struct request *request, BOOL notify )
1696 DWORD ret;
1699 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1700 if (eol)
1702 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1703 break;
1705 request->read_pos = request->read_size = 0; /* discard everything */
1706 if ((ret = read_more_data( request, -1, notify ))) return ret;
1707 } while (request->read_size);
1708 return ERROR_SUCCESS;
1711 /* read the size of the next chunk */
1712 static DWORD start_next_chunk( struct request *request, BOOL notify )
1714 DWORD ret, chunk_size = 0;
1716 assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
1718 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1720 /* read terminator for the previous chunk */
1721 if (!request->read_chunked_size && (ret = discard_eol( request, notify ))) return ret;
1723 for (;;)
1725 while (request->read_size)
1727 char ch = request->read_buf[request->read_pos];
1728 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1729 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1730 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1731 else if (ch == ';' || ch == '\r' || ch == '\n')
1733 TRACE("reading %u byte chunk\n", chunk_size);
1735 if (request->content_length == ~0u) request->content_length = chunk_size;
1736 else request->content_length += chunk_size;
1738 request->read_chunked_size = chunk_size;
1739 if (!chunk_size) request->read_chunked_eof = TRUE;
1741 return discard_eol( request, notify );
1743 remove_data( request, 1 );
1745 if ((ret = read_more_data( request, -1, notify ))) return ret;
1746 if (!request->read_size)
1748 request->content_length = request->content_read = 0;
1749 request->read_chunked_size = 0;
1750 return ERROR_SUCCESS;
1755 static DWORD refill_buffer( struct request *request, BOOL notify )
1757 int len = sizeof(request->read_buf);
1758 DWORD ret;
1760 if (request->read_chunked)
1762 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1763 if (request->read_chunked_size == ~0u || !request->read_chunked_size)
1765 if ((ret = start_next_chunk( request, notify ))) return ret;
1767 len = min( len, request->read_chunked_size );
1769 else if (request->content_length != ~0u)
1771 len = min( len, request->content_length - request->content_read );
1774 if (len <= request->read_size) return ERROR_SUCCESS;
1775 if ((ret = read_more_data( request, len, notify ))) return ret;
1776 if (!request->read_size) request->content_length = request->content_read = 0;
1777 return ERROR_SUCCESS;
1780 static void finished_reading( struct request *request )
1782 BOOL close = FALSE;
1783 WCHAR connection[20];
1784 DWORD size = sizeof(connection);
1786 if (!request->netconn) return;
1788 if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
1789 else if (!query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
1790 !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
1792 if (!wcsicmp( connection, L"close" )) close = TRUE;
1794 else if (!wcscmp( request->version, L"HTTP/1.0" )) close = TRUE;
1795 if (close)
1797 close_connection( request );
1798 return;
1801 cache_connection( request->netconn );
1802 request->netconn = NULL;
1805 /* return the size of data available to be read immediately */
1806 static DWORD get_available_data( struct request *request )
1808 if (request->read_chunked) return min( request->read_chunked_size, request->read_size );
1809 return request->read_size;
1812 /* check if we have reached the end of the data to read */
1813 static BOOL end_of_read_data( struct request *request )
1815 if (!request->content_length) return TRUE;
1816 if (request->read_chunked) return request->read_chunked_eof;
1817 if (request->content_length == ~0u) return FALSE;
1818 return (request->content_length == request->content_read);
1821 static DWORD read_data( struct request *request, void *buffer, DWORD size, DWORD *read, BOOL async )
1823 int count, bytes_read = 0;
1824 DWORD ret = ERROR_SUCCESS;
1826 if (end_of_read_data( request )) goto done;
1828 while (size)
1830 if (!(count = get_available_data( request )))
1832 if ((ret = refill_buffer( request, async ))) goto done;
1833 if (!(count = get_available_data( request ))) goto done;
1835 count = min( count, size );
1836 memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
1837 remove_data( request, count );
1838 if (request->read_chunked) request->read_chunked_size -= count;
1839 size -= count;
1840 bytes_read += count;
1841 request->content_read += count;
1842 if (end_of_read_data( request )) goto done;
1844 if (request->read_chunked && !request->read_chunked_size) ret = refill_buffer( request, async );
1846 done:
1847 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
1848 if (async)
1850 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
1851 else
1853 WINHTTP_ASYNC_RESULT result;
1854 result.dwResult = API_READ_DATA;
1855 result.dwError = ret;
1856 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
1860 if (!ret && read) *read = bytes_read;
1861 if (end_of_read_data( request )) finished_reading( request );
1862 return ret;
1865 /* read any content returned by the server so that the connection can be reused */
1866 static void drain_content( struct request *request )
1868 DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read;
1869 char buffer[2048];
1871 refill_buffer( request, FALSE );
1872 for (;;)
1874 if (request->read_chunked) size = sizeof(buffer);
1875 else
1877 if (bytes_total >= bytes_left) return;
1878 size = min( sizeof(buffer), bytes_left - bytes_total );
1880 if (read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return;
1881 bytes_total += bytes_read;
1885 enum escape_flags
1887 ESCAPE_FLAG_NON_PRINTABLE = 0x01,
1888 ESCAPE_FLAG_SPACE = 0x02,
1889 ESCAPE_FLAG_PERCENT = 0x04,
1890 ESCAPE_FLAG_UNSAFE = 0x08,
1891 ESCAPE_FLAG_DEL = 0x10,
1892 ESCAPE_FLAG_8BIT = 0x20,
1893 ESCAPE_FLAG_STRIP_CRLF = 0x40,
1896 #define ESCAPE_MASK_DEFAULT (ESCAPE_FLAG_NON_PRINTABLE | ESCAPE_FLAG_SPACE | ESCAPE_FLAG_UNSAFE |\
1897 ESCAPE_FLAG_DEL | ESCAPE_FLAG_8BIT)
1898 #define ESCAPE_MASK_PERCENT (ESCAPE_FLAG_PERCENT | ESCAPE_MASK_DEFAULT)
1899 #define ESCAPE_MASK_DISABLE (ESCAPE_FLAG_SPACE | ESCAPE_FLAG_8BIT | ESCAPE_FLAG_STRIP_CRLF)
1901 static inline BOOL need_escape( char ch, enum escape_flags flags )
1903 static const char unsafe[] = "\"#<>[\\]^`{|}";
1904 const char *ptr = unsafe;
1906 if ((flags & ESCAPE_FLAG_SPACE) && ch == ' ') return TRUE;
1907 if ((flags & ESCAPE_FLAG_PERCENT) && ch == '%') return TRUE;
1908 if ((flags & ESCAPE_FLAG_NON_PRINTABLE) && ch < 0x20) return TRUE;
1909 if ((flags & ESCAPE_FLAG_DEL) && ch == 0x7f) return TRUE;
1910 if ((flags & ESCAPE_FLAG_8BIT) && (ch & 0x80)) return TRUE;
1911 if ((flags & ESCAPE_FLAG_UNSAFE)) while (*ptr) { if (ch == *ptr++) return TRUE; }
1912 return FALSE;
1915 static DWORD escape_string( const char *src, DWORD len, char *dst, enum escape_flags flags )
1917 static const char hex[] = "0123456789ABCDEF";
1918 DWORD i, ret = len;
1919 char *ptr = dst;
1921 for (i = 0; i < len; i++)
1923 if ((flags & ESCAPE_FLAG_STRIP_CRLF) && (src[i] == '\r' || src[i] == '\n'))
1925 ret--;
1926 continue;
1928 if (need_escape( src[i], flags ))
1930 if (dst)
1932 ptr[0] = '%';
1933 ptr[1] = hex[(src[i] >> 4) & 0xf];
1934 ptr[2] = hex[src[i] & 0xf];
1935 ptr += 3;
1937 ret += 2;
1939 else if (dst) *ptr++ = src[i];
1942 if (dst) dst[ret] = 0;
1943 return ret;
1946 static DWORD str_to_wire( const WCHAR *src, int src_len, char *dst, enum escape_flags flags )
1948 DWORD len;
1949 char *utf8;
1951 if (src_len < 0) src_len = lstrlenW( src );
1952 len = WideCharToMultiByte( CP_UTF8, 0, src, src_len, NULL, 0, NULL, NULL );
1953 if (!(utf8 = heap_alloc( len ))) return 0;
1955 WideCharToMultiByte( CP_UTF8, 0, src, -1, utf8, len, NULL, NULL );
1956 len = escape_string( utf8, len, dst, flags );
1957 heap_free( utf8 );
1959 return len;
1962 static char *build_wire_path( struct request *request, DWORD *ret_len )
1964 WCHAR *full_path;
1965 const WCHAR *start, *path, *query = NULL;
1966 DWORD len, len_path = 0, len_query = 0;
1967 enum escape_flags path_flags, query_flags;
1968 char *ret;
1970 if (!wcsicmp( request->connect->hostname, request->connect->servername )) start = full_path = request->path;
1971 else if (!(full_path = build_absolute_request_path( request, &start ))) return NULL;
1973 len = lstrlenW( full_path );
1974 if ((path = wcschr( start, '/' )))
1976 len_path = lstrlenW( path );
1977 if ((query = wcschr( path, '?' )))
1979 len_query = lstrlenW( query );
1980 len_path -= len_query;
1984 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_MASK_DISABLE;
1985 else if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_MASK_PERCENT;
1986 else path_flags = ESCAPE_MASK_DEFAULT;
1988 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_MASK_DISABLE;
1989 else query_flags = path_flags;
1991 *ret_len = str_to_wire( full_path, len - len_path - len_query, NULL, 0 );
1992 if (path) *ret_len += str_to_wire( path, len_path, NULL, path_flags );
1993 if (query) *ret_len += str_to_wire( query, len_query, NULL, query_flags );
1995 if ((ret = heap_alloc( *ret_len + 1 )))
1997 len = str_to_wire( full_path, len - len_path - len_query, ret, 0 );
1998 if (path) len += str_to_wire( path, len_path, ret + len, path_flags );
1999 if (query) str_to_wire( query, len_query, ret + len, query_flags );
2002 if (full_path != request->path) heap_free( full_path );
2003 return ret;
2006 static char *build_wire_request( struct request *request, DWORD *len )
2008 char *path, *ptr, *ret;
2009 DWORD i, len_path;
2011 if (!(path = build_wire_path( request, &len_path ))) return NULL;
2013 *len = str_to_wire( request->verb, -1, NULL, 0 ) + 1; /* ' ' */
2014 *len += len_path + 1; /* ' ' */
2015 *len += str_to_wire( request->version, -1, NULL, 0 );
2017 for (i = 0; i < request->num_headers; i++)
2019 if (request->headers[i].is_request)
2021 *len += str_to_wire( request->headers[i].field, -1, NULL, 0 ) + 2; /* ': ' */
2022 *len += str_to_wire( request->headers[i].value, -1, NULL, 0 ) + 2; /* '\r\n' */
2025 *len += 4; /* '\r\n\r\n' */
2027 if ((ret = ptr = heap_alloc( *len + 1 )))
2029 ptr += str_to_wire( request->verb, -1, ptr, 0 );
2030 *ptr++ = ' ';
2031 memcpy( ptr, path, len_path );
2032 ptr += len_path;
2033 *ptr++ = ' ';
2034 ptr += str_to_wire( request->version, -1, ptr, 0 );
2036 for (i = 0; i < request->num_headers; i++)
2038 if (request->headers[i].is_request)
2040 *ptr++ = '\r';
2041 *ptr++ = '\n';
2042 ptr += str_to_wire( request->headers[i].field, -1, ptr, 0 );
2043 *ptr++ = ':';
2044 *ptr++ = ' ';
2045 ptr += str_to_wire( request->headers[i].value, -1, ptr, 0 );
2048 memcpy( ptr, "\r\n\r\n", sizeof("\r\n\r\n") );
2051 heap_free( path );
2052 return ret;
2055 static WCHAR *create_websocket_key(void)
2057 WCHAR *ret;
2058 char buf[16];
2059 DWORD base64_len = ((sizeof(buf) + 2) * 4) / 3;
2060 if (!RtlGenRandom( buf, sizeof(buf) )) return NULL;
2061 if ((ret = heap_alloc( (base64_len + 1) * sizeof(WCHAR) ))) encode_base64( buf, sizeof(buf), ret );
2062 return ret;
2065 static DWORD add_websocket_key_header( struct request *request )
2067 WCHAR *key = create_websocket_key();
2068 if (!key) return ERROR_OUTOFMEMORY;
2069 process_header( request, L"Sec-WebSocket-Key", key, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE );
2070 heap_free( key );
2071 return ERROR_SUCCESS;
2074 static DWORD send_request( struct request *request, const WCHAR *headers, DWORD headers_len, void *optional,
2075 DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
2077 struct connect *connect = request->connect;
2078 struct session *session = connect->session;
2079 char *wire_req;
2080 int bytes_sent;
2081 DWORD ret, len;
2083 clear_response_headers( request );
2084 drain_content( request );
2086 if (session->agent)
2087 process_header( request, L"User-Agent", session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2089 if (connect->hostname)
2090 add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
2092 if (request->creds[TARGET_SERVER][SCHEME_BASIC].username)
2093 do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC );
2095 if (total_len || (request->verb && !wcscmp( request->verb, L"POST" )))
2097 WCHAR length[21]; /* decimal long int + null */
2098 swprintf( length, ARRAY_SIZE(length), L"%ld", total_len );
2099 process_header( request, L"Content-Length", length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2101 if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE)
2103 process_header( request, L"Upgrade", L"websocket", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2104 process_header( request, L"Connection", L"Upgrade", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2105 process_header( request, L"Sec-WebSocket-Version", L"13", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2106 if ((ret = add_websocket_key_header( request ))) return ret;
2108 else if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
2110 process_header( request, L"Connection", L"Keep-Alive", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2112 if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
2114 process_header( request, L"Pragma", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2115 process_header( request, L"Cache-Control", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2117 if (headers && (ret = add_request_headers( request, headers, headers_len,
2118 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE )))
2120 TRACE("failed to add request headers: %u\n", ret);
2121 return ret;
2123 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && (ret = add_cookie_headers( request )))
2125 WARN("failed to add cookie headers: %u\n", ret);
2126 return ret;
2129 if (context) request->hdr.context = context;
2131 if ((ret = open_connection( request ))) goto end;
2132 if (!(wire_req = build_wire_request( request, &len )))
2134 ret = ERROR_OUTOFMEMORY;
2135 goto end;
2137 TRACE("full request: %s\n", debugstr_a(wire_req));
2139 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
2141 ret = netconn_send( request->netconn, wire_req, len, &bytes_sent );
2142 heap_free( wire_req );
2143 if (ret) goto end;
2145 if (optional_len)
2147 if ((ret = netconn_send( request->netconn, optional, optional_len, &bytes_sent ))) goto end;
2148 request->optional = optional;
2149 request->optional_len = optional_len;
2150 len += optional_len;
2152 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
2154 end:
2155 if (async)
2157 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
2158 else
2160 WINHTTP_ASYNC_RESULT result;
2161 result.dwResult = API_SEND_REQUEST;
2162 result.dwError = ret;
2163 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2166 return ret;
2169 static void CALLBACK task_send_request( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
2171 struct send_request *s = ctx;
2173 TRACE("running %p\n", work);
2174 send_request( s->request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
2176 release_object( &s->request->hdr );
2177 heap_free( s->headers );
2178 heap_free( s );
2181 /***********************************************************************
2182 * WinHttpSendRequest (winhttp.@)
2184 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len,
2185 LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
2187 DWORD ret;
2188 struct request *request;
2190 TRACE("%p, %s, %u, %p, %u, %u, %lx\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional,
2191 optional_len, total_len, context);
2193 if (!(request = (struct request *)grab_object( hrequest )))
2195 SetLastError( ERROR_INVALID_HANDLE );
2196 return FALSE;
2198 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2200 release_object( &request->hdr );
2201 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2202 return FALSE;
2205 if (headers && !headers_len) headers_len = lstrlenW( headers );
2207 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2209 struct send_request *s;
2211 if (!(s = heap_alloc( sizeof(*s) ))) return FALSE;
2212 s->request = request;
2213 s->headers = strdupW( headers );
2214 s->headers_len = headers_len;
2215 s->optional = optional;
2216 s->optional_len = optional_len;
2217 s->total_len = total_len;
2218 s->context = context;
2220 addref_object( &request->hdr );
2221 if ((ret = queue_task( &request->queue, task_send_request, s )))
2223 release_object( &request->hdr );
2224 heap_free( s->headers );
2225 heap_free( s );
2228 else ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
2230 release_object( &request->hdr );
2231 SetLastError( ret );
2232 return !ret;
2235 static DWORD set_credentials( struct request *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
2236 const WCHAR *password )
2238 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
2240 if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
2242 return ERROR_INVALID_PARAMETER;
2244 switch (target)
2246 case WINHTTP_AUTH_TARGET_SERVER:
2248 heap_free( request->creds[TARGET_SERVER][scheme].username );
2249 if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
2250 else if (!(request->creds[TARGET_SERVER][scheme].username = strdupW( username ))) return ERROR_OUTOFMEMORY;
2252 heap_free( request->creds[TARGET_SERVER][scheme].password );
2253 if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
2254 else if (!(request->creds[TARGET_SERVER][scheme].password = strdupW( password ))) return ERROR_OUTOFMEMORY;
2255 break;
2257 case WINHTTP_AUTH_TARGET_PROXY:
2259 heap_free( request->creds[TARGET_PROXY][scheme].username );
2260 if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
2261 else if (!(request->creds[TARGET_PROXY][scheme].username = strdupW( username ))) return ERROR_OUTOFMEMORY;
2263 heap_free( request->creds[TARGET_PROXY][scheme].password );
2264 if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
2265 else if (!(request->creds[TARGET_PROXY][scheme].password = strdupW( password ))) return ERROR_OUTOFMEMORY;
2266 break;
2268 default:
2269 WARN("unknown target %u\n", target);
2270 return ERROR_INVALID_PARAMETER;
2272 return ERROR_SUCCESS;
2275 /***********************************************************************
2276 * WinHttpSetCredentials (winhttp.@)
2278 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, LPCWSTR username,
2279 LPCWSTR password, LPVOID params )
2281 DWORD ret;
2282 struct request *request;
2284 TRACE("%p, %x, 0x%08x, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params);
2286 if (!(request = (struct request *)grab_object( hrequest )))
2288 SetLastError( ERROR_INVALID_HANDLE );
2289 return FALSE;
2291 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2293 release_object( &request->hdr );
2294 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2295 return FALSE;
2298 ret = set_credentials( request, target, scheme, username, password );
2300 release_object( &request->hdr );
2301 SetLastError( ret );
2302 return !ret;
2305 static DWORD handle_authorization( struct request *request, DWORD status )
2307 DWORD ret, i, schemes, first, level, target;
2309 switch (status)
2311 case HTTP_STATUS_DENIED:
2312 target = WINHTTP_AUTH_TARGET_SERVER;
2313 level = WINHTTP_QUERY_WWW_AUTHENTICATE;
2314 break;
2316 case HTTP_STATUS_PROXY_AUTH_REQ:
2317 target = WINHTTP_AUTH_TARGET_PROXY;
2318 level = WINHTTP_QUERY_PROXY_AUTHENTICATE;
2319 break;
2321 default:
2322 ERR("unhandled status %u\n", status);
2323 return ERROR_WINHTTP_INTERNAL_ERROR;
2326 if ((ret = query_auth_schemes( request, level, &schemes, &first ))) return ret;
2327 if (do_authorization( request, target, first )) return ERROR_SUCCESS;
2329 schemes &= ~first;
2330 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
2332 if (!(schemes & auth_schemes[i].scheme)) continue;
2333 if (do_authorization( request, target, auth_schemes[i].scheme )) return ERROR_SUCCESS;
2335 return ERROR_WINHTTP_LOGIN_FAILURE;
2338 /* set the request content length based on the headers */
2339 static void set_content_length( struct request *request, DWORD status )
2341 WCHAR encoding[20];
2342 DWORD buflen = sizeof(request->content_length);
2344 if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED ||
2345 status == HTTP_STATUS_SWITCH_PROTOCOLS || !wcscmp( request->verb, L"HEAD" ))
2347 request->content_length = 0;
2349 else
2351 if (query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
2352 NULL, &request->content_length, &buflen, NULL ))
2353 request->content_length = ~0u;
2355 buflen = sizeof(encoding);
2356 if (!query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
2357 !wcsicmp( encoding, L"chunked" ))
2359 request->content_length = ~0u;
2360 request->read_chunked = TRUE;
2361 request->read_chunked_size = ~0u;
2362 request->read_chunked_eof = FALSE;
2365 request->content_read = 0;
2368 static DWORD read_line( struct request *request, char *buffer, DWORD *len )
2370 int count, bytes_read, pos = 0;
2371 DWORD ret;
2373 for (;;)
2375 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
2376 if (eol)
2378 count = eol - (request->read_buf + request->read_pos);
2379 bytes_read = count + 1;
2381 else count = bytes_read = request->read_size;
2383 count = min( count, *len - pos );
2384 memcpy( buffer + pos, request->read_buf + request->read_pos, count );
2385 pos += count;
2386 remove_data( request, bytes_read );
2387 if (eol) break;
2389 if ((ret = read_more_data( request, -1, TRUE ))) return ret;
2390 if (!request->read_size)
2392 *len = 0;
2393 TRACE("returning empty string\n");
2394 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2397 if (pos < *len)
2399 if (pos && buffer[pos - 1] == '\r') pos--;
2400 *len = pos + 1;
2402 buffer[*len - 1] = 0;
2403 TRACE("returning %s\n", debugstr_a(buffer));
2404 return ERROR_SUCCESS;
2407 #define MAX_REPLY_LEN 1460
2408 #define INITIAL_HEADER_BUFFER_LEN 512
2410 static DWORD read_reply( struct request *request )
2412 char buffer[MAX_REPLY_LEN];
2413 DWORD ret, buflen, len, offset, crlf_len = 2; /* lstrlenW(crlf) */
2414 char *status_code, *status_text;
2415 WCHAR *versionW, *status_textW, *raw_headers;
2416 WCHAR status_codeW[4]; /* sizeof("nnn") */
2418 if (!request->netconn) return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2422 buflen = MAX_REPLY_LEN;
2423 if ((ret = read_line( request, buffer, &buflen ))) return ret;
2425 /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2426 if (!(status_code = strchr( buffer, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2427 status_code++;
2428 if (!(status_text = strchr( status_code, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2429 if ((len = status_text - status_code) != sizeof("nnn") - 1) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2430 status_text++;
2432 TRACE("version [%s] status code [%s] status text [%s]\n",
2433 debugstr_an(buffer, status_code - buffer - 1),
2434 debugstr_an(status_code, len),
2435 debugstr_a(status_text));
2437 } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2439 /* we rely on the fact that the protocol is ascii */
2440 MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2441 status_codeW[len] = 0;
2442 if ((ret = process_header( request, L"Status", status_codeW,
2443 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE ))) return ret;
2445 len = status_code - buffer;
2446 if (!(versionW = heap_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2447 MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2448 versionW[len - 1] = 0;
2450 heap_free( request->version );
2451 request->version = versionW;
2453 len = buflen - (status_text - buffer);
2454 if (!(status_textW = heap_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2455 MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2457 heap_free( request->status_text );
2458 request->status_text = status_textW;
2460 len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2461 if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2462 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2463 memcpy( raw_headers + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2465 heap_free( request->raw_headers );
2466 request->raw_headers = raw_headers;
2468 offset = buflen + crlf_len - 1;
2469 for (;;)
2471 struct header *header;
2473 buflen = MAX_REPLY_LEN;
2474 if (read_line( request, buffer, &buflen )) return ERROR_SUCCESS;
2475 if (!*buffer) buflen = 1;
2477 while (len - offset < buflen + crlf_len)
2479 WCHAR *tmp;
2480 len *= 2;
2481 if (!(tmp = heap_realloc( raw_headers, len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2482 request->raw_headers = raw_headers = tmp;
2484 if (!*buffer)
2486 memcpy( raw_headers + offset, L"\r\n", sizeof(L"\r\n") );
2487 break;
2489 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2491 if (!(header = parse_header( raw_headers + offset ))) break;
2492 if ((ret = process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2494 free_header( header );
2495 break;
2497 free_header( header );
2498 memcpy( raw_headers + offset + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2499 offset += buflen + crlf_len - 1;
2502 TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2503 return ret;
2506 static void record_cookies( struct request *request )
2508 unsigned int i;
2510 for (i = 0; i < request->num_headers; i++)
2512 struct header *set_cookie = &request->headers[i];
2513 if (!wcsicmp( set_cookie->field, L"Set-Cookie" ) && !set_cookie->is_request)
2515 set_cookies( request, set_cookie->value );
2520 static DWORD get_redirect_url( struct request *request, WCHAR **ret_url, DWORD *ret_len )
2522 DWORD size, ret;
2523 WCHAR *url;
2525 ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2526 if (ret != ERROR_INSUFFICIENT_BUFFER) return ret;
2527 if (!(url = heap_alloc( size ))) return ERROR_OUTOFMEMORY;
2528 if ((ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, url, &size, NULL )))
2530 heap_free( url );
2531 return ret;
2533 *ret_url = url;
2534 *ret_len = size / sizeof(WCHAR);
2535 return ERROR_SUCCESS;
2538 static DWORD handle_redirect( struct request *request, DWORD status )
2540 DWORD ret, len, len_loc = 0;
2541 URL_COMPONENTS uc;
2542 struct connect *connect = request->connect;
2543 INTERNET_PORT port;
2544 WCHAR *hostname = NULL, *location = NULL;
2545 int index;
2547 if ((ret = get_redirect_url( request, &location, &len_loc ))) return ret;
2549 memset( &uc, 0, sizeof(uc) );
2550 uc.dwStructSize = sizeof(uc);
2551 uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2553 if (!WinHttpCrackUrl( location, len_loc, 0, &uc )) /* assume relative redirect */
2555 WCHAR *path, *p;
2557 ret = ERROR_OUTOFMEMORY;
2558 if (location[0] == '/')
2560 if (!(path = heap_alloc( (len_loc + 1) * sizeof(WCHAR) ))) goto end;
2561 memcpy( path, location, len_loc * sizeof(WCHAR) );
2562 path[len_loc] = 0;
2564 else
2566 if ((p = wcsrchr( request->path, '/' ))) *p = 0;
2567 len = lstrlenW( request->path ) + 1 + len_loc;
2568 if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2569 lstrcpyW( path, request->path );
2570 lstrcatW( path, L"/" );
2571 memcpy( path + lstrlenW(path), location, len_loc * sizeof(WCHAR) );
2572 path[len_loc] = 0;
2574 heap_free( request->path );
2575 request->path = path;
2577 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2579 else
2581 if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2583 if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP)
2585 ret = ERROR_WINHTTP_REDIRECT_FAILED;
2586 goto end;
2588 TRACE("redirect from secure page to non-secure page\n");
2589 request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2591 else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2593 TRACE("redirect from non-secure page to secure page\n");
2594 request->hdr.flags |= WINHTTP_FLAG_SECURE;
2597 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2599 len = uc.dwHostNameLength;
2600 if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) )))
2602 ret = ERROR_OUTOFMEMORY;
2603 goto end;
2605 memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2606 hostname[len] = 0;
2608 port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2609 if (wcsicmp( connect->hostname, hostname ) || connect->serverport != port)
2611 heap_free( connect->hostname );
2612 connect->hostname = hostname;
2613 connect->hostport = port;
2614 if (!set_server_for_hostname( connect, hostname, port ))
2616 ret = ERROR_OUTOFMEMORY;
2617 goto end;
2620 netconn_close( request->netconn );
2621 request->netconn = NULL;
2622 request->content_length = request->content_read = 0;
2623 request->read_pos = request->read_size = 0;
2624 request->read_chunked = request->read_chunked_eof = FALSE;
2626 else heap_free( hostname );
2628 if ((ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2629 if ((ret = open_connection( request ))) goto end;
2631 heap_free( request->path );
2632 request->path = NULL;
2633 if (uc.dwUrlPathLength)
2635 len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2636 if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2637 memcpy( request->path, uc.lpszUrlPath, (len + 1) * sizeof(WCHAR) );
2638 request->path[len] = 0;
2640 else request->path = strdupW( L"/" );
2643 /* remove content-type/length headers */
2644 if ((index = get_header_index( request, L"Content-Type", 0, TRUE )) >= 0) delete_header( request, index );
2645 if ((index = get_header_index( request, L"Content-Length", 0, TRUE )) >= 0 ) delete_header( request, index );
2647 if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !wcscmp( request->verb, L"POST" ))
2649 heap_free( request->verb );
2650 request->verb = strdupW( L"GET" );
2651 request->optional = NULL;
2652 request->optional_len = 0;
2655 end:
2656 heap_free( location );
2657 return ret;
2660 static BOOL is_passport_request( struct request *request )
2662 static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t','1','.','4'};
2663 WCHAR buf[1024];
2664 DWORD len = ARRAY_SIZE(buf);
2666 if (!(request->connect->session->passport_flags & WINHTTP_ENABLE_PASSPORT_AUTH) ||
2667 query_headers( request, WINHTTP_QUERY_WWW_AUTHENTICATE, NULL, buf, &len, NULL )) return FALSE;
2669 if (!wcsnicmp( buf, passportW, ARRAY_SIZE(passportW) ) &&
2670 (buf[ARRAY_SIZE(passportW)] == ' ' || !buf[ARRAY_SIZE(passportW)])) return TRUE;
2672 return FALSE;
2675 static DWORD handle_passport_redirect( struct request *request )
2677 DWORD ret, flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
2678 int i, len = lstrlenW( request->raw_headers );
2679 WCHAR *p = request->raw_headers;
2681 if ((ret = process_header( request, L"Status", L"401", flags, FALSE ))) return ret;
2683 for (i = 0; i < len; i++)
2685 if (i <= len - 3 && p[i] == '3' && p[i + 1] == '0' && p[i + 2] == '2')
2687 p[i] = '4';
2688 p[i + 2] = '1';
2689 break;
2692 return ERROR_SUCCESS;
2695 static DWORD receive_response( struct request *request, BOOL async )
2697 DWORD ret, size, query, status;
2699 if (!request->netconn) return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2701 netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout );
2702 for (;;)
2704 if ((ret = read_reply( request ))) break;
2706 size = sizeof(DWORD);
2707 query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2708 if ((ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
2710 set_content_length( request, status );
2712 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2714 if (status == HTTP_STATUS_REDIRECT && is_passport_request( request ))
2716 ret = handle_passport_redirect( request );
2718 else if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2720 if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2721 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) break;
2723 if (++request->redirect_count > request->max_redirects) return ERROR_WINHTTP_REDIRECT_FAILED;
2725 if ((ret = handle_redirect( request, status ))) break;
2727 /* recurse synchronously */
2728 if (!(ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2730 else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2732 if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
2734 if ((ret = handle_authorization( request, status ))) break;
2736 /* recurse synchronously */
2737 if (!(ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2739 break;
2742 if (request->netconn) netconn_set_timeout( request->netconn, FALSE, request->receive_timeout );
2743 if (request->content_length) ret = refill_buffer( request, FALSE );
2745 if (async)
2747 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
2748 else
2750 WINHTTP_ASYNC_RESULT result;
2751 result.dwResult = API_RECEIVE_RESPONSE;
2752 result.dwError = ret;
2753 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2756 return ret;
2759 static void CALLBACK task_receive_response( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
2761 struct receive_response *r = ctx;
2763 TRACE("running %p\n", work);
2764 receive_response( r->request, TRUE );
2766 release_object( &r->request->hdr );
2767 heap_free( r );
2770 /***********************************************************************
2771 * WinHttpReceiveResponse (winhttp.@)
2773 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
2775 DWORD ret;
2776 struct request *request;
2778 TRACE("%p, %p\n", hrequest, reserved);
2780 if (!(request = (struct request *)grab_object( hrequest )))
2782 SetLastError( ERROR_INVALID_HANDLE );
2783 return FALSE;
2785 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2787 release_object( &request->hdr );
2788 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2789 return FALSE;
2792 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2794 struct receive_response *r;
2796 if (!(r = heap_alloc( sizeof(*r) ))) return FALSE;
2797 r->request = request;
2799 addref_object( &request->hdr );
2800 if ((ret = queue_task( &request->queue, task_receive_response, r )))
2802 release_object( &request->hdr );
2803 heap_free( r );
2806 else ret = receive_response( request, FALSE );
2808 release_object( &request->hdr );
2809 SetLastError( ret );
2810 return !ret;
2813 static DWORD query_data_available( struct request *request, DWORD *available, BOOL async )
2815 DWORD ret = ERROR_SUCCESS, count = 0;
2817 if (end_of_read_data( request )) goto done;
2819 count = get_available_data( request );
2820 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
2821 if (!count)
2823 if ((ret = refill_buffer( request, async ))) goto done;
2824 count = get_available_data( request );
2825 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
2828 done:
2829 TRACE("%u bytes available\n", count);
2830 if (async)
2832 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
2833 else
2835 WINHTTP_ASYNC_RESULT result;
2836 result.dwResult = API_QUERY_DATA_AVAILABLE;
2837 result.dwError = ret;
2838 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2842 if (!ret && available) *available = count;
2843 return ret;
2846 static void CALLBACK task_query_data_available( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
2848 struct query_data *q = ctx;
2850 TRACE("running %p\n", work);
2851 query_data_available( q->request, q->available, TRUE );
2853 release_object( &q->request->hdr );
2854 heap_free( q );
2857 /***********************************************************************
2858 * WinHttpQueryDataAvailable (winhttp.@)
2860 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
2862 DWORD ret;
2863 struct request *request;
2865 TRACE("%p, %p\n", hrequest, available);
2867 if (!(request = (struct request *)grab_object( hrequest )))
2869 SetLastError( ERROR_INVALID_HANDLE );
2870 return FALSE;
2872 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2874 release_object( &request->hdr );
2875 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2876 return FALSE;
2879 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2881 struct query_data *q;
2883 if (!(q = heap_alloc( sizeof(*q) ))) return FALSE;
2884 q->request = request;
2885 q->available = available;
2887 addref_object( &request->hdr );
2888 if ((ret = queue_task( &request->queue, task_query_data_available, q )))
2890 release_object( &request->hdr );
2891 heap_free( q );
2894 else ret = query_data_available( request, available, FALSE );
2896 release_object( &request->hdr );
2897 SetLastError( ret );
2898 return !ret;
2901 static void CALLBACK task_read_data( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
2903 struct read_data *r = ctx;
2905 TRACE("running %p\n", work);
2906 read_data( r->request, r->buffer, r->to_read, r->read, TRUE );
2908 release_object( &r->request->hdr );
2909 heap_free( r );
2912 /***********************************************************************
2913 * WinHttpReadData (winhttp.@)
2915 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
2917 DWORD ret;
2918 struct request *request;
2920 TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
2922 if (!(request = (struct request *)grab_object( hrequest )))
2924 SetLastError( ERROR_INVALID_HANDLE );
2925 return FALSE;
2927 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2929 release_object( &request->hdr );
2930 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2931 return FALSE;
2934 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2936 struct read_data *r;
2938 if (!(r = heap_alloc( sizeof(*r) ))) return FALSE;
2939 r->request = request;
2940 r->buffer = buffer;
2941 r->to_read = to_read;
2942 r->read = read;
2944 addref_object( &request->hdr );
2945 if ((ret = queue_task( &request->queue, task_read_data, r )))
2947 release_object( &request->hdr );
2948 heap_free( r );
2951 else ret = read_data( request, buffer, to_read, read, FALSE );
2953 release_object( &request->hdr );
2954 SetLastError( ret );
2955 return !ret;
2958 static DWORD write_data( struct request *request, const void *buffer, DWORD to_write, DWORD *written, BOOL async )
2960 DWORD ret;
2961 int num_bytes;
2963 ret = netconn_send( request->netconn, buffer, to_write, &num_bytes );
2965 if (async)
2967 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) );
2968 else
2970 WINHTTP_ASYNC_RESULT result;
2971 result.dwResult = API_WRITE_DATA;
2972 result.dwError = ret;
2973 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2976 if (!ret && written) *written = num_bytes;
2977 return ret;
2980 static void CALLBACK task_write_data( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
2982 struct write_data *w = ctx;
2984 TRACE("running %p\n", work);
2985 write_data( w->request, w->buffer, w->to_write, w->written, TRUE );
2987 release_object( &w->request->hdr );
2988 heap_free( w );
2991 /***********************************************************************
2992 * WinHttpWriteData (winhttp.@)
2994 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written )
2996 DWORD ret;
2997 struct request *request;
2999 TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written);
3001 if (!(request = (struct request *)grab_object( hrequest )))
3003 SetLastError( ERROR_INVALID_HANDLE );
3004 return FALSE;
3006 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3008 release_object( &request->hdr );
3009 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3010 return FALSE;
3013 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3015 struct write_data *w;
3017 if (!(w = heap_alloc( sizeof(*w) ))) return FALSE;
3018 w->request = request;
3019 w->buffer = buffer;
3020 w->to_write = to_write;
3021 w->written = written;
3023 addref_object( &request->hdr );
3024 if ((ret = queue_task( &request->queue, task_write_data, w )))
3026 release_object( &request->hdr );
3027 heap_free( w );
3030 else ret = write_data( request, buffer, to_write, written, FALSE );
3032 release_object( &request->hdr );
3033 SetLastError( ret );
3034 return !ret;
3037 static BOOL socket_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
3039 FIXME("unimplemented option %u\n", option);
3040 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3041 return FALSE;
3044 static void socket_destroy( struct object_header *hdr )
3046 struct socket *socket = (struct socket *)hdr;
3048 TRACE("%p\n", socket);
3050 stop_queue( &socket->send_q );
3051 stop_queue( &socket->recv_q );
3053 release_object( &socket->request->hdr );
3054 heap_free( socket );
3057 static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
3059 FIXME("unimplemented option %u\n", option);
3060 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3061 return FALSE;
3064 static const struct object_vtbl socket_vtbl =
3066 socket_destroy,
3067 socket_query_option,
3068 socket_set_option,
3071 HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR context )
3073 struct socket *socket;
3074 struct request *request;
3075 HINTERNET hsocket = NULL;
3077 TRACE("%p, %08lx\n", hrequest, context);
3079 if (!(request = (struct request *)grab_object( hrequest )))
3081 SetLastError( ERROR_INVALID_HANDLE );
3082 return NULL;
3084 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3086 release_object( &request->hdr );
3087 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3088 return NULL;
3090 if (!(socket = heap_alloc_zero( sizeof(struct socket) )))
3092 release_object( &request->hdr );
3093 return NULL;
3095 socket->hdr.type = WINHTTP_HANDLE_TYPE_SOCKET;
3096 socket->hdr.vtbl = &socket_vtbl;
3097 socket->hdr.refs = 1;
3098 socket->hdr.callback = request->hdr.callback;
3099 socket->hdr.notify_mask = request->hdr.notify_mask;
3100 socket->hdr.context = context;
3102 addref_object( &request->hdr );
3103 socket->request = request;
3105 if ((hsocket = alloc_handle( &socket->hdr )))
3107 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hsocket, sizeof(hsocket) );
3110 release_object( &socket->hdr );
3111 release_object( &request->hdr );
3112 TRACE("returning %p\n", hsocket);
3113 if (hsocket) SetLastError( ERROR_SUCCESS );
3114 return hsocket;
3117 static DWORD send_bytes( struct netconn *netconn, char *bytes, int len )
3119 int count;
3120 DWORD err;
3121 if ((err = netconn_send( netconn, bytes, len, &count ))) return err;
3122 return (count == len) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
3125 #define FIN_BIT (1 << 7)
3126 #define MASK_BIT (1 << 7)
3127 #define RESERVED_BIT (7 << 4)
3128 #define CONTROL_BIT (1 << 3)
3130 static DWORD send_frame( struct netconn *netconn, enum socket_opcode opcode, USHORT status, const char *buf,
3131 DWORD buflen, BOOL final )
3133 DWORD i = 0, j, ret, offset = 2, len = buflen;
3134 char hdr[14], byte, *mask = NULL;
3136 TRACE("sending %02x frame\n", opcode);
3138 if (opcode == SOCKET_OPCODE_CLOSE) len += sizeof(status);
3140 hdr[0] = final ? (char)FIN_BIT : 0;
3141 hdr[0] |= opcode;
3142 hdr[1] = (char)MASK_BIT;
3143 if (len < 126) hdr[1] |= len;
3144 else if (len < 65536)
3146 hdr[1] |= 126;
3147 hdr[2] = len >> 8;
3148 hdr[3] = len & 0xff;
3149 offset += 2;
3151 else
3153 hdr[1] |= 127;
3154 hdr[2] = hdr[3] = hdr[4] = hdr[5] = 0;
3155 hdr[6] = len >> 24;
3156 hdr[7] = (len >> 16) & 0xff;
3157 hdr[8] = (len >> 8) & 0xff;
3158 hdr[9] = len & 0xff;
3159 offset += 8;
3162 if ((ret = send_bytes( netconn, hdr, offset ))) return ret;
3163 if (len)
3165 mask = &hdr[offset];
3166 RtlGenRandom( mask, 4 );
3167 if ((ret = send_bytes( netconn, mask, 4 ))) return ret;
3170 if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */
3172 byte = (status >> 8) ^ mask[i++ % 4];
3173 if ((ret = send_bytes( netconn, &byte, 1 ))) return ret;
3175 byte = (status & 0xff) ^ mask[i++ % 4];
3176 if ((ret = send_bytes( netconn, &byte, 1 ))) return ret;
3179 for (j = 0; j < buflen; j++)
3181 byte = buf[j] ^ mask[i++ % 4];
3182 if ((ret = send_bytes( netconn, &byte, 1 ))) return ret;
3185 return ERROR_SUCCESS;
3188 static enum socket_opcode map_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
3190 switch (type)
3192 case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE: return SOCKET_OPCODE_TEXT;
3193 case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE: return SOCKET_OPCODE_BINARY;
3194 case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE: return SOCKET_OPCODE_CLOSE;
3195 default:
3196 FIXME("buffer type %u not supported\n", type);
3197 return SOCKET_OPCODE_INVALID;
3201 static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len,
3202 BOOL async )
3204 enum socket_opcode opcode = map_buffer_type( type );
3205 DWORD ret;
3207 ret = send_frame( socket->request->netconn, opcode, 0, buf, len, TRUE );
3208 if (async)
3210 if (!ret)
3212 WINHTTP_WEB_SOCKET_STATUS status;
3213 status.dwBytesTransferred = len;
3214 status.eBufferType = type;
3215 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) );
3217 else
3219 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3220 result.AsyncResult.dwResult = API_WRITE_DATA;
3221 result.AsyncResult.dwError = ret;
3222 result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION;
3223 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3226 return ret;
3229 static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
3231 struct socket_send *s = ctx;
3233 TRACE("running %p\n", work);
3234 socket_send( s->socket, s->type, s->buf, s->len, TRUE );
3236 release_object( &s->socket->hdr );
3237 heap_free( s );
3240 DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len )
3242 struct socket *socket;
3243 DWORD ret;
3245 TRACE("%p, %u, %p, %u\n", hsocket, type, buf, len);
3247 if (len && !buf) return ERROR_INVALID_PARAMETER;
3248 if (type != WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE && type != WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE)
3250 FIXME("buffer type %u not supported\n", type);
3251 return ERROR_NOT_SUPPORTED;
3254 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3255 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3257 release_object( &socket->hdr );
3258 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3260 if (socket->state != SOCKET_STATE_OPEN)
3262 release_object( &socket->hdr );
3263 return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
3266 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3268 struct socket_send *s;
3270 if (!(s = heap_alloc( sizeof(*s) ))) return FALSE;
3271 s->socket = socket;
3272 s->type = type;
3273 s->buf = buf;
3274 s->len = len;
3276 addref_object( &socket->hdr );
3277 if ((ret = queue_task( &socket->send_q, task_socket_send, s )))
3279 release_object( &socket->hdr );
3280 heap_free( s );
3283 else ret = socket_send( socket, type, buf, len, FALSE );
3285 release_object( &socket->hdr );
3286 return ret;
3289 static DWORD receive_bytes( struct netconn *netconn, char *buf, DWORD len, DWORD *ret_len )
3291 DWORD err;
3292 if ((err = netconn_recv( netconn, buf, len, 0, (int *)ret_len ))) return err;
3293 if (*ret_len != len) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3294 return ERROR_SUCCESS;
3297 static BOOL is_supported_opcode( enum socket_opcode opcode )
3299 switch (opcode)
3301 case SOCKET_OPCODE_TEXT:
3302 case SOCKET_OPCODE_BINARY:
3303 case SOCKET_OPCODE_CLOSE:
3304 case SOCKET_OPCODE_PING:
3305 case SOCKET_OPCODE_PONG:
3306 return TRUE;
3307 default:
3308 FIXME( "opcode %02x not handled\n", opcode );
3309 return FALSE;
3313 static DWORD receive_frame( struct netconn *netconn, DWORD *ret_len, enum socket_opcode *opcode )
3315 DWORD ret, len, count;
3316 char hdr[2];
3318 if ((ret = receive_bytes( netconn, hdr, sizeof(hdr), &count ))) return ret;
3319 if ((hdr[0] & RESERVED_BIT) || (hdr[1] & MASK_BIT) || !is_supported_opcode( hdr[0] & 0xf ))
3321 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3323 *opcode = hdr[0] & 0xf;
3324 TRACE("received %02x frame\n", *opcode);
3326 len = hdr[1] & ~MASK_BIT;
3327 if (len == 126)
3329 USHORT len16;
3330 if ((ret = receive_bytes( netconn, (char *)&len16, sizeof(len16), &count ))) return ret;
3331 len = RtlUshortByteSwap( len16 );
3333 else if (len == 127)
3335 ULONGLONG len64;
3336 if ((ret = receive_bytes( netconn, (char *)&len64, sizeof(len64), &count ))) return ret;
3337 if ((len64 = RtlUlonglongByteSwap( len64 )) > ~0u) return ERROR_NOT_SUPPORTED;
3338 len = len64;
3341 *ret_len = len;
3342 return ERROR_SUCCESS;
3345 static void CALLBACK task_socket_send_pong( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
3347 struct socket_send *s = ctx;
3349 TRACE("running %p\n", work);
3350 send_frame( s->socket->request->netconn, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE );
3352 release_object( &s->socket->hdr );
3353 heap_free( s );
3356 static DWORD socket_send_pong( struct socket *socket )
3358 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3360 struct socket_send *s;
3361 DWORD ret;
3363 if (!(s = heap_alloc( sizeof(*s) ))) return ERROR_OUTOFMEMORY;
3364 s->socket = socket;
3366 addref_object( &socket->hdr );
3367 if ((ret = queue_task( &socket->send_q, task_socket_send_pong, s )))
3369 release_object( &socket->hdr );
3370 heap_free( s );
3372 return ret;
3374 return send_frame( socket->request->netconn, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE );
3377 static DWORD socket_drain( struct socket *socket )
3379 struct netconn *netconn = socket->request->netconn;
3380 DWORD ret, count;
3382 while (socket->read_size)
3384 char buf[1024];
3385 if ((ret = receive_bytes( netconn, buf, min(socket->read_size, sizeof(buf)), &count ))) return ret;
3386 socket->read_size -= count;
3388 return ERROR_SUCCESS;
3391 static DWORD handle_control_frame( struct socket *socket )
3393 switch (socket->opcode)
3395 case SOCKET_OPCODE_PING:
3396 return socket_send_pong( socket );
3398 case SOCKET_OPCODE_PONG:
3399 return socket_drain( socket );
3401 default:
3402 ERR("unhandled control opcode %02x\n", socket->opcode);
3403 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3406 return ERROR_SUCCESS;
3409 static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( enum socket_opcode opcode, BOOL fragment )
3411 switch (opcode)
3413 case SOCKET_OPCODE_TEXT:
3414 if (fragment) return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3415 return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
3417 case SOCKET_OPCODE_BINARY:
3418 if (fragment) return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3419 return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
3421 case SOCKET_OPCODE_CLOSE:
3422 return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
3424 default:
3425 FIXME("opcode %02x not handled\n", opcode);
3426 return ~0u;
3430 static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len,
3431 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type, BOOL async )
3433 struct netconn *netconn = socket->request->netconn;
3434 DWORD count, ret = ERROR_SUCCESS;
3436 if (!socket->read_size)
3438 for (;;)
3440 if (!(ret = receive_frame( netconn, &socket->read_size, &socket->opcode )))
3442 if (!(socket->opcode & CONTROL_BIT) || (ret = handle_control_frame( socket ))) break;
3444 else if (ret == WSAETIMEDOUT) ret = socket_send_pong( socket );
3445 if (ret) break;
3448 if (!ret) ret = receive_bytes( netconn, buf, min(len, socket->read_size), &count );
3449 if (!ret)
3451 socket->read_size -= count;
3452 if (!async)
3454 *ret_len = count;
3455 *ret_type = map_opcode( socket->opcode, socket->read_size != 0 );
3458 if (async)
3460 if (!ret)
3462 WINHTTP_WEB_SOCKET_STATUS status;
3463 status.dwBytesTransferred = count;
3464 status.eBufferType = map_opcode( socket->opcode, socket->read_size != 0 );
3465 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
3467 else
3469 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3470 result.AsyncResult.dwResult = API_READ_DATA;
3471 result.AsyncResult.dwError = ret;
3472 result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
3473 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3476 return ret;
3479 static void CALLBACK task_socket_receive( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
3481 struct socket_receive *r = ctx;
3483 TRACE("running %p\n", work);
3484 socket_receive( r->socket, r->buf, r->len, NULL, NULL, TRUE );
3486 release_object( &r->socket->hdr );
3487 heap_free( r );
3490 DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *ret_len,
3491 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
3493 struct socket *socket;
3494 DWORD ret;
3496 TRACE("%p, %p, %u, %p, %p\n", hsocket, buf, len, ret_len, ret_type);
3498 if (!buf || !len) return ERROR_INVALID_PARAMETER;
3500 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3501 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3503 release_object( &socket->hdr );
3504 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3506 if (socket->state != SOCKET_STATE_OPEN)
3508 release_object( &socket->hdr );
3509 return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
3512 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3514 struct socket_receive *r;
3516 if (!(r = heap_alloc( sizeof(*r) ))) return FALSE;
3517 r->socket = socket;
3518 r->buf = buf;
3519 r->len = len;
3521 addref_object( &socket->hdr );
3522 if ((ret = queue_task( &socket->recv_q, task_socket_receive, r )))
3524 release_object( &socket->hdr );
3525 heap_free( r );
3528 else ret = socket_receive( socket, buf, len, ret_len, ret_type, FALSE );
3530 release_object( &socket->hdr );
3531 return ret;
3534 static DWORD socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len, BOOL async )
3536 struct netconn *netconn = socket->request->netconn;
3537 DWORD ret;
3539 stop_queue( &socket->send_q );
3540 if (!(ret = send_frame( netconn, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE )))
3542 socket->state = SOCKET_STATE_SHUTDOWN;
3544 if (async)
3546 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 );
3547 else
3549 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3550 result.AsyncResult.dwResult = API_WRITE_DATA;
3551 result.AsyncResult.dwError = ret;
3552 result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION;
3553 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3556 return ret;
3559 static void CALLBACK task_socket_shutdown( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
3561 struct socket_shutdown *s = ctx;
3563 socket_shutdown( s->socket, s->status, s->reason, s->len, TRUE );
3565 TRACE("running %p\n", work);
3566 release_object( &s->socket->hdr );
3567 heap_free( s );
3570 DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
3572 struct socket *socket;
3573 DWORD ret;
3575 TRACE("%p, %u, %p, %u\n", hsocket, status, reason, len);
3577 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
3579 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3580 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3582 release_object( &socket->hdr );
3583 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3585 if (socket->state >= SOCKET_STATE_SHUTDOWN)
3587 release_object( &socket->hdr );
3588 return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
3591 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3593 struct socket_shutdown *s;
3595 if (!(s = heap_alloc( sizeof(*s) ))) return FALSE;
3596 s->socket = socket;
3597 s->status = status;
3598 memcpy( s->reason, reason, len );
3599 s->len = len;
3601 addref_object( &socket->hdr );
3602 if ((ret = queue_task( &socket->send_q, task_socket_shutdown, s )))
3604 release_object( &socket->hdr );
3605 heap_free( s );
3608 else ret = socket_shutdown( socket, status, reason, len, FALSE );
3610 release_object( &socket->hdr );
3611 return ret;
3614 static DWORD socket_close( struct socket *socket, USHORT status, const void *reason, DWORD len, BOOL async )
3616 struct netconn *netconn = socket->request->netconn;
3617 DWORD ret, count;
3619 if ((ret = socket_drain( socket ))) goto done;
3621 if (socket->state < SOCKET_STATE_SHUTDOWN)
3623 stop_queue( &socket->send_q );
3624 if ((ret = send_frame( netconn, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE ))) goto done;
3625 socket->state = SOCKET_STATE_SHUTDOWN;
3628 if ((ret = receive_frame( netconn, &count, &socket->opcode ))) goto done;
3629 if (socket->opcode != SOCKET_OPCODE_CLOSE ||
3630 (count && (count < sizeof(socket->status) || count > sizeof(socket->status) + sizeof(socket->reason))))
3632 ret = ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3633 goto done;
3636 if (count)
3638 DWORD reason_len = count - sizeof(socket->status);
3639 if ((ret = receive_bytes( netconn, (char *)&socket->status, sizeof(socket->status), &count ))) goto done;
3640 socket->status = RtlUshortByteSwap( socket->status );
3641 if ((ret = receive_bytes( netconn, socket->reason, reason_len, &socket->reason_len ))) goto done;
3643 socket->state = SOCKET_STATE_CLOSED;
3645 done:
3646 if (async)
3648 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NULL, 0 );
3649 else
3651 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3652 result.AsyncResult.dwResult = API_READ_DATA; /* FIXME */
3653 result.AsyncResult.dwError = ret;
3654 result.Operation = WINHTTP_WEB_SOCKET_CLOSE_OPERATION;
3655 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3658 return ret;
3661 static void CALLBACK task_socket_close( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
3663 struct socket_shutdown *s = ctx;
3665 socket_close( s->socket, s->status, s->reason, s->len, TRUE );
3667 TRACE("running %p\n", work);
3668 release_object( &s->socket->hdr );
3669 heap_free( s );
3672 DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
3674 struct socket *socket;
3675 DWORD ret;
3677 TRACE("%p, %u, %p, %u\n", hsocket, status, reason, len);
3679 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
3681 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3682 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3684 release_object( &socket->hdr );
3685 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3687 if (socket->state >= SOCKET_STATE_CLOSED)
3689 release_object( &socket->hdr );
3690 return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
3693 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3695 struct socket_shutdown *s;
3697 if (!(s = heap_alloc( sizeof(*s) ))) return FALSE;
3698 s->socket = socket;
3699 s->status = status;
3700 memcpy( s->reason, reason, len );
3701 s->len = len;
3703 addref_object( &socket->hdr );
3704 if ((ret = queue_task( &socket->recv_q, task_socket_close, s )))
3706 release_object( &socket->hdr );
3707 heap_free( s );
3710 else ret = socket_close( socket, status, reason, len, FALSE );
3712 release_object( &socket->hdr );
3713 return ret;
3716 DWORD WINAPI WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket, USHORT *status, void *reason, DWORD len,
3717 DWORD *ret_len )
3719 struct socket *socket;
3720 DWORD ret;
3722 TRACE("%p, %p, %p, %u, %p\n", hsocket, status, reason, len, ret_len);
3724 if (!status || (len && !reason) || !ret_len) return ERROR_INVALID_PARAMETER;
3726 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3727 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3729 release_object( &socket->hdr );
3730 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3732 if (socket->state < SOCKET_STATE_CLOSED)
3734 release_object( &socket->hdr );
3735 return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
3738 *status = socket->status;
3739 *ret_len = socket->reason_len;
3740 if (socket->reason_len > len) ret = ERROR_INSUFFICIENT_BUFFER;
3741 else
3743 memcpy( reason, socket->reason, socket->reason_len );
3744 ret = ERROR_SUCCESS;
3747 release_object( &socket->hdr );
3748 return ret;
3751 enum request_state
3753 REQUEST_STATE_INITIALIZED,
3754 REQUEST_STATE_CANCELLED,
3755 REQUEST_STATE_OPEN,
3756 REQUEST_STATE_SENT,
3757 REQUEST_STATE_RESPONSE_RECEIVED
3760 struct winhttp_request
3762 IWinHttpRequest IWinHttpRequest_iface;
3763 LONG refs;
3764 CRITICAL_SECTION cs;
3765 enum request_state state;
3766 HINTERNET hsession;
3767 HINTERNET hconnect;
3768 HINTERNET hrequest;
3769 VARIANT data;
3770 WCHAR *verb;
3771 HANDLE done;
3772 HANDLE wait;
3773 HANDLE cancel;
3774 BOOL proc_running;
3775 char *buffer;
3776 DWORD offset;
3777 DWORD bytes_available;
3778 DWORD bytes_read;
3779 DWORD error;
3780 DWORD logon_policy;
3781 DWORD disable_feature;
3782 LONG resolve_timeout;
3783 LONG connect_timeout;
3784 LONG send_timeout;
3785 LONG receive_timeout;
3786 WINHTTP_PROXY_INFO proxy;
3787 BOOL async;
3788 UINT url_codepage;
3791 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
3793 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
3796 static ULONG WINAPI winhttp_request_AddRef(
3797 IWinHttpRequest *iface )
3799 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3800 return InterlockedIncrement( &request->refs );
3803 /* critical section must be held */
3804 static void cancel_request( struct winhttp_request *request )
3806 if (request->state <= REQUEST_STATE_CANCELLED) return;
3808 if (request->proc_running)
3810 SetEvent( request->cancel );
3811 LeaveCriticalSection( &request->cs );
3813 WaitForSingleObject( request->done, INFINITE );
3815 EnterCriticalSection( &request->cs );
3817 request->state = REQUEST_STATE_CANCELLED;
3820 /* critical section must be held */
3821 static void free_request( struct winhttp_request *request )
3823 if (request->state < REQUEST_STATE_INITIALIZED) return;
3824 WinHttpCloseHandle( request->hrequest );
3825 WinHttpCloseHandle( request->hconnect );
3826 WinHttpCloseHandle( request->hsession );
3827 CloseHandle( request->done );
3828 CloseHandle( request->wait );
3829 CloseHandle( request->cancel );
3830 heap_free( request->proxy.lpszProxy );
3831 heap_free( request->proxy.lpszProxyBypass );
3832 heap_free( request->buffer );
3833 heap_free( request->verb );
3834 VariantClear( &request->data );
3837 static ULONG WINAPI winhttp_request_Release(
3838 IWinHttpRequest *iface )
3840 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3841 LONG refs = InterlockedDecrement( &request->refs );
3842 if (!refs)
3844 TRACE("destroying %p\n", request);
3846 EnterCriticalSection( &request->cs );
3847 cancel_request( request );
3848 free_request( request );
3849 LeaveCriticalSection( &request->cs );
3850 request->cs.DebugInfo->Spare[0] = 0;
3851 DeleteCriticalSection( &request->cs );
3852 heap_free( request );
3854 return refs;
3857 static HRESULT WINAPI winhttp_request_QueryInterface(
3858 IWinHttpRequest *iface,
3859 REFIID riid,
3860 void **obj )
3862 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3864 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
3866 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
3867 IsEqualGUID( riid, &IID_IDispatch ) ||
3868 IsEqualGUID( riid, &IID_IUnknown ))
3870 *obj = iface;
3872 else
3874 FIXME("interface %s not implemented\n", debugstr_guid(riid));
3875 return E_NOINTERFACE;
3877 IWinHttpRequest_AddRef( iface );
3878 return S_OK;
3881 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
3882 IWinHttpRequest *iface,
3883 UINT *count )
3885 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3887 TRACE("%p, %p\n", request, count);
3888 *count = 1;
3889 return S_OK;
3892 enum type_id
3894 IWinHttpRequest_tid,
3895 last_tid
3898 static ITypeLib *winhttp_typelib;
3899 static ITypeInfo *winhttp_typeinfo[last_tid];
3901 static REFIID winhttp_tid_id[] =
3903 &IID_IWinHttpRequest
3906 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
3908 HRESULT hr;
3910 if (!winhttp_typelib)
3912 ITypeLib *typelib;
3914 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
3915 if (FAILED(hr))
3917 ERR("LoadRegTypeLib failed: %08x\n", hr);
3918 return hr;
3920 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
3921 ITypeLib_Release( typelib );
3923 if (!winhttp_typeinfo[tid])
3925 ITypeInfo *typeinfo;
3927 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
3928 if (FAILED(hr))
3930 ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(winhttp_tid_id[tid]), hr);
3931 return hr;
3933 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
3934 ITypeInfo_Release( typeinfo );
3936 *ret = winhttp_typeinfo[tid];
3937 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
3938 return S_OK;
3941 void release_typelib(void)
3943 unsigned i;
3945 for (i = 0; i < ARRAY_SIZE(winhttp_typeinfo); i++)
3946 if (winhttp_typeinfo[i])
3947 ITypeInfo_Release(winhttp_typeinfo[i]);
3949 if (winhttp_typelib)
3950 ITypeLib_Release(winhttp_typelib);
3953 static HRESULT WINAPI winhttp_request_GetTypeInfo(
3954 IWinHttpRequest *iface,
3955 UINT index,
3956 LCID lcid,
3957 ITypeInfo **info )
3959 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3960 TRACE("%p, %u, %u, %p\n", request, index, lcid, info);
3962 return get_typeinfo( IWinHttpRequest_tid, info );
3965 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
3966 IWinHttpRequest *iface,
3967 REFIID riid,
3968 LPOLESTR *names,
3969 UINT count,
3970 LCID lcid,
3971 DISPID *dispid )
3973 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3974 ITypeInfo *typeinfo;
3975 HRESULT hr;
3977 TRACE("%p, %s, %p, %u, %u, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid);
3979 if (!names || !count || !dispid) return E_INVALIDARG;
3981 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
3982 if (SUCCEEDED(hr))
3984 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
3985 ITypeInfo_Release( typeinfo );
3987 return hr;
3990 static HRESULT WINAPI winhttp_request_Invoke(
3991 IWinHttpRequest *iface,
3992 DISPID member,
3993 REFIID riid,
3994 LCID lcid,
3995 WORD flags,
3996 DISPPARAMS *params,
3997 VARIANT *result,
3998 EXCEPINFO *excep_info,
3999 UINT *arg_err )
4001 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4002 ITypeInfo *typeinfo;
4003 HRESULT hr;
4005 TRACE("%p, %d, %s, %d, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
4006 lcid, flags, params, result, excep_info, arg_err);
4008 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
4010 if (member == DISPID_HTTPREQUEST_OPTION)
4012 VARIANT ret_value, option;
4013 UINT err_pos;
4015 if (!result) result = &ret_value;
4016 if (!arg_err) arg_err = &err_pos;
4018 VariantInit( &option );
4019 VariantInit( result );
4021 if (!flags) return S_OK;
4023 if (flags == DISPATCH_PROPERTYPUT)
4025 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4026 if (FAILED(hr)) return hr;
4028 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
4029 if (FAILED(hr))
4030 WARN("put_Option(%d) failed: %x\n", V_I4( &option ), hr);
4031 return hr;
4033 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
4035 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4036 if (FAILED(hr)) return hr;
4038 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
4039 if (FAILED(hr))
4040 WARN("get_Option(%d) failed: %x\n", V_I4( &option ), hr);
4041 return hr;
4044 FIXME("unsupported flags %x\n", flags);
4045 return E_NOTIMPL;
4048 /* fallback to standard implementation */
4050 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4051 if (SUCCEEDED(hr))
4053 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
4054 params, result, excep_info, arg_err );
4055 ITypeInfo_Release( typeinfo );
4057 return hr;
4060 static HRESULT WINAPI winhttp_request_SetProxy(
4061 IWinHttpRequest *iface,
4062 HTTPREQUEST_PROXY_SETTING proxy_setting,
4063 VARIANT proxy_server,
4064 VARIANT bypass_list )
4066 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4067 DWORD err = ERROR_SUCCESS;
4069 TRACE("%p, %u, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
4070 debugstr_variant(&bypass_list));
4072 EnterCriticalSection( &request->cs );
4073 switch (proxy_setting)
4075 case HTTPREQUEST_PROXYSETTING_DEFAULT:
4076 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
4077 heap_free( request->proxy.lpszProxy );
4078 heap_free( request->proxy.lpszProxyBypass );
4079 request->proxy.lpszProxy = NULL;
4080 request->proxy.lpszProxyBypass = NULL;
4081 break;
4083 case HTTPREQUEST_PROXYSETTING_DIRECT:
4084 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
4085 heap_free( request->proxy.lpszProxy );
4086 heap_free( request->proxy.lpszProxyBypass );
4087 request->proxy.lpszProxy = NULL;
4088 request->proxy.lpszProxyBypass = NULL;
4089 break;
4091 case HTTPREQUEST_PROXYSETTING_PROXY:
4092 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
4093 if (V_VT( &proxy_server ) == VT_BSTR)
4095 heap_free( request->proxy.lpszProxy );
4096 request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) );
4098 if (V_VT( &bypass_list ) == VT_BSTR)
4100 heap_free( request->proxy.lpszProxyBypass );
4101 request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) );
4103 break;
4105 default:
4106 err = ERROR_INVALID_PARAMETER;
4107 break;
4109 LeaveCriticalSection( &request->cs );
4110 return HRESULT_FROM_WIN32( err );
4113 static HRESULT WINAPI winhttp_request_SetCredentials(
4114 IWinHttpRequest *iface,
4115 BSTR username,
4116 BSTR password,
4117 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
4119 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4120 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
4121 DWORD err = ERROR_SUCCESS;
4123 TRACE("%p, %s, %p, 0x%08x\n", request, debugstr_w(username), password, flags);
4125 EnterCriticalSection( &request->cs );
4126 if (request->state < REQUEST_STATE_OPEN)
4128 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4129 goto done;
4131 switch (flags)
4133 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
4134 target = WINHTTP_AUTH_TARGET_SERVER;
4135 break;
4136 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
4137 target = WINHTTP_AUTH_TARGET_PROXY;
4138 break;
4139 default:
4140 err = ERROR_INVALID_PARAMETER;
4141 goto done;
4143 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
4145 err = GetLastError();
4147 done:
4148 LeaveCriticalSection( &request->cs );
4149 return HRESULT_FROM_WIN32( err );
4152 static void initialize_request( struct winhttp_request *request )
4154 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
4155 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
4156 request->done = CreateEventW( NULL, FALSE, FALSE, NULL );
4157 request->connect_timeout = 60000;
4158 request->send_timeout = 30000;
4159 request->receive_timeout = 30000;
4160 request->url_codepage = CP_UTF8;
4161 VariantInit( &request->data );
4162 request->state = REQUEST_STATE_INITIALIZED;
4165 static void reset_request( struct winhttp_request *request )
4167 cancel_request( request );
4168 WinHttpCloseHandle( request->hrequest );
4169 request->hrequest = NULL;
4170 WinHttpCloseHandle( request->hconnect );
4171 request->hconnect = NULL;
4172 heap_free( request->buffer );
4173 request->buffer = NULL;
4174 heap_free( request->verb );
4175 request->verb = NULL;
4176 request->offset = 0;
4177 request->bytes_available = 0;
4178 request->bytes_read = 0;
4179 request->error = ERROR_SUCCESS;
4180 request->logon_policy = 0;
4181 request->disable_feature = 0;
4182 request->async = FALSE;
4183 request->connect_timeout = 60000;
4184 request->send_timeout = 30000;
4185 request->receive_timeout = 30000;
4186 request->url_codepage = CP_UTF8;
4187 heap_free( request->proxy.lpszProxy );
4188 request->proxy.lpszProxy = NULL;
4189 heap_free( request->proxy.lpszProxyBypass );
4190 request->proxy.lpszProxyBypass = NULL;
4191 VariantClear( &request->data );
4192 request->state = REQUEST_STATE_INITIALIZED;
4195 static HRESULT WINAPI winhttp_request_Open(
4196 IWinHttpRequest *iface,
4197 BSTR method,
4198 BSTR url,
4199 VARIANT async )
4201 static const WCHAR httpsW[] = {'h','t','t','p','s'};
4202 static const WCHAR *acceptW[] = {L"*/*", NULL};
4203 static const WCHAR user_agentW[] = L"Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)";
4204 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4205 URL_COMPONENTS uc;
4206 WCHAR *hostname, *path = NULL, *verb = NULL;
4207 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
4209 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
4210 debugstr_variant(&async));
4212 if (!method || !url) return E_INVALIDARG;
4214 memset( &uc, 0, sizeof(uc) );
4215 uc.dwStructSize = sizeof(uc);
4216 uc.dwSchemeLength = ~0u;
4217 uc.dwHostNameLength = ~0u;
4218 uc.dwUrlPathLength = ~0u;
4219 uc.dwExtraInfoLength = ~0u;
4220 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( GetLastError() );
4222 EnterCriticalSection( &request->cs );
4223 reset_request( request );
4225 if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
4226 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
4227 hostname[uc.dwHostNameLength] = 0;
4229 if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
4230 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
4231 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
4233 if (!(verb = strdupW( method ))) goto error;
4234 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
4235 else request->async = FALSE;
4237 if (!request->hsession)
4239 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
4240 WINHTTP_FLAG_ASYNC )))
4242 err = GetLastError();
4243 goto error;
4245 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4247 WinHttpCloseHandle( request->hsession );
4248 request->hsession = NULL;
4249 err = GetLastError();
4250 goto error;
4253 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4255 err = GetLastError();
4256 goto error;
4259 len = ARRAY_SIZE( httpsW );
4260 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
4262 flags |= WINHTTP_FLAG_SECURE;
4264 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
4266 err = GetLastError();
4267 goto error;
4269 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
4271 request->state = REQUEST_STATE_OPEN;
4272 request->verb = verb;
4273 heap_free( hostname );
4274 heap_free( path );
4275 LeaveCriticalSection( &request->cs );
4276 return S_OK;
4278 error:
4279 WinHttpCloseHandle( request->hconnect );
4280 request->hconnect = NULL;
4281 heap_free( hostname );
4282 heap_free( path );
4283 heap_free( verb );
4284 LeaveCriticalSection( &request->cs );
4285 return HRESULT_FROM_WIN32( err );
4288 static HRESULT WINAPI winhttp_request_SetRequestHeader(
4289 IWinHttpRequest *iface,
4290 BSTR header,
4291 BSTR value )
4293 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4294 DWORD len, err = ERROR_SUCCESS;
4295 WCHAR *str;
4297 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
4299 if (!header) return E_INVALIDARG;
4301 EnterCriticalSection( &request->cs );
4302 if (request->state < REQUEST_STATE_OPEN)
4304 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4305 goto done;
4307 if (request->state >= REQUEST_STATE_SENT)
4309 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
4310 goto done;
4312 len = lstrlenW( header ) + 4;
4313 if (value) len += lstrlenW( value );
4314 if (!(str = heap_alloc( (len + 1) * sizeof(WCHAR) )))
4316 err = ERROR_OUTOFMEMORY;
4317 goto done;
4319 swprintf( str, len + 1, L"%s: %s\r\n", header, value ? value : L"" );
4320 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
4321 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
4323 err = GetLastError();
4325 heap_free( str );
4327 done:
4328 LeaveCriticalSection( &request->cs );
4329 return HRESULT_FROM_WIN32( err );
4332 static HRESULT WINAPI winhttp_request_GetResponseHeader(
4333 IWinHttpRequest *iface,
4334 BSTR header,
4335 BSTR *value )
4337 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4338 DWORD size, err = ERROR_SUCCESS;
4340 TRACE("%p, %p\n", request, header);
4342 EnterCriticalSection( &request->cs );
4343 if (request->state < REQUEST_STATE_SENT)
4345 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4346 goto done;
4348 if (!header || !value)
4350 err = ERROR_INVALID_PARAMETER;
4351 goto done;
4353 size = 0;
4354 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
4356 err = GetLastError();
4357 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4359 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
4361 err = ERROR_OUTOFMEMORY;
4362 goto done;
4364 err = ERROR_SUCCESS;
4365 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
4367 err = GetLastError();
4368 SysFreeString( *value );
4370 done:
4371 LeaveCriticalSection( &request->cs );
4372 return HRESULT_FROM_WIN32( err );
4375 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
4376 IWinHttpRequest *iface,
4377 BSTR *headers )
4379 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4380 DWORD size, err = ERROR_SUCCESS;
4382 TRACE("%p, %p\n", request, headers);
4384 if (!headers) return E_INVALIDARG;
4386 EnterCriticalSection( &request->cs );
4387 if (request->state < REQUEST_STATE_SENT)
4389 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4390 goto done;
4392 size = 0;
4393 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
4395 err = GetLastError();
4396 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4398 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
4400 err = ERROR_OUTOFMEMORY;
4401 goto done;
4403 err = ERROR_SUCCESS;
4404 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
4406 err = GetLastError();
4407 SysFreeString( *headers );
4409 done:
4410 LeaveCriticalSection( &request->cs );
4411 return HRESULT_FROM_WIN32( err );
4414 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
4416 struct winhttp_request *request = (struct winhttp_request *)context;
4418 switch (status)
4420 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
4421 request->bytes_available = *(DWORD *)buffer;
4422 request->error = ERROR_SUCCESS;
4423 break;
4424 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
4425 request->bytes_read = size;
4426 request->error = ERROR_SUCCESS;
4427 break;
4428 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
4430 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
4431 request->error = result->dwError;
4432 break;
4434 default:
4435 request->error = ERROR_SUCCESS;
4436 break;
4438 SetEvent( request->wait );
4441 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
4443 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
4444 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
4447 static DWORD wait_for_completion( struct winhttp_request *request )
4449 HANDLE handles[2] = { request->wait, request->cancel };
4450 DWORD ret;
4452 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
4454 case WAIT_OBJECT_0:
4455 ret = request->error;
4456 break;
4457 case WAIT_OBJECT_0 + 1:
4458 ret = request->error = ERROR_CANCELLED;
4459 SetEvent( request->done );
4460 break;
4461 default:
4462 ret = request->error = GetLastError();
4463 break;
4465 return ret;
4468 static HRESULT request_receive( struct winhttp_request *request )
4470 DWORD err, size, buflen = 4096;
4472 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
4473 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
4475 return HRESULT_FROM_WIN32( GetLastError() );
4477 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
4478 if (!wcscmp( request->verb, L"HEAD" ))
4480 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
4481 return S_OK;
4483 if (!(request->buffer = heap_alloc( buflen ))) return E_OUTOFMEMORY;
4484 request->buffer[0] = 0;
4485 size = 0;
4488 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
4489 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
4491 err = GetLastError();
4492 goto error;
4494 if ((err = wait_for_completion( request ))) goto error;
4495 if (!request->bytes_available) break;
4496 size += request->bytes_available;
4497 if (buflen < size)
4499 char *tmp;
4500 while (buflen < size) buflen *= 2;
4501 if (!(tmp = heap_realloc( request->buffer, buflen )))
4503 err = ERROR_OUTOFMEMORY;
4504 goto error;
4506 request->buffer = tmp;
4508 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
4509 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
4510 request->bytes_available, &request->bytes_read ))
4512 err = GetLastError();
4513 goto error;
4515 if ((err = wait_for_completion( request ))) goto error;
4516 request->offset += request->bytes_read;
4517 } while (request->bytes_read);
4519 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
4520 return S_OK;
4522 error:
4523 heap_free( request->buffer );
4524 request->buffer = NULL;
4525 return HRESULT_FROM_WIN32( err );
4528 static DWORD request_set_parameters( struct winhttp_request *request )
4530 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
4531 sizeof(request->proxy) )) return GetLastError();
4533 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
4534 sizeof(request->logon_policy) )) return GetLastError();
4536 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
4537 sizeof(request->disable_feature) )) return GetLastError();
4539 if (!WinHttpSetTimeouts( request->hrequest,
4540 request->resolve_timeout,
4541 request->connect_timeout,
4542 request->send_timeout,
4543 request->receive_timeout )) return GetLastError();
4544 return ERROR_SUCCESS;
4547 static void request_set_utf8_content_type( struct winhttp_request *request )
4549 WCHAR headerW[64];
4550 int len;
4552 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"text/plain" );
4553 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
4555 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"charset=utf-8" );
4556 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
4559 static HRESULT request_send( struct winhttp_request *request )
4561 SAFEARRAY *sa = NULL;
4562 VARIANT data;
4563 char *ptr = NULL;
4564 LONG size = 0;
4565 HRESULT hr;
4566 DWORD err;
4568 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
4569 if (wcscmp( request->verb, L"GET" ))
4571 VariantInit( &data );
4572 if (V_VT( &request->data ) == VT_BSTR)
4574 UINT cp = CP_ACP;
4575 const WCHAR *str = V_BSTR( &request->data );
4576 int i, len = lstrlenW( str );
4578 for (i = 0; i < len; i++)
4580 if (str[i] > 127)
4582 cp = CP_UTF8;
4583 break;
4586 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
4587 if (!(ptr = heap_alloc( size ))) return E_OUTOFMEMORY;
4588 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
4589 if (cp == CP_UTF8) request_set_utf8_content_type( request );
4591 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
4593 sa = V_ARRAY( &data );
4594 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
4595 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
4597 SafeArrayUnaccessData( sa );
4598 return hr;
4600 size++;
4603 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT );
4604 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
4606 err = GetLastError();
4607 goto error;
4609 if ((err = wait_for_completion( request ))) goto error;
4610 if (sa) SafeArrayUnaccessData( sa );
4611 else heap_free( ptr );
4612 request->state = REQUEST_STATE_SENT;
4613 return S_OK;
4615 error:
4616 if (sa) SafeArrayUnaccessData( sa );
4617 else heap_free( ptr );
4618 return HRESULT_FROM_WIN32( err );
4621 static void CALLBACK send_and_receive_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
4623 struct winhttp_request *request = (struct winhttp_request *)ctx;
4624 if (request_send( request ) == S_OK) request_receive( request );
4625 SetEvent( request->done );
4628 /* critical section must be held */
4629 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
4631 HANDLE done = request->done;
4632 DWORD err, ret;
4634 LeaveCriticalSection( &request->cs );
4635 while ((err = MsgWaitForMultipleObjects( 1, &done, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
4637 MSG msg;
4638 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
4640 TranslateMessage( &msg );
4641 DispatchMessageW( &msg );
4644 switch (err)
4646 case WAIT_OBJECT_0:
4647 ret = request->error;
4648 break;
4649 case WAIT_TIMEOUT:
4650 ret = ERROR_TIMEOUT;
4651 break;
4652 default:
4653 ret = GetLastError();
4654 break;
4656 EnterCriticalSection( &request->cs );
4657 if (err == WAIT_OBJECT_0) request->proc_running = FALSE;
4658 return ret;
4661 static HRESULT WINAPI winhttp_request_Send(
4662 IWinHttpRequest *iface,
4663 VARIANT body )
4665 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4666 HRESULT hr;
4668 TRACE("%p, %s\n", request, debugstr_variant(&body));
4670 EnterCriticalSection( &request->cs );
4671 if (request->state < REQUEST_STATE_OPEN)
4673 LeaveCriticalSection( &request->cs );
4674 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
4676 if (request->state >= REQUEST_STATE_SENT)
4678 LeaveCriticalSection( &request->cs );
4679 return S_OK;
4681 VariantClear( &request->data );
4682 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
4684 LeaveCriticalSection( &request->cs );
4685 return hr;
4687 if (!TrySubmitThreadpoolCallback( send_and_receive_proc, request, NULL ))
4689 LeaveCriticalSection( &request->cs );
4690 return HRESULT_FROM_WIN32( GetLastError() );
4692 request->proc_running = TRUE;
4693 if (!request->async)
4695 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
4697 LeaveCriticalSection( &request->cs );
4698 return hr;
4701 static HRESULT WINAPI winhttp_request_get_Status(
4702 IWinHttpRequest *iface,
4703 LONG *status )
4705 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4706 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
4708 TRACE("%p, %p\n", request, status);
4710 if (!status) return E_INVALIDARG;
4712 EnterCriticalSection( &request->cs );
4713 if (request->state < REQUEST_STATE_SENT)
4715 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4716 goto done;
4718 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
4719 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
4721 err = GetLastError();
4722 goto done;
4724 *status = status_code;
4726 done:
4727 LeaveCriticalSection( &request->cs );
4728 return HRESULT_FROM_WIN32( err );
4731 static HRESULT WINAPI winhttp_request_get_StatusText(
4732 IWinHttpRequest *iface,
4733 BSTR *status )
4735 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4736 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
4738 TRACE("%p, %p\n", request, status);
4740 if (!status) return E_INVALIDARG;
4742 EnterCriticalSection( &request->cs );
4743 if (request->state < REQUEST_STATE_SENT)
4745 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4746 goto done;
4748 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
4750 err = GetLastError();
4751 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4753 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
4755 err = ERROR_OUTOFMEMORY;
4756 goto done;
4758 index = 0;
4759 err = ERROR_SUCCESS;
4760 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
4762 err = GetLastError();
4763 SysFreeString( *status );
4765 done:
4766 LeaveCriticalSection( &request->cs );
4767 return HRESULT_FROM_WIN32( err );
4770 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
4772 WCHAR *buffer, *p;
4773 DWORD size;
4775 *codepage = CP_ACP;
4776 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
4777 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4779 if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY;
4780 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
4782 return GetLastError();
4784 if ((p = wcsstr( buffer, L"charset" )))
4786 p += lstrlenW( L"charset" );
4787 while (*p == ' ') p++;
4788 if (*p++ == '=')
4790 while (*p == ' ') p++;
4791 if (!wcsicmp( p, L"utf-8" )) *codepage = CP_UTF8;
4794 heap_free( buffer );
4796 return ERROR_SUCCESS;
4799 static HRESULT WINAPI winhttp_request_get_ResponseText(
4800 IWinHttpRequest *iface,
4801 BSTR *body )
4803 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4804 UINT codepage;
4805 DWORD err = ERROR_SUCCESS;
4806 int len;
4808 TRACE("%p, %p\n", request, body);
4810 if (!body) return E_INVALIDARG;
4812 EnterCriticalSection( &request->cs );
4813 if (request->state < REQUEST_STATE_SENT)
4815 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4816 goto done;
4818 if ((err = request_get_codepage( request, &codepage ))) goto done;
4819 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
4820 if (!(*body = SysAllocStringLen( NULL, len )))
4822 err = ERROR_OUTOFMEMORY;
4823 goto done;
4825 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
4826 (*body)[len] = 0;
4828 done:
4829 LeaveCriticalSection( &request->cs );
4830 return HRESULT_FROM_WIN32( err );
4833 static HRESULT WINAPI winhttp_request_get_ResponseBody(
4834 IWinHttpRequest *iface,
4835 VARIANT *body )
4837 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4838 SAFEARRAY *sa;
4839 HRESULT hr;
4840 DWORD err = ERROR_SUCCESS;
4841 char *ptr;
4843 TRACE("%p, %p\n", request, body);
4845 if (!body) return E_INVALIDARG;
4847 EnterCriticalSection( &request->cs );
4848 if (request->state < REQUEST_STATE_SENT)
4850 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4851 goto done;
4853 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
4855 err = ERROR_OUTOFMEMORY;
4856 goto done;
4858 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
4860 SafeArrayDestroy( sa );
4861 LeaveCriticalSection( &request->cs );
4862 return hr;
4864 memcpy( ptr, request->buffer, request->offset );
4865 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
4867 SafeArrayDestroy( sa );
4868 LeaveCriticalSection( &request->cs );
4869 return hr;
4871 V_VT( body ) = VT_ARRAY|VT_UI1;
4872 V_ARRAY( body ) = sa;
4874 done:
4875 LeaveCriticalSection( &request->cs );
4876 return HRESULT_FROM_WIN32( err );
4879 struct stream
4881 IStream IStream_iface;
4882 LONG refs;
4883 char *data;
4884 ULARGE_INTEGER pos, size;
4887 static inline struct stream *impl_from_IStream( IStream *iface )
4889 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
4892 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
4894 struct stream *stream = impl_from_IStream( iface );
4896 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
4898 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
4900 *obj = iface;
4902 else
4904 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4905 return E_NOINTERFACE;
4907 IStream_AddRef( iface );
4908 return S_OK;
4911 static ULONG WINAPI stream_AddRef( IStream *iface )
4913 struct stream *stream = impl_from_IStream( iface );
4914 return InterlockedIncrement( &stream->refs );
4917 static ULONG WINAPI stream_Release( IStream *iface )
4919 struct stream *stream = impl_from_IStream( iface );
4920 LONG refs = InterlockedDecrement( &stream->refs );
4921 if (!refs)
4923 heap_free( stream->data );
4924 heap_free( stream );
4926 return refs;
4929 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
4931 struct stream *stream = impl_from_IStream( iface );
4932 ULONG size;
4934 if (stream->pos.QuadPart >= stream->size.QuadPart)
4936 *read = 0;
4937 return S_FALSE;
4940 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
4941 memcpy( buf, stream->data + stream->pos.QuadPart, size );
4942 stream->pos.QuadPart += size;
4943 *read = size;
4945 return S_OK;
4948 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
4950 FIXME("\n");
4951 return E_NOTIMPL;
4954 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
4956 struct stream *stream = impl_from_IStream( iface );
4958 if (origin == STREAM_SEEK_SET)
4959 stream->pos.QuadPart = move.QuadPart;
4960 else if (origin == STREAM_SEEK_CUR)
4961 stream->pos.QuadPart += move.QuadPart;
4962 else if (origin == STREAM_SEEK_END)
4963 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
4965 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
4966 return S_OK;
4969 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
4971 FIXME("\n");
4972 return E_NOTIMPL;
4975 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
4976 ULARGE_INTEGER *written )
4978 FIXME("\n");
4979 return E_NOTIMPL;
4982 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
4984 FIXME("\n");
4985 return E_NOTIMPL;
4988 static HRESULT WINAPI stream_Revert( IStream *iface )
4990 FIXME("\n");
4991 return E_NOTIMPL;
4994 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
4996 FIXME("\n");
4997 return E_NOTIMPL;
5000 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5002 FIXME("\n");
5003 return E_NOTIMPL;
5006 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
5008 FIXME("\n");
5009 return E_NOTIMPL;
5012 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
5014 FIXME("\n");
5015 return E_NOTIMPL;
5018 static const IStreamVtbl stream_vtbl =
5020 stream_QueryInterface,
5021 stream_AddRef,
5022 stream_Release,
5023 stream_Read,
5024 stream_Write,
5025 stream_Seek,
5026 stream_SetSize,
5027 stream_CopyTo,
5028 stream_Commit,
5029 stream_Revert,
5030 stream_LockRegion,
5031 stream_UnlockRegion,
5032 stream_Stat,
5033 stream_Clone
5036 static HRESULT WINAPI winhttp_request_get_ResponseStream(
5037 IWinHttpRequest *iface,
5038 VARIANT *body )
5040 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5041 DWORD err = ERROR_SUCCESS;
5042 struct stream *stream;
5044 TRACE("%p, %p\n", request, body);
5046 if (!body) return E_INVALIDARG;
5048 EnterCriticalSection( &request->cs );
5049 if (request->state < REQUEST_STATE_SENT)
5051 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5052 goto done;
5054 if (!(stream = heap_alloc( sizeof(*stream) )))
5056 err = ERROR_OUTOFMEMORY;
5057 goto done;
5059 stream->IStream_iface.lpVtbl = &stream_vtbl;
5060 stream->refs = 1;
5061 if (!(stream->data = heap_alloc( request->offset )))
5063 heap_free( stream );
5064 err = ERROR_OUTOFMEMORY;
5065 goto done;
5067 memcpy( stream->data, request->buffer, request->offset );
5068 stream->pos.QuadPart = 0;
5069 stream->size.QuadPart = request->offset;
5070 V_VT( body ) = VT_UNKNOWN;
5071 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
5073 done:
5074 LeaveCriticalSection( &request->cs );
5075 return HRESULT_FROM_WIN32( err );
5078 static HRESULT WINAPI winhttp_request_get_Option(
5079 IWinHttpRequest *iface,
5080 WinHttpRequestOption option,
5081 VARIANT *value )
5083 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5084 HRESULT hr = S_OK;
5086 TRACE("%p, %u, %p\n", request, option, value);
5088 EnterCriticalSection( &request->cs );
5089 switch (option)
5091 case WinHttpRequestOption_URLCodePage:
5092 V_VT( value ) = VT_I4;
5093 V_I4( value ) = request->url_codepage;
5094 break;
5095 default:
5096 FIXME("unimplemented option %u\n", option);
5097 hr = E_NOTIMPL;
5098 break;
5100 LeaveCriticalSection( &request->cs );
5101 return hr;
5104 static HRESULT WINAPI winhttp_request_put_Option(
5105 IWinHttpRequest *iface,
5106 WinHttpRequestOption option,
5107 VARIANT value )
5109 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5110 HRESULT hr = S_OK;
5112 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
5114 EnterCriticalSection( &request->cs );
5115 switch (option)
5117 case WinHttpRequestOption_EnableRedirects:
5119 if (V_BOOL( &value ))
5120 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
5121 else
5122 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
5123 break;
5125 case WinHttpRequestOption_URLCodePage:
5127 VARIANT cp;
5128 VariantInit( &cp );
5129 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
5130 if (SUCCEEDED( hr ))
5132 request->url_codepage = V_UI4( &cp );
5133 TRACE("URL codepage: %u\n", request->url_codepage);
5135 else if (V_VT( &value ) == VT_BSTR && !wcsicmp( V_BSTR( &value ), L"utf-8" ))
5137 TRACE("URL codepage: UTF-8\n");
5138 request->url_codepage = CP_UTF8;
5139 hr = S_OK;
5141 else
5142 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
5143 break;
5145 default:
5146 FIXME("unimplemented option %u\n", option);
5147 hr = E_NOTIMPL;
5148 break;
5150 LeaveCriticalSection( &request->cs );
5151 return hr;
5154 static HRESULT WINAPI winhttp_request_WaitForResponse(
5155 IWinHttpRequest *iface,
5156 VARIANT timeout,
5157 VARIANT_BOOL *succeeded )
5159 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5160 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
5162 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
5164 EnterCriticalSection( &request->cs );
5165 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
5167 LeaveCriticalSection( &request->cs );
5168 return S_OK;
5170 switch ((err = request_wait( request, msecs )))
5172 case ERROR_TIMEOUT:
5173 if (succeeded) *succeeded = VARIANT_FALSE;
5174 err = ERROR_SUCCESS;
5175 break;
5177 default:
5178 if (succeeded) *succeeded = VARIANT_TRUE;
5179 break;
5181 LeaveCriticalSection( &request->cs );
5182 return HRESULT_FROM_WIN32( err );
5185 static HRESULT WINAPI winhttp_request_Abort(
5186 IWinHttpRequest *iface )
5188 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5190 TRACE("%p\n", request);
5192 EnterCriticalSection( &request->cs );
5193 cancel_request( request );
5194 LeaveCriticalSection( &request->cs );
5195 return S_OK;
5198 static HRESULT WINAPI winhttp_request_SetTimeouts(
5199 IWinHttpRequest *iface,
5200 LONG resolve_timeout,
5201 LONG connect_timeout,
5202 LONG send_timeout,
5203 LONG receive_timeout )
5205 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5207 TRACE("%p, %d, %d, %d, %d\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout);
5209 EnterCriticalSection( &request->cs );
5210 request->resolve_timeout = resolve_timeout;
5211 request->connect_timeout = connect_timeout;
5212 request->send_timeout = send_timeout;
5213 request->receive_timeout = receive_timeout;
5214 LeaveCriticalSection( &request->cs );
5215 return S_OK;
5218 static HRESULT WINAPI winhttp_request_SetClientCertificate(
5219 IWinHttpRequest *iface,
5220 BSTR certificate )
5222 FIXME("\n");
5223 return E_NOTIMPL;
5226 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
5227 IWinHttpRequest *iface,
5228 WinHttpRequestAutoLogonPolicy policy )
5230 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5231 HRESULT hr = S_OK;
5233 TRACE("%p, %u\n", request, policy );
5235 EnterCriticalSection( &request->cs );
5236 switch (policy)
5238 case AutoLogonPolicy_Always:
5239 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
5240 break;
5241 case AutoLogonPolicy_OnlyIfBypassProxy:
5242 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
5243 break;
5244 case AutoLogonPolicy_Never:
5245 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
5246 break;
5247 default: hr = E_INVALIDARG;
5248 break;
5250 LeaveCriticalSection( &request->cs );
5251 return hr;
5254 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
5256 winhttp_request_QueryInterface,
5257 winhttp_request_AddRef,
5258 winhttp_request_Release,
5259 winhttp_request_GetTypeInfoCount,
5260 winhttp_request_GetTypeInfo,
5261 winhttp_request_GetIDsOfNames,
5262 winhttp_request_Invoke,
5263 winhttp_request_SetProxy,
5264 winhttp_request_SetCredentials,
5265 winhttp_request_Open,
5266 winhttp_request_SetRequestHeader,
5267 winhttp_request_GetResponseHeader,
5268 winhttp_request_GetAllResponseHeaders,
5269 winhttp_request_Send,
5270 winhttp_request_get_Status,
5271 winhttp_request_get_StatusText,
5272 winhttp_request_get_ResponseText,
5273 winhttp_request_get_ResponseBody,
5274 winhttp_request_get_ResponseStream,
5275 winhttp_request_get_Option,
5276 winhttp_request_put_Option,
5277 winhttp_request_WaitForResponse,
5278 winhttp_request_Abort,
5279 winhttp_request_SetTimeouts,
5280 winhttp_request_SetClientCertificate,
5281 winhttp_request_SetAutoLogonPolicy
5284 HRESULT WinHttpRequest_create( void **obj )
5286 struct winhttp_request *request;
5288 TRACE("%p\n", obj);
5290 if (!(request = heap_alloc_zero( sizeof(*request) ))) return E_OUTOFMEMORY;
5291 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
5292 request->refs = 1;
5293 InitializeCriticalSection( &request->cs );
5294 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
5295 initialize_request( request );
5297 *obj = &request->IWinHttpRequest_iface;
5298 TRACE("returning iface %p\n", *obj);
5299 return S_OK;