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 = ∈
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