Release 5.0-rc1.
[wine.git] / dlls / winhttp / request.c
blob7c8e6ba89e039a2962b2f7c1710a92514f2bf407
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"
37 #include "wine/debug.h"
38 #include "winhttp_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
42 #define DEFAULT_KEEP_ALIVE_TIMEOUT 30000
44 static const WCHAR *attribute_table[] =
46 L"Mime-Version", /* WINHTTP_QUERY_MIME_VERSION = 0 */
47 L"Content-Type" , /* WINHTTP_QUERY_CONTENT_TYPE = 1 */
48 L"Content-Transfer-Encoding", /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
49 L"Content-ID", /* WINHTTP_QUERY_CONTENT_ID = 3 */
50 NULL, /* WINHTTP_QUERY_CONTENT_DESCRIPTION = 4 */
51 L"Content-Length", /* WINHTTP_QUERY_CONTENT_LENGTH = 5 */
52 L"Content-Language", /* WINHTTP_QUERY_CONTENT_LANGUAGE = 6 */
53 L"Allow", /* WINHTTP_QUERY_ALLOW = 7 */
54 L"Public", /* WINHTTP_QUERY_PUBLIC = 8 */
55 L"Date", /* WINHTTP_QUERY_DATE = 9 */
56 L"Expires", /* WINHTTP_QUERY_EXPIRES = 10 */
57 L"Last-Modified", /* WINHTTP_QUERY_LAST_MODIFIEDcw = 11 */
58 NULL, /* WINHTTP_QUERY_MESSAGE_ID = 12 */
59 L"URI", /* WINHTTP_QUERY_URI = 13 */
60 L"From", /* WINHTTP_QUERY_DERIVED_FROM = 14 */
61 NULL, /* WINHTTP_QUERY_COST = 15 */
62 NULL, /* WINHTTP_QUERY_LINK = 16 */
63 L"Pragma", /* WINHTTP_QUERY_PRAGMA = 17 */
64 NULL, /* WINHTTP_QUERY_VERSION = 18 */
65 L"Status", /* WINHTTP_QUERY_STATUS_CODE = 19 */
66 NULL, /* WINHTTP_QUERY_STATUS_TEXT = 20 */
67 NULL, /* WINHTTP_QUERY_RAW_HEADERS = 21 */
68 NULL, /* WINHTTP_QUERY_RAW_HEADERS_CRLF = 22 */
69 L"Connection", /* WINHTTP_QUERY_CONNECTION = 23 */
70 L"Accept", /* WINHTTP_QUERY_ACCEPT = 24 */
71 L"Accept-Charset", /* WINHTTP_QUERY_ACCEPT_CHARSET = 25 */
72 L"Accept-Encoding", /* WINHTTP_QUERY_ACCEPT_ENCODING = 26 */
73 L"Accept-Language", /* WINHTTP_QUERY_ACCEPT_LANGUAGE = 27 */
74 L"Authorization", /* WINHTTP_QUERY_AUTHORIZATION = 28 */
75 L"Content-Encoding", /* WINHTTP_QUERY_CONTENT_ENCODING = 29 */
76 NULL, /* WINHTTP_QUERY_FORWARDED = 30 */
77 NULL, /* WINHTTP_QUERY_FROM = 31 */
78 L"If-Modified-Since", /* WINHTTP_QUERY_IF_MODIFIED_SINCE = 32 */
79 L"Location", /* WINHTTP_QUERY_LOCATION = 33 */
80 NULL, /* WINHTTP_QUERY_ORIG_URI = 34 */
81 L"Referer", /* WINHTTP_QUERY_REFERER = 35 */
82 L"Retry-After", /* WINHTTP_QUERY_RETRY_AFTER = 36 */
83 L"Server", /* WINHTTP_QUERY_SERVER = 37 */
84 NULL, /* WINHTTP_TITLE = 38 */
85 L"User-Agent", /* WINHTTP_QUERY_USER_AGENT = 39 */
86 L"WWW-Authenticate", /* WINHTTP_QUERY_WWW_AUTHENTICATE = 40 */
87 L"Proxy-Authenticate", /* WINHTTP_QUERY_PROXY_AUTHENTICATE = 41 */
88 L"Accept-Ranges", /* WINHTTP_QUERY_ACCEPT_RANGES = 42 */
89 L"Set-Cookie", /* WINHTTP_QUERY_SET_COOKIE = 43 */
90 L"Cookie", /* WINHTTP_QUERY_COOKIE = 44 */
91 NULL, /* WINHTTP_QUERY_REQUEST_METHOD = 45 */
92 NULL, /* WINHTTP_QUERY_REFRESH = 46 */
93 NULL, /* WINHTTP_QUERY_CONTENT_DISPOSITION = 47 */
94 L"Age", /* WINHTTP_QUERY_AGE = 48 */
95 L"Cache-Control", /* WINHTTP_QUERY_CACHE_CONTROL = 49 */
96 L"Content-Base", /* WINHTTP_QUERY_CONTENT_BASE = 50 */
97 L"Content-Location", /* WINHTTP_QUERY_CONTENT_LOCATION = 51 */
98 L"Content-MD5", /* WINHTTP_QUERY_CONTENT_MD5 = 52 */
99 L"Content-Range", /* WINHTTP_QUERY_CONTENT_RANGE = 53 */
100 L"ETag", /* WINHTTP_QUERY_ETAG = 54 */
101 L"Host", /* WINHTTP_QUERY_HOST = 55 */
102 L"If-Match", /* WINHTTP_QUERY_IF_MATCH = 56 */
103 L"If-None-Match", /* WINHTTP_QUERY_IF_NONE_MATCH = 57 */
104 L"If-Range", /* WINHTTP_QUERY_IF_RANGE = 58 */
105 L"If-Unmodified-Since", /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
106 L"Max-Forwards", /* WINHTTP_QUERY_MAX_FORWARDS = 60 */
107 L"Proxy-Authorization", /* WINHTTP_QUERY_PROXY_AUTHORIZATION = 61 */
108 L"Range", /* WINHTTP_QUERY_RANGE = 62 */
109 L"Transfer-Encoding", /* WINHTTP_QUERY_TRANSFER_ENCODING = 63 */
110 L"Upgrade", /* WINHTTP_QUERY_UPGRADE = 64 */
111 L"Vary", /* WINHTTP_QUERY_VARY = 65 */
112 L"Via", /* WINHTTP_QUERY_VIA = 66 */
113 L"Warning", /* WINHTTP_QUERY_WARNING = 67 */
114 L"Expect", /* WINHTTP_QUERY_EXPECT = 68 */
115 L"Proxy-Connection", /* WINHTTP_QUERY_PROXY_CONNECTION = 69 */
116 L"Unless-Modified-Since", /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
117 NULL, /* WINHTTP_QUERY_PROXY_SUPPORT = 75 */
118 NULL, /* WINHTTP_QUERY_AUTHENTICATION_INFO = 76 */
119 NULL, /* WINHTTP_QUERY_PASSPORT_URLS = 77 */
120 NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
123 static struct task_header *dequeue_task( struct request *request )
125 struct task_header *task;
127 EnterCriticalSection( &request->task_cs );
128 TRACE("%u tasks queued\n", list_count( &request->task_queue ));
129 task = LIST_ENTRY( list_head( &request->task_queue ), struct task_header, entry );
130 if (task) list_remove( &task->entry );
131 LeaveCriticalSection( &request->task_cs );
133 TRACE("returning task %p\n", task);
134 return task;
137 static void CALLBACK task_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
139 struct request *request = ctx;
140 HANDLE handles[2];
142 handles[0] = request->task_wait;
143 handles[1] = request->task_cancel;
144 for (;;)
146 DWORD err = WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
147 switch (err)
149 case WAIT_OBJECT_0:
151 struct task_header *task;
152 while ((task = dequeue_task( request )))
154 task->proc( task );
155 release_object( &task->request->hdr );
156 heap_free( task );
158 break;
160 case WAIT_OBJECT_0 + 1:
161 TRACE("exiting\n");
162 CloseHandle( request->task_cancel );
163 CloseHandle( request->task_wait );
164 request->task_cs.DebugInfo->Spare[0] = 0;
165 DeleteCriticalSection( &request->task_cs );
166 request->hdr.vtbl->destroy( &request->hdr );
167 return;
169 default:
170 ERR("wait failed %u (%u)\n", err, GetLastError());
171 break;
176 static BOOL queue_task( struct task_header *task )
178 struct request *request = task->request;
180 if (!request->task_wait)
182 if (!(request->task_wait = CreateEventW( NULL, FALSE, FALSE, NULL ))) return FALSE;
183 if (!(request->task_cancel = CreateEventW( NULL, FALSE, FALSE, NULL )))
185 CloseHandle( request->task_wait );
186 request->task_wait = NULL;
187 return FALSE;
189 if (!TrySubmitThreadpoolCallback( task_proc, request, NULL ))
191 CloseHandle( request->task_wait );
192 request->task_wait = NULL;
193 CloseHandle( request->task_cancel );
194 request->task_cancel = NULL;
195 return FALSE;
197 request->task_proc_running = TRUE;
198 InitializeCriticalSection( &request->task_cs );
199 request->task_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": request.task_cs");
202 EnterCriticalSection( &request->task_cs );
203 TRACE("queueing task %p\n", task );
204 list_add_tail( &request->task_queue, &task->entry );
205 LeaveCriticalSection( &request->task_cs );
207 SetEvent( request->task_wait );
208 return TRUE;
211 static void free_header( struct header *header )
213 heap_free( header->field );
214 heap_free( header->value );
215 heap_free( header );
218 static BOOL valid_token_char( WCHAR c )
220 if (c < 32 || c == 127) return FALSE;
221 switch (c)
223 case '(': case ')':
224 case '<': case '>':
225 case '@': case ',':
226 case ';': case ':':
227 case '\\': case '\"':
228 case '/': case '[':
229 case ']': case '?':
230 case '=': case '{':
231 case '}': case ' ':
232 case '\t':
233 return FALSE;
234 default:
235 return TRUE;
239 static struct header *parse_header( const WCHAR *string )
241 const WCHAR *p, *q;
242 struct header *header;
243 int len;
245 p = string;
246 if (!(q = wcschr( p, ':' )))
248 WARN("no ':' in line %s\n", debugstr_w(string));
249 return NULL;
251 if (q == string)
253 WARN("empty field name in line %s\n", debugstr_w(string));
254 return NULL;
256 while (*p != ':')
258 if (!valid_token_char( *p ))
260 WARN("invalid character in field name %s\n", debugstr_w(string));
261 return NULL;
263 p++;
265 len = q - string;
266 if (!(header = heap_alloc_zero( sizeof(struct header) ))) return NULL;
267 if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) )))
269 heap_free( header );
270 return NULL;
272 memcpy( header->field, string, len * sizeof(WCHAR) );
273 header->field[len] = 0;
275 q++; /* skip past colon */
276 while (*q == ' ') q++;
277 len = lstrlenW( q );
279 if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
281 free_header( header );
282 return NULL;
284 memcpy( header->value, q, len * sizeof(WCHAR) );
285 header->value[len] = 0;
287 return header;
290 static int get_header_index( struct request *request, const WCHAR *field, int requested_index, BOOL request_only )
292 int index;
294 TRACE("%s\n", debugstr_w(field));
296 for (index = 0; index < request->num_headers; index++)
298 if (wcsicmp( request->headers[index].field, field )) continue;
299 if (request_only && !request->headers[index].is_request) continue;
300 if (!request_only && request->headers[index].is_request) continue;
302 if (!requested_index) break;
303 requested_index--;
305 if (index >= request->num_headers) index = -1;
306 TRACE("returning %d\n", index);
307 return index;
310 static BOOL insert_header( struct request *request, struct header *header )
312 DWORD count = request->num_headers + 1;
313 struct header *hdrs;
315 if (request->headers)
316 hdrs = heap_realloc_zero( request->headers, sizeof(struct header) * count );
317 else
318 hdrs = heap_alloc_zero( sizeof(struct header) );
319 if (!hdrs) return FALSE;
321 request->headers = hdrs;
322 request->headers[count - 1].field = strdupW( header->field );
323 request->headers[count - 1].value = strdupW( header->value );
324 request->headers[count - 1].is_request = header->is_request;
325 request->num_headers = count;
326 return TRUE;
329 static BOOL delete_header( struct request *request, DWORD index )
331 if (!request->num_headers) return FALSE;
332 if (index >= request->num_headers) return FALSE;
333 request->num_headers--;
335 heap_free( request->headers[index].field );
336 heap_free( request->headers[index].value );
338 memmove( &request->headers[index], &request->headers[index + 1],
339 (request->num_headers - index) * sizeof(struct header) );
340 memset( &request->headers[request->num_headers], 0, sizeof(struct header) );
341 return TRUE;
344 BOOL process_header( struct request *request, const WCHAR *field, const WCHAR *value, DWORD flags, BOOL request_only )
346 int index;
347 struct header hdr;
349 TRACE("%s: %s 0x%08x\n", debugstr_w(field), debugstr_w(value), flags);
351 if ((index = get_header_index( request, field, 0, request_only )) >= 0)
353 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return FALSE;
356 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
358 if (index >= 0)
360 delete_header( request, index );
361 if (!value || !value[0]) return TRUE;
363 else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD))
365 SetLastError( ERROR_WINHTTP_HEADER_NOT_FOUND );
366 return FALSE;
369 hdr.field = (LPWSTR)field;
370 hdr.value = (LPWSTR)value;
371 hdr.is_request = request_only;
372 return insert_header( request, &hdr );
374 else if (value)
377 if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
378 index >= 0)
380 WCHAR *tmp;
381 int len, len_orig, len_value;
382 struct header *header = &request->headers[index];
384 len_orig = lstrlenW( header->value );
385 len_value = lstrlenW( value );
387 len = len_orig + len_value + 2;
388 if (!(tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return FALSE;
389 header->value = tmp;
390 header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
391 header->value[len_orig++] = ' ';
393 memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
394 header->value[len] = 0;
395 return TRUE;
397 else
399 hdr.field = (LPWSTR)field;
400 hdr.value = (LPWSTR)value;
401 hdr.is_request = request_only;
402 return insert_header( request, &hdr );
406 return TRUE;
409 BOOL add_request_headers( struct request *request, const WCHAR *headers, DWORD len, DWORD flags )
411 BOOL ret = FALSE;
412 WCHAR *buffer, *p, *q;
413 struct header *header;
415 if (len == ~0u) len = lstrlenW( headers );
416 if (!len) return TRUE;
417 if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
418 memcpy( buffer, headers, len * sizeof(WCHAR) );
419 buffer[len] = 0;
421 p = buffer;
424 q = p;
425 while (*q)
427 if (q[0] == '\n' && q[1] == '\r')
429 q[0] = '\r';
430 q[1] = '\n';
432 if (q[0] == '\r' && q[1] == '\n') break;
433 q++;
435 if (!*p) break;
436 if (*q == '\r')
438 *q = 0;
439 q += 2; /* jump over \r\n */
441 if ((header = parse_header( p )))
443 ret = process_header( request, header->field, header->value, flags, TRUE );
444 free_header( header );
446 p = q;
447 } while (ret);
449 heap_free( buffer );
450 return ret;
453 /***********************************************************************
454 * WinHttpAddRequestHeaders (winhttp.@)
456 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags )
458 BOOL ret;
459 struct request *request;
461 TRACE("%p, %s, %u, 0x%08x\n", hrequest, debugstr_wn(headers, len), len, flags);
463 if (!headers || !len)
465 SetLastError( ERROR_INVALID_PARAMETER );
466 return FALSE;
468 if (!(request = (struct request *)grab_object( hrequest )))
470 SetLastError( ERROR_INVALID_HANDLE );
471 return FALSE;
473 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
475 release_object( &request->hdr );
476 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
477 return FALSE;
480 ret = add_request_headers( request, headers, len, flags );
482 release_object( &request->hdr );
483 if (ret) SetLastError( ERROR_SUCCESS );
484 return ret;
487 static WCHAR *build_absolute_request_path( struct request *request, const WCHAR **path )
489 const WCHAR *scheme;
490 WCHAR *ret;
491 int len, offset;
493 scheme = (request->netconn ? request->netconn->secure : (request->hdr.flags & WINHTTP_FLAG_SECURE)) ? L"https" : L"http";
495 len = lstrlenW( scheme ) + lstrlenW( request->connect->hostname ) + 4; /* '://' + nul */
496 if (request->connect->hostport) len += 6; /* ':' between host and port, up to 5 for port */
498 len += lstrlenW( request->path );
499 if ((ret = heap_alloc( len * sizeof(WCHAR) )))
501 offset = swprintf( ret, len, L"%s://%s", scheme, request->connect->hostname );
502 if (request->connect->hostport)
504 offset += swprintf( ret + offset, len - offset, L":%u", request->connect->hostport );
506 lstrcpyW( ret + offset, request->path );
507 if (path) *path = ret + offset;
510 return ret;
513 static WCHAR *build_request_string( struct request *request )
515 WCHAR *path, *ret;
516 unsigned int i, len;
518 if (!wcsicmp( request->connect->hostname, request->connect->servername )) path = request->path;
519 else if (!(path = build_absolute_request_path( request, NULL ))) return NULL;
521 len = lstrlenW( request->verb ) + 1 /* ' ' */;
522 len += lstrlenW( path ) + 1 /* ' ' */;
523 len += lstrlenW( request->version );
525 for (i = 0; i < request->num_headers; i++)
527 if (request->headers[i].is_request)
528 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
530 len += 4; /* '\r\n\r\n' */
532 if ((ret = heap_alloc( (len + 1) * sizeof(WCHAR) )))
534 lstrcpyW( ret, request->verb );
535 lstrcatW( ret, L" " );
536 lstrcatW( ret, path );
537 lstrcatW( ret, L" " );
538 lstrcatW( ret, request->version );
540 for (i = 0; i < request->num_headers; i++)
542 if (request->headers[i].is_request)
544 lstrcatW( ret, L"\r\n" );
545 lstrcatW( ret, request->headers[i].field );
546 lstrcatW( ret, L": " );
547 lstrcatW( ret, request->headers[i].value );
550 lstrcatW( ret, L"\r\n\r\n" );
553 if (path != request->path) heap_free( path );
554 return ret;
557 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
559 static BOOL query_headers( struct request *request, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
560 DWORD *index )
562 struct header *header = NULL;
563 BOOL request_only, ret = FALSE;
564 int requested_index, header_index = -1;
565 DWORD attr, len;
567 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
568 requested_index = index ? *index : 0;
570 attr = level & ~QUERY_MODIFIER_MASK;
571 switch (attr)
573 case WINHTTP_QUERY_CUSTOM:
575 header_index = get_header_index( request, name, requested_index, request_only );
576 break;
578 case WINHTTP_QUERY_RAW_HEADERS:
580 WCHAR *headers, *p, *q;
582 if (request_only)
583 headers = build_request_string( request );
584 else
585 headers = request->raw_headers;
587 if (!(p = headers)) return FALSE;
588 for (len = 0; *p; p++) if (*p != '\r') len++;
590 if (!buffer || len * sizeof(WCHAR) > *buflen)
591 SetLastError( ERROR_INSUFFICIENT_BUFFER );
592 else
594 for (p = headers, q = buffer; *p; p++, q++)
596 if (*p != '\r') *q = *p;
597 else
599 *q = 0;
600 p++; /* skip '\n' */
603 TRACE("returning data: %s\n", debugstr_wn(buffer, len));
604 if (len) len--;
605 ret = TRUE;
607 *buflen = len * sizeof(WCHAR);
608 if (request_only) heap_free( headers );
609 return ret;
611 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
613 WCHAR *headers;
615 if (request_only)
616 headers = build_request_string( request );
617 else
618 headers = request->raw_headers;
620 if (!headers) return FALSE;
621 len = lstrlenW( headers ) * sizeof(WCHAR);
622 if (!buffer || len + sizeof(WCHAR) > *buflen)
624 len += sizeof(WCHAR);
625 SetLastError( ERROR_INSUFFICIENT_BUFFER );
627 else
629 memcpy( buffer, headers, len + sizeof(WCHAR) );
630 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
631 ret = TRUE;
633 *buflen = len;
634 if (request_only) heap_free( headers );
635 return ret;
637 case WINHTTP_QUERY_VERSION:
638 len = lstrlenW( request->version ) * sizeof(WCHAR);
639 if (!buffer || len + sizeof(WCHAR) > *buflen)
641 len += sizeof(WCHAR);
642 SetLastError( ERROR_INSUFFICIENT_BUFFER );
644 else
646 lstrcpyW( buffer, request->version );
647 TRACE("returning string: %s\n", debugstr_w(buffer));
648 ret = TRUE;
650 *buflen = len;
651 return ret;
653 case WINHTTP_QUERY_STATUS_TEXT:
654 len = lstrlenW( request->status_text ) * sizeof(WCHAR);
655 if (!buffer || len + sizeof(WCHAR) > *buflen)
657 len += sizeof(WCHAR);
658 SetLastError( ERROR_INSUFFICIENT_BUFFER );
660 else
662 lstrcpyW( buffer, request->status_text );
663 TRACE("returning string: %s\n", debugstr_w(buffer));
664 ret = TRUE;
666 *buflen = len;
667 return ret;
669 case WINHTTP_QUERY_REQUEST_METHOD:
670 len = lstrlenW( request->verb ) * sizeof(WCHAR);
671 if (!buffer || len + sizeof(WCHAR) > *buflen)
673 len += sizeof(WCHAR);
674 SetLastError( ERROR_INSUFFICIENT_BUFFER );
676 else
678 lstrcpyW( buffer, request->verb );
679 TRACE("returning string: %s\n", debugstr_w(buffer));
680 ret = TRUE;
682 *buflen = len;
683 return ret;
685 default:
686 if (attr >= ARRAY_SIZE(attribute_table))
688 SetLastError( ERROR_INVALID_PARAMETER );
689 return FALSE;
691 if (!attribute_table[attr])
693 FIXME("attribute %u not implemented\n", attr);
694 SetLastError( ERROR_WINHTTP_HEADER_NOT_FOUND );
695 return FALSE;
697 TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
698 header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
699 break;
702 if (header_index >= 0)
704 header = &request->headers[header_index];
706 if (!header || (request_only && !header->is_request))
708 SetLastError( ERROR_WINHTTP_HEADER_NOT_FOUND );
709 return FALSE;
711 if (level & WINHTTP_QUERY_FLAG_NUMBER)
713 if (!buffer || sizeof(int) > *buflen)
715 SetLastError( ERROR_INSUFFICIENT_BUFFER );
717 else
719 int *number = buffer;
720 *number = wcstol( header->value, NULL, 10 );
721 TRACE("returning number: %d\n", *number);
722 ret = TRUE;
724 *buflen = sizeof(int);
726 else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
728 SYSTEMTIME *st = buffer;
729 if (!buffer || sizeof(SYSTEMTIME) > *buflen)
731 SetLastError( ERROR_INSUFFICIENT_BUFFER );
733 else if ((ret = WinHttpTimeToSystemTime( header->value, st )))
735 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
736 st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
737 st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
739 *buflen = sizeof(SYSTEMTIME);
741 else if (header->value)
743 len = lstrlenW( header->value ) * sizeof(WCHAR);
744 if (!buffer || len + sizeof(WCHAR) > *buflen)
746 len += sizeof(WCHAR);
747 SetLastError( ERROR_INSUFFICIENT_BUFFER );
749 else
751 lstrcpyW( buffer, header->value );
752 TRACE("returning string: %s\n", debugstr_w(buffer));
753 ret = TRUE;
755 *buflen = len;
757 if (ret && index) *index += 1;
758 return ret;
761 /***********************************************************************
762 * WinHttpQueryHeaders (winhttp.@)
764 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
766 BOOL ret;
767 struct request *request;
769 TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index);
771 if (!(request = (struct request *)grab_object( hrequest )))
773 SetLastError( ERROR_INVALID_HANDLE );
774 return FALSE;
776 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
778 release_object( &request->hdr );
779 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
780 return FALSE;
783 ret = query_headers( request, level, name, buffer, buflen, index );
785 release_object( &request->hdr );
786 if (ret) SetLastError( ERROR_SUCCESS );
787 return ret;
790 static const struct
792 const WCHAR *str;
793 unsigned int len;
794 DWORD scheme;
796 auth_schemes[] =
798 { L"Basic", ARRAY_SIZE(L"Basic") - 1, WINHTTP_AUTH_SCHEME_BASIC },
799 { L"NTLM", ARRAY_SIZE(L"NTLM") - 1, WINHTTP_AUTH_SCHEME_NTLM },
800 { L"Passport", ARRAY_SIZE(L"Passport") - 1, WINHTTP_AUTH_SCHEME_PASSPORT },
801 { L"Digest", ARRAY_SIZE(L"Digest") - 1, WINHTTP_AUTH_SCHEME_DIGEST },
802 { L"Negotiate", ARRAY_SIZE(L"Negotiate") - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
805 static enum auth_scheme scheme_from_flag( DWORD flag )
807 int i;
809 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++) if (flag == auth_schemes[i].scheme) return i;
810 return SCHEME_INVALID;
813 static DWORD auth_scheme_from_header( const WCHAR *header )
815 unsigned int i;
817 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
819 if (!wcsnicmp( header, auth_schemes[i].str, auth_schemes[i].len ) &&
820 (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
822 return 0;
825 static BOOL query_auth_schemes( struct request *request, DWORD level, DWORD *supported, DWORD *first )
827 DWORD index = 0, supported_schemes = 0, first_scheme = 0;
828 BOOL ret = FALSE;
830 for (;;)
832 WCHAR *buffer;
833 DWORD size, scheme;
835 size = 0;
836 query_headers( request, level, NULL, NULL, &size, &index );
837 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break;
839 if (!(buffer = heap_alloc( size ))) return FALSE;
840 if (!query_headers( request, level, NULL, buffer, &size, &index ))
842 heap_free( buffer );
843 return FALSE;
845 scheme = auth_scheme_from_header( buffer );
846 heap_free( buffer );
847 if (!scheme) continue;
849 if (!first_scheme) first_scheme = scheme;
850 supported_schemes |= scheme;
852 ret = TRUE;
855 if (ret)
857 *supported = supported_schemes;
858 *first = first_scheme;
860 return ret;
863 /***********************************************************************
864 * WinHttpQueryAuthSchemes (winhttp.@)
866 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
868 BOOL ret = FALSE;
869 struct request *request;
871 TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
873 if (!(request = (struct request *)grab_object( hrequest )))
875 SetLastError( ERROR_INVALID_HANDLE );
876 return FALSE;
878 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
880 release_object( &request->hdr );
881 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
882 return FALSE;
884 if (!supported || !first || !target)
886 release_object( &request->hdr );
887 SetLastError( ERROR_INVALID_PARAMETER );
888 return FALSE;
892 if (query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first ))
894 *target = WINHTTP_AUTH_TARGET_SERVER;
895 ret = TRUE;
897 else if (query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first ))
899 *target = WINHTTP_AUTH_TARGET_PROXY;
900 ret = TRUE;
902 else SetLastError( ERROR_INVALID_OPERATION );
904 release_object( &request->hdr );
905 if (ret) SetLastError( ERROR_SUCCESS );
906 return ret;
909 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
911 UINT n = 0, x;
912 static const char base64enc[] =
913 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
915 while (len > 0)
917 /* first 6 bits, all from bin[0] */
918 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
919 x = (bin[0] & 3) << 4;
921 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
922 if (len == 1)
924 base64[n++] = base64enc[x];
925 base64[n++] = '=';
926 base64[n++] = '=';
927 break;
929 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
930 x = (bin[1] & 0x0f) << 2;
932 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
933 if (len == 2)
935 base64[n++] = base64enc[x];
936 base64[n++] = '=';
937 break;
939 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
941 /* last 6 bits, all from bin [2] */
942 base64[n++] = base64enc[bin[2] & 0x3f];
943 bin += 3;
944 len -= 3;
946 base64[n] = 0;
947 return n;
950 static inline char decode_char( WCHAR c )
952 if (c >= 'A' && c <= 'Z') return c - 'A';
953 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
954 if (c >= '0' && c <= '9') return c - '0' + 52;
955 if (c == '+') return 62;
956 if (c == '/') return 63;
957 return 64;
960 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
962 unsigned int i = 0;
963 char c0, c1, c2, c3;
964 const WCHAR *p = base64;
966 while (len > 4)
968 if ((c0 = decode_char( p[0] )) > 63) return 0;
969 if ((c1 = decode_char( p[1] )) > 63) return 0;
970 if ((c2 = decode_char( p[2] )) > 63) return 0;
971 if ((c3 = decode_char( p[3] )) > 63) return 0;
973 if (buf)
975 buf[i + 0] = (c0 << 2) | (c1 >> 4);
976 buf[i + 1] = (c1 << 4) | (c2 >> 2);
977 buf[i + 2] = (c2 << 6) | c3;
979 len -= 4;
980 i += 3;
981 p += 4;
983 if (p[2] == '=')
985 if ((c0 = decode_char( p[0] )) > 63) return 0;
986 if ((c1 = decode_char( p[1] )) > 63) return 0;
988 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
989 i++;
991 else if (p[3] == '=')
993 if ((c0 = decode_char( p[0] )) > 63) return 0;
994 if ((c1 = decode_char( p[1] )) > 63) return 0;
995 if ((c2 = decode_char( p[2] )) > 63) return 0;
997 if (buf)
999 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1000 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1002 i += 2;
1004 else
1006 if ((c0 = decode_char( p[0] )) > 63) return 0;
1007 if ((c1 = decode_char( p[1] )) > 63) return 0;
1008 if ((c2 = decode_char( p[2] )) > 63) return 0;
1009 if ((c3 = decode_char( p[3] )) > 63) return 0;
1011 if (buf)
1013 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1014 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1015 buf[i + 2] = (c2 << 6) | c3;
1017 i += 3;
1019 return i;
1022 static struct authinfo *alloc_authinfo(void)
1024 struct authinfo *ret;
1026 if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
1028 SecInvalidateHandle( &ret->cred );
1029 SecInvalidateHandle( &ret->ctx );
1030 memset( &ret->exp, 0, sizeof(ret->exp) );
1031 ret->scheme = 0;
1032 ret->attr = 0;
1033 ret->max_token = 0;
1034 ret->data = NULL;
1035 ret->data_len = 0;
1036 ret->finished = FALSE;
1037 return ret;
1040 void destroy_authinfo( struct authinfo *authinfo )
1042 if (!authinfo) return;
1044 if (SecIsValidHandle( &authinfo->ctx ))
1045 DeleteSecurityContext( &authinfo->ctx );
1046 if (SecIsValidHandle( &authinfo->cred ))
1047 FreeCredentialsHandle( &authinfo->cred );
1049 heap_free( authinfo->data );
1050 heap_free( authinfo );
1053 static BOOL get_authvalue( struct request *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
1055 DWORD size, index = 0;
1056 for (;;)
1058 size = len;
1059 if (!query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
1060 if (auth_scheme_from_header( buffer ) == scheme) break;
1062 return TRUE;
1065 static BOOL do_authorization( struct request *request, DWORD target, DWORD scheme_flag )
1067 struct authinfo *authinfo, **auth_ptr;
1068 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1069 const WCHAR *auth_target, *username, *password;
1070 WCHAR auth_value[2048], *auth_reply;
1071 DWORD len = sizeof(auth_value), len_scheme, flags;
1072 BOOL ret, has_auth_value;
1074 if (scheme == SCHEME_INVALID) return FALSE;
1076 switch (target)
1078 case WINHTTP_AUTH_TARGET_SERVER:
1079 has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len );
1080 auth_ptr = &request->authinfo;
1081 auth_target = L"Authorization";
1082 if (request->creds[TARGET_SERVER][scheme].username)
1084 if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE;
1085 username = request->creds[TARGET_SERVER][scheme].username;
1086 password = request->creds[TARGET_SERVER][scheme].password;
1088 else
1090 if (!has_auth_value) return FALSE;
1091 username = request->connect->username;
1092 password = request->connect->password;
1094 break;
1096 case WINHTTP_AUTH_TARGET_PROXY:
1097 if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1098 return FALSE;
1099 auth_ptr = &request->proxy_authinfo;
1100 auth_target = L"Proxy-Authorization";
1101 if (request->creds[TARGET_PROXY][scheme].username)
1103 username = request->creds[TARGET_PROXY][scheme].username;
1104 password = request->creds[TARGET_PROXY][scheme].password;
1106 else
1108 username = request->connect->session->proxy_username;
1109 password = request->connect->session->proxy_password;
1111 break;
1113 default:
1114 WARN("unknown target %x\n", target);
1115 return FALSE;
1117 authinfo = *auth_ptr;
1119 switch (scheme)
1121 case SCHEME_BASIC:
1123 int userlen, passlen;
1125 if (!username || !password) return FALSE;
1126 if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1128 userlen = WideCharToMultiByte( CP_UTF8, 0, username, lstrlenW( username ), NULL, 0, NULL, NULL );
1129 passlen = WideCharToMultiByte( CP_UTF8, 0, password, lstrlenW( password ), NULL, 0, NULL, NULL );
1131 authinfo->data_len = userlen + 1 + passlen;
1132 if (!(authinfo->data = heap_alloc( authinfo->data_len ))) return FALSE;
1134 WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1135 authinfo->data[userlen] = ':';
1136 WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1138 authinfo->scheme = SCHEME_BASIC;
1139 authinfo->finished = TRUE;
1140 break;
1142 case SCHEME_NTLM:
1143 case SCHEME_NEGOTIATE:
1145 SECURITY_STATUS status;
1146 SecBufferDesc out_desc, in_desc;
1147 SecBuffer out, in;
1148 ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1149 const WCHAR *p;
1150 BOOL first = FALSE;
1152 if (!authinfo)
1154 TimeStamp exp;
1155 SEC_WINNT_AUTH_IDENTITY_W id;
1156 WCHAR *domain, *user;
1158 if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1160 first = TRUE;
1161 domain = (WCHAR *)username;
1162 user = wcschr( username, '\\' );
1164 if (user) user++;
1165 else
1167 user = (WCHAR *)username;
1168 domain = NULL;
1170 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1171 id.User = user;
1172 id.UserLength = lstrlenW( user );
1173 id.Domain = domain;
1174 id.DomainLength = domain ? user - domain - 1 : 0;
1175 id.Password = (WCHAR *)password;
1176 id.PasswordLength = lstrlenW( password );
1178 status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1179 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1180 &authinfo->cred, &exp );
1181 if (status == SEC_E_OK)
1183 PSecPkgInfoW info;
1184 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1185 if (status == SEC_E_OK)
1187 authinfo->max_token = info->cbMaxToken;
1188 FreeContextBuffer( info );
1191 if (status != SEC_E_OK)
1193 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1194 debugstr_w(auth_schemes[scheme].str), status);
1195 heap_free( authinfo );
1196 return FALSE;
1198 authinfo->scheme = scheme;
1200 else if (authinfo->finished) return FALSE;
1202 if ((lstrlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1203 wcsnicmp( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1205 ERR("authentication scheme changed from %s to %s\n",
1206 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1207 destroy_authinfo( authinfo );
1208 *auth_ptr = NULL;
1209 return FALSE;
1211 in.BufferType = SECBUFFER_TOKEN;
1212 in.cbBuffer = 0;
1213 in.pvBuffer = NULL;
1215 in_desc.ulVersion = 0;
1216 in_desc.cBuffers = 1;
1217 in_desc.pBuffers = &in;
1219 p = auth_value + auth_schemes[scheme].len;
1220 if (*p == ' ')
1222 int len = lstrlenW( ++p );
1223 in.cbBuffer = decode_base64( p, len, NULL );
1224 if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) {
1225 destroy_authinfo( authinfo );
1226 *auth_ptr = NULL;
1227 return FALSE;
1229 decode_base64( p, len, in.pvBuffer );
1231 out.BufferType = SECBUFFER_TOKEN;
1232 out.cbBuffer = authinfo->max_token;
1233 if (!(out.pvBuffer = heap_alloc( authinfo->max_token )))
1235 heap_free( in.pvBuffer );
1236 destroy_authinfo( authinfo );
1237 *auth_ptr = NULL;
1238 return FALSE;
1240 out_desc.ulVersion = 0;
1241 out_desc.cBuffers = 1;
1242 out_desc.pBuffers = &out;
1244 status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1245 first ? request->connect->servername : NULL, flags, 0,
1246 SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1247 &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1248 heap_free( in.pvBuffer );
1249 if (status == SEC_E_OK)
1251 heap_free( authinfo->data );
1252 authinfo->data = out.pvBuffer;
1253 authinfo->data_len = out.cbBuffer;
1254 authinfo->finished = TRUE;
1255 TRACE("sending last auth packet\n");
1257 else if (status == SEC_I_CONTINUE_NEEDED)
1259 heap_free( authinfo->data );
1260 authinfo->data = out.pvBuffer;
1261 authinfo->data_len = out.cbBuffer;
1262 TRACE("sending next auth packet\n");
1264 else
1266 ERR("InitializeSecurityContextW failed with error 0x%08x\n", status);
1267 heap_free( out.pvBuffer );
1268 destroy_authinfo( authinfo );
1269 *auth_ptr = NULL;
1270 return FALSE;
1272 break;
1274 default:
1275 ERR("invalid scheme %u\n", scheme);
1276 return FALSE;
1278 *auth_ptr = authinfo;
1280 len_scheme = auth_schemes[authinfo->scheme].len;
1281 len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1282 if (!(auth_reply = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1284 memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1285 auth_reply[len_scheme] = ' ';
1286 encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1288 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1289 ret = process_header( request, auth_target, auth_reply, flags, TRUE );
1290 heap_free( auth_reply );
1291 return ret;
1294 static WCHAR *build_proxy_connect_string( struct request *request )
1296 WCHAR *ret, *host;
1297 unsigned int i;
1298 int len = lstrlenW( request->connect->hostname ) + 7;
1300 if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return NULL;
1301 len = swprintf( host, len, L"%s:%u", request->connect->hostname, request->connect->hostport );
1303 len += ARRAY_SIZE(L"CONNECT");
1304 len += ARRAY_SIZE(L"HTTP/1.1");
1306 for (i = 0; i < request->num_headers; i++)
1308 if (request->headers[i].is_request)
1309 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
1311 len += 4; /* '\r\n\r\n' */
1313 if ((ret = heap_alloc( (len + 1) * sizeof(WCHAR) )))
1315 lstrcpyW( ret, L"CONNECT" );
1316 lstrcatW( ret, L" " );
1317 lstrcatW( ret, host );
1318 lstrcatW( ret, L" " );
1319 lstrcatW( ret, L"HTTP/1.1" );
1321 for (i = 0; i < request->num_headers; i++)
1323 if (request->headers[i].is_request)
1325 lstrcatW( ret, L"\r\n" );
1326 lstrcatW( ret, request->headers[i].field );
1327 lstrcatW( ret, L": " );
1328 lstrcatW( ret, request->headers[i].value );
1331 lstrcatW( ret, L"\r\n\r\n" );
1334 heap_free( host );
1335 return ret;
1338 static BOOL read_reply( struct request *request );
1340 static BOOL secure_proxy_connect( struct request *request )
1342 WCHAR *str;
1343 char *strA;
1344 int len, bytes_sent;
1345 BOOL ret;
1347 if (!(str = build_proxy_connect_string( request ))) return FALSE;
1348 strA = strdupWA( str );
1349 heap_free( str );
1350 if (!strA) return FALSE;
1352 len = strlen( strA );
1353 ret = netconn_send( request->netconn, strA, len, &bytes_sent );
1354 heap_free( strA );
1355 if (ret) ret = read_reply( request );
1357 return ret;
1360 static WCHAR *addr_to_str( struct sockaddr_storage *addr )
1362 char buf[INET6_ADDRSTRLEN];
1363 void *src;
1365 switch (addr->ss_family)
1367 case AF_INET:
1368 src = &((struct sockaddr_in *)addr)->sin_addr;
1369 break;
1370 case AF_INET6:
1371 src = &((struct sockaddr_in6 *)addr)->sin6_addr;
1372 break;
1373 default:
1374 WARN("unsupported address family %d\n", addr->ss_family);
1375 return NULL;
1377 if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL;
1378 return strdupAW( buf );
1381 static CRITICAL_SECTION connection_pool_cs;
1382 static CRITICAL_SECTION_DEBUG connection_pool_debug =
1384 0, 0, &connection_pool_cs,
1385 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
1386 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
1388 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
1390 static struct list connection_pool = LIST_INIT( connection_pool );
1392 void release_host( struct hostdata *host )
1394 LONG ref;
1396 EnterCriticalSection( &connection_pool_cs );
1397 if (!(ref = --host->ref)) list_remove( &host->entry );
1398 LeaveCriticalSection( &connection_pool_cs );
1399 if (ref) return;
1401 assert( list_empty( &host->connections ) );
1402 heap_free( host->hostname );
1403 heap_free( host );
1406 static BOOL connection_collector_running;
1408 static void CALLBACK connection_collector( TP_CALLBACK_INSTANCE *instance, void *ctx )
1410 unsigned int remaining_connections;
1411 struct netconn *netconn, *next_netconn;
1412 struct hostdata *host, *next_host;
1413 ULONGLONG now;
1417 /* FIXME: Use more sophisticated method */
1418 Sleep(5000);
1419 remaining_connections = 0;
1420 now = GetTickCount64();
1422 EnterCriticalSection(&connection_pool_cs);
1424 LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, struct hostdata, entry)
1426 LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, struct netconn, entry)
1428 if (netconn->keep_until < now)
1430 TRACE("freeing %p\n", netconn);
1431 list_remove(&netconn->entry);
1432 netconn_close(netconn);
1434 else remaining_connections++;
1438 if (!remaining_connections) connection_collector_running = FALSE;
1440 LeaveCriticalSection(&connection_pool_cs);
1441 } while(remaining_connections);
1443 FreeLibraryWhenCallbackReturns( instance, winhttp_instance );
1446 static void cache_connection( struct netconn *netconn )
1448 TRACE( "caching connection %p\n", netconn );
1450 EnterCriticalSection( &connection_pool_cs );
1452 netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT;
1453 list_add_head( &netconn->host->connections, &netconn->entry );
1455 if (!connection_collector_running)
1457 HMODULE module;
1459 GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)winhttp_instance, &module );
1461 if (TrySubmitThreadpoolCallback( connection_collector, NULL, NULL )) connection_collector_running = TRUE;
1462 else FreeLibrary( winhttp_instance );
1465 LeaveCriticalSection( &connection_pool_cs );
1468 static DWORD map_secure_protocols( DWORD mask )
1470 DWORD ret = 0;
1471 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT;
1472 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT;
1473 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT;
1474 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT;
1475 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT;
1476 return ret;
1479 static BOOL ensure_cred_handle( struct request *request )
1481 SECURITY_STATUS status = SEC_E_OK;
1483 if (request->cred_handle_initialized) return TRUE;
1485 if (!request->cred_handle_initialized)
1487 SCHANNEL_CRED cred;
1488 memset( &cred, 0, sizeof(cred) );
1489 cred.dwVersion = SCHANNEL_CRED_VERSION;
1490 cred.grbitEnabledProtocols = map_secure_protocols( request->connect->session->secure_protocols );
1491 if (request->client_cert)
1493 cred.paCred = &request->client_cert;
1494 cred.cCreds = 1;
1496 status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
1497 &cred, NULL, NULL, &request->cred_handle, NULL );
1498 if (status == SEC_E_OK)
1499 request->cred_handle_initialized = TRUE;
1502 if (status != SEC_E_OK)
1504 WARN( "AcquireCredentialsHandleW failed: 0x%08x\n", status );
1505 return FALSE;
1507 return TRUE;
1510 static BOOL open_connection( struct request *request )
1512 BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
1513 struct hostdata *host = NULL, *iter;
1514 struct netconn *netconn = NULL;
1515 struct connect *connect;
1516 WCHAR *addressW = NULL;
1517 INTERNET_PORT port;
1518 DWORD len;
1520 if (request->netconn) goto done;
1522 connect = request->connect;
1523 port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1525 EnterCriticalSection( &connection_pool_cs );
1527 LIST_FOR_EACH_ENTRY( iter, &connection_pool, struct hostdata, entry )
1529 if (iter->port == port && !wcscmp( connect->servername, iter->hostname ) && !is_secure == !iter->secure)
1531 host = iter;
1532 host->ref++;
1533 break;
1537 if (!host)
1539 if ((host = heap_alloc( sizeof(*host) )))
1541 host->ref = 1;
1542 host->secure = is_secure;
1543 host->port = port;
1544 list_init( &host->connections );
1545 if ((host->hostname = strdupW( connect->servername )))
1547 list_add_head( &connection_pool, &host->entry );
1549 else
1551 heap_free( host );
1552 host = NULL;
1557 LeaveCriticalSection( &connection_pool_cs );
1559 if (!host) return FALSE;
1561 for (;;)
1563 EnterCriticalSection( &connection_pool_cs );
1564 if (!list_empty( &host->connections ))
1566 netconn = LIST_ENTRY( list_head( &host->connections ), struct netconn, entry );
1567 list_remove( &netconn->entry );
1569 LeaveCriticalSection( &connection_pool_cs );
1570 if (!netconn) break;
1572 if (netconn_is_alive( netconn )) break;
1573 TRACE("connection %p no longer alive, closing\n", netconn);
1574 netconn_close( netconn );
1575 netconn = NULL;
1578 if (!connect->resolved && netconn)
1580 connect->sockaddr = netconn->sockaddr;
1581 connect->resolved = TRUE;
1584 if (!connect->resolved)
1586 len = lstrlenW( host->hostname ) + 1;
1587 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
1589 if (!netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout ))
1591 release_host( host );
1592 return FALSE;
1594 connect->resolved = TRUE;
1596 if (!(addressW = addr_to_str( &connect->sockaddr )))
1598 release_host( host );
1599 return FALSE;
1601 len = lstrlenW( addressW ) + 1;
1602 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1605 if (!netconn)
1607 if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
1609 release_host( host );
1610 return FALSE;
1613 TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1615 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
1617 if (!(netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout )))
1619 heap_free( addressW );
1620 release_host( host );
1621 return FALSE;
1623 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1624 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1626 request->netconn = netconn;
1628 if (is_secure)
1630 if (connect->session->proxy_server &&
1631 wcsicmp( connect->hostname, connect->servername ))
1633 if (!secure_proxy_connect( request ))
1635 request->netconn = NULL;
1636 heap_free( addressW );
1637 netconn_close( netconn );
1638 return FALSE;
1642 CertFreeCertificateContext( request->server_cert );
1643 request->server_cert = NULL;
1645 if (!ensure_cred_handle( request ) ||
1646 !netconn_secure_connect( netconn, connect->hostname, request->security_flags,
1647 &request->cred_handle, request->check_revocation ))
1649 request->netconn = NULL;
1650 heap_free( addressW );
1651 netconn_close( netconn );
1652 return FALSE;
1656 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, lstrlenW(addressW) + 1 );
1658 else
1660 TRACE("using connection %p\n", netconn);
1662 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1663 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1664 request->netconn = netconn;
1667 if (netconn->secure && !(request->server_cert = netconn_get_certificate( netconn )))
1669 heap_free( addressW );
1670 netconn_close( netconn );
1671 return FALSE;
1674 done:
1675 request->read_pos = request->read_size = 0;
1676 request->read_chunked = FALSE;
1677 request->read_chunked_size = ~0u;
1678 request->read_chunked_eof = FALSE;
1679 heap_free( addressW );
1680 return TRUE;
1683 void close_connection( struct request *request )
1685 if (!request->netconn) return;
1687 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1688 netconn_close( request->netconn );
1689 request->netconn = NULL;
1690 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1693 static BOOL add_host_header( struct request *request, DWORD modifier )
1695 BOOL ret;
1696 DWORD len;
1697 WCHAR *host;
1698 struct connect *connect = request->connect;
1699 INTERNET_PORT port;
1701 port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1703 if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1705 return process_header( request, L"Host", connect->hostname, modifier, TRUE );
1707 len = lstrlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1708 if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
1709 swprintf( host, len, L"%s:%u", connect->hostname, port );
1710 ret = process_header( request, L"Host", host, modifier, TRUE );
1711 heap_free( host );
1712 return ret;
1715 static void clear_response_headers( struct request *request )
1717 unsigned int i;
1719 for (i = 0; i < request->num_headers; i++)
1721 if (!request->headers[i].field) continue;
1722 if (!request->headers[i].value) continue;
1723 if (request->headers[i].is_request) continue;
1724 delete_header( request, i );
1725 i--;
1729 /* remove some amount of data from the read buffer */
1730 static void remove_data( struct request *request, int count )
1732 if (!(request->read_size -= count)) request->read_pos = 0;
1733 else request->read_pos += count;
1736 /* read some more data into the read buffer */
1737 static BOOL read_more_data( struct request *request, int maxlen, BOOL notify )
1739 int len;
1740 BOOL ret;
1742 if (request->read_chunked_eof) return FALSE;
1744 if (request->read_size && request->read_pos)
1746 /* move existing data to the start of the buffer */
1747 memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1748 request->read_pos = 0;
1750 if (maxlen == -1) maxlen = sizeof(request->read_buf);
1752 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1754 ret = netconn_recv( request->netconn, request->read_buf + request->read_size,
1755 maxlen - request->read_size, 0, &len );
1757 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1759 request->read_size += len;
1760 return ret;
1763 /* discard data contents until we reach end of line */
1764 static BOOL discard_eol( struct request *request, BOOL notify )
1768 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1769 if (eol)
1771 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1772 break;
1774 request->read_pos = request->read_size = 0; /* discard everything */
1775 if (!read_more_data( request, -1, notify )) return FALSE;
1776 } while (request->read_size);
1777 return TRUE;
1780 /* read the size of the next chunk */
1781 static BOOL start_next_chunk( struct request *request, BOOL notify )
1783 DWORD chunk_size = 0;
1785 assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
1787 if (request->read_chunked_eof) return FALSE;
1789 /* read terminator for the previous chunk */
1790 if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE;
1792 for (;;)
1794 while (request->read_size)
1796 char ch = request->read_buf[request->read_pos];
1797 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1798 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1799 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1800 else if (ch == ';' || ch == '\r' || ch == '\n')
1802 TRACE("reading %u byte chunk\n", chunk_size);
1804 if (request->content_length == ~0u) request->content_length = chunk_size;
1805 else request->content_length += chunk_size;
1807 request->read_chunked_size = chunk_size;
1808 if (!chunk_size) request->read_chunked_eof = TRUE;
1810 return discard_eol( request, notify );
1812 remove_data( request, 1 );
1814 if (!read_more_data( request, -1, notify )) return FALSE;
1815 if (!request->read_size)
1817 request->content_length = request->content_read = 0;
1818 request->read_chunked_size = 0;
1819 return TRUE;
1824 static BOOL refill_buffer( struct request *request, BOOL notify )
1826 int len = sizeof(request->read_buf);
1828 if (request->read_chunked)
1830 if (request->read_chunked_eof) return FALSE;
1831 if (request->read_chunked_size == ~0u || !request->read_chunked_size)
1833 if (!start_next_chunk( request, notify )) return FALSE;
1835 len = min( len, request->read_chunked_size );
1837 else if (request->content_length != ~0u)
1839 len = min( len, request->content_length - request->content_read );
1842 if (len <= request->read_size) return TRUE;
1843 if (!read_more_data( request, len, notify )) return FALSE;
1844 if (!request->read_size) request->content_length = request->content_read = 0;
1845 return TRUE;
1848 static void finished_reading( struct request *request )
1850 BOOL close = FALSE;
1851 WCHAR connection[20];
1852 DWORD size = sizeof(connection);
1854 if (!request->netconn) return;
1856 if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
1857 else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
1858 query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
1860 if (!wcsicmp( connection, L"close" )) close = TRUE;
1862 else if (!wcscmp( request->version, L"HTTP/1.0" )) close = TRUE;
1863 if (close)
1865 close_connection( request );
1866 return;
1869 cache_connection( request->netconn );
1870 request->netconn = NULL;
1873 /* return the size of data available to be read immediately */
1874 static DWORD get_available_data( struct request *request )
1876 if (request->read_chunked) return min( request->read_chunked_size, request->read_size );
1877 return request->read_size;
1880 /* check if we have reached the end of the data to read */
1881 static BOOL end_of_read_data( struct request *request )
1883 if (!request->content_length) return TRUE;
1884 if (request->read_chunked) return request->read_chunked_eof;
1885 if (request->content_length == ~0u) return FALSE;
1886 return (request->content_length == request->content_read);
1889 static BOOL read_data( struct request *request, void *buffer, DWORD size, DWORD *read, BOOL async )
1891 int count, bytes_read = 0;
1892 BOOL ret = TRUE;
1894 if (end_of_read_data( request )) goto done;
1896 while (size)
1898 if (!(count = get_available_data( request )))
1900 if (!(ret = refill_buffer( request, async ))) goto done;
1901 if (!(count = get_available_data( request ))) goto done;
1903 count = min( count, size );
1904 memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
1905 remove_data( request, count );
1906 if (request->read_chunked) request->read_chunked_size -= count;
1907 size -= count;
1908 bytes_read += count;
1909 request->content_read += count;
1910 if (end_of_read_data( request )) goto done;
1912 if (request->read_chunked && !request->read_chunked_size) ret = refill_buffer( request, async );
1914 done:
1915 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
1916 if (async)
1918 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
1919 else
1921 WINHTTP_ASYNC_RESULT result;
1922 result.dwResult = API_READ_DATA;
1923 result.dwError = GetLastError();
1924 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
1928 if (ret && read) *read = bytes_read;
1929 if (end_of_read_data( request )) finished_reading( request );
1930 return ret;
1933 /* read any content returned by the server so that the connection can be reused */
1934 static void drain_content( struct request *request )
1936 DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read;
1937 char buffer[2048];
1939 refill_buffer( request, FALSE );
1940 for (;;)
1942 if (request->read_chunked) size = sizeof(buffer);
1943 else
1945 if (bytes_total >= bytes_left) return;
1946 size = min( sizeof(buffer), bytes_left - bytes_total );
1948 if (!read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return;
1949 bytes_total += bytes_read;
1953 enum escape_flags
1955 ESCAPE_FLAG_NON_PRINTABLE = 0x01,
1956 ESCAPE_FLAG_SPACE = 0x02,
1957 ESCAPE_FLAG_PERCENT = 0x04,
1958 ESCAPE_FLAG_UNSAFE = 0x08,
1959 ESCAPE_FLAG_DEL = 0x10,
1960 ESCAPE_FLAG_8BIT = 0x20,
1961 ESCAPE_FLAG_STRIP_CRLF = 0x40,
1964 #define ESCAPE_MASK_DEFAULT (ESCAPE_FLAG_NON_PRINTABLE | ESCAPE_FLAG_SPACE | ESCAPE_FLAG_UNSAFE |\
1965 ESCAPE_FLAG_DEL | ESCAPE_FLAG_8BIT)
1966 #define ESCAPE_MASK_PERCENT (ESCAPE_FLAG_PERCENT | ESCAPE_MASK_DEFAULT)
1967 #define ESCAPE_MASK_DISABLE (ESCAPE_FLAG_SPACE | ESCAPE_FLAG_8BIT | ESCAPE_FLAG_STRIP_CRLF)
1969 static inline BOOL need_escape( char ch, enum escape_flags flags )
1971 static const char unsafe[] = "\"#<>[\\]^`{|}";
1972 const char *ptr = unsafe;
1974 if ((flags & ESCAPE_FLAG_SPACE) && ch == ' ') return TRUE;
1975 if ((flags & ESCAPE_FLAG_PERCENT) && ch == '%') return TRUE;
1976 if ((flags & ESCAPE_FLAG_NON_PRINTABLE) && ch < 0x20) return TRUE;
1977 if ((flags & ESCAPE_FLAG_DEL) && ch == 0x7f) return TRUE;
1978 if ((flags & ESCAPE_FLAG_8BIT) && (ch & 0x80)) return TRUE;
1979 if ((flags & ESCAPE_FLAG_UNSAFE)) while (*ptr) { if (ch == *ptr++) return TRUE; }
1980 return FALSE;
1983 static DWORD escape_string( const char *src, DWORD len, char *dst, enum escape_flags flags )
1985 static const char hex[] = "0123456789ABCDEF";
1986 DWORD i, ret = len;
1987 char *ptr = dst;
1989 for (i = 0; i < len; i++)
1991 if ((flags & ESCAPE_FLAG_STRIP_CRLF) && (src[i] == '\r' || src[i] == '\n'))
1993 ret--;
1994 continue;
1996 if (need_escape( src[i], flags ))
1998 if (dst)
2000 ptr[0] = '%';
2001 ptr[1] = hex[(src[i] >> 4) & 0xf];
2002 ptr[2] = hex[src[i] & 0xf];
2003 ptr += 3;
2005 ret += 2;
2007 else if (dst) *ptr++ = src[i];
2010 if (dst) dst[ret] = 0;
2011 return ret;
2014 static DWORD str_to_wire( const WCHAR *src, int src_len, char *dst, enum escape_flags flags )
2016 DWORD len;
2017 char *utf8;
2019 if (src_len < 0) src_len = lstrlenW( src );
2020 len = WideCharToMultiByte( CP_UTF8, 0, src, src_len, NULL, 0, NULL, NULL );
2021 if (!(utf8 = heap_alloc( len ))) return 0;
2023 WideCharToMultiByte( CP_UTF8, 0, src, -1, utf8, len, NULL, NULL );
2024 len = escape_string( utf8, len, dst, flags );
2025 heap_free( utf8 );
2027 return len;
2030 static char *build_wire_path( struct request *request, DWORD *ret_len )
2032 WCHAR *full_path;
2033 const WCHAR *start, *path, *query = NULL;
2034 DWORD len, len_path = 0, len_query = 0;
2035 enum escape_flags path_flags, query_flags;
2036 char *ret;
2038 if (!wcsicmp( request->connect->hostname, request->connect->servername )) start = full_path = request->path;
2039 else if (!(full_path = build_absolute_request_path( request, &start ))) return NULL;
2041 len = lstrlenW( full_path );
2042 if ((path = wcschr( start, '/' )))
2044 len_path = lstrlenW( path );
2045 if ((query = wcschr( path, '?' )))
2047 len_query = lstrlenW( query );
2048 len_path -= len_query;
2052 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_MASK_DISABLE;
2053 else if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_MASK_PERCENT;
2054 else path_flags = ESCAPE_MASK_DEFAULT;
2056 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_MASK_DISABLE;
2057 else query_flags = path_flags;
2059 *ret_len = str_to_wire( full_path, len - len_path - len_query, NULL, 0 );
2060 if (path) *ret_len += str_to_wire( path, len_path, NULL, path_flags );
2061 if (query) *ret_len += str_to_wire( query, len_query, NULL, query_flags );
2063 if ((ret = heap_alloc( *ret_len + 1 )))
2065 len = str_to_wire( full_path, len - len_path - len_query, ret, 0 );
2066 if (path) len += str_to_wire( path, len_path, ret + len, path_flags );
2067 if (query) str_to_wire( query, len_query, ret + len, query_flags );
2070 if (full_path != request->path) heap_free( full_path );
2071 return ret;
2074 static char *build_wire_request( struct request *request, DWORD *len )
2076 char *path, *ptr, *ret;
2077 DWORD i, len_path;
2079 if (!(path = build_wire_path( request, &len_path ))) return NULL;
2081 *len = str_to_wire( request->verb, -1, NULL, 0 ) + 1; /* ' ' */
2082 *len += len_path + 1; /* ' ' */
2083 *len += str_to_wire( request->version, -1, NULL, 0 );
2085 for (i = 0; i < request->num_headers; i++)
2087 if (request->headers[i].is_request)
2089 *len += str_to_wire( request->headers[i].field, -1, NULL, 0 ) + 2; /* ': ' */
2090 *len += str_to_wire( request->headers[i].value, -1, NULL, 0 ) + 2; /* '\r\n' */
2093 *len += 4; /* '\r\n\r\n' */
2095 if ((ret = ptr = heap_alloc( *len + 1 )))
2097 ptr += str_to_wire( request->verb, -1, ptr, 0 );
2098 *ptr++ = ' ';
2099 memcpy( ptr, path, len_path );
2100 ptr += len_path;
2101 *ptr++ = ' ';
2102 ptr += str_to_wire( request->version, -1, ptr, 0 );
2104 for (i = 0; i < request->num_headers; i++)
2106 if (request->headers[i].is_request)
2108 *ptr++ = '\r';
2109 *ptr++ = '\n';
2110 ptr += str_to_wire( request->headers[i].field, -1, ptr, 0 );
2111 *ptr++ = ':';
2112 *ptr++ = ' ';
2113 ptr += str_to_wire( request->headers[i].value, -1, ptr, 0 );
2116 memcpy( ptr, "\r\n\r\n", sizeof("\r\n\r\n") );
2119 heap_free( path );
2120 return ret;
2123 static BOOL send_request( struct request *request, const WCHAR *headers, DWORD headers_len, void *optional,
2124 DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
2126 BOOL ret = FALSE;
2127 struct connect *connect = request->connect;
2128 struct session *session = connect->session;
2129 char *wire_req;
2130 int bytes_sent;
2131 DWORD len;
2133 clear_response_headers( request );
2134 drain_content( request );
2136 if (session->agent)
2137 process_header( request, L"User-Agent", session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2139 if (connect->hostname)
2140 add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
2142 if (request->creds[TARGET_SERVER][SCHEME_BASIC].username)
2143 do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC );
2145 if (total_len || (request->verb && !wcscmp( request->verb, L"POST" )))
2147 WCHAR length[21]; /* decimal long int + null */
2148 swprintf( length, ARRAY_SIZE(length), L"%ld", total_len );
2149 process_header( request, L"Content-Length", length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2151 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
2153 process_header( request, L"Connection", L"Keep-Alive", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2155 if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
2157 process_header( request, L"Pragma", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2158 process_header( request, L"Cache-Control", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2160 if (headers && !add_request_headers( request, headers, headers_len, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
2162 TRACE("failed to add request headers\n");
2163 return FALSE;
2165 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request ))
2167 WARN("failed to add cookie headers\n");
2168 return FALSE;
2171 if (context) request->hdr.context = context;
2173 if (!(ret = open_connection( request ))) goto end;
2174 if (!(wire_req = build_wire_request( request, &len ))) goto end;
2175 TRACE("full request: %s\n", debugstr_a(wire_req));
2177 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
2179 ret = netconn_send( request->netconn, wire_req, len, &bytes_sent );
2180 heap_free( wire_req );
2181 if (!ret) goto end;
2183 if (optional_len)
2185 if (!netconn_send( request->netconn, optional, optional_len, &bytes_sent )) goto end;
2186 request->optional = optional;
2187 request->optional_len = optional_len;
2188 len += optional_len;
2190 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
2192 end:
2193 if (async)
2195 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
2196 else
2198 WINHTTP_ASYNC_RESULT result;
2199 result.dwResult = API_SEND_REQUEST;
2200 result.dwError = GetLastError();
2201 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2204 return ret;
2207 static void task_send_request( struct task_header *task )
2209 struct send_request *s = (struct send_request *)task;
2210 send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
2211 heap_free( s->headers );
2214 /***********************************************************************
2215 * WinHttpSendRequest (winhttp.@)
2217 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len,
2218 LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
2220 BOOL ret;
2221 struct request *request;
2223 TRACE("%p, %s, %u, %p, %u, %u, %lx\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional,
2224 optional_len, total_len, context);
2226 if (!(request = (struct request *)grab_object( hrequest )))
2228 SetLastError( ERROR_INVALID_HANDLE );
2229 return FALSE;
2231 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2233 release_object( &request->hdr );
2234 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2235 return FALSE;
2238 if (headers && !headers_len) headers_len = lstrlenW( headers );
2240 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2242 struct send_request *s;
2244 if (!(s = heap_alloc( sizeof(struct send_request) ))) return FALSE;
2245 s->hdr.request = request;
2246 s->hdr.proc = task_send_request;
2247 s->headers = strdupW( headers );
2248 s->headers_len = headers_len;
2249 s->optional = optional;
2250 s->optional_len = optional_len;
2251 s->total_len = total_len;
2252 s->context = context;
2254 addref_object( &request->hdr );
2255 ret = queue_task( (struct task_header *)s );
2257 else
2258 ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
2260 release_object( &request->hdr );
2261 if (ret) SetLastError( ERROR_SUCCESS );
2262 return ret;
2265 static BOOL set_credentials( struct request *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
2266 const WCHAR *password )
2268 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
2270 if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
2272 SetLastError( ERROR_INVALID_PARAMETER );
2273 return FALSE;
2275 switch (target)
2277 case WINHTTP_AUTH_TARGET_SERVER:
2279 heap_free( request->creds[TARGET_SERVER][scheme].username );
2280 if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
2281 else if (!(request->creds[TARGET_SERVER][scheme].username = strdupW( username ))) return FALSE;
2283 heap_free( request->creds[TARGET_SERVER][scheme].password );
2284 if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
2285 else if (!(request->creds[TARGET_SERVER][scheme].password = strdupW( password ))) return FALSE;
2286 break;
2288 case WINHTTP_AUTH_TARGET_PROXY:
2290 heap_free( request->creds[TARGET_PROXY][scheme].username );
2291 if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
2292 else if (!(request->creds[TARGET_PROXY][scheme].username = strdupW( username ))) return FALSE;
2294 heap_free( request->creds[TARGET_PROXY][scheme].password );
2295 if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
2296 else if (!(request->creds[TARGET_PROXY][scheme].password = strdupW( password ))) return FALSE;
2297 break;
2299 default:
2300 WARN("unknown target %u\n", target);
2301 return FALSE;
2303 return TRUE;
2306 /***********************************************************************
2307 * WinHttpSetCredentials (winhttp.@)
2309 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, LPCWSTR username,
2310 LPCWSTR password, LPVOID params )
2312 BOOL ret;
2313 struct request *request;
2315 TRACE("%p, %x, 0x%08x, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params);
2317 if (!(request = (struct request *)grab_object( hrequest )))
2319 SetLastError( ERROR_INVALID_HANDLE );
2320 return FALSE;
2322 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2324 release_object( &request->hdr );
2325 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2326 return FALSE;
2329 ret = set_credentials( request, target, scheme, username, password );
2331 release_object( &request->hdr );
2332 if (ret) SetLastError( ERROR_SUCCESS );
2333 return ret;
2336 static BOOL handle_authorization( struct request *request, DWORD status )
2338 DWORD i, schemes, first, level, target;
2340 switch (status)
2342 case HTTP_STATUS_DENIED:
2343 target = WINHTTP_AUTH_TARGET_SERVER;
2344 level = WINHTTP_QUERY_WWW_AUTHENTICATE;
2345 break;
2347 case HTTP_STATUS_PROXY_AUTH_REQ:
2348 target = WINHTTP_AUTH_TARGET_PROXY;
2349 level = WINHTTP_QUERY_PROXY_AUTHENTICATE;
2350 break;
2352 default:
2353 WARN("unhandled status %u\n", status);
2354 return FALSE;
2357 if (!query_auth_schemes( request, level, &schemes, &first )) return FALSE;
2358 if (do_authorization( request, target, first )) return TRUE;
2360 schemes &= ~first;
2361 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
2363 if (!(schemes & auth_schemes[i].scheme)) continue;
2364 if (do_authorization( request, target, auth_schemes[i].scheme )) return TRUE;
2366 return FALSE;
2369 /* set the request content length based on the headers */
2370 static DWORD set_content_length( struct request *request, DWORD status )
2372 WCHAR encoding[20];
2373 DWORD buflen = sizeof(request->content_length);
2375 if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED || !wcscmp( request->verb, L"HEAD" ))
2376 request->content_length = 0;
2377 else
2379 if (!query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
2380 NULL, &request->content_length, &buflen, NULL ))
2381 request->content_length = ~0u;
2383 buflen = sizeof(encoding);
2384 if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
2385 !wcsicmp( encoding, L"chunked" ))
2387 request->content_length = ~0u;
2388 request->read_chunked = TRUE;
2389 request->read_chunked_size = ~0u;
2390 request->read_chunked_eof = FALSE;
2393 request->content_read = 0;
2394 return request->content_length;
2397 static BOOL read_line( struct request *request, char *buffer, DWORD *len )
2399 int count, bytes_read, pos = 0;
2401 for (;;)
2403 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
2404 if (eol)
2406 count = eol - (request->read_buf + request->read_pos);
2407 bytes_read = count + 1;
2409 else count = bytes_read = request->read_size;
2411 count = min( count, *len - pos );
2412 memcpy( buffer + pos, request->read_buf + request->read_pos, count );
2413 pos += count;
2414 remove_data( request, bytes_read );
2415 if (eol) break;
2417 if (!read_more_data( request, -1, TRUE )) return FALSE;
2418 if (!request->read_size)
2420 *len = 0;
2421 TRACE("returning empty string\n");
2422 return FALSE;
2425 if (pos < *len)
2427 if (pos && buffer[pos - 1] == '\r') pos--;
2428 *len = pos + 1;
2430 buffer[*len - 1] = 0;
2431 TRACE("returning %s\n", debugstr_a(buffer));
2432 return TRUE;
2435 #define MAX_REPLY_LEN 1460
2436 #define INITIAL_HEADER_BUFFER_LEN 512
2438 static BOOL read_reply( struct request *request )
2440 char buffer[MAX_REPLY_LEN];
2441 DWORD buflen, len, offset, crlf_len = 2; /* lstrlenW(crlf) */
2442 char *status_code, *status_text;
2443 WCHAR *versionW, *status_textW, *raw_headers;
2444 WCHAR status_codeW[4]; /* sizeof("nnn") */
2446 if (!request->netconn) return FALSE;
2450 buflen = MAX_REPLY_LEN;
2451 if (!read_line( request, buffer, &buflen )) return FALSE;
2453 /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2454 if (!(status_code = strchr( buffer, ' ' ))) return FALSE;
2455 status_code++;
2456 if (!(status_text = strchr( status_code, ' ' ))) return FALSE;
2457 if ((len = status_text - status_code) != sizeof("nnn") - 1) return FALSE;
2458 status_text++;
2460 TRACE("version [%s] status code [%s] status text [%s]\n",
2461 debugstr_an(buffer, status_code - buffer - 1),
2462 debugstr_an(status_code, len),
2463 debugstr_a(status_text));
2465 } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2467 /* we rely on the fact that the protocol is ascii */
2468 MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2469 status_codeW[len] = 0;
2470 if (!(process_header( request, L"Status", status_codeW,
2471 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE )))
2472 return FALSE;
2474 len = status_code - buffer;
2475 if (!(versionW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2476 MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2477 versionW[len - 1] = 0;
2479 heap_free( request->version );
2480 request->version = versionW;
2482 len = buflen - (status_text - buffer);
2483 if (!(status_textW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2484 MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2486 heap_free( request->status_text );
2487 request->status_text = status_textW;
2489 len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2490 if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2491 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2492 memcpy( raw_headers + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2494 heap_free( request->raw_headers );
2495 request->raw_headers = raw_headers;
2497 offset = buflen + crlf_len - 1;
2498 for (;;)
2500 struct header *header;
2502 buflen = MAX_REPLY_LEN;
2503 if (!read_line( request, buffer, &buflen )) return TRUE;
2504 if (!*buffer) buflen = 1;
2506 while (len - offset < buflen + crlf_len)
2508 WCHAR *tmp;
2509 len *= 2;
2510 if (!(tmp = heap_realloc( raw_headers, len * sizeof(WCHAR) ))) return FALSE;
2511 request->raw_headers = raw_headers = tmp;
2513 if (!*buffer)
2515 memcpy( raw_headers + offset, L"\r\n", sizeof(L"\r\n") );
2516 break;
2518 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2520 if (!(header = parse_header( raw_headers + offset ))) break;
2521 if (!(process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2523 free_header( header );
2524 break;
2526 free_header( header );
2527 memcpy( raw_headers + offset + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2528 offset += buflen + crlf_len - 1;
2531 TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2532 return TRUE;
2535 static void record_cookies( struct request *request )
2537 unsigned int i;
2539 for (i = 0; i < request->num_headers; i++)
2541 struct header *set_cookie = &request->headers[i];
2542 if (!wcsicmp( set_cookie->field, L"Set-Cookie" ) && !set_cookie->is_request)
2544 set_cookies( request, set_cookie->value );
2549 static WCHAR *get_redirect_url( struct request *request, DWORD *len )
2551 DWORD size;
2552 WCHAR *ret;
2554 query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2555 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return NULL;
2556 if (!(ret = heap_alloc( size ))) return NULL;
2557 *len = size / sizeof(WCHAR) - 1;
2558 if (query_headers( request, WINHTTP_QUERY_LOCATION, NULL, ret, &size, NULL )) return ret;
2559 heap_free( ret );
2560 return NULL;
2563 static BOOL handle_redirect( struct request *request, DWORD status )
2565 BOOL ret = FALSE;
2566 DWORD len, len_loc;
2567 URL_COMPONENTS uc;
2568 struct connect *connect = request->connect;
2569 INTERNET_PORT port;
2570 WCHAR *hostname = NULL, *location;
2571 int index;
2573 if (!(location = get_redirect_url( request, &len_loc ))) return FALSE;
2575 memset( &uc, 0, sizeof(uc) );
2576 uc.dwStructSize = sizeof(uc);
2577 uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2579 if (!WinHttpCrackUrl( location, len_loc, 0, &uc )) /* assume relative redirect */
2581 WCHAR *path, *p;
2583 if (location[0] == '/')
2585 if (!(path = heap_alloc( (len_loc + 1) * sizeof(WCHAR) ))) goto end;
2586 memcpy( path, location, len_loc * sizeof(WCHAR) );
2587 path[len_loc] = 0;
2589 else
2591 if ((p = wcsrchr( request->path, '/' ))) *p = 0;
2592 len = lstrlenW( request->path ) + 1 + len_loc;
2593 if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2594 lstrcpyW( path, request->path );
2595 lstrcatW( path, L"/" );
2596 memcpy( path + lstrlenW(path), location, len_loc * sizeof(WCHAR) );
2597 path[len_loc] = 0;
2599 heap_free( request->path );
2600 request->path = path;
2602 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2604 else
2606 if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2608 if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP) goto end;
2609 TRACE("redirect from secure page to non-secure page\n");
2610 request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2612 else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2614 TRACE("redirect from non-secure page to secure page\n");
2615 request->hdr.flags |= WINHTTP_FLAG_SECURE;
2618 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2620 len = uc.dwHostNameLength;
2621 if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2622 memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2623 hostname[len] = 0;
2625 port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2626 if (wcsicmp( connect->hostname, hostname ) || connect->serverport != port)
2628 heap_free( connect->hostname );
2629 connect->hostname = hostname;
2630 connect->hostport = port;
2631 if (!(ret = set_server_for_hostname( connect, hostname, port ))) goto end;
2633 netconn_close( request->netconn );
2634 request->netconn = NULL;
2635 request->content_length = request->content_read = 0;
2636 request->read_pos = request->read_size = 0;
2637 request->read_chunked = request->read_chunked_eof = FALSE;
2639 else heap_free( hostname );
2641 if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2642 if (!(ret = open_connection( request ))) goto end;
2644 heap_free( request->path );
2645 request->path = NULL;
2646 if (uc.dwUrlPathLength)
2648 len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2649 if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2650 memcpy( request->path, uc.lpszUrlPath, (len + 1) * sizeof(WCHAR) );
2651 request->path[len] = 0;
2653 else request->path = strdupW( L"/" );
2656 /* remove content-type/length headers */
2657 if ((index = get_header_index( request, L"Content-Type", 0, TRUE )) >= 0) delete_header( request, index );
2658 if ((index = get_header_index( request, L"Content-Length", 0, TRUE )) >= 0 ) delete_header( request, index );
2660 if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !wcscmp( request->verb, L"POST" ))
2662 heap_free( request->verb );
2663 request->verb = strdupW( L"GET" );
2664 request->optional = NULL;
2665 request->optional_len = 0;
2667 ret = TRUE;
2669 end:
2670 heap_free( location );
2671 return ret;
2674 static BOOL is_passport_request( struct request *request )
2676 static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t','1','.','4'};
2677 WCHAR buf[1024];
2678 DWORD len = ARRAY_SIZE(buf);
2680 if (!(request->connect->session->passport_flags & WINHTTP_ENABLE_PASSPORT_AUTH) ||
2681 !query_headers( request, WINHTTP_QUERY_WWW_AUTHENTICATE, NULL, buf, &len, NULL )) return FALSE;
2683 if (!wcsnicmp( buf, passportW, ARRAY_SIZE(passportW) ) &&
2684 (buf[ARRAY_SIZE(passportW)] == ' ' || !buf[ARRAY_SIZE(passportW)])) return TRUE;
2686 return FALSE;
2689 static BOOL handle_passport_redirect( struct request *request )
2691 DWORD flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
2692 int i, len = lstrlenW( request->raw_headers );
2693 WCHAR *p = request->raw_headers;
2695 if (!process_header( request, L"Status", L"401", flags, FALSE )) return FALSE;
2697 for (i = 0; i < len; i++)
2699 if (i <= len - 3 && p[i] == '3' && p[i + 1] == '0' && p[i + 2] == '2')
2701 p[i] = '4';
2702 p[i + 2] = '1';
2703 break;
2706 return TRUE;
2709 static BOOL receive_response( struct request *request, BOOL async )
2711 BOOL ret;
2712 DWORD size, query, status;
2714 if (!request->netconn)
2716 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_STATE );
2717 return FALSE;
2720 netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout );
2721 for (;;)
2723 if (!(ret = read_reply( request )))
2725 SetLastError( ERROR_WINHTTP_INVALID_SERVER_RESPONSE );
2726 break;
2728 size = sizeof(DWORD);
2729 query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2730 if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
2732 set_content_length( request, status );
2734 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2736 if (status == HTTP_STATUS_REDIRECT && is_passport_request( request ))
2738 ret = handle_passport_redirect( request );
2740 else if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2742 if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2743 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) break;
2745 if (!(ret = handle_redirect( request, status ))) break;
2747 /* recurse synchronously */
2748 if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2750 else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2752 if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
2754 if (!handle_authorization( request, status )) break;
2756 /* recurse synchronously */
2757 if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2759 break;
2762 netconn_set_timeout( request->netconn, FALSE, request->receive_timeout );
2763 if (request->content_length) ret = refill_buffer( request, FALSE );
2765 if (async)
2767 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
2768 else
2770 WINHTTP_ASYNC_RESULT result;
2771 result.dwResult = API_RECEIVE_RESPONSE;
2772 result.dwError = GetLastError();
2773 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2776 return ret;
2779 static void task_receive_response( struct task_header *task )
2781 struct receive_response *r = (struct receive_response *)task;
2782 receive_response( r->hdr.request, TRUE );
2785 /***********************************************************************
2786 * WinHttpReceiveResponse (winhttp.@)
2788 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
2790 BOOL ret;
2791 struct request *request;
2793 TRACE("%p, %p\n", hrequest, reserved);
2795 if (!(request = (struct request *)grab_object( hrequest )))
2797 SetLastError( ERROR_INVALID_HANDLE );
2798 return FALSE;
2800 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2802 release_object( &request->hdr );
2803 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2804 return FALSE;
2807 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2809 struct receive_response *r;
2811 if (!(r = heap_alloc( sizeof(struct receive_response) ))) return FALSE;
2812 r->hdr.request = request;
2813 r->hdr.proc = task_receive_response;
2815 addref_object( &request->hdr );
2816 ret = queue_task( (struct task_header *)r );
2818 else
2819 ret = receive_response( request, FALSE );
2821 release_object( &request->hdr );
2822 if (ret) SetLastError( ERROR_SUCCESS );
2823 return ret;
2826 static BOOL query_data_available( struct request *request, DWORD *available, BOOL async )
2828 DWORD count = 0;
2829 BOOL ret = TRUE;
2831 if (end_of_read_data( request )) goto done;
2833 count = get_available_data( request );
2834 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
2835 if (!count)
2837 if (!(ret = refill_buffer( request, async ))) goto done;
2838 count = get_available_data( request );
2839 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
2842 done:
2843 TRACE("%u bytes available\n", count);
2844 if (async)
2846 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
2847 else
2849 WINHTTP_ASYNC_RESULT result;
2850 result.dwResult = API_QUERY_DATA_AVAILABLE;
2851 result.dwError = GetLastError();
2852 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2856 if (ret && available) *available = count;
2857 return ret;
2860 static void task_query_data_available( struct task_header *task )
2862 struct query_data *q = (struct query_data *)task;
2863 query_data_available( q->hdr.request, q->available, TRUE );
2866 /***********************************************************************
2867 * WinHttpQueryDataAvailable (winhttp.@)
2869 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
2871 BOOL ret;
2872 struct request *request;
2874 TRACE("%p, %p\n", hrequest, available);
2876 if (!(request = (struct request *)grab_object( hrequest )))
2878 SetLastError( ERROR_INVALID_HANDLE );
2879 return FALSE;
2881 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2883 release_object( &request->hdr );
2884 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2885 return FALSE;
2888 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2890 struct query_data *q;
2892 if (!(q = heap_alloc( sizeof(struct query_data) ))) return FALSE;
2893 q->hdr.request = request;
2894 q->hdr.proc = task_query_data_available;
2895 q->available = available;
2897 addref_object( &request->hdr );
2898 ret = queue_task( (struct task_header *)q );
2900 else
2901 ret = query_data_available( request, available, FALSE );
2903 release_object( &request->hdr );
2904 if (ret) SetLastError( ERROR_SUCCESS );
2905 return ret;
2908 static void task_read_data( struct task_header *task )
2910 struct read_data *r = (struct read_data *)task;
2911 read_data( r->hdr.request, r->buffer, r->to_read, r->read, TRUE );
2914 /***********************************************************************
2915 * WinHttpReadData (winhttp.@)
2917 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
2919 BOOL ret;
2920 struct request *request;
2922 TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
2924 if (!(request = (struct request *)grab_object( hrequest )))
2926 SetLastError( ERROR_INVALID_HANDLE );
2927 return FALSE;
2929 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2931 release_object( &request->hdr );
2932 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2933 return FALSE;
2936 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2938 struct read_data *r;
2940 if (!(r = heap_alloc( sizeof(struct read_data) ))) return FALSE;
2941 r->hdr.request = request;
2942 r->hdr.proc = task_read_data;
2943 r->buffer = buffer;
2944 r->to_read = to_read;
2945 r->read = read;
2947 addref_object( &request->hdr );
2948 ret = queue_task( (struct task_header *)r );
2950 else
2951 ret = read_data( request, buffer, to_read, read, FALSE );
2953 release_object( &request->hdr );
2954 if (ret) SetLastError( ERROR_SUCCESS );
2955 return ret;
2958 static BOOL write_data( struct request *request, const void *buffer, DWORD to_write, DWORD *written, BOOL async )
2960 BOOL 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 = GetLastError();
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 task_write_data( struct task_header *task )
2982 struct write_data *w = (struct write_data *)task;
2983 write_data( w->hdr.request, w->buffer, w->to_write, w->written, TRUE );
2986 /***********************************************************************
2987 * WinHttpWriteData (winhttp.@)
2989 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written )
2991 BOOL ret;
2992 struct request *request;
2994 TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written);
2996 if (!(request = (struct request *)grab_object( hrequest )))
2998 SetLastError( ERROR_INVALID_HANDLE );
2999 return FALSE;
3001 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3003 release_object( &request->hdr );
3004 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3005 return FALSE;
3008 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3010 struct write_data *w;
3012 if (!(w = heap_alloc( sizeof(struct write_data) ))) return FALSE;
3013 w->hdr.request = request;
3014 w->hdr.proc = task_write_data;
3015 w->buffer = buffer;
3016 w->to_write = to_write;
3017 w->written = written;
3019 addref_object( &request->hdr );
3020 ret = queue_task( (struct task_header *)w );
3022 else
3023 ret = write_data( request, buffer, to_write, written, FALSE );
3025 release_object( &request->hdr );
3026 if (ret) SetLastError( ERROR_SUCCESS );
3027 return ret;
3030 enum request_state
3032 REQUEST_STATE_INITIALIZED,
3033 REQUEST_STATE_CANCELLED,
3034 REQUEST_STATE_OPEN,
3035 REQUEST_STATE_SENT,
3036 REQUEST_STATE_RESPONSE_RECEIVED
3039 struct winhttp_request
3041 IWinHttpRequest IWinHttpRequest_iface;
3042 LONG refs;
3043 CRITICAL_SECTION cs;
3044 enum request_state state;
3045 HINTERNET hsession;
3046 HINTERNET hconnect;
3047 HINTERNET hrequest;
3048 VARIANT data;
3049 WCHAR *verb;
3050 HANDLE done;
3051 HANDLE wait;
3052 HANDLE cancel;
3053 BOOL proc_running;
3054 char *buffer;
3055 DWORD offset;
3056 DWORD bytes_available;
3057 DWORD bytes_read;
3058 DWORD error;
3059 DWORD logon_policy;
3060 DWORD disable_feature;
3061 LONG resolve_timeout;
3062 LONG connect_timeout;
3063 LONG send_timeout;
3064 LONG receive_timeout;
3065 WINHTTP_PROXY_INFO proxy;
3066 BOOL async;
3067 UINT url_codepage;
3070 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
3072 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
3075 static ULONG WINAPI winhttp_request_AddRef(
3076 IWinHttpRequest *iface )
3078 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3079 return InterlockedIncrement( &request->refs );
3082 /* critical section must be held */
3083 static void cancel_request( struct winhttp_request *request )
3085 if (request->state <= REQUEST_STATE_CANCELLED) return;
3087 if (request->proc_running)
3089 SetEvent( request->cancel );
3090 LeaveCriticalSection( &request->cs );
3092 WaitForSingleObject( request->done, INFINITE );
3094 EnterCriticalSection( &request->cs );
3096 request->state = REQUEST_STATE_CANCELLED;
3099 /* critical section must be held */
3100 static void free_request( struct winhttp_request *request )
3102 if (request->state < REQUEST_STATE_INITIALIZED) return;
3103 WinHttpCloseHandle( request->hrequest );
3104 WinHttpCloseHandle( request->hconnect );
3105 WinHttpCloseHandle( request->hsession );
3106 CloseHandle( request->done );
3107 CloseHandle( request->wait );
3108 CloseHandle( request->cancel );
3109 heap_free( request->proxy.lpszProxy );
3110 heap_free( request->proxy.lpszProxyBypass );
3111 heap_free( request->buffer );
3112 heap_free( request->verb );
3113 VariantClear( &request->data );
3116 static ULONG WINAPI winhttp_request_Release(
3117 IWinHttpRequest *iface )
3119 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3120 LONG refs = InterlockedDecrement( &request->refs );
3121 if (!refs)
3123 TRACE("destroying %p\n", request);
3125 EnterCriticalSection( &request->cs );
3126 cancel_request( request );
3127 free_request( request );
3128 LeaveCriticalSection( &request->cs );
3129 request->cs.DebugInfo->Spare[0] = 0;
3130 DeleteCriticalSection( &request->cs );
3131 heap_free( request );
3133 return refs;
3136 static HRESULT WINAPI winhttp_request_QueryInterface(
3137 IWinHttpRequest *iface,
3138 REFIID riid,
3139 void **obj )
3141 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3143 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
3145 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
3146 IsEqualGUID( riid, &IID_IDispatch ) ||
3147 IsEqualGUID( riid, &IID_IUnknown ))
3149 *obj = iface;
3151 else
3153 FIXME("interface %s not implemented\n", debugstr_guid(riid));
3154 return E_NOINTERFACE;
3156 IWinHttpRequest_AddRef( iface );
3157 return S_OK;
3160 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
3161 IWinHttpRequest *iface,
3162 UINT *count )
3164 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3166 TRACE("%p, %p\n", request, count);
3167 *count = 1;
3168 return S_OK;
3171 enum type_id
3173 IWinHttpRequest_tid,
3174 last_tid
3177 static ITypeLib *winhttp_typelib;
3178 static ITypeInfo *winhttp_typeinfo[last_tid];
3180 static REFIID winhttp_tid_id[] =
3182 &IID_IWinHttpRequest
3185 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
3187 HRESULT hr;
3189 if (!winhttp_typelib)
3191 ITypeLib *typelib;
3193 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
3194 if (FAILED(hr))
3196 ERR("LoadRegTypeLib failed: %08x\n", hr);
3197 return hr;
3199 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
3200 ITypeLib_Release( typelib );
3202 if (!winhttp_typeinfo[tid])
3204 ITypeInfo *typeinfo;
3206 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
3207 if (FAILED(hr))
3209 ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(winhttp_tid_id[tid]), hr);
3210 return hr;
3212 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
3213 ITypeInfo_Release( typeinfo );
3215 *ret = winhttp_typeinfo[tid];
3216 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
3217 return S_OK;
3220 void release_typelib(void)
3222 unsigned i;
3224 for (i = 0; i < ARRAY_SIZE(winhttp_typeinfo); i++)
3225 if (winhttp_typeinfo[i])
3226 ITypeInfo_Release(winhttp_typeinfo[i]);
3228 if (winhttp_typelib)
3229 ITypeLib_Release(winhttp_typelib);
3232 static HRESULT WINAPI winhttp_request_GetTypeInfo(
3233 IWinHttpRequest *iface,
3234 UINT index,
3235 LCID lcid,
3236 ITypeInfo **info )
3238 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3239 TRACE("%p, %u, %u, %p\n", request, index, lcid, info);
3241 return get_typeinfo( IWinHttpRequest_tid, info );
3244 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
3245 IWinHttpRequest *iface,
3246 REFIID riid,
3247 LPOLESTR *names,
3248 UINT count,
3249 LCID lcid,
3250 DISPID *dispid )
3252 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3253 ITypeInfo *typeinfo;
3254 HRESULT hr;
3256 TRACE("%p, %s, %p, %u, %u, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid);
3258 if (!names || !count || !dispid) return E_INVALIDARG;
3260 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
3261 if (SUCCEEDED(hr))
3263 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
3264 ITypeInfo_Release( typeinfo );
3266 return hr;
3269 static HRESULT WINAPI winhttp_request_Invoke(
3270 IWinHttpRequest *iface,
3271 DISPID member,
3272 REFIID riid,
3273 LCID lcid,
3274 WORD flags,
3275 DISPPARAMS *params,
3276 VARIANT *result,
3277 EXCEPINFO *excep_info,
3278 UINT *arg_err )
3280 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3281 ITypeInfo *typeinfo;
3282 HRESULT hr;
3284 TRACE("%p, %d, %s, %d, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
3285 lcid, flags, params, result, excep_info, arg_err);
3287 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
3289 if (member == DISPID_HTTPREQUEST_OPTION)
3291 VARIANT ret_value, option;
3292 UINT err_pos;
3294 if (!result) result = &ret_value;
3295 if (!arg_err) arg_err = &err_pos;
3297 VariantInit( &option );
3298 VariantInit( result );
3300 if (!flags) return S_OK;
3302 if (flags == DISPATCH_PROPERTYPUT)
3304 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
3305 if (FAILED(hr)) return hr;
3307 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
3308 if (FAILED(hr))
3309 WARN("put_Option(%d) failed: %x\n", V_I4( &option ), hr);
3310 return hr;
3312 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
3314 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
3315 if (FAILED(hr)) return hr;
3317 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
3318 if (FAILED(hr))
3319 WARN("get_Option(%d) failed: %x\n", V_I4( &option ), hr);
3320 return hr;
3323 FIXME("unsupported flags %x\n", flags);
3324 return E_NOTIMPL;
3327 /* fallback to standard implementation */
3329 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
3330 if (SUCCEEDED(hr))
3332 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
3333 params, result, excep_info, arg_err );
3334 ITypeInfo_Release( typeinfo );
3336 return hr;
3339 static HRESULT WINAPI winhttp_request_SetProxy(
3340 IWinHttpRequest *iface,
3341 HTTPREQUEST_PROXY_SETTING proxy_setting,
3342 VARIANT proxy_server,
3343 VARIANT bypass_list )
3345 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3346 DWORD err = ERROR_SUCCESS;
3348 TRACE("%p, %u, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
3349 debugstr_variant(&bypass_list));
3351 EnterCriticalSection( &request->cs );
3352 switch (proxy_setting)
3354 case HTTPREQUEST_PROXYSETTING_DEFAULT:
3355 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
3356 heap_free( request->proxy.lpszProxy );
3357 heap_free( request->proxy.lpszProxyBypass );
3358 request->proxy.lpszProxy = NULL;
3359 request->proxy.lpszProxyBypass = NULL;
3360 break;
3362 case HTTPREQUEST_PROXYSETTING_DIRECT:
3363 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
3364 heap_free( request->proxy.lpszProxy );
3365 heap_free( request->proxy.lpszProxyBypass );
3366 request->proxy.lpszProxy = NULL;
3367 request->proxy.lpszProxyBypass = NULL;
3368 break;
3370 case HTTPREQUEST_PROXYSETTING_PROXY:
3371 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
3372 if (V_VT( &proxy_server ) == VT_BSTR)
3374 heap_free( request->proxy.lpszProxy );
3375 request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) );
3377 if (V_VT( &bypass_list ) == VT_BSTR)
3379 heap_free( request->proxy.lpszProxyBypass );
3380 request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) );
3382 break;
3384 default:
3385 err = ERROR_INVALID_PARAMETER;
3386 break;
3388 LeaveCriticalSection( &request->cs );
3389 return HRESULT_FROM_WIN32( err );
3392 static HRESULT WINAPI winhttp_request_SetCredentials(
3393 IWinHttpRequest *iface,
3394 BSTR username,
3395 BSTR password,
3396 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
3398 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3399 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
3400 DWORD err = ERROR_SUCCESS;
3402 TRACE("%p, %s, %p, 0x%08x\n", request, debugstr_w(username), password, flags);
3404 EnterCriticalSection( &request->cs );
3405 if (request->state < REQUEST_STATE_OPEN)
3407 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
3408 goto done;
3410 switch (flags)
3412 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
3413 target = WINHTTP_AUTH_TARGET_SERVER;
3414 break;
3415 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
3416 target = WINHTTP_AUTH_TARGET_PROXY;
3417 break;
3418 default:
3419 err = ERROR_INVALID_PARAMETER;
3420 goto done;
3422 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
3424 err = GetLastError();
3426 done:
3427 LeaveCriticalSection( &request->cs );
3428 return HRESULT_FROM_WIN32( err );
3431 static void initialize_request( struct winhttp_request *request )
3433 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
3434 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
3435 request->done = CreateEventW( NULL, FALSE, FALSE, NULL );
3436 request->connect_timeout = 60000;
3437 request->send_timeout = 30000;
3438 request->receive_timeout = 30000;
3439 request->url_codepage = CP_UTF8;
3440 VariantInit( &request->data );
3441 request->state = REQUEST_STATE_INITIALIZED;
3444 static void reset_request( struct winhttp_request *request )
3446 cancel_request( request );
3447 WinHttpCloseHandle( request->hrequest );
3448 request->hrequest = NULL;
3449 WinHttpCloseHandle( request->hconnect );
3450 request->hconnect = NULL;
3451 heap_free( request->buffer );
3452 request->buffer = NULL;
3453 heap_free( request->verb );
3454 request->verb = NULL;
3455 request->offset = 0;
3456 request->bytes_available = 0;
3457 request->bytes_read = 0;
3458 request->error = ERROR_SUCCESS;
3459 request->logon_policy = 0;
3460 request->disable_feature = 0;
3461 request->async = FALSE;
3462 request->connect_timeout = 60000;
3463 request->send_timeout = 30000;
3464 request->receive_timeout = 30000;
3465 request->url_codepage = CP_UTF8;
3466 heap_free( request->proxy.lpszProxy );
3467 request->proxy.lpszProxy = NULL;
3468 heap_free( request->proxy.lpszProxyBypass );
3469 request->proxy.lpszProxyBypass = NULL;
3470 VariantClear( &request->data );
3471 request->state = REQUEST_STATE_INITIALIZED;
3474 static HRESULT WINAPI winhttp_request_Open(
3475 IWinHttpRequest *iface,
3476 BSTR method,
3477 BSTR url,
3478 VARIANT async )
3480 static const WCHAR httpsW[] = {'h','t','t','p','s'};
3481 static const WCHAR *acceptW[] = {L"*/*", NULL};
3482 static const WCHAR user_agentW[] = L"Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)";
3483 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3484 URL_COMPONENTS uc;
3485 WCHAR *hostname, *path = NULL, *verb = NULL;
3486 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
3488 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
3489 debugstr_variant(&async));
3491 if (!method || !url) return E_INVALIDARG;
3493 memset( &uc, 0, sizeof(uc) );
3494 uc.dwStructSize = sizeof(uc);
3495 uc.dwSchemeLength = ~0u;
3496 uc.dwHostNameLength = ~0u;
3497 uc.dwUrlPathLength = ~0u;
3498 uc.dwExtraInfoLength = ~0u;
3499 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( GetLastError() );
3501 EnterCriticalSection( &request->cs );
3502 reset_request( request );
3504 if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
3505 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
3506 hostname[uc.dwHostNameLength] = 0;
3508 if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
3509 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
3510 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
3512 if (!(verb = strdupW( method ))) goto error;
3513 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
3514 else request->async = FALSE;
3516 if (!request->hsession)
3518 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
3519 WINHTTP_FLAG_ASYNC )))
3521 err = GetLastError();
3522 goto error;
3524 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
3526 WinHttpCloseHandle( request->hsession );
3527 request->hsession = NULL;
3528 err = GetLastError();
3529 goto error;
3532 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
3534 err = GetLastError();
3535 goto error;
3538 len = ARRAY_SIZE( httpsW );
3539 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
3541 flags |= WINHTTP_FLAG_SECURE;
3543 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
3545 err = GetLastError();
3546 goto error;
3548 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
3550 request->state = REQUEST_STATE_OPEN;
3551 request->verb = verb;
3552 heap_free( hostname );
3553 heap_free( path );
3554 LeaveCriticalSection( &request->cs );
3555 return S_OK;
3557 error:
3558 WinHttpCloseHandle( request->hconnect );
3559 request->hconnect = NULL;
3560 heap_free( hostname );
3561 heap_free( path );
3562 heap_free( verb );
3563 LeaveCriticalSection( &request->cs );
3564 return HRESULT_FROM_WIN32( err );
3567 static HRESULT WINAPI winhttp_request_SetRequestHeader(
3568 IWinHttpRequest *iface,
3569 BSTR header,
3570 BSTR value )
3572 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3573 DWORD len, err = ERROR_SUCCESS;
3574 WCHAR *str;
3576 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
3578 if (!header) return E_INVALIDARG;
3580 EnterCriticalSection( &request->cs );
3581 if (request->state < REQUEST_STATE_OPEN)
3583 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
3584 goto done;
3586 if (request->state >= REQUEST_STATE_SENT)
3588 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
3589 goto done;
3591 len = lstrlenW( header ) + 4;
3592 if (value) len += lstrlenW( value );
3593 if (!(str = heap_alloc( (len + 1) * sizeof(WCHAR) )))
3595 err = ERROR_OUTOFMEMORY;
3596 goto done;
3598 swprintf( str, len + 1, L"%s: %s\r\n", header, value ? value : L"" );
3599 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
3600 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
3602 err = GetLastError();
3604 heap_free( str );
3606 done:
3607 LeaveCriticalSection( &request->cs );
3608 return HRESULT_FROM_WIN32( err );
3611 static HRESULT WINAPI winhttp_request_GetResponseHeader(
3612 IWinHttpRequest *iface,
3613 BSTR header,
3614 BSTR *value )
3616 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3617 DWORD size, err = ERROR_SUCCESS;
3619 TRACE("%p, %p\n", request, header);
3621 EnterCriticalSection( &request->cs );
3622 if (request->state < REQUEST_STATE_SENT)
3624 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3625 goto done;
3627 if (!header || !value)
3629 err = ERROR_INVALID_PARAMETER;
3630 goto done;
3632 size = 0;
3633 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
3635 err = GetLastError();
3636 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3638 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
3640 err = ERROR_OUTOFMEMORY;
3641 goto done;
3643 err = ERROR_SUCCESS;
3644 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
3646 err = GetLastError();
3647 SysFreeString( *value );
3649 done:
3650 LeaveCriticalSection( &request->cs );
3651 return HRESULT_FROM_WIN32( err );
3654 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
3655 IWinHttpRequest *iface,
3656 BSTR *headers )
3658 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3659 DWORD size, err = ERROR_SUCCESS;
3661 TRACE("%p, %p\n", request, headers);
3663 if (!headers) return E_INVALIDARG;
3665 EnterCriticalSection( &request->cs );
3666 if (request->state < REQUEST_STATE_SENT)
3668 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3669 goto done;
3671 size = 0;
3672 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
3674 err = GetLastError();
3675 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3677 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
3679 err = ERROR_OUTOFMEMORY;
3680 goto done;
3682 err = ERROR_SUCCESS;
3683 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
3685 err = GetLastError();
3686 SysFreeString( *headers );
3688 done:
3689 LeaveCriticalSection( &request->cs );
3690 return HRESULT_FROM_WIN32( err );
3693 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
3695 struct winhttp_request *request = (struct winhttp_request *)context;
3697 switch (status)
3699 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
3700 request->bytes_available = *(DWORD *)buffer;
3701 request->error = ERROR_SUCCESS;
3702 break;
3703 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
3704 request->bytes_read = size;
3705 request->error = ERROR_SUCCESS;
3706 break;
3707 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
3709 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
3710 request->error = result->dwError;
3711 break;
3713 default:
3714 request->error = ERROR_SUCCESS;
3715 break;
3717 SetEvent( request->wait );
3720 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
3722 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
3723 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
3726 static DWORD wait_for_completion( struct winhttp_request *request )
3728 HANDLE handles[2] = { request->wait, request->cancel };
3729 DWORD ret;
3731 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
3733 case WAIT_OBJECT_0:
3734 ret = request->error;
3735 break;
3736 case WAIT_OBJECT_0 + 1:
3737 ret = request->error = ERROR_CANCELLED;
3738 SetEvent( request->done );
3739 break;
3740 default:
3741 ret = request->error = GetLastError();
3742 break;
3744 return ret;
3747 static HRESULT request_receive( struct winhttp_request *request )
3749 DWORD err, size, buflen = 4096;
3751 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
3752 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
3754 return HRESULT_FROM_WIN32( GetLastError() );
3756 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
3757 if (!wcscmp( request->verb, L"HEAD" ))
3759 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
3760 return S_OK;
3762 if (!(request->buffer = heap_alloc( buflen ))) return E_OUTOFMEMORY;
3763 request->buffer[0] = 0;
3764 size = 0;
3767 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
3768 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
3770 err = GetLastError();
3771 goto error;
3773 if ((err = wait_for_completion( request ))) goto error;
3774 if (!request->bytes_available) break;
3775 size += request->bytes_available;
3776 if (buflen < size)
3778 char *tmp;
3779 while (buflen < size) buflen *= 2;
3780 if (!(tmp = heap_realloc( request->buffer, buflen )))
3782 err = ERROR_OUTOFMEMORY;
3783 goto error;
3785 request->buffer = tmp;
3787 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
3788 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
3789 request->bytes_available, &request->bytes_read ))
3791 err = GetLastError();
3792 goto error;
3794 if ((err = wait_for_completion( request ))) goto error;
3795 request->offset += request->bytes_read;
3796 } while (request->bytes_read);
3798 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
3799 return S_OK;
3801 error:
3802 heap_free( request->buffer );
3803 request->buffer = NULL;
3804 return HRESULT_FROM_WIN32( err );
3807 static DWORD request_set_parameters( struct winhttp_request *request )
3809 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
3810 sizeof(request->proxy) )) return GetLastError();
3812 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
3813 sizeof(request->logon_policy) )) return GetLastError();
3815 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
3816 sizeof(request->disable_feature) )) return GetLastError();
3818 if (!WinHttpSetTimeouts( request->hrequest,
3819 request->resolve_timeout,
3820 request->connect_timeout,
3821 request->send_timeout,
3822 request->receive_timeout )) return GetLastError();
3823 return ERROR_SUCCESS;
3826 static void request_set_utf8_content_type( struct winhttp_request *request )
3828 WCHAR headerW[64];
3829 int len;
3831 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"text/plain" );
3832 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
3834 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"charset=utf-8" );
3835 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
3838 static HRESULT request_send( struct winhttp_request *request )
3840 SAFEARRAY *sa = NULL;
3841 VARIANT data;
3842 char *ptr = NULL;
3843 LONG size = 0;
3844 HRESULT hr;
3845 DWORD err;
3847 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
3848 if (wcscmp( request->verb, L"GET" ))
3850 VariantInit( &data );
3851 if (V_VT( &request->data ) == VT_BSTR)
3853 UINT cp = CP_ACP;
3854 const WCHAR *str = V_BSTR( &request->data );
3855 int i, len = lstrlenW( str );
3857 for (i = 0; i < len; i++)
3859 if (str[i] > 127)
3861 cp = CP_UTF8;
3862 break;
3865 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
3866 if (!(ptr = heap_alloc( size ))) return E_OUTOFMEMORY;
3867 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
3868 if (cp == CP_UTF8) request_set_utf8_content_type( request );
3870 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
3872 sa = V_ARRAY( &data );
3873 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
3874 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
3876 SafeArrayUnaccessData( sa );
3877 return hr;
3879 size++;
3882 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT );
3883 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
3885 err = GetLastError();
3886 goto error;
3888 if ((err = wait_for_completion( request ))) goto error;
3889 if (sa) SafeArrayUnaccessData( sa );
3890 else heap_free( ptr );
3891 request->state = REQUEST_STATE_SENT;
3892 return S_OK;
3894 error:
3895 if (sa) SafeArrayUnaccessData( sa );
3896 else heap_free( ptr );
3897 return HRESULT_FROM_WIN32( err );
3900 static void CALLBACK send_and_receive_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
3902 struct winhttp_request *request = (struct winhttp_request *)ctx;
3903 if (request_send( request ) == S_OK) request_receive( request );
3904 SetEvent( request->done );
3907 /* critical section must be held */
3908 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
3910 HANDLE done = request->done;
3911 DWORD err, ret;
3913 LeaveCriticalSection( &request->cs );
3914 while ((err = MsgWaitForMultipleObjects( 1, &done, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
3916 MSG msg;
3917 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
3919 TranslateMessage( &msg );
3920 DispatchMessageW( &msg );
3923 switch (err)
3925 case WAIT_OBJECT_0:
3926 ret = request->error;
3927 break;
3928 case WAIT_TIMEOUT:
3929 ret = ERROR_TIMEOUT;
3930 break;
3931 default:
3932 ret = GetLastError();
3933 break;
3935 EnterCriticalSection( &request->cs );
3936 if (err == WAIT_OBJECT_0) request->proc_running = FALSE;
3937 return ret;
3940 static HRESULT WINAPI winhttp_request_Send(
3941 IWinHttpRequest *iface,
3942 VARIANT body )
3944 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3945 HRESULT hr;
3947 TRACE("%p, %s\n", request, debugstr_variant(&body));
3949 EnterCriticalSection( &request->cs );
3950 if (request->state < REQUEST_STATE_OPEN)
3952 LeaveCriticalSection( &request->cs );
3953 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
3955 if (request->state >= REQUEST_STATE_SENT)
3957 LeaveCriticalSection( &request->cs );
3958 return S_OK;
3960 VariantClear( &request->data );
3961 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
3963 LeaveCriticalSection( &request->cs );
3964 return hr;
3966 if (!TrySubmitThreadpoolCallback( send_and_receive_proc, request, NULL ))
3968 LeaveCriticalSection( &request->cs );
3969 return HRESULT_FROM_WIN32( GetLastError() );
3971 request->proc_running = TRUE;
3972 if (!request->async)
3974 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
3976 LeaveCriticalSection( &request->cs );
3977 return hr;
3980 static HRESULT WINAPI winhttp_request_get_Status(
3981 IWinHttpRequest *iface,
3982 LONG *status )
3984 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3985 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
3987 TRACE("%p, %p\n", request, status);
3989 if (!status) return E_INVALIDARG;
3991 EnterCriticalSection( &request->cs );
3992 if (request->state < REQUEST_STATE_SENT)
3994 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3995 goto done;
3997 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
3998 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
4000 err = GetLastError();
4001 goto done;
4003 *status = status_code;
4005 done:
4006 LeaveCriticalSection( &request->cs );
4007 return HRESULT_FROM_WIN32( err );
4010 static HRESULT WINAPI winhttp_request_get_StatusText(
4011 IWinHttpRequest *iface,
4012 BSTR *status )
4014 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4015 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
4017 TRACE("%p, %p\n", request, status);
4019 if (!status) return E_INVALIDARG;
4021 EnterCriticalSection( &request->cs );
4022 if (request->state < REQUEST_STATE_SENT)
4024 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4025 goto done;
4027 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
4029 err = GetLastError();
4030 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4032 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
4034 err = ERROR_OUTOFMEMORY;
4035 goto done;
4037 index = 0;
4038 err = ERROR_SUCCESS;
4039 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
4041 err = GetLastError();
4042 SysFreeString( *status );
4044 done:
4045 LeaveCriticalSection( &request->cs );
4046 return HRESULT_FROM_WIN32( err );
4049 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
4051 WCHAR *buffer, *p;
4052 DWORD size;
4054 *codepage = CP_ACP;
4055 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
4056 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4058 if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY;
4059 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
4061 return GetLastError();
4063 if ((p = wcsstr( buffer, L"charset" )))
4065 p += lstrlenW( L"charset" );
4066 while (*p == ' ') p++;
4067 if (*p++ == '=')
4069 while (*p == ' ') p++;
4070 if (!wcsicmp( p, L"utf-8" )) *codepage = CP_UTF8;
4073 heap_free( buffer );
4075 return ERROR_SUCCESS;
4078 static HRESULT WINAPI winhttp_request_get_ResponseText(
4079 IWinHttpRequest *iface,
4080 BSTR *body )
4082 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4083 UINT codepage;
4084 DWORD err = ERROR_SUCCESS;
4085 int len;
4087 TRACE("%p, %p\n", request, body);
4089 if (!body) return E_INVALIDARG;
4091 EnterCriticalSection( &request->cs );
4092 if (request->state < REQUEST_STATE_SENT)
4094 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4095 goto done;
4097 if ((err = request_get_codepage( request, &codepage ))) goto done;
4098 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
4099 if (!(*body = SysAllocStringLen( NULL, len )))
4101 err = ERROR_OUTOFMEMORY;
4102 goto done;
4104 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
4105 (*body)[len] = 0;
4107 done:
4108 LeaveCriticalSection( &request->cs );
4109 return HRESULT_FROM_WIN32( err );
4112 static HRESULT WINAPI winhttp_request_get_ResponseBody(
4113 IWinHttpRequest *iface,
4114 VARIANT *body )
4116 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4117 SAFEARRAY *sa;
4118 HRESULT hr;
4119 DWORD err = ERROR_SUCCESS;
4120 char *ptr;
4122 TRACE("%p, %p\n", request, body);
4124 if (!body) return E_INVALIDARG;
4126 EnterCriticalSection( &request->cs );
4127 if (request->state < REQUEST_STATE_SENT)
4129 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4130 goto done;
4132 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
4134 err = ERROR_OUTOFMEMORY;
4135 goto done;
4137 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
4139 SafeArrayDestroy( sa );
4140 LeaveCriticalSection( &request->cs );
4141 return hr;
4143 memcpy( ptr, request->buffer, request->offset );
4144 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
4146 SafeArrayDestroy( sa );
4147 LeaveCriticalSection( &request->cs );
4148 return hr;
4150 V_VT( body ) = VT_ARRAY|VT_UI1;
4151 V_ARRAY( body ) = sa;
4153 done:
4154 LeaveCriticalSection( &request->cs );
4155 return HRESULT_FROM_WIN32( err );
4158 struct stream
4160 IStream IStream_iface;
4161 LONG refs;
4162 char *data;
4163 ULARGE_INTEGER pos, size;
4166 static inline struct stream *impl_from_IStream( IStream *iface )
4168 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
4171 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
4173 struct stream *stream = impl_from_IStream( iface );
4175 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
4177 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
4179 *obj = iface;
4181 else
4183 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4184 return E_NOINTERFACE;
4186 IStream_AddRef( iface );
4187 return S_OK;
4190 static ULONG WINAPI stream_AddRef( IStream *iface )
4192 struct stream *stream = impl_from_IStream( iface );
4193 return InterlockedIncrement( &stream->refs );
4196 static ULONG WINAPI stream_Release( IStream *iface )
4198 struct stream *stream = impl_from_IStream( iface );
4199 LONG refs = InterlockedDecrement( &stream->refs );
4200 if (!refs)
4202 heap_free( stream->data );
4203 heap_free( stream );
4205 return refs;
4208 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
4210 struct stream *stream = impl_from_IStream( iface );
4211 ULONG size;
4213 if (stream->pos.QuadPart >= stream->size.QuadPart)
4215 *read = 0;
4216 return S_FALSE;
4219 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
4220 memcpy( buf, stream->data + stream->pos.QuadPart, size );
4221 stream->pos.QuadPart += size;
4222 *read = size;
4224 return S_OK;
4227 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
4229 FIXME("\n");
4230 return E_NOTIMPL;
4233 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
4235 struct stream *stream = impl_from_IStream( iface );
4237 if (origin == STREAM_SEEK_SET)
4238 stream->pos.QuadPart = move.QuadPart;
4239 else if (origin == STREAM_SEEK_CUR)
4240 stream->pos.QuadPart += move.QuadPart;
4241 else if (origin == STREAM_SEEK_END)
4242 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
4244 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
4245 return S_OK;
4248 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
4250 FIXME("\n");
4251 return E_NOTIMPL;
4254 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
4255 ULARGE_INTEGER *written )
4257 FIXME("\n");
4258 return E_NOTIMPL;
4261 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
4263 FIXME("\n");
4264 return E_NOTIMPL;
4267 static HRESULT WINAPI stream_Revert( IStream *iface )
4269 FIXME("\n");
4270 return E_NOTIMPL;
4273 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
4275 FIXME("\n");
4276 return E_NOTIMPL;
4279 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
4281 FIXME("\n");
4282 return E_NOTIMPL;
4285 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
4287 FIXME("\n");
4288 return E_NOTIMPL;
4291 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
4293 FIXME("\n");
4294 return E_NOTIMPL;
4297 static const IStreamVtbl stream_vtbl =
4299 stream_QueryInterface,
4300 stream_AddRef,
4301 stream_Release,
4302 stream_Read,
4303 stream_Write,
4304 stream_Seek,
4305 stream_SetSize,
4306 stream_CopyTo,
4307 stream_Commit,
4308 stream_Revert,
4309 stream_LockRegion,
4310 stream_UnlockRegion,
4311 stream_Stat,
4312 stream_Clone
4315 static HRESULT WINAPI winhttp_request_get_ResponseStream(
4316 IWinHttpRequest *iface,
4317 VARIANT *body )
4319 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4320 DWORD err = ERROR_SUCCESS;
4321 struct stream *stream;
4323 TRACE("%p, %p\n", request, body);
4325 if (!body) return E_INVALIDARG;
4327 EnterCriticalSection( &request->cs );
4328 if (request->state < REQUEST_STATE_SENT)
4330 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4331 goto done;
4333 if (!(stream = heap_alloc( sizeof(*stream) )))
4335 err = ERROR_OUTOFMEMORY;
4336 goto done;
4338 stream->IStream_iface.lpVtbl = &stream_vtbl;
4339 stream->refs = 1;
4340 if (!(stream->data = heap_alloc( request->offset )))
4342 heap_free( stream );
4343 err = ERROR_OUTOFMEMORY;
4344 goto done;
4346 memcpy( stream->data, request->buffer, request->offset );
4347 stream->pos.QuadPart = 0;
4348 stream->size.QuadPart = request->offset;
4349 V_VT( body ) = VT_UNKNOWN;
4350 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
4352 done:
4353 LeaveCriticalSection( &request->cs );
4354 return HRESULT_FROM_WIN32( err );
4357 static HRESULT WINAPI winhttp_request_get_Option(
4358 IWinHttpRequest *iface,
4359 WinHttpRequestOption option,
4360 VARIANT *value )
4362 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4363 HRESULT hr = S_OK;
4365 TRACE("%p, %u, %p\n", request, option, value);
4367 EnterCriticalSection( &request->cs );
4368 switch (option)
4370 case WinHttpRequestOption_URLCodePage:
4371 V_VT( value ) = VT_I4;
4372 V_I4( value ) = request->url_codepage;
4373 break;
4374 default:
4375 FIXME("unimplemented option %u\n", option);
4376 hr = E_NOTIMPL;
4377 break;
4379 LeaveCriticalSection( &request->cs );
4380 return hr;
4383 static HRESULT WINAPI winhttp_request_put_Option(
4384 IWinHttpRequest *iface,
4385 WinHttpRequestOption option,
4386 VARIANT value )
4388 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4389 HRESULT hr = S_OK;
4391 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
4393 EnterCriticalSection( &request->cs );
4394 switch (option)
4396 case WinHttpRequestOption_EnableRedirects:
4398 if (V_BOOL( &value ))
4399 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
4400 else
4401 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
4402 break;
4404 case WinHttpRequestOption_URLCodePage:
4406 VARIANT cp;
4407 VariantInit( &cp );
4408 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
4409 if (SUCCEEDED( hr ))
4411 request->url_codepage = V_UI4( &cp );
4412 TRACE("URL codepage: %u\n", request->url_codepage);
4414 else if (V_VT( &value ) == VT_BSTR && !wcsicmp( V_BSTR( &value ), L"utf-8" ))
4416 TRACE("URL codepage: UTF-8\n");
4417 request->url_codepage = CP_UTF8;
4418 hr = S_OK;
4420 else
4421 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
4422 break;
4424 default:
4425 FIXME("unimplemented option %u\n", option);
4426 hr = E_NOTIMPL;
4427 break;
4429 LeaveCriticalSection( &request->cs );
4430 return hr;
4433 static HRESULT WINAPI winhttp_request_WaitForResponse(
4434 IWinHttpRequest *iface,
4435 VARIANT timeout,
4436 VARIANT_BOOL *succeeded )
4438 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4439 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
4441 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
4443 EnterCriticalSection( &request->cs );
4444 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
4446 LeaveCriticalSection( &request->cs );
4447 return S_OK;
4449 switch ((err = request_wait( request, msecs )))
4451 case ERROR_TIMEOUT:
4452 if (succeeded) *succeeded = VARIANT_FALSE;
4453 err = ERROR_SUCCESS;
4454 break;
4456 default:
4457 if (succeeded) *succeeded = VARIANT_TRUE;
4458 break;
4460 LeaveCriticalSection( &request->cs );
4461 return HRESULT_FROM_WIN32( err );
4464 static HRESULT WINAPI winhttp_request_Abort(
4465 IWinHttpRequest *iface )
4467 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4469 TRACE("%p\n", request);
4471 EnterCriticalSection( &request->cs );
4472 cancel_request( request );
4473 LeaveCriticalSection( &request->cs );
4474 return S_OK;
4477 static HRESULT WINAPI winhttp_request_SetTimeouts(
4478 IWinHttpRequest *iface,
4479 LONG resolve_timeout,
4480 LONG connect_timeout,
4481 LONG send_timeout,
4482 LONG receive_timeout )
4484 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4486 TRACE("%p, %d, %d, %d, %d\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout);
4488 EnterCriticalSection( &request->cs );
4489 request->resolve_timeout = resolve_timeout;
4490 request->connect_timeout = connect_timeout;
4491 request->send_timeout = send_timeout;
4492 request->receive_timeout = receive_timeout;
4493 LeaveCriticalSection( &request->cs );
4494 return S_OK;
4497 static HRESULT WINAPI winhttp_request_SetClientCertificate(
4498 IWinHttpRequest *iface,
4499 BSTR certificate )
4501 FIXME("\n");
4502 return E_NOTIMPL;
4505 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
4506 IWinHttpRequest *iface,
4507 WinHttpRequestAutoLogonPolicy policy )
4509 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4510 HRESULT hr = S_OK;
4512 TRACE("%p, %u\n", request, policy );
4514 EnterCriticalSection( &request->cs );
4515 switch (policy)
4517 case AutoLogonPolicy_Always:
4518 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
4519 break;
4520 case AutoLogonPolicy_OnlyIfBypassProxy:
4521 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
4522 break;
4523 case AutoLogonPolicy_Never:
4524 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
4525 break;
4526 default: hr = E_INVALIDARG;
4527 break;
4529 LeaveCriticalSection( &request->cs );
4530 return hr;
4533 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
4535 winhttp_request_QueryInterface,
4536 winhttp_request_AddRef,
4537 winhttp_request_Release,
4538 winhttp_request_GetTypeInfoCount,
4539 winhttp_request_GetTypeInfo,
4540 winhttp_request_GetIDsOfNames,
4541 winhttp_request_Invoke,
4542 winhttp_request_SetProxy,
4543 winhttp_request_SetCredentials,
4544 winhttp_request_Open,
4545 winhttp_request_SetRequestHeader,
4546 winhttp_request_GetResponseHeader,
4547 winhttp_request_GetAllResponseHeaders,
4548 winhttp_request_Send,
4549 winhttp_request_get_Status,
4550 winhttp_request_get_StatusText,
4551 winhttp_request_get_ResponseText,
4552 winhttp_request_get_ResponseBody,
4553 winhttp_request_get_ResponseStream,
4554 winhttp_request_get_Option,
4555 winhttp_request_put_Option,
4556 winhttp_request_WaitForResponse,
4557 winhttp_request_Abort,
4558 winhttp_request_SetTimeouts,
4559 winhttp_request_SetClientCertificate,
4560 winhttp_request_SetAutoLogonPolicy
4563 HRESULT WinHttpRequest_create( void **obj )
4565 struct winhttp_request *request;
4567 TRACE("%p\n", obj);
4569 if (!(request = heap_alloc_zero( sizeof(*request) ))) return E_OUTOFMEMORY;
4570 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
4571 request->refs = 1;
4572 InitializeCriticalSection( &request->cs );
4573 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
4574 initialize_request( request );
4576 *obj = &request->IWinHttpRequest_iface;
4577 TRACE("returning iface %p\n", *obj);
4578 return S_OK;