xref: /reactos/dll/win32/winhttp/request.c (revision cc439606)
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
6  *
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.
11  *
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.
16  *
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
20  */
21 
22 #define COBJMACROS
23 #include "config.h"
24 #include "wine/port.h"
25 
26 #include <stdarg.h>
27 #include <assert.h>
28 #ifdef HAVE_ARPA_INET_H
29 # include <arpa/inet.h>
30 #endif
31 
32 #include "windef.h"
33 #include "winbase.h"
34 #include "ole2.h"
35 #include "initguid.h"
36 #include "httprequest.h"
37 #include "httprequestid.h"
38 #include "schannel.h"
39 #include "winhttp.h"
40 
41 #include "winhttp_private.h"
42 
43 #include "wine/debug.h"
44 
45 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
46 
47 #ifdef __REACTOS__
48 #include "inet_ntop.c"
49 #endif
50 
51 #define DEFAULT_KEEP_ALIVE_TIMEOUT 30000
52 
53 static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0};
54 static const WCHAR attr_accept_charset[] = {'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0};
55 static const WCHAR attr_accept_encoding[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0};
56 static const WCHAR attr_accept_language[] = {'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0};
57 static const WCHAR attr_accept_ranges[] = {'A','c','c','e','p','t','-','R','a','n','g','e','s',0};
58 static const WCHAR attr_age[] = {'A','g','e',0};
59 static const WCHAR attr_allow[] = {'A','l','l','o','w',0};
60 static const WCHAR attr_authorization[] = {'A','u','t','h','o','r','i','z','a','t','i','o','n',0};
61 static const WCHAR attr_cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',0};
62 static const WCHAR attr_connection[] = {'C','o','n','n','e','c','t','i','o','n',0};
63 static const WCHAR attr_content_base[] = {'C','o','n','t','e','n','t','-','B','a','s','e',0};
64 static const WCHAR attr_content_encoding[] = {'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0};
65 static const WCHAR attr_content_id[] = {'C','o','n','t','e','n','t','-','I','D',0};
66 static const WCHAR attr_content_language[] = {'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0};
67 static const WCHAR attr_content_length[] = {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
68 static const WCHAR attr_content_location[] = {'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0};
69 static const WCHAR attr_content_md5[] = {'C','o','n','t','e','n','t','-','M','D','5',0};
70 static const WCHAR attr_content_range[] = {'C','o','n','t','e','n','t','-','R','a','n','g','e',0};
71 static const WCHAR attr_content_transfer_encoding[] = {'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0};
72 static const WCHAR attr_content_type[] = {'C','o','n','t','e','n','t','-','T','y','p','e',0};
73 static const WCHAR attr_cookie[] = {'C','o','o','k','i','e',0};
74 static const WCHAR attr_date[] = {'D','a','t','e',0};
75 static const WCHAR attr_from[] = {'F','r','o','m',0};
76 static const WCHAR attr_etag[] = {'E','T','a','g',0};
77 static const WCHAR attr_expect[] = {'E','x','p','e','c','t',0};
78 static const WCHAR attr_expires[] = {'E','x','p','i','r','e','s',0};
79 static const WCHAR attr_host[] = {'H','o','s','t',0};
80 static const WCHAR attr_if_match[] = {'I','f','-','M','a','t','c','h',0};
81 static const WCHAR attr_if_modified_since[] = {'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
82 static const WCHAR attr_if_none_match[] = {'I','f','-','N','o','n','e','-','M','a','t','c','h',0};
83 static const WCHAR attr_if_range[] = {'I','f','-','R','a','n','g','e',0};
84 static const WCHAR attr_if_unmodified_since[] = {'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
85 static const WCHAR attr_last_modified[] = {'L','a','s','t','-','M','o','d','i','f','i','e','d',0};
86 static const WCHAR attr_location[] = {'L','o','c','a','t','i','o','n',0};
87 static const WCHAR attr_max_forwards[] = {'M','a','x','-','F','o','r','w','a','r','d','s',0};
88 static const WCHAR attr_mime_version[] = {'M','i','m','e','-','V','e','r','s','i','o','n',0};
89 static const WCHAR attr_pragma[] = {'P','r','a','g','m','a',0};
90 static const WCHAR attr_proxy_authenticate[] = {'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0};
91 static const WCHAR attr_proxy_authorization[] = {'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0};
92 static const WCHAR attr_proxy_connection[] = {'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0};
93 static const WCHAR attr_public[] = {'P','u','b','l','i','c',0};
94 static const WCHAR attr_range[] = {'R','a','n','g','e',0};
95 static const WCHAR attr_referer[] = {'R','e','f','e','r','e','r',0};
96 static const WCHAR attr_retry_after[] = {'R','e','t','r','y','-','A','f','t','e','r',0};
97 static const WCHAR attr_server[] = {'S','e','r','v','e','r',0};
98 static const WCHAR attr_set_cookie[] = {'S','e','t','-','C','o','o','k','i','e',0};
99 static const WCHAR attr_status[] = {'S','t','a','t','u','s',0};
100 static const WCHAR attr_transfer_encoding[] = {'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0};
101 static const WCHAR attr_unless_modified_since[] = {'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
102 static const WCHAR attr_upgrade[] = {'U','p','g','r','a','d','e',0};
103 static const WCHAR attr_uri[] = {'U','R','I',0};
104 static const WCHAR attr_user_agent[] = {'U','s','e','r','-','A','g','e','n','t',0};
105 static const WCHAR attr_vary[] = {'V','a','r','y',0};
106 static const WCHAR attr_via[] = {'V','i','a',0};
107 static const WCHAR attr_warning[] = {'W','a','r','n','i','n','g',0};
108 static const WCHAR attr_www_authenticate[] = {'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0};
109 
110 static const WCHAR *attribute_table[] =
111 {
112     attr_mime_version,              /* WINHTTP_QUERY_MIME_VERSION               = 0  */
113     attr_content_type,              /* WINHTTP_QUERY_CONTENT_TYPE               = 1  */
114     attr_content_transfer_encoding, /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING  = 2  */
115     attr_content_id,                /* WINHTTP_QUERY_CONTENT_ID                 = 3  */
116     NULL,                           /* WINHTTP_QUERY_CONTENT_DESCRIPTION        = 4  */
117     attr_content_length,            /* WINHTTP_QUERY_CONTENT_LENGTH             = 5  */
118     attr_content_language,          /* WINHTTP_QUERY_CONTENT_LANGUAGE           = 6  */
119     attr_allow,                     /* WINHTTP_QUERY_ALLOW                      = 7  */
120     attr_public,                    /* WINHTTP_QUERY_PUBLIC                     = 8  */
121     attr_date,                      /* WINHTTP_QUERY_DATE                       = 9  */
122     attr_expires,                   /* WINHTTP_QUERY_EXPIRES                    = 10 */
123     attr_last_modified,             /* WINHTTP_QUERY_LAST_MODIFIEDcw            = 11 */
124     NULL,                           /* WINHTTP_QUERY_MESSAGE_ID                 = 12 */
125     attr_uri,                       /* WINHTTP_QUERY_URI                        = 13 */
126     attr_from,                      /* WINHTTP_QUERY_DERIVED_FROM               = 14 */
127     NULL,                           /* WINHTTP_QUERY_COST                       = 15 */
128     NULL,                           /* WINHTTP_QUERY_LINK                       = 16 */
129     attr_pragma,                    /* WINHTTP_QUERY_PRAGMA                     = 17 */
130     NULL,                           /* WINHTTP_QUERY_VERSION                    = 18 */
131     attr_status,                    /* WINHTTP_QUERY_STATUS_CODE                = 19 */
132     NULL,                           /* WINHTTP_QUERY_STATUS_TEXT                = 20 */
133     NULL,                           /* WINHTTP_QUERY_RAW_HEADERS                = 21 */
134     NULL,                           /* WINHTTP_QUERY_RAW_HEADERS_CRLF           = 22 */
135     attr_connection,                /* WINHTTP_QUERY_CONNECTION                 = 23 */
136     attr_accept,                    /* WINHTTP_QUERY_ACCEPT                     = 24 */
137     attr_accept_charset,            /* WINHTTP_QUERY_ACCEPT_CHARSET             = 25 */
138     attr_accept_encoding,           /* WINHTTP_QUERY_ACCEPT_ENCODING            = 26 */
139     attr_accept_language,           /* WINHTTP_QUERY_ACCEPT_LANGUAGE            = 27 */
140     attr_authorization,             /* WINHTTP_QUERY_AUTHORIZATION              = 28 */
141     attr_content_encoding,          /* WINHTTP_QUERY_CONTENT_ENCODING           = 29 */
142     NULL,                           /* WINHTTP_QUERY_FORWARDED                  = 30 */
143     NULL,                           /* WINHTTP_QUERY_FROM                       = 31 */
144     attr_if_modified_since,         /* WINHTTP_QUERY_IF_MODIFIED_SINCE          = 32 */
145     attr_location,                  /* WINHTTP_QUERY_LOCATION                   = 33 */
146     NULL,                           /* WINHTTP_QUERY_ORIG_URI                   = 34 */
147     attr_referer,                   /* WINHTTP_QUERY_REFERER                    = 35 */
148     attr_retry_after,               /* WINHTTP_QUERY_RETRY_AFTER                = 36 */
149     attr_server,                    /* WINHTTP_QUERY_SERVER                     = 37 */
150     NULL,                           /* WINHTTP_TITLE                            = 38 */
151     attr_user_agent,                /* WINHTTP_QUERY_USER_AGENT                 = 39 */
152     attr_www_authenticate,          /* WINHTTP_QUERY_WWW_AUTHENTICATE           = 40 */
153     attr_proxy_authenticate,        /* WINHTTP_QUERY_PROXY_AUTHENTICATE         = 41 */
154     attr_accept_ranges,             /* WINHTTP_QUERY_ACCEPT_RANGES              = 42 */
155     attr_set_cookie,                /* WINHTTP_QUERY_SET_COOKIE                 = 43 */
156     attr_cookie,                    /* WINHTTP_QUERY_COOKIE                     = 44 */
157     NULL,                           /* WINHTTP_QUERY_REQUEST_METHOD             = 45 */
158     NULL,                           /* WINHTTP_QUERY_REFRESH                    = 46 */
159     NULL,                           /* WINHTTP_QUERY_CONTENT_DISPOSITION        = 47 */
160     attr_age,                       /* WINHTTP_QUERY_AGE                        = 48 */
161     attr_cache_control,             /* WINHTTP_QUERY_CACHE_CONTROL              = 49 */
162     attr_content_base,              /* WINHTTP_QUERY_CONTENT_BASE               = 50 */
163     attr_content_location,          /* WINHTTP_QUERY_CONTENT_LOCATION           = 51 */
164     attr_content_md5,               /* WINHTTP_QUERY_CONTENT_MD5                = 52 */
165     attr_content_range,             /* WINHTTP_QUERY_CONTENT_RANGE              = 53 */
166     attr_etag,                      /* WINHTTP_QUERY_ETAG                       = 54 */
167     attr_host,                      /* WINHTTP_QUERY_HOST                       = 55 */
168     attr_if_match,                  /* WINHTTP_QUERY_IF_MATCH                   = 56 */
169     attr_if_none_match,             /* WINHTTP_QUERY_IF_NONE_MATCH              = 57 */
170     attr_if_range,                  /* WINHTTP_QUERY_IF_RANGE                   = 58 */
171     attr_if_unmodified_since,       /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE        = 59 */
172     attr_max_forwards,              /* WINHTTP_QUERY_MAX_FORWARDS               = 60 */
173     attr_proxy_authorization,       /* WINHTTP_QUERY_PROXY_AUTHORIZATION        = 61 */
174     attr_range,                     /* WINHTTP_QUERY_RANGE                      = 62 */
175     attr_transfer_encoding,         /* WINHTTP_QUERY_TRANSFER_ENCODING          = 63 */
176     attr_upgrade,                   /* WINHTTP_QUERY_UPGRADE                    = 64 */
177     attr_vary,                      /* WINHTTP_QUERY_VARY                       = 65 */
178     attr_via,                       /* WINHTTP_QUERY_VIA                        = 66 */
179     attr_warning,                   /* WINHTTP_QUERY_WARNING                    = 67 */
180     attr_expect,                    /* WINHTTP_QUERY_EXPECT                     = 68 */
181     attr_proxy_connection,          /* WINHTTP_QUERY_PROXY_CONNECTION           = 69 */
182     attr_unless_modified_since,     /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE      = 70 */
183     NULL,                           /* WINHTTP_QUERY_PROXY_SUPPORT              = 75 */
184     NULL,                           /* WINHTTP_QUERY_AUTHENTICATION_INFO        = 76 */
185     NULL,                           /* WINHTTP_QUERY_PASSPORT_URLS              = 77 */
186     NULL                            /* WINHTTP_QUERY_PASSPORT_CONFIG            = 78 */
187 };
188 
189 static task_header_t *dequeue_task( request_t *request )
190 {
191     task_header_t *task;
192 
193     EnterCriticalSection( &request->task_cs );
194     TRACE("%u tasks queued\n", list_count( &request->task_queue ));
195     task = LIST_ENTRY( list_head( &request->task_queue ), task_header_t, entry );
196     if (task) list_remove( &task->entry );
197     LeaveCriticalSection( &request->task_cs );
198 
199     TRACE("returning task %p\n", task);
200     return task;
201 }
202 
203 static DWORD CALLBACK task_proc( LPVOID param )
204 {
205     request_t *request = param;
206     HANDLE handles[2];
207 
208     handles[0] = request->task_wait;
209     handles[1] = request->task_cancel;
210     for (;;)
211     {
212         DWORD err = WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
213         switch (err)
214         {
215         case WAIT_OBJECT_0:
216         {
217             task_header_t *task;
218             while ((task = dequeue_task( request )))
219             {
220                 task->proc( task );
221                 release_object( &task->request->hdr );
222                 heap_free( task );
223             }
224             break;
225         }
226         case WAIT_OBJECT_0 + 1:
227             TRACE("exiting\n");
228             CloseHandle( request->task_cancel );
229             CloseHandle( request->task_wait );
230             request->task_cs.DebugInfo->Spare[0] = 0;
231             DeleteCriticalSection( &request->task_cs );
232             request->hdr.vtbl->destroy( &request->hdr );
233             return 0;
234 
235         default:
236             ERR("wait failed %u (%u)\n", err, GetLastError());
237             break;
238         }
239     }
240     return 0;
241 }
242 
243 static BOOL queue_task( task_header_t *task )
244 {
245     request_t *request = task->request;
246 
247     if (!request->task_thread)
248     {
249         if (!(request->task_wait = CreateEventW( NULL, FALSE, FALSE, NULL ))) return FALSE;
250         if (!(request->task_cancel = CreateEventW( NULL, FALSE, FALSE, NULL )))
251         {
252             CloseHandle( request->task_wait );
253             request->task_wait = NULL;
254             return FALSE;
255         }
256         if (!(request->task_thread = CreateThread( NULL, 0, task_proc, request, 0, NULL )))
257         {
258             CloseHandle( request->task_wait );
259             request->task_wait = NULL;
260             CloseHandle( request->task_cancel );
261             request->task_cancel = NULL;
262             return FALSE;
263         }
264         InitializeCriticalSection( &request->task_cs );
265         request->task_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": request.task_cs");
266     }
267 
268     EnterCriticalSection( &request->task_cs );
269     TRACE("queueing task %p\n", task );
270     list_add_tail( &request->task_queue, &task->entry );
271     LeaveCriticalSection( &request->task_cs );
272 
273     SetEvent( request->task_wait );
274     return TRUE;
275 }
276 
277 static void free_header( header_t *header )
278 {
279     heap_free( header->field );
280     heap_free( header->value );
281     heap_free( header );
282 }
283 
284 static BOOL valid_token_char( WCHAR c )
285 {
286     if (c < 32 || c == 127) return FALSE;
287     switch (c)
288     {
289     case '(': case ')':
290     case '<': case '>':
291     case '@': case ',':
292     case ';': case ':':
293     case '\\': case '\"':
294     case '/': case '[':
295     case ']': case '?':
296     case '=': case '{':
297     case '}': case ' ':
298     case '\t':
299         return FALSE;
300     default:
301         return TRUE;
302     }
303 }
304 
305 static header_t *parse_header( LPCWSTR string )
306 {
307     const WCHAR *p, *q;
308     header_t *header;
309     int len;
310 
311     p = string;
312     if (!(q = strchrW( p, ':' )))
313     {
314         WARN("no ':' in line %s\n", debugstr_w(string));
315         return NULL;
316     }
317     if (q == string)
318     {
319         WARN("empty field name in line %s\n", debugstr_w(string));
320         return NULL;
321     }
322     while (*p != ':')
323     {
324         if (!valid_token_char( *p ))
325         {
326             WARN("invalid character in field name %s\n", debugstr_w(string));
327             return NULL;
328         }
329         p++;
330     }
331     len = q - string;
332     if (!(header = heap_alloc_zero( sizeof(header_t) ))) return NULL;
333     if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) )))
334     {
335         heap_free( header );
336         return NULL;
337     }
338     memcpy( header->field, string, len * sizeof(WCHAR) );
339     header->field[len] = 0;
340 
341     q++; /* skip past colon */
342     while (*q == ' ') q++;
343     len = strlenW( q );
344 
345     if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
346     {
347         free_header( header );
348         return NULL;
349     }
350     memcpy( header->value, q, len * sizeof(WCHAR) );
351     header->value[len] = 0;
352 
353     return header;
354 }
355 
356 static int get_header_index( request_t *request, LPCWSTR field, int requested_index, BOOL request_only )
357 {
358     int index;
359 
360     TRACE("%s\n", debugstr_w(field));
361 
362     for (index = 0; index < request->num_headers; index++)
363     {
364         if (strcmpiW( request->headers[index].field, field )) continue;
365         if (request_only && !request->headers[index].is_request) continue;
366         if (!request_only && request->headers[index].is_request) continue;
367 
368         if (!requested_index) break;
369         requested_index--;
370     }
371     if (index >= request->num_headers) index = -1;
372     TRACE("returning %d\n", index);
373     return index;
374 }
375 
376 static BOOL insert_header( request_t *request, header_t *header )
377 {
378     DWORD count = request->num_headers + 1;
379     header_t *hdrs;
380 
381     if (request->headers)
382         hdrs = heap_realloc_zero( request->headers, sizeof(header_t) * count );
383     else
384         hdrs = heap_alloc_zero( sizeof(header_t) );
385     if (!hdrs) return FALSE;
386 
387     request->headers = hdrs;
388     request->headers[count - 1].field = strdupW( header->field );
389     request->headers[count - 1].value = strdupW( header->value );
390     request->headers[count - 1].is_request = header->is_request;
391     request->num_headers = count;
392     return TRUE;
393 }
394 
395 static BOOL delete_header( request_t *request, DWORD index )
396 {
397     if (!request->num_headers) return FALSE;
398     if (index >= request->num_headers) return FALSE;
399     request->num_headers--;
400 
401     heap_free( request->headers[index].field );
402     heap_free( request->headers[index].value );
403 
404     memmove( &request->headers[index], &request->headers[index + 1], (request->num_headers - index) * sizeof(header_t) );
405     memset( &request->headers[request->num_headers], 0, sizeof(header_t) );
406     return TRUE;
407 }
408 
409 BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only )
410 {
411     int index;
412     header_t hdr;
413 
414     TRACE("%s: %s 0x%08x\n", debugstr_w(field), debugstr_w(value), flags);
415 
416     if ((index = get_header_index( request, field, 0, request_only )) >= 0)
417     {
418         if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return FALSE;
419     }
420 
421     if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
422     {
423         if (index >= 0)
424         {
425             delete_header( request, index );
426             if (!value || !value[0]) return TRUE;
427         }
428         else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD))
429         {
430             set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND );
431             return FALSE;
432         }
433 
434         hdr.field = (LPWSTR)field;
435         hdr.value = (LPWSTR)value;
436         hdr.is_request = request_only;
437         return insert_header( request, &hdr );
438     }
439     else if (value)
440     {
441 
442         if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
443             index >= 0)
444         {
445             WCHAR *tmp;
446             int len, len_orig, len_value;
447             header_t *header = &request->headers[index];
448 
449             len_orig = strlenW( header->value );
450             len_value = strlenW( value );
451 
452             len = len_orig + len_value + 2;
453             if (!(tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return FALSE;
454             header->value = tmp;
455             header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
456             header->value[len_orig++] = ' ';
457 
458             memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
459             header->value[len] = 0;
460             return TRUE;
461         }
462         else
463         {
464             hdr.field = (LPWSTR)field;
465             hdr.value = (LPWSTR)value;
466             hdr.is_request = request_only;
467             return insert_header( request, &hdr );
468         }
469     }
470 
471     return TRUE;
472 }
473 
474 BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags )
475 {
476     BOOL ret = FALSE;
477     WCHAR *buffer, *p, *q;
478     header_t *header;
479 
480     if (len == ~0u) len = strlenW( headers );
481     if (!len) return TRUE;
482     if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
483     memcpy( buffer, headers, len * sizeof(WCHAR) );
484     buffer[len] = 0;
485 
486     p = buffer;
487     do
488     {
489         q = p;
490         while (*q)
491         {
492             if (q[0] == '\n' && q[1] == '\r')
493             {
494                 q[0] = '\r';
495                 q[1] = '\n';
496             }
497             if (q[0] == '\r' && q[1] == '\n') break;
498             q++;
499         }
500         if (!*p) break;
501         if (*q == '\r')
502         {
503             *q = 0;
504             q += 2; /* jump over \r\n */
505         }
506         if ((header = parse_header( p )))
507         {
508             ret = process_header( request, header->field, header->value, flags, TRUE );
509             free_header( header );
510         }
511         p = q;
512     } while (ret);
513 
514     heap_free( buffer );
515     return ret;
516 }
517 
518 /***********************************************************************
519  *          WinHttpAddRequestHeaders (winhttp.@)
520  */
521 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags )
522 {
523     BOOL ret;
524     request_t *request;
525 
526     TRACE("%p, %s, %u, 0x%08x\n", hrequest, debugstr_wn(headers, len), len, flags);
527 
528     if (!headers || !len)
529     {
530         set_last_error( ERROR_INVALID_PARAMETER );
531         return FALSE;
532     }
533     if (!(request = (request_t *)grab_object( hrequest )))
534     {
535         set_last_error( ERROR_INVALID_HANDLE );
536         return FALSE;
537     }
538     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
539     {
540         release_object( &request->hdr );
541         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
542         return FALSE;
543     }
544 
545     ret = add_request_headers( request, headers, len, flags );
546 
547     release_object( &request->hdr );
548     if (ret) set_last_error( ERROR_SUCCESS );
549     return ret;
550 }
551 
552 static WCHAR *build_request_path( request_t *request )
553 {
554     WCHAR *ret;
555 
556     if (strcmpiW( request->connect->hostname, request->connect->servername ))
557     {
558         static const WCHAR http[] = { 'h','t','t','p',0 };
559         static const WCHAR https[] = { 'h','t','t','p','s',0 };
560         static const WCHAR fmt[] = { '%','s',':','/','/','%','s',0 };
561         LPCWSTR scheme = (request->netconn ? request->netconn->secure : (request->hdr.flags & WINHTTP_FLAG_SECURE)) ? https : http;
562         int len;
563 
564         len = strlenW( scheme ) + strlenW( request->connect->hostname );
565         /* 3 characters for '://', 1 for NUL. */
566         len += 4;
567         if (request->connect->hostport)
568         {
569             /* 1 for ':' between host and port, up to 5 for port */
570             len += 6;
571         }
572         if (request->path)
573             len += strlenW( request->path );
574         if ((ret = heap_alloc( len * sizeof(WCHAR) )))
575         {
576             sprintfW( ret, fmt, scheme, request->connect->hostname );
577             if (request->connect->hostport)
578             {
579                 static const WCHAR colonFmt[] = { ':','%','u',0 };
580 
581                 sprintfW( ret + strlenW( ret ), colonFmt,
582                     request->connect->hostport );
583             }
584             if (request->path)
585                 strcatW( ret, request->path );
586         }
587     }
588     else
589         ret = request->path;
590     return ret;
591 }
592 
593 static WCHAR *build_request_string( request_t *request )
594 {
595     static const WCHAR space[]   = {' ',0};
596     static const WCHAR crlf[]    = {'\r','\n',0};
597     static const WCHAR colon[]   = {':',' ',0};
598     static const WCHAR twocrlf[] = {'\r','\n','\r','\n',0};
599 
600     WCHAR *path, *ret;
601     const WCHAR **headers, **p;
602     unsigned int len, i = 0, j;
603 
604     /* allocate space for an array of all the string pointers to be added */
605     len = request->num_headers * 4 + 7;
606     if (!(headers = heap_alloc( len * sizeof(LPCWSTR) ))) return NULL;
607 
608     path = build_request_path( request );
609     headers[i++] = request->verb;
610     headers[i++] = space;
611     headers[i++] = path;
612     headers[i++] = space;
613     headers[i++] = request->version;
614 
615     for (j = 0; j < request->num_headers; j++)
616     {
617         if (request->headers[j].is_request)
618         {
619             headers[i++] = crlf;
620             headers[i++] = request->headers[j].field;
621             headers[i++] = colon;
622             headers[i++] = request->headers[j].value;
623 
624             TRACE("adding header %s (%s)\n", debugstr_w(request->headers[j].field),
625                   debugstr_w(request->headers[j].value));
626         }
627     }
628     headers[i++] = twocrlf;
629     headers[i] = NULL;
630 
631     len = 0;
632     for (p = headers; *p; p++) len += strlenW( *p );
633     len++;
634 
635     if (!(ret = heap_alloc( len * sizeof(WCHAR) )))
636         goto out;
637     *ret = 0;
638     for (p = headers; *p; p++) strcatW( ret, *p );
639 
640 out:
641     if (path != request->path)
642         heap_free( path );
643     heap_free( headers );
644     return ret;
645 }
646 
647 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
648 
649 static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
650 {
651     header_t *header = NULL;
652     BOOL request_only, ret = FALSE;
653     int requested_index, header_index = -1;
654     DWORD attr, len;
655 
656     request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
657     requested_index = index ? *index : 0;
658 
659     attr = level & ~QUERY_MODIFIER_MASK;
660     switch (attr)
661     {
662     case WINHTTP_QUERY_CUSTOM:
663     {
664         header_index = get_header_index( request, name, requested_index, request_only );
665         break;
666     }
667     case WINHTTP_QUERY_RAW_HEADERS:
668     {
669         WCHAR *headers, *p, *q;
670 
671         if (request_only)
672             headers = build_request_string( request );
673         else
674             headers = request->raw_headers;
675 
676         if (!(p = headers)) return FALSE;
677         for (len = 0; *p; p++) if (*p != '\r') len++;
678 
679         if (!buffer || len * sizeof(WCHAR) > *buflen)
680             set_last_error( ERROR_INSUFFICIENT_BUFFER );
681         else
682         {
683             for (p = headers, q = buffer; *p; p++, q++)
684             {
685                 if (*p != '\r') *q = *p;
686                 else
687                 {
688                     *q = 0;
689                     p++; /* skip '\n' */
690                 }
691             }
692             TRACE("returning data: %s\n", debugstr_wn(buffer, len));
693             if (len) len--;
694             ret = TRUE;
695         }
696         *buflen = len * sizeof(WCHAR);
697         if (request_only) heap_free( headers );
698         return ret;
699     }
700     case WINHTTP_QUERY_RAW_HEADERS_CRLF:
701     {
702         WCHAR *headers;
703 
704         if (request_only)
705             headers = build_request_string( request );
706         else
707             headers = request->raw_headers;
708 
709         if (!headers) return FALSE;
710         len = strlenW( headers ) * sizeof(WCHAR);
711         if (!buffer || len + sizeof(WCHAR) > *buflen)
712         {
713             len += sizeof(WCHAR);
714             set_last_error( ERROR_INSUFFICIENT_BUFFER );
715         }
716         else
717         {
718             memcpy( buffer, headers, len + sizeof(WCHAR) );
719             TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
720             ret = TRUE;
721         }
722         *buflen = len;
723         if (request_only) heap_free( headers );
724         return ret;
725     }
726     case WINHTTP_QUERY_VERSION:
727         len = strlenW( request->version ) * sizeof(WCHAR);
728         if (!buffer || len + sizeof(WCHAR) > *buflen)
729         {
730             len += sizeof(WCHAR);
731             set_last_error( ERROR_INSUFFICIENT_BUFFER );
732         }
733         else
734         {
735             strcpyW( buffer, request->version );
736             TRACE("returning string: %s\n", debugstr_w(buffer));
737             ret = TRUE;
738         }
739         *buflen = len;
740         return ret;
741 
742     case WINHTTP_QUERY_STATUS_TEXT:
743         len = strlenW( request->status_text ) * sizeof(WCHAR);
744         if (!buffer || len + sizeof(WCHAR) > *buflen)
745         {
746             len += sizeof(WCHAR);
747             set_last_error( ERROR_INSUFFICIENT_BUFFER );
748         }
749         else
750         {
751             strcpyW( buffer, request->status_text );
752             TRACE("returning string: %s\n", debugstr_w(buffer));
753             ret = TRUE;
754         }
755         *buflen = len;
756         return ret;
757 
758     default:
759         if (attr >= sizeof(attribute_table)/sizeof(attribute_table[0]) || !attribute_table[attr])
760         {
761             FIXME("attribute %u not implemented\n", attr);
762             return FALSE;
763         }
764         TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
765         header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
766         break;
767     }
768 
769     if (header_index >= 0)
770     {
771         header = &request->headers[header_index];
772     }
773     if (!header || (request_only && !header->is_request))
774     {
775         set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND );
776         return FALSE;
777     }
778     if (index) *index += 1;
779     if (level & WINHTTP_QUERY_FLAG_NUMBER)
780     {
781         if (!buffer || sizeof(int) > *buflen)
782         {
783             set_last_error( ERROR_INSUFFICIENT_BUFFER );
784         }
785         else
786         {
787             int *number = buffer;
788             *number = atoiW( header->value );
789             TRACE("returning number: %d\n", *number);
790             ret = TRUE;
791         }
792         *buflen = sizeof(int);
793     }
794     else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
795     {
796         SYSTEMTIME *st = buffer;
797         if (!buffer || sizeof(SYSTEMTIME) > *buflen)
798         {
799             set_last_error( ERROR_INSUFFICIENT_BUFFER );
800         }
801         else if ((ret = WinHttpTimeToSystemTime( header->value, st )))
802         {
803             TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
804                   st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
805                   st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
806         }
807         *buflen = sizeof(SYSTEMTIME);
808     }
809     else if (header->value)
810     {
811         len = strlenW( header->value ) * sizeof(WCHAR);
812         if (!buffer || len + sizeof(WCHAR) > *buflen)
813         {
814             len += sizeof(WCHAR);
815             set_last_error( ERROR_INSUFFICIENT_BUFFER );
816         }
817         else
818         {
819             strcpyW( buffer, header->value );
820             TRACE("returning string: %s\n", debugstr_w(buffer));
821             ret = TRUE;
822         }
823         *buflen = len;
824     }
825     return ret;
826 }
827 
828 /***********************************************************************
829  *          WinHttpQueryHeaders (winhttp.@)
830  */
831 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
832 {
833     BOOL ret;
834     request_t *request;
835 
836     TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index);
837 
838     if (!(request = (request_t *)grab_object( hrequest )))
839     {
840         set_last_error( ERROR_INVALID_HANDLE );
841         return FALSE;
842     }
843     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
844     {
845         release_object( &request->hdr );
846         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
847         return FALSE;
848     }
849 
850     ret = query_headers( request, level, name, buffer, buflen, index );
851 
852     release_object( &request->hdr );
853     if (ret) set_last_error( ERROR_SUCCESS );
854     return ret;
855 }
856 
857 #undef ARRAYSIZE
858 #define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0]))
859 
860 static const WCHAR basicW[]     = {'B','a','s','i','c',0};
861 static const WCHAR ntlmW[]      = {'N','T','L','M',0};
862 static const WCHAR passportW[]  = {'P','a','s','s','p','o','r','t',0};
863 static const WCHAR digestW[]    = {'D','i','g','e','s','t',0};
864 static const WCHAR negotiateW[] = {'N','e','g','o','t','i','a','t','e',0};
865 
866 static const struct
867 {
868     const WCHAR *str;
869     unsigned int len;
870     DWORD scheme;
871 }
872 auth_schemes[] =
873 {
874     { basicW,     ARRAYSIZE(basicW) - 1,     WINHTTP_AUTH_SCHEME_BASIC },
875     { ntlmW,      ARRAYSIZE(ntlmW) - 1,      WINHTTP_AUTH_SCHEME_NTLM },
876     { passportW,  ARRAYSIZE(passportW) - 1,  WINHTTP_AUTH_SCHEME_PASSPORT },
877     { digestW,    ARRAYSIZE(digestW) - 1,    WINHTTP_AUTH_SCHEME_DIGEST },
878     { negotiateW, ARRAYSIZE(negotiateW) - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
879 };
880 static const unsigned int num_auth_schemes = sizeof(auth_schemes)/sizeof(auth_schemes[0]);
881 
882 static enum auth_scheme scheme_from_flag( DWORD flag )
883 {
884     int i;
885 
886     for (i = 0; i < num_auth_schemes; i++) if (flag == auth_schemes[i].scheme) return i;
887     return SCHEME_INVALID;
888 }
889 
890 static DWORD auth_scheme_from_header( WCHAR *header )
891 {
892     unsigned int i;
893 
894     for (i = 0; i < num_auth_schemes; i++)
895     {
896         if (!strncmpiW( header, auth_schemes[i].str, auth_schemes[i].len ) &&
897             (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
898     }
899     return 0;
900 }
901 
902 static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD supported, LPDWORD first )
903 {
904     DWORD index = 0, supported_schemes = 0, first_scheme = 0;
905     BOOL ret = FALSE;
906 
907     for (;;)
908     {
909         WCHAR *buffer;
910         DWORD size, scheme;
911 
912         size = 0;
913         query_headers( request, level, NULL, NULL, &size, &index );
914         if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) break;
915 
916         index--;
917         if (!(buffer = heap_alloc( size ))) return FALSE;
918         if (!query_headers( request, level, NULL, buffer, &size, &index ))
919         {
920             heap_free( buffer );
921             return FALSE;
922         }
923         scheme = auth_scheme_from_header( buffer );
924         heap_free( buffer );
925         if (!scheme) continue;
926 
927         if (!first_scheme) first_scheme = scheme;
928         supported_schemes |= scheme;
929 
930         ret = TRUE;
931     }
932 
933     if (ret)
934     {
935         *supported = supported_schemes;
936         *first = first_scheme;
937     }
938     return ret;
939 }
940 
941 /***********************************************************************
942  *          WinHttpQueryAuthSchemes (winhttp.@)
943  */
944 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
945 {
946     BOOL ret = FALSE;
947     request_t *request;
948 
949     TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
950 
951     if (!(request = (request_t *)grab_object( hrequest )))
952     {
953         set_last_error( ERROR_INVALID_HANDLE );
954         return FALSE;
955     }
956     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
957     {
958         release_object( &request->hdr );
959         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
960         return FALSE;
961     }
962     if (!supported || !first || !target)
963     {
964         release_object( &request->hdr );
965         set_last_error( ERROR_INVALID_PARAMETER );
966         return FALSE;
967 
968     }
969 
970     if (query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first ))
971     {
972         *target = WINHTTP_AUTH_TARGET_SERVER;
973         ret = TRUE;
974     }
975     else if (query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first ))
976     {
977         *target = WINHTTP_AUTH_TARGET_PROXY;
978         ret = TRUE;
979     }
980 
981     release_object( &request->hdr );
982     if (ret) set_last_error( ERROR_SUCCESS );
983     return ret;
984 }
985 
986 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
987 {
988     UINT n = 0, x;
989     static const char base64enc[] =
990         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
991 
992     while (len > 0)
993     {
994         /* first 6 bits, all from bin[0] */
995         base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
996         x = (bin[0] & 3) << 4;
997 
998         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
999         if (len == 1)
1000         {
1001             base64[n++] = base64enc[x];
1002             base64[n++] = '=';
1003             base64[n++] = '=';
1004             break;
1005         }
1006         base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
1007         x = (bin[1] & 0x0f) << 2;
1008 
1009         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1010         if (len == 2)
1011         {
1012             base64[n++] = base64enc[x];
1013             base64[n++] = '=';
1014             break;
1015         }
1016         base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
1017 
1018         /* last 6 bits, all from bin [2] */
1019         base64[n++] = base64enc[bin[2] & 0x3f];
1020         bin += 3;
1021         len -= 3;
1022     }
1023     base64[n] = 0;
1024     return n;
1025 }
1026 
1027 static inline char decode_char( WCHAR c )
1028 {
1029     if (c >= 'A' && c <= 'Z') return c - 'A';
1030     if (c >= 'a' && c <= 'z') return c - 'a' + 26;
1031     if (c >= '0' && c <= '9') return c - '0' + 52;
1032     if (c == '+') return 62;
1033     if (c == '/') return 63;
1034     return 64;
1035 }
1036 
1037 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
1038 {
1039     unsigned int i = 0;
1040     char c0, c1, c2, c3;
1041     const WCHAR *p = base64;
1042 
1043     while (len > 4)
1044     {
1045         if ((c0 = decode_char( p[0] )) > 63) return 0;
1046         if ((c1 = decode_char( p[1] )) > 63) return 0;
1047         if ((c2 = decode_char( p[2] )) > 63) return 0;
1048         if ((c3 = decode_char( p[3] )) > 63) return 0;
1049 
1050         if (buf)
1051         {
1052             buf[i + 0] = (c0 << 2) | (c1 >> 4);
1053             buf[i + 1] = (c1 << 4) | (c2 >> 2);
1054             buf[i + 2] = (c2 << 6) |  c3;
1055         }
1056         len -= 4;
1057         i += 3;
1058         p += 4;
1059     }
1060     if (p[2] == '=')
1061     {
1062         if ((c0 = decode_char( p[0] )) > 63) return 0;
1063         if ((c1 = decode_char( p[1] )) > 63) return 0;
1064 
1065         if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
1066         i++;
1067     }
1068     else if (p[3] == '=')
1069     {
1070         if ((c0 = decode_char( p[0] )) > 63) return 0;
1071         if ((c1 = decode_char( p[1] )) > 63) return 0;
1072         if ((c2 = decode_char( p[2] )) > 63) return 0;
1073 
1074         if (buf)
1075         {
1076             buf[i + 0] = (c0 << 2) | (c1 >> 4);
1077             buf[i + 1] = (c1 << 4) | (c2 >> 2);
1078         }
1079         i += 2;
1080     }
1081     else
1082     {
1083         if ((c0 = decode_char( p[0] )) > 63) return 0;
1084         if ((c1 = decode_char( p[1] )) > 63) return 0;
1085         if ((c2 = decode_char( p[2] )) > 63) return 0;
1086         if ((c3 = decode_char( p[3] )) > 63) return 0;
1087 
1088         if (buf)
1089         {
1090             buf[i + 0] = (c0 << 2) | (c1 >> 4);
1091             buf[i + 1] = (c1 << 4) | (c2 >> 2);
1092             buf[i + 2] = (c2 << 6) |  c3;
1093         }
1094         i += 3;
1095     }
1096     return i;
1097 }
1098 
1099 static struct authinfo *alloc_authinfo(void)
1100 {
1101     struct authinfo *ret;
1102 
1103     if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
1104 
1105     SecInvalidateHandle( &ret->cred );
1106     SecInvalidateHandle( &ret->ctx );
1107     memset( &ret->exp, 0, sizeof(ret->exp) );
1108     ret->scheme    = 0;
1109     ret->attr      = 0;
1110     ret->max_token = 0;
1111     ret->data      = NULL;
1112     ret->data_len  = 0;
1113     ret->finished  = FALSE;
1114     return ret;
1115 }
1116 
1117 void destroy_authinfo( struct authinfo *authinfo )
1118 {
1119     if (!authinfo) return;
1120 
1121     if (SecIsValidHandle( &authinfo->ctx ))
1122         DeleteSecurityContext( &authinfo->ctx );
1123     if (SecIsValidHandle( &authinfo->cred ))
1124         FreeCredentialsHandle( &authinfo->cred );
1125 
1126     heap_free( authinfo->data );
1127     heap_free( authinfo );
1128 }
1129 
1130 static BOOL get_authvalue( request_t *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
1131 {
1132     DWORD size, index = 0;
1133     for (;;)
1134     {
1135         size = len;
1136         if (!query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
1137         if (auth_scheme_from_header( buffer ) == scheme) break;
1138     }
1139     return TRUE;
1140 }
1141 
1142 static BOOL do_authorization( request_t *request, DWORD target, DWORD scheme_flag )
1143 {
1144     struct authinfo *authinfo, **auth_ptr;
1145     enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1146     const WCHAR *auth_target, *username, *password;
1147     WCHAR auth_value[2048], *auth_reply;
1148     DWORD len = sizeof(auth_value), len_scheme, flags;
1149     BOOL ret, has_auth_value;
1150 
1151     if (scheme == SCHEME_INVALID) return FALSE;
1152 
1153     switch (target)
1154     {
1155     case WINHTTP_AUTH_TARGET_SERVER:
1156         has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len );
1157         auth_ptr = &request->authinfo;
1158         auth_target = attr_authorization;
1159         if (request->creds[TARGET_SERVER][scheme].username)
1160         {
1161             if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE;
1162             username = request->creds[TARGET_SERVER][scheme].username;
1163             password = request->creds[TARGET_SERVER][scheme].password;
1164         }
1165         else
1166         {
1167             if (!has_auth_value) return FALSE;
1168             username = request->connect->username;
1169             password = request->connect->password;
1170         }
1171         break;
1172 
1173     case WINHTTP_AUTH_TARGET_PROXY:
1174         if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1175             return FALSE;
1176         auth_ptr = &request->proxy_authinfo;
1177         auth_target = attr_proxy_authorization;
1178         if (request->creds[TARGET_PROXY][scheme].username)
1179         {
1180             username = request->creds[TARGET_PROXY][scheme].username;
1181             password = request->creds[TARGET_PROXY][scheme].password;
1182         }
1183         else
1184         {
1185             username = request->connect->session->proxy_username;
1186             password = request->connect->session->proxy_password;
1187         }
1188         break;
1189 
1190     default:
1191         WARN("unknown target %x\n", target);
1192         return FALSE;
1193     }
1194     authinfo = *auth_ptr;
1195 
1196     switch (scheme)
1197     {
1198     case SCHEME_BASIC:
1199     {
1200         int userlen, passlen;
1201 
1202         if (!username || !password) return FALSE;
1203         if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1204 
1205         userlen = WideCharToMultiByte( CP_UTF8, 0, username, strlenW( username ), NULL, 0, NULL, NULL );
1206         passlen = WideCharToMultiByte( CP_UTF8, 0, password, strlenW( password ), NULL, 0, NULL, NULL );
1207 
1208         authinfo->data_len = userlen + 1 + passlen;
1209         if (!(authinfo->data = heap_alloc( authinfo->data_len ))) return FALSE;
1210 
1211         WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1212         authinfo->data[userlen] = ':';
1213         WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1214 
1215         authinfo->scheme   = SCHEME_BASIC;
1216         authinfo->finished = TRUE;
1217         break;
1218     }
1219     case SCHEME_NTLM:
1220     case SCHEME_NEGOTIATE:
1221     {
1222         SECURITY_STATUS status;
1223         SecBufferDesc out_desc, in_desc;
1224         SecBuffer out, in;
1225         ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1226         const WCHAR *p;
1227         BOOL first = FALSE;
1228 
1229         if (!authinfo)
1230         {
1231             TimeStamp exp;
1232             SEC_WINNT_AUTH_IDENTITY_W id;
1233             WCHAR *domain, *user;
1234 
1235             if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1236 
1237             first = TRUE;
1238             domain = (WCHAR *)username;
1239             user = strchrW( username, '\\' );
1240 
1241             if (user) user++;
1242             else
1243             {
1244                 user = (WCHAR *)username;
1245                 domain = NULL;
1246             }
1247             id.Flags          = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1248             id.User           = user;
1249             id.UserLength     = strlenW( user );
1250             id.Domain         = domain;
1251             id.DomainLength   = domain ? user - domain - 1 : 0;
1252             id.Password       = (WCHAR *)password;
1253             id.PasswordLength = strlenW( password );
1254 
1255             status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1256                                                 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1257                                                 &authinfo->cred, &exp );
1258             if (status == SEC_E_OK)
1259             {
1260                 PSecPkgInfoW info;
1261                 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1262                 if (status == SEC_E_OK)
1263                 {
1264                     authinfo->max_token = info->cbMaxToken;
1265                     FreeContextBuffer( info );
1266                 }
1267             }
1268             if (status != SEC_E_OK)
1269             {
1270                 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1271                      debugstr_w(auth_schemes[scheme].str), status);
1272                 heap_free( authinfo );
1273                 return FALSE;
1274             }
1275             authinfo->scheme = scheme;
1276         }
1277         else if (authinfo->finished) return FALSE;
1278 
1279         if ((strlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1280             strncmpiW( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1281         {
1282             ERR("authentication scheme changed from %s to %s\n",
1283                 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1284             destroy_authinfo( authinfo );
1285             *auth_ptr = NULL;
1286             return FALSE;
1287         }
1288         in.BufferType = SECBUFFER_TOKEN;
1289         in.cbBuffer   = 0;
1290         in.pvBuffer   = NULL;
1291 
1292         in_desc.ulVersion = 0;
1293         in_desc.cBuffers  = 1;
1294         in_desc.pBuffers  = &in;
1295 
1296         p = auth_value + auth_schemes[scheme].len;
1297         if (*p == ' ')
1298         {
1299             int len = strlenW( ++p );
1300             in.cbBuffer = decode_base64( p, len, NULL );
1301             if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) {
1302                 destroy_authinfo( authinfo );
1303                 *auth_ptr = NULL;
1304                 return FALSE;
1305             }
1306             decode_base64( p, len, in.pvBuffer );
1307         }
1308         out.BufferType = SECBUFFER_TOKEN;
1309         out.cbBuffer   = authinfo->max_token;
1310         if (!(out.pvBuffer = heap_alloc( authinfo->max_token )))
1311         {
1312             heap_free( in.pvBuffer );
1313             destroy_authinfo( authinfo );
1314             *auth_ptr = NULL;
1315             return FALSE;
1316         }
1317         out_desc.ulVersion = 0;
1318         out_desc.cBuffers  = 1;
1319         out_desc.pBuffers  = &out;
1320 
1321         status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1322                                              first ? request->connect->servername : NULL, flags, 0,
1323                                              SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1324                                              &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1325         heap_free( in.pvBuffer );
1326         if (status == SEC_E_OK)
1327         {
1328             heap_free( authinfo->data );
1329             authinfo->data     = out.pvBuffer;
1330             authinfo->data_len = out.cbBuffer;
1331             authinfo->finished = TRUE;
1332             TRACE("sending last auth packet\n");
1333         }
1334         else if (status == SEC_I_CONTINUE_NEEDED)
1335         {
1336             heap_free( authinfo->data );
1337             authinfo->data     = out.pvBuffer;
1338             authinfo->data_len = out.cbBuffer;
1339             TRACE("sending next auth packet\n");
1340         }
1341         else
1342         {
1343             ERR("InitializeSecurityContextW failed with error 0x%08x\n", status);
1344             heap_free( out.pvBuffer );
1345             destroy_authinfo( authinfo );
1346             *auth_ptr = NULL;
1347             return FALSE;
1348         }
1349         break;
1350     }
1351     default:
1352         ERR("invalid scheme %u\n", scheme);
1353         return FALSE;
1354     }
1355     *auth_ptr = authinfo;
1356 
1357     len_scheme = auth_schemes[authinfo->scheme].len;
1358     len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1359     if (!(auth_reply = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1360 
1361     memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1362     auth_reply[len_scheme] = ' ';
1363     encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1364 
1365     flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1366     ret = process_header( request, auth_target, auth_reply, flags, TRUE );
1367     heap_free( auth_reply );
1368     return ret;
1369 }
1370 
1371 static LPWSTR concatenate_string_list( LPCWSTR *list, int len )
1372 {
1373     LPCWSTR *t;
1374     LPWSTR str;
1375 
1376     for( t = list; *t ; t++  )
1377         len += strlenW( *t );
1378     len++;
1379 
1380     str = heap_alloc( len * sizeof(WCHAR) );
1381     if (!str) return NULL;
1382     *str = 0;
1383 
1384     for( t = list; *t ; t++ )
1385         strcatW( str, *t );
1386 
1387     return str;
1388 }
1389 
1390 static LPWSTR build_header_request_string( request_t *request, LPCWSTR verb,
1391     LPCWSTR path, LPCWSTR version )
1392 {
1393     static const WCHAR crlf[] = {'\r','\n',0};
1394     static const WCHAR space[] = { ' ',0 };
1395     static const WCHAR colon[] = { ':',' ',0 };
1396     static const WCHAR twocrlf[] = {'\r','\n','\r','\n', 0};
1397     LPWSTR requestString;
1398     DWORD len, n;
1399     LPCWSTR *req;
1400     UINT i;
1401     LPWSTR p;
1402 
1403     /* allocate space for an array of all the string pointers to be added */
1404     len = (request->num_headers) * 4 + 10;
1405     req = heap_alloc( len * sizeof(LPCWSTR) );
1406     if (!req) return NULL;
1407 
1408     /* add the verb, path and HTTP version string */
1409     n = 0;
1410     req[n++] = verb;
1411     req[n++] = space;
1412     req[n++] = path;
1413     req[n++] = space;
1414     req[n++] = version;
1415 
1416     /* Append custom request headers */
1417     for (i = 0; i < request->num_headers; i++)
1418     {
1419         if (request->headers[i].is_request)
1420         {
1421             req[n++] = crlf;
1422             req[n++] = request->headers[i].field;
1423             req[n++] = colon;
1424             req[n++] = request->headers[i].value;
1425 
1426             TRACE("Adding custom header %s (%s)\n",
1427                    debugstr_w(request->headers[i].field),
1428                    debugstr_w(request->headers[i].value));
1429         }
1430     }
1431 
1432     if( n >= len )
1433         ERR("oops. buffer overrun\n");
1434 
1435     req[n] = NULL;
1436     requestString = concatenate_string_list( req, 4 );
1437     heap_free( req );
1438     if (!requestString) return NULL;
1439 
1440     /*
1441      * Set (header) termination string for request
1442      * Make sure there are exactly two new lines at the end of the request
1443      */
1444     p = &requestString[strlenW(requestString)-1];
1445     while ( (*p == '\n') || (*p == '\r') )
1446        p--;
1447     strcpyW( p+1, twocrlf );
1448 
1449     return requestString;
1450 }
1451 
1452 static BOOL read_reply( request_t *request );
1453 
1454 static BOOL secure_proxy_connect( request_t *request )
1455 {
1456     static const WCHAR verbConnect[] = {'C','O','N','N','E','C','T',0};
1457     static const WCHAR fmt[] = {'%','s',':','%','u',0};
1458     BOOL ret = FALSE;
1459     LPWSTR path;
1460     connect_t *connect = request->connect;
1461 
1462     path = heap_alloc( (strlenW( connect->hostname ) + 13) * sizeof(WCHAR) );
1463     if (path)
1464     {
1465         LPWSTR requestString;
1466 
1467         sprintfW( path, fmt, connect->hostname, connect->hostport );
1468         requestString = build_header_request_string( request, verbConnect,
1469             path, http1_1 );
1470         heap_free( path );
1471         if (requestString)
1472         {
1473             LPSTR req_ascii = strdupWA( requestString );
1474 
1475             heap_free( requestString );
1476             if (req_ascii)
1477             {
1478                 int len = strlen( req_ascii ), bytes_sent;
1479 
1480                 ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent );
1481                 heap_free( req_ascii );
1482                 if (ret)
1483                     ret = read_reply( request );
1484             }
1485         }
1486     }
1487     return ret;
1488 }
1489 
1490 #ifndef INET6_ADDRSTRLEN
1491 #define INET6_ADDRSTRLEN 46
1492 #endif
1493 
1494 static WCHAR *addr_to_str( struct sockaddr_storage *addr )
1495 {
1496     char buf[INET6_ADDRSTRLEN];
1497     void *src;
1498 
1499     switch (addr->ss_family)
1500     {
1501     case AF_INET:
1502         src = &((struct sockaddr_in *)addr)->sin_addr;
1503         break;
1504     case AF_INET6:
1505         src = &((struct sockaddr_in6 *)addr)->sin6_addr;
1506         break;
1507     default:
1508         WARN("unsupported address family %d\n", addr->ss_family);
1509         return NULL;
1510     }
1511     if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL;
1512     return strdupAW( buf );
1513 }
1514 
1515 static CRITICAL_SECTION connection_pool_cs;
1516 static CRITICAL_SECTION_DEBUG connection_pool_debug =
1517 {
1518     0, 0, &connection_pool_cs,
1519     { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
1520       0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
1521 };
1522 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
1523 
1524 static struct list connection_pool = LIST_INIT( connection_pool );
1525 
1526 void release_host( hostdata_t *host )
1527 {
1528     LONG ref;
1529 
1530     EnterCriticalSection( &connection_pool_cs );
1531     if (!(ref = --host->ref)) list_remove( &host->entry );
1532     LeaveCriticalSection( &connection_pool_cs );
1533     if (ref) return;
1534 
1535     assert( list_empty( &host->connections ) );
1536     heap_free( host->hostname );
1537     heap_free( host );
1538 }
1539 
1540 static BOOL connection_collector_running;
1541 
1542 static DWORD WINAPI connection_collector(void *arg)
1543 {
1544     unsigned int remaining_connections;
1545     netconn_t *netconn, *next_netconn;
1546     hostdata_t *host, *next_host;
1547     ULONGLONG now;
1548 
1549     do
1550     {
1551         /* FIXME: Use more sophisticated method */
1552         Sleep(5000);
1553         remaining_connections = 0;
1554         now = GetTickCount64();
1555 
1556         EnterCriticalSection(&connection_pool_cs);
1557 
1558         LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, hostdata_t, entry)
1559         {
1560             LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, netconn_t, entry)
1561             {
1562                 if (netconn->keep_until < now)
1563                 {
1564                     TRACE("freeing %p\n", netconn);
1565                     list_remove(&netconn->entry);
1566                     netconn_close(netconn);
1567                 }
1568                 else
1569                 {
1570                     remaining_connections++;
1571                 }
1572             }
1573         }
1574 
1575         if (!remaining_connections) connection_collector_running = FALSE;
1576 
1577         LeaveCriticalSection(&connection_pool_cs);
1578     } while(remaining_connections);
1579 
1580     FreeLibraryAndExitThread( winhttp_instance, 0 );
1581 }
1582 
1583 static void cache_connection( netconn_t *netconn )
1584 {
1585     TRACE( "caching connection %p\n", netconn );
1586 
1587     EnterCriticalSection( &connection_pool_cs );
1588 
1589     netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT;
1590     list_add_head( &netconn->host->connections, &netconn->entry );
1591 
1592     if (!connection_collector_running)
1593     {
1594         HMODULE module;
1595         HANDLE thread;
1596 
1597         GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)winhttp_instance, &module );
1598 
1599         thread = CreateThread(NULL, 0, connection_collector, NULL, 0, NULL);
1600         if (thread)
1601         {
1602             CloseHandle( thread );
1603             connection_collector_running = TRUE;
1604         }
1605         else
1606         {
1607             FreeLibrary( winhttp_instance );
1608         }
1609     }
1610 
1611     LeaveCriticalSection( &connection_pool_cs );
1612 }
1613 
1614 static DWORD map_secure_protocols( DWORD mask )
1615 {
1616     DWORD ret = 0;
1617     if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT;
1618     if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT;
1619     if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT;
1620     if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT;
1621     if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT;
1622     return ret;
1623 }
1624 
1625 static BOOL ensure_cred_handle( session_t *session )
1626 {
1627     SCHANNEL_CRED cred;
1628     SECURITY_STATUS status;
1629 
1630     if (session->cred_handle_initialized) return TRUE;
1631 
1632     memset( &cred, 0, sizeof(cred) );
1633     cred.dwVersion             = SCHANNEL_CRED_VERSION;
1634     cred.grbitEnabledProtocols = map_secure_protocols( session->secure_protocols );
1635     if ((status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, &cred,
1636                                              NULL, NULL, &session->cred_handle, NULL )) != SEC_E_OK)
1637     {
1638         WARN( "AcquireCredentialsHandleW failed: 0x%08x\n", status );
1639         return FALSE;
1640     }
1641     session->cred_handle_initialized = TRUE;
1642     return TRUE;
1643 }
1644 
1645 static BOOL open_connection( request_t *request )
1646 {
1647     BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
1648     hostdata_t *host = NULL, *iter;
1649     netconn_t *netconn = NULL;
1650     connect_t *connect;
1651     WCHAR *addressW = NULL;
1652     INTERNET_PORT port;
1653     DWORD len;
1654 
1655     if (request->netconn) goto done;
1656 
1657     connect = request->connect;
1658     port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1659 
1660     EnterCriticalSection( &connection_pool_cs );
1661 
1662     LIST_FOR_EACH_ENTRY( iter, &connection_pool, hostdata_t, entry )
1663     {
1664         if (iter->port == port && !strcmpW( connect->servername, iter->hostname ) && !is_secure == !iter->secure)
1665         {
1666             host = iter;
1667             host->ref++;
1668             break;
1669         }
1670     }
1671 
1672     if (!host)
1673     {
1674         if ((host = heap_alloc( sizeof(*host) )))
1675         {
1676             host->ref = 1;
1677             host->secure = is_secure;
1678             host->port = port;
1679             list_init( &host->connections );
1680             if ((host->hostname = strdupW( connect->servername )))
1681             {
1682                 list_add_head( &connection_pool, &host->entry );
1683             }
1684             else
1685             {
1686                 heap_free( host );
1687                 host = NULL;
1688             }
1689         }
1690     }
1691 
1692     LeaveCriticalSection( &connection_pool_cs );
1693 
1694     if (!host) return FALSE;
1695 
1696     for (;;)
1697     {
1698         EnterCriticalSection( &connection_pool_cs );
1699         if (!list_empty( &host->connections ))
1700         {
1701             netconn = LIST_ENTRY( list_head( &host->connections ), netconn_t, entry );
1702             list_remove( &netconn->entry );
1703         }
1704         LeaveCriticalSection( &connection_pool_cs );
1705         if (!netconn) break;
1706 
1707         if (netconn_is_alive( netconn )) break;
1708         TRACE("connection %p no longer alive, closing\n", netconn);
1709         netconn_close( netconn );
1710         netconn = NULL;
1711     }
1712 
1713     if (!connect->resolved && netconn)
1714     {
1715         connect->sockaddr = netconn->sockaddr;
1716         connect->resolved = TRUE;
1717     }
1718 
1719     if (!connect->resolved)
1720     {
1721         len = strlenW( host->hostname ) + 1;
1722         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
1723 
1724         if (!netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout ))
1725         {
1726             release_host( host );
1727             return FALSE;
1728         }
1729         connect->resolved = TRUE;
1730 
1731         if (!(addressW = addr_to_str( &connect->sockaddr )))
1732         {
1733             release_host( host );
1734             return FALSE;
1735         }
1736         len = strlenW( addressW ) + 1;
1737         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1738     }
1739 
1740     if (!netconn)
1741     {
1742         if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
1743         {
1744             release_host( host );
1745             return FALSE;
1746         }
1747 
1748         TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1749 
1750         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
1751 
1752         if (!(netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout )))
1753         {
1754             heap_free( addressW );
1755             release_host( host );
1756             return FALSE;
1757         }
1758         netconn_set_timeout( netconn, TRUE, request->send_timeout );
1759         netconn_set_timeout( netconn, FALSE, request->recv_timeout );
1760         if (is_secure)
1761         {
1762             if (connect->session->proxy_server &&
1763                 strcmpiW( connect->hostname, connect->servername ))
1764             {
1765                 if (!secure_proxy_connect( request ))
1766                 {
1767                     heap_free( addressW );
1768                     netconn_close( netconn );
1769                     return FALSE;
1770                 }
1771             }
1772             if (!ensure_cred_handle( connect->session ) ||
1773                 !netconn_secure_connect( netconn, connect->hostname, request->security_flags,
1774                                          &connect->session->cred_handle ))
1775             {
1776                 heap_free( addressW );
1777                 netconn_close( netconn );
1778                 return FALSE;
1779             }
1780         }
1781 
1782         request->netconn = netconn;
1783         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 );
1784     }
1785     else
1786     {
1787         TRACE("using connection %p\n", netconn);
1788 
1789         netconn_set_timeout( netconn, TRUE, request->send_timeout );
1790         netconn_set_timeout( netconn, FALSE, request->recv_timeout );
1791         request->netconn = netconn;
1792     }
1793 
1794 done:
1795     request->read_pos = request->read_size = 0;
1796     request->read_chunked = FALSE;
1797     request->read_chunked_size = ~0u;
1798     request->read_chunked_eof = FALSE;
1799     heap_free( addressW );
1800     return TRUE;
1801 }
1802 
1803 void close_connection( request_t *request )
1804 {
1805     if (!request->netconn) return;
1806 
1807     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1808     netconn_close( request->netconn );
1809     request->netconn = NULL;
1810     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1811 }
1812 
1813 static BOOL add_host_header( request_t *request, DWORD modifier )
1814 {
1815     BOOL ret;
1816     DWORD len;
1817     WCHAR *host;
1818     static const WCHAR fmt[] = {'%','s',':','%','u',0};
1819     connect_t *connect = request->connect;
1820     INTERNET_PORT port;
1821 
1822     port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1823 
1824     if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1825     {
1826         return process_header( request, attr_host, connect->hostname, modifier, TRUE );
1827     }
1828     len = strlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1829     if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
1830     sprintfW( host, fmt, connect->hostname, port );
1831     ret = process_header( request, attr_host, host, modifier, TRUE );
1832     heap_free( host );
1833     return ret;
1834 }
1835 
1836 static void clear_response_headers( request_t *request )
1837 {
1838     unsigned int i;
1839 
1840     for (i = 0; i < request->num_headers; i++)
1841     {
1842         if (!request->headers[i].field) continue;
1843         if (!request->headers[i].value) continue;
1844         if (request->headers[i].is_request) continue;
1845         delete_header( request, i );
1846         i--;
1847     }
1848 }
1849 
1850 /* remove some amount of data from the read buffer */
1851 static void remove_data( request_t *request, int count )
1852 {
1853     if (!(request->read_size -= count)) request->read_pos = 0;
1854     else request->read_pos += count;
1855 }
1856 
1857 /* read some more data into the read buffer */
1858 static BOOL read_more_data( request_t *request, int maxlen, BOOL notify )
1859 {
1860     int len;
1861     BOOL ret;
1862 
1863     if (request->read_chunked_eof) return FALSE;
1864 
1865     if (request->read_size && request->read_pos)
1866     {
1867         /* move existing data to the start of the buffer */
1868         memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1869         request->read_pos = 0;
1870     }
1871     if (maxlen == -1) maxlen = sizeof(request->read_buf);
1872 
1873     if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1874 
1875     ret = netconn_recv( request->netconn, request->read_buf + request->read_size,
1876                         maxlen - request->read_size, 0, &len );
1877 
1878     if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1879 
1880     request->read_size += len;
1881     return ret;
1882 }
1883 
1884 /* discard data contents until we reach end of line */
1885 static BOOL discard_eol( request_t *request, BOOL notify )
1886 {
1887     do
1888     {
1889         char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1890         if (eol)
1891         {
1892             remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1893             break;
1894         }
1895         request->read_pos = request->read_size = 0;  /* discard everything */
1896         if (!read_more_data( request, -1, notify )) return FALSE;
1897     } while (request->read_size);
1898     return TRUE;
1899 }
1900 
1901 /* read the size of the next chunk */
1902 static BOOL start_next_chunk( request_t *request, BOOL notify )
1903 {
1904     DWORD chunk_size = 0;
1905 
1906     assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
1907 
1908     if (request->read_chunked_eof) return FALSE;
1909 
1910     /* read terminator for the previous chunk */
1911     if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE;
1912 
1913     for (;;)
1914     {
1915         while (request->read_size)
1916         {
1917             char ch = request->read_buf[request->read_pos];
1918             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1919             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1920             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1921             else if (ch == ';' || ch == '\r' || ch == '\n')
1922             {
1923                 TRACE("reading %u byte chunk\n", chunk_size);
1924 
1925                 if (request->content_length == ~0u) request->content_length = chunk_size;
1926                 else request->content_length += chunk_size;
1927 
1928                 request->read_chunked_size = chunk_size;
1929                 if (!chunk_size) request->read_chunked_eof = TRUE;
1930 
1931                 return discard_eol( request, notify );
1932             }
1933             remove_data( request, 1 );
1934         }
1935         if (!read_more_data( request, -1, notify )) return FALSE;
1936         if (!request->read_size)
1937         {
1938             request->content_length = request->content_read = 0;
1939             request->read_chunked_size = 0;
1940             return TRUE;
1941         }
1942     }
1943 }
1944 
1945 static BOOL refill_buffer( request_t *request, BOOL notify )
1946 {
1947     int len = sizeof(request->read_buf);
1948 
1949     if (request->read_chunked)
1950     {
1951         if (request->read_chunked_eof) return FALSE;
1952         if (request->read_chunked_size == ~0u || !request->read_chunked_size)
1953         {
1954             if (!start_next_chunk( request, notify )) return FALSE;
1955         }
1956         len = min( len, request->read_chunked_size );
1957     }
1958     else if (request->content_length != ~0u)
1959     {
1960         len = min( len, request->content_length - request->content_read );
1961     }
1962 
1963     if (len <= request->read_size) return TRUE;
1964     if (!read_more_data( request, len, notify )) return FALSE;
1965     if (!request->read_size) request->content_length = request->content_read = 0;
1966     return TRUE;
1967 }
1968 
1969 static void finished_reading( request_t *request )
1970 {
1971     static const WCHAR closeW[] = {'c','l','o','s','e',0};
1972 
1973     BOOL close = FALSE;
1974     WCHAR connection[20];
1975     DWORD size = sizeof(connection);
1976 
1977     if (!request->netconn) return;
1978 
1979     if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
1980     else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
1981              query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
1982     {
1983         if (!strcmpiW( connection, closeW )) close = TRUE;
1984     }
1985     else if (!strcmpW( request->version, http1_0 )) close = TRUE;
1986     if (close)
1987     {
1988         close_connection( request );
1989         return;
1990     }
1991 
1992     cache_connection( request->netconn );
1993     request->netconn = NULL;
1994 }
1995 
1996 /* return the size of data available to be read immediately */
1997 static DWORD get_available_data( request_t *request )
1998 {
1999     if (request->read_chunked) return min( request->read_chunked_size, request->read_size );
2000     return request->read_size;
2001 }
2002 
2003 /* check if we have reached the end of the data to read */
2004 static BOOL end_of_read_data( request_t *request )
2005 {
2006     if (!request->content_length) return TRUE;
2007     if (request->read_chunked) return request->read_chunked_eof;
2008     if (request->content_length == ~0u) return FALSE;
2009     return (request->content_length == request->content_read);
2010 }
2011 
2012 static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
2013 {
2014     int count, bytes_read = 0;
2015 
2016     if (end_of_read_data( request )) goto done;
2017 
2018     while (size)
2019     {
2020         if (!(count = get_available_data( request )))
2021         {
2022             if (!refill_buffer( request, async )) goto done;
2023             if (!(count = get_available_data( request ))) goto done;
2024         }
2025         count = min( count, size );
2026         memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
2027         remove_data( request, count );
2028         if (request->read_chunked) request->read_chunked_size -= count;
2029         size -= count;
2030         bytes_read += count;
2031         request->content_read += count;
2032         if (end_of_read_data( request )) goto done;
2033     }
2034     if (request->read_chunked && !request->read_chunked_size) refill_buffer( request, async );
2035 
2036 done:
2037     TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
2038 
2039     if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
2040     if (read) *read = bytes_read;
2041     if (end_of_read_data( request )) finished_reading( request );
2042     return TRUE;
2043 }
2044 
2045 /* read any content returned by the server so that the connection can be reused */
2046 static void drain_content( request_t *request )
2047 {
2048     DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read;
2049     char buffer[2048];
2050 
2051     refill_buffer( request, FALSE );
2052     for (;;)
2053     {
2054         if (request->read_chunked) size = sizeof(buffer);
2055         else
2056         {
2057             if (bytes_total >= bytes_left) return;
2058             size = min( sizeof(buffer), bytes_left - bytes_total );
2059         }
2060         if (!read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return;
2061         bytes_total += bytes_read;
2062     }
2063 }
2064 
2065 static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
2066                           DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
2067 {
2068     static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0};
2069     static const WCHAR no_cache[]   = {'n','o','-','c','a','c','h','e',0};
2070     static const WCHAR length_fmt[] = {'%','l','d',0};
2071 
2072     BOOL ret = FALSE;
2073     connect_t *connect = request->connect;
2074     session_t *session = connect->session;
2075     WCHAR *req = NULL;
2076     char *req_ascii;
2077     int bytes_sent;
2078     DWORD len;
2079 
2080     clear_response_headers( request );
2081     drain_content( request );
2082 
2083     if (session->agent)
2084         process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2085 
2086     if (connect->hostname)
2087         add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
2088 
2089     if (request->creds[TARGET_SERVER][SCHEME_BASIC].username)
2090         do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC );
2091 
2092     if (total_len || (request->verb && !strcmpW( request->verb, postW )))
2093     {
2094         WCHAR length[21]; /* decimal long int + null */
2095         sprintfW( length, length_fmt, total_len );
2096         process_header( request, attr_content_length, length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2097     }
2098     if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
2099     {
2100         process_header( request, attr_connection, keep_alive, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2101     }
2102     if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
2103     {
2104         process_header( request, attr_pragma, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2105         process_header( request, attr_cache_control, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2106     }
2107     if (headers && !add_request_headers( request, headers, headers_len, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
2108     {
2109         TRACE("failed to add request headers\n");
2110         return FALSE;
2111     }
2112     if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request ))
2113     {
2114         WARN("failed to add cookie headers\n");
2115         return FALSE;
2116     }
2117 
2118     if (context) request->hdr.context = context;
2119 
2120     if (!(ret = open_connection( request ))) goto end;
2121     if (!(req = build_request_string( request ))) goto end;
2122 
2123     if (!(req_ascii = strdupWA( req ))) goto end;
2124     TRACE("full request: %s\n", debugstr_a(req_ascii));
2125     len = strlen(req_ascii);
2126 
2127     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
2128 
2129     ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent );
2130     heap_free( req_ascii );
2131     if (!ret) goto end;
2132 
2133     if (optional_len)
2134     {
2135         if (!netconn_send( request->netconn, optional, optional_len, &bytes_sent )) goto end;
2136         request->optional = optional;
2137         request->optional_len = optional_len;
2138         len += optional_len;
2139     }
2140     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
2141 
2142 end:
2143     if (async)
2144     {
2145         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
2146         else
2147         {
2148             WINHTTP_ASYNC_RESULT result;
2149             result.dwResult = API_SEND_REQUEST;
2150             result.dwError  = get_last_error();
2151             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2152         }
2153     }
2154     heap_free( req );
2155     return ret;
2156 }
2157 
2158 static void task_send_request( task_header_t *task )
2159 {
2160     send_request_t *s = (send_request_t *)task;
2161     send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
2162     heap_free( s->headers );
2163 }
2164 
2165 /***********************************************************************
2166  *          WinHttpSendRequest (winhttp.@)
2167  */
2168 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len,
2169                                 LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
2170 {
2171     BOOL ret;
2172     request_t *request;
2173 
2174     TRACE("%p, %s, %u, %u, %u, %lx\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional_len,
2175           total_len, context);
2176 
2177     if (!(request = (request_t *)grab_object( hrequest )))
2178     {
2179         set_last_error( ERROR_INVALID_HANDLE );
2180         return FALSE;
2181     }
2182     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2183     {
2184         release_object( &request->hdr );
2185         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2186         return FALSE;
2187     }
2188 
2189     if (headers && !headers_len) headers_len = strlenW( headers );
2190 
2191     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2192     {
2193         send_request_t *s;
2194 
2195         if (!(s = heap_alloc( sizeof(send_request_t) ))) return FALSE;
2196         s->hdr.request  = request;
2197         s->hdr.proc     = task_send_request;
2198         s->headers      = strdupW( headers );
2199         s->headers_len  = headers_len;
2200         s->optional     = optional;
2201         s->optional_len = optional_len;
2202         s->total_len    = total_len;
2203         s->context      = context;
2204 
2205         addref_object( &request->hdr );
2206         ret = queue_task( (task_header_t *)s );
2207     }
2208     else
2209         ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
2210 
2211     release_object( &request->hdr );
2212     if (ret) set_last_error( ERROR_SUCCESS );
2213     return ret;
2214 }
2215 
2216 static BOOL set_credentials( request_t *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
2217                              const WCHAR *password )
2218 {
2219     enum auth_scheme scheme = scheme_from_flag( scheme_flag );
2220 
2221     if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
2222     {
2223         set_last_error( ERROR_INVALID_PARAMETER );
2224         return FALSE;
2225     }
2226     switch (target)
2227     {
2228     case WINHTTP_AUTH_TARGET_SERVER:
2229     {
2230         heap_free( request->creds[TARGET_SERVER][scheme].username );
2231         if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
2232         else if (!(request->creds[TARGET_SERVER][scheme].username = strdupW( username ))) return FALSE;
2233 
2234         heap_free( request->creds[TARGET_SERVER][scheme].password );
2235         if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
2236         else if (!(request->creds[TARGET_SERVER][scheme].password = strdupW( password ))) return FALSE;
2237         break;
2238     }
2239     case WINHTTP_AUTH_TARGET_PROXY:
2240     {
2241         heap_free( request->creds[TARGET_PROXY][scheme].username );
2242         if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
2243         else if (!(request->creds[TARGET_PROXY][scheme].username = strdupW( username ))) return FALSE;
2244 
2245         heap_free( request->creds[TARGET_PROXY][scheme].password );
2246         if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
2247         else if (!(request->creds[TARGET_PROXY][scheme].password = strdupW( password ))) return FALSE;
2248         break;
2249     }
2250     default:
2251         WARN("unknown target %u\n", target);
2252         return FALSE;
2253     }
2254     return TRUE;
2255 }
2256 
2257 /***********************************************************************
2258  *          WinHttpSetCredentials (winhttp.@)
2259  */
2260 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, LPCWSTR username,
2261                                    LPCWSTR password, LPVOID params )
2262 {
2263     BOOL ret;
2264     request_t *request;
2265 
2266     TRACE("%p, %x, 0x%08x, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params);
2267 
2268     if (!(request = (request_t *)grab_object( hrequest )))
2269     {
2270         set_last_error( ERROR_INVALID_HANDLE );
2271         return FALSE;
2272     }
2273     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2274     {
2275         release_object( &request->hdr );
2276         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2277         return FALSE;
2278     }
2279 
2280     ret = set_credentials( request, target, scheme, username, password );
2281 
2282     release_object( &request->hdr );
2283     if (ret) set_last_error( ERROR_SUCCESS );
2284     return ret;
2285 }
2286 
2287 static BOOL handle_authorization( request_t *request, DWORD status )
2288 {
2289     DWORD i, schemes, first, level, target;
2290 
2291     switch (status)
2292     {
2293     case HTTP_STATUS_DENIED:
2294         target = WINHTTP_AUTH_TARGET_SERVER;
2295         level  = WINHTTP_QUERY_WWW_AUTHENTICATE;
2296         break;
2297 
2298     case HTTP_STATUS_PROXY_AUTH_REQ:
2299         target = WINHTTP_AUTH_TARGET_PROXY;
2300         level  = WINHTTP_QUERY_PROXY_AUTHENTICATE;
2301         break;
2302 
2303     default:
2304         WARN("unhandled status %u\n", status);
2305         return FALSE;
2306     }
2307 
2308     if (!query_auth_schemes( request, level, &schemes, &first )) return FALSE;
2309     if (do_authorization( request, target, first )) return TRUE;
2310 
2311     schemes &= ~first;
2312     for (i = 0; i < num_auth_schemes; i++)
2313     {
2314         if (!(schemes & auth_schemes[i].scheme)) continue;
2315         if (do_authorization( request, target, auth_schemes[i].scheme )) return TRUE;
2316     }
2317     return FALSE;
2318 }
2319 
2320 /* set the request content length based on the headers */
2321 static DWORD set_content_length( request_t *request, DWORD status )
2322 {
2323     WCHAR encoding[20];
2324     DWORD buflen = sizeof(request->content_length);
2325 
2326     if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED || !strcmpW( request->verb, headW ))
2327         request->content_length = 0;
2328     else
2329     {
2330         if (!query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
2331                             NULL, &request->content_length, &buflen, NULL ))
2332             request->content_length = ~0u;
2333 
2334         buflen = sizeof(encoding);
2335         if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
2336             !strcmpiW( encoding, chunkedW ))
2337         {
2338             request->content_length = ~0u;
2339             request->read_chunked = TRUE;
2340             request->read_chunked_size = ~0u;
2341             request->read_chunked_eof = FALSE;
2342         }
2343     }
2344     request->content_read = 0;
2345     return request->content_length;
2346 }
2347 
2348 static BOOL read_line( request_t *request, char *buffer, DWORD *len )
2349 {
2350     int count, bytes_read, pos = 0;
2351 
2352     for (;;)
2353     {
2354         char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
2355         if (eol)
2356         {
2357             count = eol - (request->read_buf + request->read_pos);
2358             bytes_read = count + 1;
2359         }
2360         else count = bytes_read = request->read_size;
2361 
2362         count = min( count, *len - pos );
2363         memcpy( buffer + pos, request->read_buf + request->read_pos, count );
2364         pos += count;
2365         remove_data( request, bytes_read );
2366         if (eol) break;
2367 
2368         if (!read_more_data( request, -1, TRUE )) return FALSE;
2369         if (!request->read_size)
2370         {
2371             *len = 0;
2372             TRACE("returning empty string\n");
2373             return FALSE;
2374         }
2375     }
2376     if (pos < *len)
2377     {
2378         if (pos && buffer[pos - 1] == '\r') pos--;
2379         *len = pos + 1;
2380     }
2381     buffer[*len - 1] = 0;
2382     TRACE("returning %s\n", debugstr_a(buffer));
2383     return TRUE;
2384 }
2385 
2386 #define MAX_REPLY_LEN   1460
2387 #define INITIAL_HEADER_BUFFER_LEN  512
2388 
2389 static BOOL read_reply( request_t *request )
2390 {
2391     static const WCHAR crlf[] = {'\r','\n',0};
2392 
2393     char buffer[MAX_REPLY_LEN];
2394     DWORD buflen, len, offset, crlf_len = 2; /* strlenW(crlf) */
2395     char *status_code, *status_text;
2396     WCHAR *versionW, *status_textW, *raw_headers;
2397     WCHAR status_codeW[4]; /* sizeof("nnn") */
2398 
2399     if (!request->netconn) return FALSE;
2400 
2401     do
2402     {
2403         buflen = MAX_REPLY_LEN;
2404         if (!read_line( request, buffer, &buflen )) return FALSE;
2405 
2406         /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2407         if (!(status_code = strchr( buffer, ' ' ))) return FALSE;
2408         status_code++;
2409         if (!(status_text = strchr( status_code, ' ' ))) return FALSE;
2410         if ((len = status_text - status_code) != sizeof("nnn") - 1) return FALSE;
2411         status_text++;
2412 
2413         TRACE("version [%s] status code [%s] status text [%s]\n",
2414               debugstr_an(buffer, status_code - buffer - 1),
2415               debugstr_an(status_code, len),
2416               debugstr_a(status_text));
2417 
2418     } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2419 
2420     /*  we rely on the fact that the protocol is ascii */
2421     MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2422     status_codeW[len] = 0;
2423     if (!(process_header( request, attr_status, status_codeW,
2424                           WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE )))
2425         return FALSE;
2426 
2427     len = status_code - buffer;
2428     if (!(versionW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2429     MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2430     versionW[len - 1] = 0;
2431 
2432     heap_free( request->version );
2433     request->version = versionW;
2434 
2435     len = buflen - (status_text - buffer);
2436     if (!(status_textW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2437     MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2438 
2439     heap_free( request->status_text );
2440     request->status_text = status_textW;
2441 
2442     len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2443     if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2444     MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2445     memcpy( raw_headers + buflen - 1, crlf, sizeof(crlf) );
2446 
2447     heap_free( request->raw_headers );
2448     request->raw_headers = raw_headers;
2449 
2450     offset = buflen + crlf_len - 1;
2451     for (;;)
2452     {
2453         header_t *header;
2454 
2455         buflen = MAX_REPLY_LEN;
2456         if (!read_line( request, buffer, &buflen )) return TRUE;
2457         if (!*buffer) buflen = 1;
2458 
2459         while (len - offset < buflen + crlf_len)
2460         {
2461             WCHAR *tmp;
2462             len *= 2;
2463             if (!(tmp = heap_realloc( raw_headers, len * sizeof(WCHAR) ))) return FALSE;
2464             request->raw_headers = raw_headers = tmp;
2465         }
2466         if (!*buffer)
2467         {
2468             memcpy( raw_headers + offset, crlf, sizeof(crlf) );
2469             break;
2470         }
2471         MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2472 
2473         if (!(header = parse_header( raw_headers + offset ))) break;
2474         if (!(process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2475         {
2476             free_header( header );
2477             break;
2478         }
2479         free_header( header );
2480         memcpy( raw_headers + offset + buflen - 1, crlf, sizeof(crlf) );
2481         offset += buflen + crlf_len - 1;
2482     }
2483 
2484     TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2485     return TRUE;
2486 }
2487 
2488 static void record_cookies( request_t *request )
2489 {
2490     unsigned int i;
2491 
2492     for (i = 0; i < request->num_headers; i++)
2493     {
2494         header_t *set_cookie = &request->headers[i];
2495         if (!strcmpiW( set_cookie->field, attr_set_cookie ) && !set_cookie->is_request)
2496         {
2497             set_cookies( request, set_cookie->value );
2498         }
2499     }
2500 }
2501 
2502 static WCHAR *get_redirect_url( request_t *request, DWORD *len )
2503 {
2504     DWORD size;
2505     WCHAR *ret;
2506 
2507     query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2508     if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
2509     if (!(ret = heap_alloc( size ))) return NULL;
2510     *len = size / sizeof(WCHAR);
2511     if (query_headers( request, WINHTTP_QUERY_LOCATION, NULL, ret, &size, NULL )) return ret;
2512     heap_free( ret );
2513     return NULL;
2514 }
2515 
2516 static BOOL handle_redirect( request_t *request, DWORD status )
2517 {
2518     BOOL ret = FALSE;
2519     DWORD len, len_url;
2520     URL_COMPONENTS uc;
2521     connect_t *connect = request->connect;
2522     INTERNET_PORT port;
2523     WCHAR *hostname = NULL, *location;
2524     int index;
2525 
2526     if (!(location = get_redirect_url( request, &len_url ))) return FALSE;
2527 
2528     memset( &uc, 0, sizeof(uc) );
2529     uc.dwStructSize = sizeof(uc);
2530     uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2531 
2532     if (!WinHttpCrackUrl( location, len_url, 0, &uc )) /* assume relative redirect */
2533     {
2534         WCHAR *path, *p;
2535 
2536         if (location[0] == '/')
2537         {
2538             len = strlenW( location );
2539             if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2540             strcpyW( path, location );
2541         }
2542         else
2543         {
2544             if ((p = strrchrW( request->path, '/' ))) *p = 0;
2545             len = strlenW( request->path ) + 1 + strlenW( location );
2546             if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2547             strcpyW( path, request->path );
2548             strcatW( path, slashW );
2549             strcatW( path, location );
2550         }
2551         heap_free( request->path );
2552         request->path = path;
2553 
2554         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
2555     }
2556     else
2557     {
2558         if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2559         {
2560             if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP) goto end;
2561             TRACE("redirect from secure page to non-secure page\n");
2562             request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2563         }
2564         else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2565         {
2566             TRACE("redirect from non-secure page to secure page\n");
2567             request->hdr.flags |= WINHTTP_FLAG_SECURE;
2568         }
2569 
2570         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
2571 
2572         len = uc.dwHostNameLength;
2573         if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2574         memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2575         hostname[len] = 0;
2576 
2577         port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2578         if (strcmpiW( connect->hostname, hostname ) || connect->serverport != port)
2579         {
2580             heap_free( connect->hostname );
2581             connect->hostname = hostname;
2582             connect->hostport = port;
2583             if (!(ret = set_server_for_hostname( connect, hostname, port ))) goto end;
2584 
2585             netconn_close( request->netconn );
2586             request->netconn = NULL;
2587             request->content_length = request->content_read = 0;
2588             request->read_pos = request->read_size = 0;
2589             request->read_chunked = request->read_chunked_eof = FALSE;
2590         }
2591         else heap_free( hostname );
2592 
2593         if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2594         if (!(ret = open_connection( request ))) goto end;
2595 
2596         heap_free( request->path );
2597         request->path = NULL;
2598         if (uc.dwUrlPathLength)
2599         {
2600             len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2601             if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2602             strcpyW( request->path, uc.lpszUrlPath );
2603         }
2604         else request->path = strdupW( slashW );
2605     }
2606 
2607     /* remove content-type/length headers */
2608     if ((index = get_header_index( request, attr_content_type, 0, TRUE )) >= 0) delete_header( request, index );
2609     if ((index = get_header_index( request, attr_content_length, 0, TRUE )) >= 0 ) delete_header( request, index );
2610 
2611     if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !strcmpW( request->verb, postW ))
2612     {
2613         heap_free( request->verb );
2614         request->verb = strdupW( getW );
2615         request->optional = NULL;
2616         request->optional_len = 0;
2617     }
2618     ret = TRUE;
2619 
2620 end:
2621     heap_free( location );
2622     return ret;
2623 }
2624 
2625 static BOOL receive_response( request_t *request, BOOL async )
2626 {
2627     BOOL ret;
2628     DWORD size, query, status;
2629 
2630     for (;;)
2631     {
2632         if (!(ret = read_reply( request )))
2633         {
2634             set_last_error( ERROR_WINHTTP_INVALID_SERVER_RESPONSE );
2635             break;
2636         }
2637         size = sizeof(DWORD);
2638         query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2639         if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
2640 
2641         set_content_length( request, status );
2642 
2643         if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2644 
2645         if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2646         {
2647             if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2648                 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) break;
2649 
2650             if (!(ret = handle_redirect( request, status ))) break;
2651 
2652             /* recurse synchronously */
2653             if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2654         }
2655         else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2656         {
2657             if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
2658 
2659             if (!handle_authorization( request, status )) break;
2660 
2661             /* recurse synchronously */
2662             if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2663         }
2664         break;
2665     }
2666 
2667     if (request->content_length) refill_buffer( request, FALSE );
2668 
2669     if (async)
2670     {
2671         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
2672         else
2673         {
2674             WINHTTP_ASYNC_RESULT result;
2675             result.dwResult = API_RECEIVE_RESPONSE;
2676             result.dwError  = get_last_error();
2677             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2678         }
2679     }
2680     return ret;
2681 }
2682 
2683 static void task_receive_response( task_header_t *task )
2684 {
2685     receive_response_t *r = (receive_response_t *)task;
2686     receive_response( r->hdr.request, TRUE );
2687 }
2688 
2689 /***********************************************************************
2690  *          WinHttpReceiveResponse (winhttp.@)
2691  */
2692 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
2693 {
2694     BOOL ret;
2695     request_t *request;
2696 
2697     TRACE("%p, %p\n", hrequest, reserved);
2698 
2699     if (!(request = (request_t *)grab_object( hrequest )))
2700     {
2701         set_last_error( ERROR_INVALID_HANDLE );
2702         return FALSE;
2703     }
2704     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2705     {
2706         release_object( &request->hdr );
2707         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2708         return FALSE;
2709     }
2710 
2711     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2712     {
2713         receive_response_t *r;
2714 
2715         if (!(r = heap_alloc( sizeof(receive_response_t) ))) return FALSE;
2716         r->hdr.request = request;
2717         r->hdr.proc    = task_receive_response;
2718 
2719         addref_object( &request->hdr );
2720         ret = queue_task( (task_header_t *)r );
2721     }
2722     else
2723         ret = receive_response( request, FALSE );
2724 
2725     release_object( &request->hdr );
2726     if (ret) set_last_error( ERROR_SUCCESS );
2727     return ret;
2728 }
2729 
2730 static BOOL query_data_available( request_t *request, DWORD *available, BOOL async )
2731 {
2732     DWORD count = 0;
2733 
2734     if (end_of_read_data( request )) goto done;
2735 
2736     count = get_available_data( request );
2737     if (!request->read_chunked && request->netconn)
2738         count += netconn_query_data_available( request->netconn );
2739     if (!count)
2740     {
2741         refill_buffer( request, async );
2742         count = get_available_data( request );
2743         if (!request->read_chunked && request->netconn)
2744             count += netconn_query_data_available( request->netconn );
2745     }
2746 
2747 done:
2748     if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
2749     TRACE("%u bytes available\n", count);
2750     if (available) *available = count;
2751     return TRUE;
2752 }
2753 
2754 static void task_query_data_available( task_header_t *task )
2755 {
2756     query_data_t *q = (query_data_t *)task;
2757     query_data_available( q->hdr.request, q->available, TRUE );
2758 }
2759 
2760 /***********************************************************************
2761  *          WinHttpQueryDataAvailable (winhttp.@)
2762  */
2763 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
2764 {
2765     BOOL ret;
2766     request_t *request;
2767 
2768     TRACE("%p, %p\n", hrequest, available);
2769 
2770     if (!(request = (request_t *)grab_object( hrequest )))
2771     {
2772         set_last_error( ERROR_INVALID_HANDLE );
2773         return FALSE;
2774     }
2775     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2776     {
2777         release_object( &request->hdr );
2778         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2779         return FALSE;
2780     }
2781 
2782     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2783     {
2784         query_data_t *q;
2785 
2786         if (!(q = heap_alloc( sizeof(query_data_t) ))) return FALSE;
2787         q->hdr.request = request;
2788         q->hdr.proc    = task_query_data_available;
2789         q->available   = available;
2790 
2791         addref_object( &request->hdr );
2792         ret = queue_task( (task_header_t *)q );
2793     }
2794     else
2795         ret = query_data_available( request, available, FALSE );
2796 
2797     release_object( &request->hdr );
2798     if (ret) set_last_error( ERROR_SUCCESS );
2799     return ret;
2800 }
2801 
2802 static void task_read_data( task_header_t *task )
2803 {
2804     read_data_t *r = (read_data_t *)task;
2805     read_data( r->hdr.request, r->buffer, r->to_read, r->read, TRUE );
2806 }
2807 
2808 /***********************************************************************
2809  *          WinHttpReadData (winhttp.@)
2810  */
2811 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
2812 {
2813     BOOL ret;
2814     request_t *request;
2815 
2816     TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
2817 
2818     if (!(request = (request_t *)grab_object( hrequest )))
2819     {
2820         set_last_error( ERROR_INVALID_HANDLE );
2821         return FALSE;
2822     }
2823     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2824     {
2825         release_object( &request->hdr );
2826         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2827         return FALSE;
2828     }
2829 
2830     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2831     {
2832         read_data_t *r;
2833 
2834         if (!(r = heap_alloc( sizeof(read_data_t) ))) return FALSE;
2835         r->hdr.request = request;
2836         r->hdr.proc    = task_read_data;
2837         r->buffer      = buffer;
2838         r->to_read     = to_read;
2839         r->read        = read;
2840 
2841         addref_object( &request->hdr );
2842         ret = queue_task( (task_header_t *)r );
2843     }
2844     else
2845         ret = read_data( request, buffer, to_read, read, FALSE );
2846 
2847     release_object( &request->hdr );
2848     if (ret) set_last_error( ERROR_SUCCESS );
2849     return ret;
2850 }
2851 
2852 static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDWORD written, BOOL async )
2853 {
2854     BOOL ret;
2855     int num_bytes;
2856 
2857     ret = netconn_send( request->netconn, buffer, to_write, &num_bytes );
2858 
2859     if (async)
2860     {
2861         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) );
2862         else
2863         {
2864             WINHTTP_ASYNC_RESULT result;
2865             result.dwResult = API_WRITE_DATA;
2866             result.dwError  = get_last_error();
2867             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2868         }
2869     }
2870     if (ret && written) *written = num_bytes;
2871     return ret;
2872 }
2873 
2874 static void task_write_data( task_header_t *task )
2875 {
2876     write_data_t *w = (write_data_t *)task;
2877     write_data( w->hdr.request, w->buffer, w->to_write, w->written, TRUE );
2878 }
2879 
2880 /***********************************************************************
2881  *          WinHttpWriteData (winhttp.@)
2882  */
2883 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written )
2884 {
2885     BOOL ret;
2886     request_t *request;
2887 
2888     TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written);
2889 
2890     if (!(request = (request_t *)grab_object( hrequest )))
2891     {
2892         set_last_error( ERROR_INVALID_HANDLE );
2893         return FALSE;
2894     }
2895     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2896     {
2897         release_object( &request->hdr );
2898         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2899         return FALSE;
2900     }
2901 
2902     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2903     {
2904         write_data_t *w;
2905 
2906         if (!(w = heap_alloc( sizeof(write_data_t) ))) return FALSE;
2907         w->hdr.request = request;
2908         w->hdr.proc    = task_write_data;
2909         w->buffer      = buffer;
2910         w->to_write    = to_write;
2911         w->written     = written;
2912 
2913         addref_object( &request->hdr );
2914         ret = queue_task( (task_header_t *)w );
2915     }
2916     else
2917         ret = write_data( request, buffer, to_write, written, FALSE );
2918 
2919     release_object( &request->hdr );
2920     if (ret) set_last_error( ERROR_SUCCESS );
2921     return ret;
2922 }
2923 
2924 enum request_state
2925 {
2926     REQUEST_STATE_UNINITIALIZED,
2927     REQUEST_STATE_INITIALIZED,
2928     REQUEST_STATE_CANCELLED,
2929     REQUEST_STATE_OPEN,
2930     REQUEST_STATE_SENT,
2931     REQUEST_STATE_RESPONSE_RECEIVED
2932 };
2933 
2934 struct winhttp_request
2935 {
2936     IWinHttpRequest IWinHttpRequest_iface;
2937     LONG refs;
2938     CRITICAL_SECTION cs;
2939     enum request_state state;
2940     HINTERNET hsession;
2941     HINTERNET hconnect;
2942     HINTERNET hrequest;
2943     VARIANT data;
2944     WCHAR *verb;
2945     HANDLE thread;
2946     HANDLE wait;
2947     HANDLE cancel;
2948     char *buffer;
2949     DWORD offset;
2950     DWORD bytes_available;
2951     DWORD bytes_read;
2952     DWORD error;
2953     DWORD logon_policy;
2954     DWORD disable_feature;
2955     LONG resolve_timeout;
2956     LONG connect_timeout;
2957     LONG send_timeout;
2958     LONG receive_timeout;
2959     WINHTTP_PROXY_INFO proxy;
2960     BOOL async;
2961     UINT url_codepage;
2962 };
2963 
2964 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
2965 {
2966     return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
2967 }
2968 
2969 static ULONG WINAPI winhttp_request_AddRef(
2970     IWinHttpRequest *iface )
2971 {
2972     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2973     return InterlockedIncrement( &request->refs );
2974 }
2975 
2976 /* critical section must be held */
2977 static void cancel_request( struct winhttp_request *request )
2978 {
2979     if (request->state <= REQUEST_STATE_CANCELLED) return;
2980 
2981     SetEvent( request->cancel );
2982     LeaveCriticalSection( &request->cs );
2983     WaitForSingleObject( request->thread, INFINITE );
2984     EnterCriticalSection( &request->cs );
2985 
2986     request->state = REQUEST_STATE_CANCELLED;
2987 
2988     CloseHandle( request->thread );
2989     request->thread = NULL;
2990     CloseHandle( request->wait );
2991     request->wait = NULL;
2992     CloseHandle( request->cancel );
2993     request->cancel = NULL;
2994 }
2995 
2996 /* critical section must be held */
2997 static void free_request( struct winhttp_request *request )
2998 {
2999     if (request->state < REQUEST_STATE_INITIALIZED) return;
3000     WinHttpCloseHandle( request->hrequest );
3001     WinHttpCloseHandle( request->hconnect );
3002     WinHttpCloseHandle( request->hsession );
3003     CloseHandle( request->thread );
3004     CloseHandle( request->wait );
3005     CloseHandle( request->cancel );
3006     heap_free( (WCHAR *)request->proxy.lpszProxy );
3007     heap_free( (WCHAR *)request->proxy.lpszProxyBypass );
3008     heap_free( request->buffer );
3009     heap_free( request->verb );
3010     VariantClear( &request->data );
3011 }
3012 
3013 static ULONG WINAPI winhttp_request_Release(
3014     IWinHttpRequest *iface )
3015 {
3016     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3017     LONG refs = InterlockedDecrement( &request->refs );
3018     if (!refs)
3019     {
3020         TRACE("destroying %p\n", request);
3021 
3022         EnterCriticalSection( &request->cs );
3023         cancel_request( request );
3024         free_request( request );
3025         LeaveCriticalSection( &request->cs );
3026         request->cs.DebugInfo->Spare[0] = 0;
3027         DeleteCriticalSection( &request->cs );
3028         heap_free( request );
3029     }
3030     return refs;
3031 }
3032 
3033 static HRESULT WINAPI winhttp_request_QueryInterface(
3034     IWinHttpRequest *iface,
3035     REFIID riid,
3036     void **obj )
3037 {
3038     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3039 
3040     TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
3041 
3042     if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
3043         IsEqualGUID( riid, &IID_IDispatch ) ||
3044         IsEqualGUID( riid, &IID_IUnknown ))
3045     {
3046         *obj = iface;
3047     }
3048     else
3049     {
3050         FIXME("interface %s not implemented\n", debugstr_guid(riid));
3051         return E_NOINTERFACE;
3052     }
3053     IWinHttpRequest_AddRef( iface );
3054     return S_OK;
3055 }
3056 
3057 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
3058     IWinHttpRequest *iface,
3059     UINT *count )
3060 {
3061     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3062 
3063     TRACE("%p, %p\n", request, count);
3064     *count = 1;
3065     return S_OK;
3066 }
3067 
3068 enum type_id
3069 {
3070     IWinHttpRequest_tid,
3071     last_tid
3072 };
3073 
3074 static ITypeLib *winhttp_typelib;
3075 static ITypeInfo *winhttp_typeinfo[last_tid];
3076 
3077 static REFIID winhttp_tid_id[] =
3078 {
3079     &IID_IWinHttpRequest
3080 };
3081 
3082 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
3083 {
3084     HRESULT hr;
3085 
3086     if (!winhttp_typelib)
3087     {
3088         ITypeLib *typelib;
3089 
3090         hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
3091         if (FAILED(hr))
3092         {
3093             ERR("LoadRegTypeLib failed: %08x\n", hr);
3094             return hr;
3095         }
3096         if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
3097             ITypeLib_Release( typelib );
3098     }
3099     if (!winhttp_typeinfo[tid])
3100     {
3101         ITypeInfo *typeinfo;
3102 
3103         hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
3104         if (FAILED(hr))
3105         {
3106             ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(winhttp_tid_id[tid]), hr);
3107             return hr;
3108         }
3109         if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
3110             ITypeInfo_Release( typeinfo );
3111     }
3112     *ret = winhttp_typeinfo[tid];
3113     ITypeInfo_AddRef(winhttp_typeinfo[tid]);
3114     return S_OK;
3115 }
3116 
3117 void release_typelib(void)
3118 {
3119     unsigned i;
3120 
3121     for (i = 0; i < sizeof(winhttp_typeinfo)/sizeof(*winhttp_typeinfo); i++)
3122         if (winhttp_typeinfo[i])
3123             ITypeInfo_Release(winhttp_typeinfo[i]);
3124 
3125     if (winhttp_typelib)
3126         ITypeLib_Release(winhttp_typelib);
3127 }
3128 
3129 static HRESULT WINAPI winhttp_request_GetTypeInfo(
3130     IWinHttpRequest *iface,
3131     UINT index,
3132     LCID lcid,
3133     ITypeInfo **info )
3134 {
3135     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3136     TRACE("%p, %u, %u, %p\n", request, index, lcid, info);
3137 
3138     return get_typeinfo( IWinHttpRequest_tid, info );
3139 }
3140 
3141 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
3142     IWinHttpRequest *iface,
3143     REFIID riid,
3144     LPOLESTR *names,
3145     UINT count,
3146     LCID lcid,
3147     DISPID *dispid )
3148 {
3149     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3150     ITypeInfo *typeinfo;
3151     HRESULT hr;
3152 
3153     TRACE("%p, %s, %p, %u, %u, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid);
3154 
3155     if (!names || !count || !dispid) return E_INVALIDARG;
3156 
3157     hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
3158     if (SUCCEEDED(hr))
3159     {
3160         hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
3161         ITypeInfo_Release( typeinfo );
3162     }
3163     return hr;
3164 }
3165 
3166 static HRESULT WINAPI winhttp_request_Invoke(
3167     IWinHttpRequest *iface,
3168     DISPID member,
3169     REFIID riid,
3170     LCID lcid,
3171     WORD flags,
3172     DISPPARAMS *params,
3173     VARIANT *result,
3174     EXCEPINFO *excep_info,
3175     UINT *arg_err )
3176 {
3177     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3178     ITypeInfo *typeinfo;
3179     HRESULT hr;
3180 
3181     TRACE("%p, %d, %s, %d, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
3182           lcid, flags, params, result, excep_info, arg_err);
3183 
3184     if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
3185 
3186     if (member == DISPID_HTTPREQUEST_OPTION)
3187     {
3188         VARIANT ret_value, option;
3189         UINT err_pos;
3190 
3191         if (!result) result = &ret_value;
3192         if (!arg_err) arg_err = &err_pos;
3193 
3194         VariantInit( &option );
3195         VariantInit( result );
3196 
3197         if (!flags) return S_OK;
3198 
3199         if (flags == DISPATCH_PROPERTYPUT)
3200         {
3201             hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
3202             if (FAILED(hr)) return hr;
3203 
3204             hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
3205             if (FAILED(hr))
3206                 WARN("put_Option(%d) failed: %x\n", V_I4( &option ), hr);
3207             return hr;
3208         }
3209         else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
3210         {
3211             hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
3212             if (FAILED(hr)) return hr;
3213 
3214             hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
3215             if (FAILED(hr))
3216                 WARN("get_Option(%d) failed: %x\n", V_I4( &option ), hr);
3217             return hr;
3218         }
3219 
3220         FIXME("unsupported flags %x\n", flags);
3221         return E_NOTIMPL;
3222     }
3223 
3224     /* fallback to standard implementation */
3225 
3226     hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
3227     if (SUCCEEDED(hr))
3228     {
3229         hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
3230                                params, result, excep_info, arg_err );
3231         ITypeInfo_Release( typeinfo );
3232     }
3233     return hr;
3234 }
3235 
3236 static HRESULT WINAPI winhttp_request_SetProxy(
3237     IWinHttpRequest *iface,
3238     HTTPREQUEST_PROXY_SETTING proxy_setting,
3239     VARIANT proxy_server,
3240     VARIANT bypass_list )
3241 {
3242     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3243     DWORD err = ERROR_SUCCESS;
3244 
3245     TRACE("%p, %u, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
3246           debugstr_variant(&bypass_list));
3247 
3248     EnterCriticalSection( &request->cs );
3249     switch (proxy_setting)
3250     {
3251     case HTTPREQUEST_PROXYSETTING_DEFAULT:
3252         request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
3253         heap_free( (WCHAR *)request->proxy.lpszProxy );
3254         heap_free( (WCHAR *)request->proxy.lpszProxyBypass );
3255         request->proxy.lpszProxy = NULL;
3256         request->proxy.lpszProxyBypass = NULL;
3257         break;
3258 
3259     case HTTPREQUEST_PROXYSETTING_DIRECT:
3260         request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
3261         heap_free( (WCHAR *)request->proxy.lpszProxy );
3262         heap_free( (WCHAR *)request->proxy.lpszProxyBypass );
3263         request->proxy.lpszProxy = NULL;
3264         request->proxy.lpszProxyBypass = NULL;
3265         break;
3266 
3267     case HTTPREQUEST_PROXYSETTING_PROXY:
3268         request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
3269         if (V_VT( &proxy_server ) == VT_BSTR)
3270         {
3271             heap_free( (WCHAR *)request->proxy.lpszProxy );
3272             request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) );
3273         }
3274         if (V_VT( &bypass_list ) == VT_BSTR)
3275         {
3276             heap_free( (WCHAR *)request->proxy.lpszProxyBypass );
3277             request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) );
3278         }
3279         break;
3280 
3281     default:
3282         err = ERROR_INVALID_PARAMETER;
3283         break;
3284     }
3285     LeaveCriticalSection( &request->cs );
3286     return HRESULT_FROM_WIN32( err );
3287 }
3288 
3289 static HRESULT WINAPI winhttp_request_SetCredentials(
3290     IWinHttpRequest *iface,
3291     BSTR username,
3292     BSTR password,
3293     HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
3294 {
3295     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3296     DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
3297     DWORD err = ERROR_SUCCESS;
3298 
3299     TRACE("%p, %s, %p, 0x%08x\n", request, debugstr_w(username), password, flags);
3300 
3301     EnterCriticalSection( &request->cs );
3302     if (request->state < REQUEST_STATE_OPEN)
3303     {
3304         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
3305         goto done;
3306     }
3307     switch (flags)
3308     {
3309     case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
3310         target = WINHTTP_AUTH_TARGET_SERVER;
3311         break;
3312     case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
3313         target = WINHTTP_AUTH_TARGET_PROXY;
3314         break;
3315     default:
3316         err = ERROR_INVALID_PARAMETER;
3317         goto done;
3318     }
3319     if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
3320     {
3321         err = get_last_error();
3322     }
3323 done:
3324     LeaveCriticalSection( &request->cs );
3325     return HRESULT_FROM_WIN32( err );
3326 }
3327 
3328 static void initialize_request( struct winhttp_request *request )
3329 {
3330     request->hrequest = NULL;
3331     request->hconnect = NULL;
3332     request->hsession = NULL;
3333     request->thread   = NULL;
3334     request->wait     = NULL;
3335     request->cancel   = NULL;
3336     request->buffer   = NULL;
3337     request->verb     = NULL;
3338     request->offset = 0;
3339     request->bytes_available = 0;
3340     request->bytes_read = 0;
3341     request->error = ERROR_SUCCESS;
3342     request->async = FALSE;
3343     request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
3344     request->disable_feature = 0;
3345     request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
3346     request->proxy.lpszProxy = NULL;
3347     request->proxy.lpszProxyBypass = NULL;
3348     request->resolve_timeout = 0;
3349     request->connect_timeout = 60000;
3350     request->send_timeout    = 30000;
3351     request->receive_timeout = 30000;
3352     request->url_codepage = CP_UTF8;
3353     VariantInit( &request->data );
3354     request->state = REQUEST_STATE_INITIALIZED;
3355 }
3356 
3357 static void reset_request( struct winhttp_request *request )
3358 {
3359     cancel_request( request );
3360     WinHttpCloseHandle( request->hrequest );
3361     request->hrequest = NULL;
3362     WinHttpCloseHandle( request->hconnect );
3363     request->hconnect = NULL;
3364     heap_free( request->buffer );
3365     request->buffer   = NULL;
3366     heap_free( request->verb );
3367     request->verb     = NULL;
3368     request->offset   = 0;
3369     request->bytes_available = 0;
3370     request->bytes_read = 0;
3371     request->error    = ERROR_SUCCESS;
3372     request->async    = FALSE;
3373     request->url_codepage = CP_UTF8;
3374     VariantClear( &request->data );
3375     request->state = REQUEST_STATE_INITIALIZED;
3376 }
3377 
3378 static HRESULT WINAPI winhttp_request_Open(
3379     IWinHttpRequest *iface,
3380     BSTR method,
3381     BSTR url,
3382     VARIANT async )
3383 {
3384     static const WCHAR typeW[] = {'*','/','*',0};
3385     static const WCHAR *acceptW[] = {typeW, NULL};
3386     static const WCHAR httpsW[] = {'h','t','t','p','s'};
3387     static const WCHAR user_agentW[] = {
3388         'M','o','z','i','l','l','a','/','4','.','0',' ','(','c','o','m','p','a','t','i','b','l','e',';',' ',
3389         'W','i','n','3','2',';',' ','W','i','n','H','t','t','p','.','W','i','n','H','t','t','p',
3390         'R','e','q','u','e','s','t','.','5',')',0};
3391     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3392     URL_COMPONENTS uc;
3393     WCHAR *hostname, *path = NULL, *verb = NULL;
3394     DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
3395 
3396     TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
3397           debugstr_variant(&async));
3398 
3399     if (!method || !url) return E_INVALIDARG;
3400 
3401     memset( &uc, 0, sizeof(uc) );
3402     uc.dwStructSize = sizeof(uc);
3403     uc.dwSchemeLength   = ~0u;
3404     uc.dwHostNameLength = ~0u;
3405     uc.dwUrlPathLength  = ~0u;
3406     uc.dwExtraInfoLength = ~0u;
3407     if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( get_last_error() );
3408 
3409     EnterCriticalSection( &request->cs );
3410     if (request->state < REQUEST_STATE_INITIALIZED) initialize_request( request );
3411     else reset_request( request );
3412 
3413     if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
3414     memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
3415     hostname[uc.dwHostNameLength] = 0;
3416 
3417     if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
3418     memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
3419     path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
3420 
3421     if (!(verb = strdupW( method ))) goto error;
3422     if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
3423     else request->async = FALSE;
3424 
3425     if (!request->hsession)
3426     {
3427         if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
3428                                                WINHTTP_FLAG_ASYNC )))
3429         {
3430             err = get_last_error();
3431             goto error;
3432         }
3433         if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
3434         {
3435             WinHttpCloseHandle( request->hsession );
3436             request->hsession = NULL;
3437             err = get_last_error();
3438             goto error;
3439         }
3440     }
3441     else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
3442     {
3443         err = get_last_error();
3444         goto error;
3445     }
3446 
3447     len = sizeof(httpsW) / sizeof(WCHAR);
3448     if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
3449     {
3450         flags |= WINHTTP_FLAG_SECURE;
3451     }
3452     if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
3453     {
3454         err = get_last_error();
3455         goto error;
3456     }
3457     WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
3458 
3459     request->state = REQUEST_STATE_OPEN;
3460     request->verb = verb;
3461     heap_free( hostname );
3462     heap_free( path );
3463     LeaveCriticalSection( &request->cs );
3464     return S_OK;
3465 
3466 error:
3467     WinHttpCloseHandle( request->hconnect );
3468     request->hconnect = NULL;
3469     heap_free( hostname );
3470     heap_free( path );
3471     heap_free( verb );
3472     LeaveCriticalSection( &request->cs );
3473     return HRESULT_FROM_WIN32( err );
3474 }
3475 
3476 static HRESULT WINAPI winhttp_request_SetRequestHeader(
3477     IWinHttpRequest *iface,
3478     BSTR header,
3479     BSTR value )
3480 {
3481     static const WCHAR fmtW[] = {'%','s',':',' ','%','s','\r','\n',0};
3482     static const WCHAR emptyW[] = {0};
3483     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3484     DWORD len, err = ERROR_SUCCESS;
3485     WCHAR *str;
3486 
3487     TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
3488 
3489     if (!header) return E_INVALIDARG;
3490 
3491     EnterCriticalSection( &request->cs );
3492     if (request->state < REQUEST_STATE_OPEN)
3493     {
3494         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
3495         goto done;
3496     }
3497     if (request->state >= REQUEST_STATE_SENT)
3498     {
3499         err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
3500         goto done;
3501     }
3502     len = strlenW( header ) + 4;
3503     if (value) len += strlenW( value );
3504     if (!(str = heap_alloc( (len + 1) * sizeof(WCHAR) )))
3505     {
3506         err = ERROR_OUTOFMEMORY;
3507         goto done;
3508     }
3509     sprintfW( str, fmtW, header, value ? value : emptyW );
3510     if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
3511                                    WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
3512     {
3513         err = get_last_error();
3514     }
3515     heap_free( str );
3516 
3517 done:
3518     LeaveCriticalSection( &request->cs );
3519     return HRESULT_FROM_WIN32( err );
3520 }
3521 
3522 static HRESULT WINAPI winhttp_request_GetResponseHeader(
3523     IWinHttpRequest *iface,
3524     BSTR header,
3525     BSTR *value )
3526 {
3527     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3528     DWORD size, err = ERROR_SUCCESS;
3529 
3530     TRACE("%p, %p\n", request, header);
3531 
3532     EnterCriticalSection( &request->cs );
3533     if (request->state < REQUEST_STATE_SENT)
3534     {
3535         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3536         goto done;
3537     }
3538     if (!header || !value)
3539     {
3540         err = ERROR_INVALID_PARAMETER;
3541         goto done;
3542     }
3543     size = 0;
3544     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
3545     {
3546         err = get_last_error();
3547         if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3548     }
3549     if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
3550     {
3551         err = ERROR_OUTOFMEMORY;
3552         goto done;
3553     }
3554     err = ERROR_SUCCESS;
3555     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
3556     {
3557         err = get_last_error();
3558         SysFreeString( *value );
3559     }
3560 done:
3561     LeaveCriticalSection( &request->cs );
3562     return HRESULT_FROM_WIN32( err );
3563 }
3564 
3565 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
3566     IWinHttpRequest *iface,
3567     BSTR *headers )
3568 {
3569     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3570     DWORD size, err = ERROR_SUCCESS;
3571 
3572     TRACE("%p, %p\n", request, headers);
3573 
3574     if (!headers) return E_INVALIDARG;
3575 
3576     EnterCriticalSection( &request->cs );
3577     if (request->state < REQUEST_STATE_SENT)
3578     {
3579         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3580         goto done;
3581     }
3582     size = 0;
3583     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
3584     {
3585         err = get_last_error();
3586         if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3587     }
3588     if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
3589     {
3590         err = ERROR_OUTOFMEMORY;
3591         goto done;
3592     }
3593     err = ERROR_SUCCESS;
3594     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
3595     {
3596         err = get_last_error();
3597         SysFreeString( *headers );
3598     }
3599 done:
3600     LeaveCriticalSection( &request->cs );
3601     return HRESULT_FROM_WIN32( err );
3602 }
3603 
3604 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
3605 {
3606     struct winhttp_request *request = (struct winhttp_request *)context;
3607 
3608     switch (status)
3609     {
3610     case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
3611         request->bytes_available = *(DWORD *)buffer;
3612         request->error = ERROR_SUCCESS;
3613         break;
3614     case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
3615         request->bytes_read = size;
3616         request->error = ERROR_SUCCESS;
3617         break;
3618     case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
3619     {
3620         WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
3621         request->error = result->dwError;
3622         break;
3623     }
3624     default: break;
3625     }
3626     SetEvent( request->wait );
3627 }
3628 
3629 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
3630 {
3631     status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
3632     WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
3633 }
3634 
3635 static DWORD wait_for_completion( struct winhttp_request *request )
3636 {
3637     HANDLE handles[2] = { request->wait, request->cancel };
3638 
3639     switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
3640     {
3641     case WAIT_OBJECT_0:
3642         break;
3643     case WAIT_OBJECT_0 + 1:
3644         request->error = ERROR_CANCELLED;
3645         break;
3646     default:
3647         request->error = get_last_error();
3648         break;
3649     }
3650     return request->error;
3651 }
3652 
3653 static HRESULT request_receive( struct winhttp_request *request )
3654 {
3655     DWORD err, size, buflen = 4096;
3656 
3657     wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
3658     if (!WinHttpReceiveResponse( request->hrequest, NULL ))
3659     {
3660         return HRESULT_FROM_WIN32( get_last_error() );
3661     }
3662     if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
3663     if (!strcmpW( request->verb, headW ))
3664     {
3665         request->state = REQUEST_STATE_RESPONSE_RECEIVED;
3666         return S_OK;
3667     }
3668     if (!(request->buffer = heap_alloc( buflen ))) return E_OUTOFMEMORY;
3669     request->buffer[0] = 0;
3670     size = 0;
3671     do
3672     {
3673         wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
3674         if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
3675         {
3676             err = get_last_error();
3677             goto error;
3678         }
3679         if ((err = wait_for_completion( request ))) goto error;
3680         if (!request->bytes_available) break;
3681         size += request->bytes_available;
3682         if (buflen < size)
3683         {
3684             char *tmp;
3685             while (buflen < size) buflen *= 2;
3686             if (!(tmp = heap_realloc( request->buffer, buflen )))
3687             {
3688                 err = ERROR_OUTOFMEMORY;
3689                 goto error;
3690             }
3691             request->buffer = tmp;
3692         }
3693         wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
3694         if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
3695                               request->bytes_available, &request->bytes_read ))
3696         {
3697             err = get_last_error();
3698             goto error;
3699         }
3700         if ((err = wait_for_completion( request ))) goto error;
3701         request->offset += request->bytes_read;
3702     } while (request->bytes_read);
3703 
3704     request->state = REQUEST_STATE_RESPONSE_RECEIVED;
3705     return S_OK;
3706 
3707 error:
3708     heap_free( request->buffer );
3709     request->buffer = NULL;
3710     return HRESULT_FROM_WIN32( err );
3711 }
3712 
3713 static DWORD request_set_parameters( struct winhttp_request *request )
3714 {
3715     if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
3716                            sizeof(request->proxy) )) return get_last_error();
3717 
3718     if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
3719                            sizeof(request->logon_policy) )) return get_last_error();
3720 
3721     if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
3722                            sizeof(request->disable_feature) )) return get_last_error();
3723 
3724     if (!WinHttpSetTimeouts( request->hrequest,
3725                              request->resolve_timeout,
3726                              request->connect_timeout,
3727                              request->send_timeout,
3728                              request->receive_timeout )) return get_last_error();
3729     return ERROR_SUCCESS;
3730 }
3731 
3732 static void request_set_utf8_content_type( struct winhttp_request *request )
3733 {
3734     static const WCHAR fmtW[] = {'%','s',':',' ','%','s',0};
3735     static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n',0};
3736     static const WCHAR charset_utf8W[] = {'c','h','a','r','s','e','t','=','u','t','f','-','8',0};
3737     WCHAR headerW[64];
3738     int len;
3739 
3740     len = sprintfW( headerW, fmtW, attr_content_type, text_plainW );
3741     WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
3742     len = sprintfW( headerW, fmtW, attr_content_type, charset_utf8W );
3743     WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
3744 }
3745 
3746 static HRESULT request_send( struct winhttp_request *request )
3747 {
3748     SAFEARRAY *sa = NULL;
3749     VARIANT data;
3750     char *ptr = NULL;
3751     LONG size = 0;
3752     HRESULT hr;
3753     DWORD err;
3754 
3755     if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
3756     if (strcmpW( request->verb, getW ))
3757     {
3758         VariantInit( &data );
3759         if (V_VT( &request->data ) == VT_BSTR)
3760         {
3761             UINT cp = CP_ACP;
3762             const WCHAR *str = V_BSTR( &request->data );
3763             int i, len = strlenW( str );
3764 
3765             for (i = 0; i < len; i++)
3766             {
3767                 if (str[i] > 127)
3768                 {
3769                     cp = CP_UTF8;
3770                     break;
3771                 }
3772             }
3773             size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
3774             if (!(ptr = heap_alloc( size ))) return E_OUTOFMEMORY;
3775             WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
3776             if (cp == CP_UTF8) request_set_utf8_content_type( request );
3777         }
3778         else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
3779         {
3780             sa = V_ARRAY( &data );
3781             if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
3782             if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
3783             {
3784                 SafeArrayUnaccessData( sa );
3785                 return hr;
3786             }
3787             size++;
3788         }
3789     }
3790     wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT );
3791     if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
3792     {
3793         err = get_last_error();
3794         goto error;
3795     }
3796     if ((err = wait_for_completion( request ))) goto error;
3797     if (sa) SafeArrayUnaccessData( sa );
3798     else heap_free( ptr );
3799     request->state = REQUEST_STATE_SENT;
3800     return S_OK;
3801 
3802 error:
3803     if (sa) SafeArrayUnaccessData( sa );
3804     else heap_free( ptr );
3805     return HRESULT_FROM_WIN32( err );
3806 }
3807 
3808 static HRESULT request_send_and_receive( struct winhttp_request *request )
3809 {
3810     HRESULT hr = request_send( request );
3811     if (hr == S_OK) hr = request_receive( request );
3812     return hr;
3813 }
3814 
3815 static DWORD CALLBACK send_and_receive_proc( void *arg )
3816 {
3817     struct winhttp_request *request = (struct winhttp_request *)arg;
3818     return request_send_and_receive( request );
3819 }
3820 
3821 /* critical section must be held */
3822 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
3823 {
3824     HANDLE thread = request->thread;
3825     DWORD err, ret;
3826 
3827     LeaveCriticalSection( &request->cs );
3828     while ((err = MsgWaitForMultipleObjects( 1, &thread, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
3829     {
3830         MSG msg;
3831         while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
3832         {
3833             TranslateMessage( &msg );
3834             DispatchMessageW( &msg );
3835         }
3836     }
3837     switch (err)
3838     {
3839     case WAIT_OBJECT_0:
3840         ret = ERROR_SUCCESS;
3841         break;
3842     case WAIT_TIMEOUT:
3843         ret = ERROR_TIMEOUT;
3844         break;
3845     case WAIT_FAILED:
3846     default:
3847         ret = get_last_error();
3848         break;
3849     }
3850     EnterCriticalSection( &request->cs );
3851     return ret;
3852 }
3853 
3854 static HRESULT WINAPI winhttp_request_Send(
3855     IWinHttpRequest *iface,
3856     VARIANT body )
3857 {
3858     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3859     HRESULT hr;
3860 
3861     TRACE("%p, %s\n", request, debugstr_variant(&body));
3862 
3863     EnterCriticalSection( &request->cs );
3864     if (request->state < REQUEST_STATE_OPEN)
3865     {
3866         LeaveCriticalSection( &request->cs );
3867         return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
3868     }
3869     if (request->state >= REQUEST_STATE_SENT)
3870     {
3871         LeaveCriticalSection( &request->cs );
3872         return S_OK;
3873     }
3874     VariantClear( &request->data );
3875     if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
3876     {
3877         LeaveCriticalSection( &request->cs );
3878         return hr;
3879     }
3880     if (!(request->thread = CreateThread( NULL, 0, send_and_receive_proc, request, 0, NULL )))
3881     {
3882         LeaveCriticalSection( &request->cs );
3883         return HRESULT_FROM_WIN32( get_last_error() );
3884     }
3885     request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
3886     request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
3887     if (!request->async)
3888     {
3889         hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
3890     }
3891     LeaveCriticalSection( &request->cs );
3892     return hr;
3893 }
3894 
3895 static HRESULT WINAPI winhttp_request_get_Status(
3896     IWinHttpRequest *iface,
3897     LONG *status )
3898 {
3899     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3900     DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
3901 
3902     TRACE("%p, %p\n", request, status);
3903 
3904     if (!status) return E_INVALIDARG;
3905 
3906     EnterCriticalSection( &request->cs );
3907     if (request->state < REQUEST_STATE_SENT)
3908     {
3909         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3910         goto done;
3911     }
3912     flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
3913     if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
3914     {
3915         err = get_last_error();
3916         goto done;
3917     }
3918     *status = status_code;
3919 
3920 done:
3921     LeaveCriticalSection( &request->cs );
3922     return HRESULT_FROM_WIN32( err );
3923 }
3924 
3925 static HRESULT WINAPI winhttp_request_get_StatusText(
3926     IWinHttpRequest *iface,
3927     BSTR *status )
3928 {
3929     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3930     DWORD err = ERROR_SUCCESS, len = 0, index = 0;
3931 
3932     TRACE("%p, %p\n", request, status);
3933 
3934     if (!status) return E_INVALIDARG;
3935 
3936     EnterCriticalSection( &request->cs );
3937     if (request->state < REQUEST_STATE_SENT)
3938     {
3939         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3940         goto done;
3941     }
3942     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
3943     {
3944         err = get_last_error();
3945         if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3946     }
3947     if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
3948     {
3949         err = ERROR_OUTOFMEMORY;
3950         goto done;
3951     }
3952     index = 0;
3953     err = ERROR_SUCCESS;
3954     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
3955     {
3956         err = get_last_error();
3957         SysFreeString( *status );
3958     }
3959 done:
3960     LeaveCriticalSection( &request->cs );
3961     return HRESULT_FROM_WIN32( err );
3962 }
3963 
3964 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
3965 {
3966     static const WCHAR utf8W[] = {'u','t','f','-','8',0};
3967     static const WCHAR charsetW[] = {'c','h','a','r','s','e','t',0};
3968     WCHAR *buffer, *p;
3969     DWORD size;
3970 
3971     *codepage = CP_ACP;
3972     if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
3973         get_last_error() == ERROR_INSUFFICIENT_BUFFER)
3974     {
3975         if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY;
3976         if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
3977         {
3978             return get_last_error();
3979         }
3980         if ((p = strstrW( buffer, charsetW )))
3981         {
3982             p += strlenW( charsetW );
3983             while (*p == ' ') p++;
3984             if (*p++ == '=')
3985             {
3986                 while (*p == ' ') p++;
3987                 if (!strcmpiW( p, utf8W )) *codepage = CP_UTF8;
3988             }
3989         }
3990         heap_free( buffer );
3991     }
3992     return ERROR_SUCCESS;
3993 }
3994 
3995 static HRESULT WINAPI winhttp_request_get_ResponseText(
3996     IWinHttpRequest *iface,
3997     BSTR *body )
3998 {
3999     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4000     UINT codepage;
4001     DWORD err = ERROR_SUCCESS;
4002     int len;
4003 
4004     TRACE("%p, %p\n", request, body);
4005 
4006     if (!body) return E_INVALIDARG;
4007 
4008     EnterCriticalSection( &request->cs );
4009     if (request->state < REQUEST_STATE_SENT)
4010     {
4011         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4012         goto done;
4013     }
4014     if ((err = request_get_codepage( request, &codepage ))) goto done;
4015     len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
4016     if (!(*body = SysAllocStringLen( NULL, len )))
4017     {
4018         err = ERROR_OUTOFMEMORY;
4019         goto done;
4020     }
4021     MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
4022     (*body)[len] = 0;
4023 
4024 done:
4025     LeaveCriticalSection( &request->cs );
4026     return HRESULT_FROM_WIN32( err );
4027 }
4028 
4029 static HRESULT WINAPI winhttp_request_get_ResponseBody(
4030     IWinHttpRequest *iface,
4031     VARIANT *body )
4032 {
4033     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4034     SAFEARRAY *sa;
4035     HRESULT hr;
4036     DWORD err = ERROR_SUCCESS;
4037     char *ptr;
4038 
4039     TRACE("%p, %p\n", request, body);
4040 
4041     if (!body) return E_INVALIDARG;
4042 
4043     EnterCriticalSection( &request->cs );
4044     if (request->state < REQUEST_STATE_SENT)
4045     {
4046         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4047         goto done;
4048     }
4049     if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
4050     {
4051         err = ERROR_OUTOFMEMORY;
4052         goto done;
4053     }
4054     if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
4055     {
4056         SafeArrayDestroy( sa );
4057         LeaveCriticalSection( &request->cs );
4058         return hr;
4059     }
4060     memcpy( ptr, request->buffer, request->offset );
4061     if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
4062     {
4063         SafeArrayDestroy( sa );
4064         LeaveCriticalSection( &request->cs );
4065         return hr;
4066     }
4067     V_VT( body ) =  VT_ARRAY|VT_UI1;
4068     V_ARRAY( body ) = sa;
4069 
4070 done:
4071     LeaveCriticalSection( &request->cs );
4072     return HRESULT_FROM_WIN32( err );
4073 }
4074 
4075 struct stream
4076 {
4077     IStream IStream_iface;
4078     LONG    refs;
4079     char   *data;
4080     ULARGE_INTEGER pos, size;
4081 };
4082 
4083 static inline struct stream *impl_from_IStream( IStream *iface )
4084 {
4085     return CONTAINING_RECORD( iface, struct stream, IStream_iface );
4086 }
4087 
4088 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
4089 {
4090     struct stream *stream = impl_from_IStream( iface );
4091 
4092     TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
4093 
4094     if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
4095     {
4096         *obj = iface;
4097     }
4098     else
4099     {
4100         FIXME("interface %s not implemented\n", debugstr_guid(riid));
4101         return E_NOINTERFACE;
4102     }
4103     IStream_AddRef( iface );
4104     return S_OK;
4105 }
4106 
4107 static ULONG WINAPI stream_AddRef( IStream *iface )
4108 {
4109     struct stream *stream = impl_from_IStream( iface );
4110     return InterlockedIncrement( &stream->refs );
4111 }
4112 
4113 static ULONG WINAPI stream_Release( IStream *iface )
4114 {
4115     struct stream *stream = impl_from_IStream( iface );
4116     LONG refs = InterlockedDecrement( &stream->refs );
4117     if (!refs)
4118     {
4119         heap_free( stream->data );
4120         heap_free( stream );
4121     }
4122     return refs;
4123 }
4124 
4125 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
4126 {
4127     struct stream *stream = impl_from_IStream( iface );
4128     ULONG size;
4129 
4130     if (stream->pos.QuadPart >= stream->size.QuadPart)
4131     {
4132         *read = 0;
4133         return S_FALSE;
4134     }
4135 
4136     size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
4137     memcpy( buf, stream->data + stream->pos.QuadPart, size );
4138     stream->pos.QuadPart += size;
4139     *read = size;
4140 
4141     return S_OK;
4142 }
4143 
4144 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
4145 {
4146     FIXME("\n");
4147     return E_NOTIMPL;
4148 }
4149 
4150 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
4151 {
4152     struct stream *stream = impl_from_IStream( iface );
4153 
4154     if (origin == STREAM_SEEK_SET)
4155         stream->pos.QuadPart = move.QuadPart;
4156     else if (origin == STREAM_SEEK_CUR)
4157         stream->pos.QuadPart += move.QuadPart;
4158     else if (origin == STREAM_SEEK_END)
4159         stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
4160 
4161     if (newpos) newpos->QuadPart = stream->pos.QuadPart;
4162     return S_OK;
4163 }
4164 
4165 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
4166 {
4167     FIXME("\n");
4168     return E_NOTIMPL;
4169 }
4170 
4171 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
4172                                      ULARGE_INTEGER *written )
4173 {
4174     FIXME("\n");
4175     return E_NOTIMPL;
4176 }
4177 
4178 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
4179 {
4180     FIXME("\n");
4181     return E_NOTIMPL;
4182 }
4183 
4184 static HRESULT WINAPI stream_Revert( IStream *iface )
4185 {
4186     FIXME("\n");
4187     return E_NOTIMPL;
4188 }
4189 
4190 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
4191 {
4192     FIXME("\n");
4193     return E_NOTIMPL;
4194 }
4195 
4196 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
4197 {
4198     FIXME("\n");
4199     return E_NOTIMPL;
4200 }
4201 
4202 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
4203 {
4204     FIXME("\n");
4205     return E_NOTIMPL;
4206 }
4207 
4208 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
4209 {
4210     FIXME("\n");
4211     return E_NOTIMPL;
4212 }
4213 
4214 static const IStreamVtbl stream_vtbl =
4215 {
4216     stream_QueryInterface,
4217     stream_AddRef,
4218     stream_Release,
4219     stream_Read,
4220     stream_Write,
4221     stream_Seek,
4222     stream_SetSize,
4223     stream_CopyTo,
4224     stream_Commit,
4225     stream_Revert,
4226     stream_LockRegion,
4227     stream_UnlockRegion,
4228     stream_Stat,
4229     stream_Clone
4230 };
4231 
4232 static HRESULT WINAPI winhttp_request_get_ResponseStream(
4233     IWinHttpRequest *iface,
4234     VARIANT *body )
4235 {
4236     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4237     DWORD err = ERROR_SUCCESS;
4238     struct stream *stream;
4239 
4240     TRACE("%p, %p\n", request, body);
4241 
4242     if (!body) return E_INVALIDARG;
4243 
4244     EnterCriticalSection( &request->cs );
4245     if (request->state < REQUEST_STATE_SENT)
4246     {
4247         err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4248         goto done;
4249     }
4250     if (!(stream = heap_alloc( sizeof(*stream) )))
4251     {
4252         err = ERROR_OUTOFMEMORY;
4253         goto done;
4254     }
4255     stream->IStream_iface.lpVtbl = &stream_vtbl;
4256     stream->refs = 1;
4257     if (!(stream->data = heap_alloc( request->offset )))
4258     {
4259         heap_free( stream );
4260         err = ERROR_OUTOFMEMORY;
4261         goto done;
4262     }
4263     memcpy( stream->data, request->buffer, request->offset );
4264     stream->pos.QuadPart = 0;
4265     stream->size.QuadPart = request->offset;
4266     V_VT( body ) = VT_UNKNOWN;
4267     V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
4268 
4269 done:
4270     LeaveCriticalSection( &request->cs );
4271     return HRESULT_FROM_WIN32( err );
4272 }
4273 
4274 static HRESULT WINAPI winhttp_request_get_Option(
4275     IWinHttpRequest *iface,
4276     WinHttpRequestOption option,
4277     VARIANT *value )
4278 {
4279     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4280     HRESULT hr = S_OK;
4281 
4282     TRACE("%p, %u, %p\n", request, option, value);
4283 
4284     EnterCriticalSection( &request->cs );
4285     switch (option)
4286     {
4287     case WinHttpRequestOption_URLCodePage:
4288         V_VT( value ) = VT_I4;
4289         V_I4( value ) = request->url_codepage;
4290         break;
4291     default:
4292         FIXME("unimplemented option %u\n", option);
4293         hr = E_NOTIMPL;
4294         break;
4295     }
4296     LeaveCriticalSection( &request->cs );
4297     return hr;
4298 }
4299 
4300 static HRESULT WINAPI winhttp_request_put_Option(
4301     IWinHttpRequest *iface,
4302     WinHttpRequestOption option,
4303     VARIANT value )
4304 {
4305     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4306     HRESULT hr = S_OK;
4307 
4308     TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
4309 
4310     EnterCriticalSection( &request->cs );
4311     switch (option)
4312     {
4313     case WinHttpRequestOption_EnableRedirects:
4314     {
4315         if (V_BOOL( &value ))
4316             request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
4317         else
4318             request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
4319         break;
4320     }
4321     case WinHttpRequestOption_URLCodePage:
4322     {
4323         static const WCHAR utf8W[] = {'u','t','f','-','8',0};
4324         VARIANT cp;
4325 
4326         VariantInit( &cp );
4327         hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
4328         if (SUCCEEDED( hr ))
4329         {
4330             request->url_codepage = V_UI4( &cp );
4331             TRACE("URL codepage: %u\n", request->url_codepage);
4332         }
4333         else if (V_VT( &value ) == VT_BSTR && !strcmpiW( V_BSTR( &value ), utf8W ))
4334         {
4335             TRACE("URL codepage: UTF-8\n");
4336             request->url_codepage = CP_UTF8;
4337             hr = S_OK;
4338         }
4339         else
4340             FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
4341         break;
4342     }
4343     default:
4344         FIXME("unimplemented option %u\n", option);
4345         hr = E_NOTIMPL;
4346         break;
4347     }
4348     LeaveCriticalSection( &request->cs );
4349     return hr;
4350 }
4351 
4352 static HRESULT WINAPI winhttp_request_WaitForResponse(
4353     IWinHttpRequest *iface,
4354     VARIANT timeout,
4355     VARIANT_BOOL *succeeded )
4356 {
4357     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4358     DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
4359 
4360     TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
4361 
4362     EnterCriticalSection( &request->cs );
4363     if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
4364     {
4365         LeaveCriticalSection( &request->cs );
4366         return S_OK;
4367     }
4368     switch ((err = request_wait( request, msecs )))
4369     {
4370     case ERROR_TIMEOUT:
4371         if (succeeded) *succeeded = VARIANT_FALSE;
4372         err = ERROR_SUCCESS;
4373         break;
4374 
4375     case ERROR_SUCCESS:
4376         if (succeeded) *succeeded = VARIANT_TRUE;
4377         break;
4378 
4379     default: break;
4380     }
4381     LeaveCriticalSection( &request->cs );
4382     return HRESULT_FROM_WIN32( err );
4383 }
4384 
4385 static HRESULT WINAPI winhttp_request_Abort(
4386     IWinHttpRequest *iface )
4387 {
4388     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4389 
4390     TRACE("%p\n", request);
4391 
4392     EnterCriticalSection( &request->cs );
4393     cancel_request( request );
4394     LeaveCriticalSection( &request->cs );
4395     return S_OK;
4396 }
4397 
4398 static HRESULT WINAPI winhttp_request_SetTimeouts(
4399     IWinHttpRequest *iface,
4400     LONG resolve_timeout,
4401     LONG connect_timeout,
4402     LONG send_timeout,
4403     LONG receive_timeout )
4404 {
4405     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4406 
4407     TRACE("%p, %d, %d, %d, %d\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout);
4408 
4409     EnterCriticalSection( &request->cs );
4410     request->resolve_timeout = resolve_timeout;
4411     request->connect_timeout = connect_timeout;
4412     request->send_timeout    = send_timeout;
4413     request->receive_timeout = receive_timeout;
4414     LeaveCriticalSection( &request->cs );
4415     return S_OK;
4416 }
4417 
4418 static HRESULT WINAPI winhttp_request_SetClientCertificate(
4419     IWinHttpRequest *iface,
4420     BSTR certificate )
4421 {
4422     FIXME("\n");
4423     return E_NOTIMPL;
4424 }
4425 
4426 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
4427     IWinHttpRequest *iface,
4428     WinHttpRequestAutoLogonPolicy policy )
4429 {
4430     struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4431     HRESULT hr = S_OK;
4432 
4433     TRACE("%p, %u\n", request, policy );
4434 
4435     EnterCriticalSection( &request->cs );
4436     switch (policy)
4437     {
4438     case AutoLogonPolicy_Always:
4439         request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
4440         break;
4441     case AutoLogonPolicy_OnlyIfBypassProxy:
4442         request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
4443         break;
4444     case AutoLogonPolicy_Never:
4445         request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
4446         break;
4447     default: hr = E_INVALIDARG;
4448         break;
4449     }
4450     LeaveCriticalSection( &request->cs );
4451     return hr;
4452 }
4453 
4454 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
4455 {
4456     winhttp_request_QueryInterface,
4457     winhttp_request_AddRef,
4458     winhttp_request_Release,
4459     winhttp_request_GetTypeInfoCount,
4460     winhttp_request_GetTypeInfo,
4461     winhttp_request_GetIDsOfNames,
4462     winhttp_request_Invoke,
4463     winhttp_request_SetProxy,
4464     winhttp_request_SetCredentials,
4465     winhttp_request_Open,
4466     winhttp_request_SetRequestHeader,
4467     winhttp_request_GetResponseHeader,
4468     winhttp_request_GetAllResponseHeaders,
4469     winhttp_request_Send,
4470     winhttp_request_get_Status,
4471     winhttp_request_get_StatusText,
4472     winhttp_request_get_ResponseText,
4473     winhttp_request_get_ResponseBody,
4474     winhttp_request_get_ResponseStream,
4475     winhttp_request_get_Option,
4476     winhttp_request_put_Option,
4477     winhttp_request_WaitForResponse,
4478     winhttp_request_Abort,
4479     winhttp_request_SetTimeouts,
4480     winhttp_request_SetClientCertificate,
4481     winhttp_request_SetAutoLogonPolicy
4482 };
4483 
4484 HRESULT WinHttpRequest_create( void **obj )
4485 {
4486     struct winhttp_request *request;
4487 
4488     TRACE("%p\n", obj);
4489 
4490     if (!(request = heap_alloc( sizeof(*request) ))) return E_OUTOFMEMORY;
4491     request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
4492     request->refs = 1;
4493     request->state = REQUEST_STATE_UNINITIALIZED;
4494     request->proxy.lpszProxy = NULL;
4495     request->proxy.lpszProxyBypass = NULL;
4496     request->url_codepage = CP_UTF8;
4497     InitializeCriticalSection( &request->cs );
4498     request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
4499 
4500     *obj = &request->IWinHttpRequest_iface;
4501     TRACE("returning iface %p\n", *obj);
4502     return S_OK;
4503 }
4504