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