qedit: Implement IAMTimelineObj_GetTimelineType and add tests.
[wine.git] / dlls / winhttp / request.c
blobb2b72c7ccb84bcdafa8b0a4f336924c5e21d2ac5
1 /*
2 * Copyright 2004 Mike McCormack for CodeWeavers
3 * Copyright 2006 Rob Shearman for CodeWeavers
4 * Copyright 2008, 2011 Hans Leidekker for CodeWeavers
5 * Copyright 2009 Juan Lang
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
23 #include "config.h"
24 #include "wine/port.h"
26 #include <stdarg.h>
27 #include <assert.h>
28 #ifdef HAVE_ARPA_INET_H
29 # include <arpa/inet.h>
30 #endif
32 #include "windef.h"
33 #include "winbase.h"
34 #include "ole2.h"
35 #include "initguid.h"
36 #include "httprequest.h"
37 #include "httprequestid.h"
38 #include "winhttp.h"
40 #include "winhttp_private.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
46 static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0};
47 static const WCHAR attr_accept_charset[] = {'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0};
48 static const WCHAR attr_accept_encoding[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0};
49 static const WCHAR attr_accept_language[] = {'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0};
50 static const WCHAR attr_accept_ranges[] = {'A','c','c','e','p','t','-','R','a','n','g','e','s',0};
51 static const WCHAR attr_age[] = {'A','g','e',0};
52 static const WCHAR attr_allow[] = {'A','l','l','o','w',0};
53 static const WCHAR attr_authorization[] = {'A','u','t','h','o','r','i','z','a','t','i','o','n',0};
54 static const WCHAR attr_cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',0};
55 static const WCHAR attr_connection[] = {'C','o','n','n','e','c','t','i','o','n',0};
56 static const WCHAR attr_content_base[] = {'C','o','n','t','e','n','t','-','B','a','s','e',0};
57 static const WCHAR attr_content_encoding[] = {'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0};
58 static const WCHAR attr_content_id[] = {'C','o','n','t','e','n','t','-','I','D',0};
59 static const WCHAR attr_content_language[] = {'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0};
60 static const WCHAR attr_content_length[] = {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
61 static const WCHAR attr_content_location[] = {'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0};
62 static const WCHAR attr_content_md5[] = {'C','o','n','t','e','n','t','-','M','D','5',0};
63 static const WCHAR attr_content_range[] = {'C','o','n','t','e','n','t','-','R','a','n','g','e',0};
64 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};
65 static const WCHAR attr_content_type[] = {'C','o','n','t','e','n','t','-','T','y','p','e',0};
66 static const WCHAR attr_cookie[] = {'C','o','o','k','i','e',0};
67 static const WCHAR attr_date[] = {'D','a','t','e',0};
68 static const WCHAR attr_from[] = {'F','r','o','m',0};
69 static const WCHAR attr_etag[] = {'E','T','a','g',0};
70 static const WCHAR attr_expect[] = {'E','x','p','e','c','t',0};
71 static const WCHAR attr_expires[] = {'E','x','p','i','r','e','s',0};
72 static const WCHAR attr_host[] = {'H','o','s','t',0};
73 static const WCHAR attr_if_match[] = {'I','f','-','M','a','t','c','h',0};
74 static const WCHAR attr_if_modified_since[] = {'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
75 static const WCHAR attr_if_none_match[] = {'I','f','-','N','o','n','e','-','M','a','t','c','h',0};
76 static const WCHAR attr_if_range[] = {'I','f','-','R','a','n','g','e',0};
77 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};
78 static const WCHAR attr_last_modified[] = {'L','a','s','t','-','M','o','d','i','f','i','e','d',0};
79 static const WCHAR attr_location[] = {'L','o','c','a','t','i','o','n',0};
80 static const WCHAR attr_max_forwards[] = {'M','a','x','-','F','o','r','w','a','r','d','s',0};
81 static const WCHAR attr_mime_version[] = {'M','i','m','e','-','V','e','r','s','i','o','n',0};
82 static const WCHAR attr_pragma[] = {'P','r','a','g','m','a',0};
83 static const WCHAR attr_proxy_authenticate[] = {'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0};
84 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};
85 static const WCHAR attr_proxy_connection[] = {'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0};
86 static const WCHAR attr_public[] = {'P','u','b','l','i','c',0};
87 static const WCHAR attr_range[] = {'R','a','n','g','e',0};
88 static const WCHAR attr_referer[] = {'R','e','f','e','r','e','r',0};
89 static const WCHAR attr_retry_after[] = {'R','e','t','r','y','-','A','f','t','e','r',0};
90 static const WCHAR attr_server[] = {'S','e','r','v','e','r',0};
91 static const WCHAR attr_set_cookie[] = {'S','e','t','-','C','o','o','k','i','e',0};
92 static const WCHAR attr_status[] = {'S','t','a','t','u','s',0};
93 static const WCHAR attr_transfer_encoding[] = {'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0};
94 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};
95 static const WCHAR attr_upgrade[] = {'U','p','g','r','a','d','e',0};
96 static const WCHAR attr_uri[] = {'U','R','I',0};
97 static const WCHAR attr_user_agent[] = {'U','s','e','r','-','A','g','e','n','t',0};
98 static const WCHAR attr_vary[] = {'V','a','r','y',0};
99 static const WCHAR attr_via[] = {'V','i','a',0};
100 static const WCHAR attr_warning[] = {'W','a','r','n','i','n','g',0};
101 static const WCHAR attr_www_authenticate[] = {'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0};
103 static const WCHAR *attribute_table[] =
105 attr_mime_version, /* WINHTTP_QUERY_MIME_VERSION = 0 */
106 attr_content_type, /* WINHTTP_QUERY_CONTENT_TYPE = 1 */
107 attr_content_transfer_encoding, /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
108 attr_content_id, /* WINHTTP_QUERY_CONTENT_ID = 3 */
109 NULL, /* WINHTTP_QUERY_CONTENT_DESCRIPTION = 4 */
110 attr_content_length, /* WINHTTP_QUERY_CONTENT_LENGTH = 5 */
111 attr_content_language, /* WINHTTP_QUERY_CONTENT_LANGUAGE = 6 */
112 attr_allow, /* WINHTTP_QUERY_ALLOW = 7 */
113 attr_public, /* WINHTTP_QUERY_PUBLIC = 8 */
114 attr_date, /* WINHTTP_QUERY_DATE = 9 */
115 attr_expires, /* WINHTTP_QUERY_EXPIRES = 10 */
116 attr_last_modified, /* WINHTTP_QUERY_LAST_MODIFIEDcw = 11 */
117 NULL, /* WINHTTP_QUERY_MESSAGE_ID = 12 */
118 attr_uri, /* WINHTTP_QUERY_URI = 13 */
119 attr_from, /* WINHTTP_QUERY_DERIVED_FROM = 14 */
120 NULL, /* WINHTTP_QUERY_COST = 15 */
121 NULL, /* WINHTTP_QUERY_LINK = 16 */
122 attr_pragma, /* WINHTTP_QUERY_PRAGMA = 17 */
123 NULL, /* WINHTTP_QUERY_VERSION = 18 */
124 attr_status, /* WINHTTP_QUERY_STATUS_CODE = 19 */
125 NULL, /* WINHTTP_QUERY_STATUS_TEXT = 20 */
126 NULL, /* WINHTTP_QUERY_RAW_HEADERS = 21 */
127 NULL, /* WINHTTP_QUERY_RAW_HEADERS_CRLF = 22 */
128 attr_connection, /* WINHTTP_QUERY_CONNECTION = 23 */
129 attr_accept, /* WINHTTP_QUERY_ACCEPT = 24 */
130 attr_accept_charset, /* WINHTTP_QUERY_ACCEPT_CHARSET = 25 */
131 attr_accept_encoding, /* WINHTTP_QUERY_ACCEPT_ENCODING = 26 */
132 attr_accept_language, /* WINHTTP_QUERY_ACCEPT_LANGUAGE = 27 */
133 attr_authorization, /* WINHTTP_QUERY_AUTHORIZATION = 28 */
134 attr_content_encoding, /* WINHTTP_QUERY_CONTENT_ENCODING = 29 */
135 NULL, /* WINHTTP_QUERY_FORWARDED = 30 */
136 NULL, /* WINHTTP_QUERY_FROM = 31 */
137 attr_if_modified_since, /* WINHTTP_QUERY_IF_MODIFIED_SINCE = 32 */
138 attr_location, /* WINHTTP_QUERY_LOCATION = 33 */
139 NULL, /* WINHTTP_QUERY_ORIG_URI = 34 */
140 attr_referer, /* WINHTTP_QUERY_REFERER = 35 */
141 attr_retry_after, /* WINHTTP_QUERY_RETRY_AFTER = 36 */
142 attr_server, /* WINHTTP_QUERY_SERVER = 37 */
143 NULL, /* WINHTTP_TITLE = 38 */
144 attr_user_agent, /* WINHTTP_QUERY_USER_AGENT = 39 */
145 attr_www_authenticate, /* WINHTTP_QUERY_WWW_AUTHENTICATE = 40 */
146 attr_proxy_authenticate, /* WINHTTP_QUERY_PROXY_AUTHENTICATE = 41 */
147 attr_accept_ranges, /* WINHTTP_QUERY_ACCEPT_RANGES = 42 */
148 attr_set_cookie, /* WINHTTP_QUERY_SET_COOKIE = 43 */
149 attr_cookie, /* WINHTTP_QUERY_COOKIE = 44 */
150 NULL, /* WINHTTP_QUERY_REQUEST_METHOD = 45 */
151 NULL, /* WINHTTP_QUERY_REFRESH = 46 */
152 NULL, /* WINHTTP_QUERY_CONTENT_DISPOSITION = 47 */
153 attr_age, /* WINHTTP_QUERY_AGE = 48 */
154 attr_cache_control, /* WINHTTP_QUERY_CACHE_CONTROL = 49 */
155 attr_content_base, /* WINHTTP_QUERY_CONTENT_BASE = 50 */
156 attr_content_location, /* WINHTTP_QUERY_CONTENT_LOCATION = 51 */
157 attr_content_md5, /* WINHTTP_QUERY_CONTENT_MD5 = 52 */
158 attr_content_range, /* WINHTTP_QUERY_CONTENT_RANGE = 53 */
159 attr_etag, /* WINHTTP_QUERY_ETAG = 54 */
160 attr_host, /* WINHTTP_QUERY_HOST = 55 */
161 attr_if_match, /* WINHTTP_QUERY_IF_MATCH = 56 */
162 attr_if_none_match, /* WINHTTP_QUERY_IF_NONE_MATCH = 57 */
163 attr_if_range, /* WINHTTP_QUERY_IF_RANGE = 58 */
164 attr_if_unmodified_since, /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
165 attr_max_forwards, /* WINHTTP_QUERY_MAX_FORWARDS = 60 */
166 attr_proxy_authorization, /* WINHTTP_QUERY_PROXY_AUTHORIZATION = 61 */
167 attr_range, /* WINHTTP_QUERY_RANGE = 62 */
168 attr_transfer_encoding, /* WINHTTP_QUERY_TRANSFER_ENCODING = 63 */
169 attr_upgrade, /* WINHTTP_QUERY_UPGRADE = 64 */
170 attr_vary, /* WINHTTP_QUERY_VARY = 65 */
171 attr_via, /* WINHTTP_QUERY_VIA = 66 */
172 attr_warning, /* WINHTTP_QUERY_WARNING = 67 */
173 attr_expect, /* WINHTTP_QUERY_EXPECT = 68 */
174 attr_proxy_connection, /* WINHTTP_QUERY_PROXY_CONNECTION = 69 */
175 attr_unless_modified_since, /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
176 NULL, /* WINHTTP_QUERY_PROXY_SUPPORT = 75 */
177 NULL, /* WINHTTP_QUERY_AUTHENTICATION_INFO = 76 */
178 NULL, /* WINHTTP_QUERY_PASSPORT_URLS = 77 */
179 NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
182 static task_header_t *dequeue_task( request_t *request )
184 task_header_t *task;
186 EnterCriticalSection( &request->task_cs );
187 TRACE("%u tasks queued\n", list_count( &request->task_queue ));
188 task = LIST_ENTRY( list_head( &request->task_queue ), task_header_t, entry );
189 if (task) list_remove( &task->entry );
190 LeaveCriticalSection( &request->task_cs );
192 TRACE("returning task %p\n", task);
193 return task;
196 static DWORD CALLBACK task_proc( LPVOID param )
198 request_t *request = param;
199 HANDLE handles[2];
201 handles[0] = request->task_wait;
202 handles[1] = request->task_cancel;
203 for (;;)
205 DWORD err = WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
206 switch (err)
208 case WAIT_OBJECT_0:
210 task_header_t *task;
211 while ((task = dequeue_task( request )))
213 task->proc( task );
214 release_object( &task->request->hdr );
215 heap_free( task );
217 break;
219 case WAIT_OBJECT_0 + 1:
220 TRACE("exiting\n");
221 CloseHandle( request->task_cancel );
222 CloseHandle( request->task_wait );
223 request->task_cs.DebugInfo->Spare[0] = 0;
224 DeleteCriticalSection( &request->task_cs );
225 request->hdr.vtbl->destroy( &request->hdr );
226 return 0;
228 default:
229 ERR("wait failed %u (%u)\n", err, GetLastError());
230 break;
233 return 0;
236 static BOOL queue_task( task_header_t *task )
238 request_t *request = task->request;
240 if (!request->task_thread)
242 if (!(request->task_wait = CreateEventW( NULL, FALSE, FALSE, NULL ))) return FALSE;
243 if (!(request->task_cancel = CreateEventW( NULL, FALSE, FALSE, NULL )))
245 CloseHandle( request->task_wait );
246 request->task_wait = NULL;
247 return FALSE;
249 if (!(request->task_thread = CreateThread( NULL, 0, task_proc, request, 0, NULL )))
251 CloseHandle( request->task_wait );
252 request->task_wait = NULL;
253 CloseHandle( request->task_cancel );
254 request->task_cancel = NULL;
255 return FALSE;
257 InitializeCriticalSection( &request->task_cs );
258 request->task_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": request.task_cs");
261 EnterCriticalSection( &request->task_cs );
262 TRACE("queueing task %p\n", task );
263 list_add_tail( &request->task_queue, &task->entry );
264 LeaveCriticalSection( &request->task_cs );
266 SetEvent( request->task_wait );
267 return TRUE;
270 static void free_header( header_t *header )
272 heap_free( header->field );
273 heap_free( header->value );
274 heap_free( header );
277 static BOOL valid_token_char( WCHAR c )
279 if (c < 32 || c == 127) return FALSE;
280 switch (c)
282 case '(': case ')':
283 case '<': case '>':
284 case '@': case ',':
285 case ';': case ':':
286 case '\\': case '\"':
287 case '/': case '[':
288 case ']': case '?':
289 case '=': case '{':
290 case '}': case ' ':
291 case '\t':
292 return FALSE;
293 default:
294 return TRUE;
298 static header_t *parse_header( LPCWSTR string )
300 const WCHAR *p, *q;
301 header_t *header;
302 int len;
304 p = string;
305 if (!(q = strchrW( p, ':' )))
307 WARN("no ':' in line %s\n", debugstr_w(string));
308 return NULL;
310 if (q == string)
312 WARN("empty field name in line %s\n", debugstr_w(string));
313 return NULL;
315 while (*p != ':')
317 if (!valid_token_char( *p ))
319 WARN("invalid character in field name %s\n", debugstr_w(string));
320 return NULL;
322 p++;
324 len = q - string;
325 if (!(header = heap_alloc_zero( sizeof(header_t) ))) return NULL;
326 if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) )))
328 heap_free( header );
329 return NULL;
331 memcpy( header->field, string, len * sizeof(WCHAR) );
332 header->field[len] = 0;
334 q++; /* skip past colon */
335 while (*q == ' ') q++;
336 len = strlenW( q );
338 if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
340 free_header( header );
341 return NULL;
343 memcpy( header->value, q, len * sizeof(WCHAR) );
344 header->value[len] = 0;
346 return header;
349 static int get_header_index( request_t *request, LPCWSTR field, int requested_index, BOOL request_only )
351 int index;
353 TRACE("%s\n", debugstr_w(field));
355 for (index = 0; index < request->num_headers; index++)
357 if (strcmpiW( request->headers[index].field, field )) continue;
358 if (request_only && !request->headers[index].is_request) continue;
359 if (!request_only && request->headers[index].is_request) continue;
361 if (!requested_index) break;
362 requested_index--;
364 if (index >= request->num_headers) index = -1;
365 TRACE("returning %d\n", index);
366 return index;
369 static BOOL insert_header( request_t *request, header_t *header )
371 DWORD count;
372 header_t *hdrs;
374 count = request->num_headers + 1;
375 if (count > 1)
376 hdrs = heap_realloc_zero( request->headers, sizeof(header_t) * count );
377 else
378 hdrs = heap_alloc_zero( sizeof(header_t) * count );
380 if (hdrs)
382 request->headers = hdrs;
383 request->headers[count - 1].field = strdupW( header->field );
384 request->headers[count - 1].value = strdupW( header->value );
385 request->headers[count - 1].is_request = header->is_request;
386 request->num_headers++;
387 return TRUE;
389 return FALSE;
392 static BOOL delete_header( request_t *request, DWORD index )
394 if (!request->num_headers) return FALSE;
395 if (index >= request->num_headers) return FALSE;
396 request->num_headers--;
398 heap_free( request->headers[index].field );
399 heap_free( request->headers[index].value );
401 memmove( &request->headers[index], &request->headers[index + 1], (request->num_headers - index) * sizeof(header_t) );
402 memset( &request->headers[request->num_headers], 0, sizeof(header_t) );
403 return TRUE;
406 static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only )
408 int index;
409 header_t hdr;
411 TRACE("%s: %s 0x%08x\n", debugstr_w(field), debugstr_w(value), flags);
413 if ((index = get_header_index( request, field, 0, request_only )) >= 0)
415 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return FALSE;
418 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
420 if (index >= 0)
422 delete_header( request, index );
423 if (!value || !value[0]) return TRUE;
425 else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD))
427 set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND );
428 return FALSE;
431 hdr.field = (LPWSTR)field;
432 hdr.value = (LPWSTR)value;
433 hdr.is_request = request_only;
434 return insert_header( request, &hdr );
436 else if (value)
439 if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
440 index >= 0)
442 WCHAR *tmp;
443 int len, len_orig, len_value;
444 header_t *header = &request->headers[index];
446 len_orig = strlenW( header->value );
447 len_value = strlenW( value );
449 len = len_orig + len_value + 2;
450 if (!(tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return FALSE;
451 header->value = tmp;
452 header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
453 header->value[len_orig++] = ' ';
455 memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
456 header->value[len] = 0;
457 return TRUE;
459 else
461 hdr.field = (LPWSTR)field;
462 hdr.value = (LPWSTR)value;
463 hdr.is_request = request_only;
464 return insert_header( request, &hdr );
468 return TRUE;
471 BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags )
473 BOOL ret = FALSE;
474 WCHAR *buffer, *p, *q;
475 header_t *header;
477 if (len == ~0u) len = strlenW( headers );
478 if (!len) return TRUE;
479 if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
480 memcpy( buffer, headers, len * sizeof(WCHAR) );
481 buffer[len] = 0;
483 p = buffer;
486 q = p;
487 while (*q)
489 if (q[0] == '\n' && q[1] == '\r')
491 q[0] = '\r';
492 q[1] = '\n';
494 if (q[0] == '\r' && q[1] == '\n') break;
495 q++;
497 if (!*p) break;
498 if (*q == '\r')
500 *q = 0;
501 q += 2; /* jump over \r\n */
503 if ((header = parse_header( p )))
505 ret = process_header( request, header->field, header->value, flags, TRUE );
506 free_header( header );
508 p = q;
509 } while (ret);
511 heap_free( buffer );
512 return ret;
515 /***********************************************************************
516 * WinHttpAddRequestHeaders (winhttp.@)
518 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags )
520 BOOL ret;
521 request_t *request;
523 TRACE("%p, %s, 0x%x, 0x%08x\n", hrequest, debugstr_w(headers), len, flags);
525 if (!headers || !len)
527 set_last_error( ERROR_INVALID_PARAMETER );
528 return FALSE;
530 if (!(request = (request_t *)grab_object( hrequest )))
532 set_last_error( ERROR_INVALID_HANDLE );
533 return FALSE;
535 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
537 release_object( &request->hdr );
538 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
539 return FALSE;
542 ret = add_request_headers( request, headers, len, flags );
544 release_object( &request->hdr );
545 if (ret) set_last_error( ERROR_SUCCESS );
546 return ret;
549 static WCHAR *build_request_path( request_t *request )
551 WCHAR *ret;
553 if (strcmpiW( request->connect->hostname, request->connect->servername ))
555 static const WCHAR http[] = { 'h','t','t','p',0 };
556 static const WCHAR https[] = { 'h','t','t','p','s',0 };
557 static const WCHAR fmt[] = { '%','s',':','/','/','%','s',0 };
558 LPCWSTR scheme = request->netconn.secure ? https : http;
559 int len;
561 len = strlenW( scheme ) + strlenW( request->connect->hostname );
562 /* 3 characters for '://', 1 for NUL. */
563 len += 4;
564 if (request->connect->hostport)
566 /* 1 for ':' between host and port, up to 5 for port */
567 len += 6;
569 if (request->path)
570 len += strlenW( request->path );
571 if ((ret = heap_alloc( len * sizeof(WCHAR) )))
573 sprintfW( ret, fmt, scheme, request->connect->hostname );
574 if (request->connect->hostport)
576 static const WCHAR colonFmt[] = { ':','%','u',0 };
578 sprintfW( ret + strlenW( ret ), colonFmt,
579 request->connect->hostport );
581 if (request->path)
582 strcatW( ret, request->path );
585 else
586 ret = request->path;
587 return ret;
590 static WCHAR *build_request_string( request_t *request )
592 static const WCHAR space[] = {' ',0};
593 static const WCHAR crlf[] = {'\r','\n',0};
594 static const WCHAR colon[] = {':',' ',0};
595 static const WCHAR twocrlf[] = {'\r','\n','\r','\n',0};
597 WCHAR *path, *ret;
598 const WCHAR **headers, **p;
599 unsigned int len, i = 0, j;
601 /* allocate space for an array of all the string pointers to be added */
602 len = request->num_headers * 4 + 7;
603 if (!(headers = heap_alloc( len * sizeof(LPCWSTR) ))) return NULL;
605 path = build_request_path( request );
606 headers[i++] = request->verb;
607 headers[i++] = space;
608 headers[i++] = path;
609 headers[i++] = space;
610 headers[i++] = request->version;
612 for (j = 0; j < request->num_headers; j++)
614 if (request->headers[j].is_request)
616 headers[i++] = crlf;
617 headers[i++] = request->headers[j].field;
618 headers[i++] = colon;
619 headers[i++] = request->headers[j].value;
621 TRACE("adding header %s (%s)\n", debugstr_w(request->headers[j].field),
622 debugstr_w(request->headers[j].value));
625 headers[i++] = twocrlf;
626 headers[i] = NULL;
628 len = 0;
629 for (p = headers; *p; p++) len += strlenW( *p );
630 len++;
632 if (!(ret = heap_alloc( len * sizeof(WCHAR) )))
633 goto out;
634 *ret = 0;
635 for (p = headers; *p; p++) strcatW( ret, *p );
637 out:
638 if (path != request->path)
639 heap_free( path );
640 heap_free( headers );
641 return ret;
644 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
646 static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
648 header_t *header = NULL;
649 BOOL request_only, ret = FALSE;
650 int requested_index, header_index = -1;
651 DWORD attr, len;
653 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
654 requested_index = index ? *index : 0;
656 attr = level & ~QUERY_MODIFIER_MASK;
657 switch (attr)
659 case WINHTTP_QUERY_CUSTOM:
661 header_index = get_header_index( request, name, requested_index, request_only );
662 break;
664 case WINHTTP_QUERY_RAW_HEADERS:
666 WCHAR *headers, *p, *q;
668 if (request_only)
669 headers = build_request_string( request );
670 else
671 headers = request->raw_headers;
673 if (!(p = headers)) return FALSE;
674 for (len = 0; *p; p++) if (*p != '\r') len++;
676 if (!buffer || len * sizeof(WCHAR) > *buflen)
677 set_last_error( ERROR_INSUFFICIENT_BUFFER );
678 else
680 for (p = headers, q = buffer; *p; p++, q++)
682 if (*p != '\r') *q = *p;
683 else
685 *q = 0;
686 p++; /* skip '\n' */
689 TRACE("returning data: %s\n", debugstr_wn(buffer, len));
690 if (len) len--;
691 ret = TRUE;
693 *buflen = len * sizeof(WCHAR);
694 if (request_only) heap_free( headers );
695 return ret;
697 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
699 WCHAR *headers;
701 if (request_only)
702 headers = build_request_string( request );
703 else
704 headers = request->raw_headers;
706 if (!headers) return FALSE;
707 len = strlenW( headers ) * sizeof(WCHAR);
708 if (!buffer || len + sizeof(WCHAR) > *buflen)
710 len += sizeof(WCHAR);
711 set_last_error( ERROR_INSUFFICIENT_BUFFER );
713 else
715 memcpy( buffer, headers, len + sizeof(WCHAR) );
716 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
717 ret = TRUE;
719 *buflen = len;
720 if (request_only) heap_free( headers );
721 return ret;
723 case WINHTTP_QUERY_VERSION:
724 len = strlenW( request->version ) * sizeof(WCHAR);
725 if (!buffer || len + sizeof(WCHAR) > *buflen)
727 len += sizeof(WCHAR);
728 set_last_error( ERROR_INSUFFICIENT_BUFFER );
730 else
732 strcpyW( buffer, request->version );
733 TRACE("returning string: %s\n", debugstr_w(buffer));
734 ret = TRUE;
736 *buflen = len;
737 return ret;
739 case WINHTTP_QUERY_STATUS_TEXT:
740 len = strlenW( request->status_text ) * sizeof(WCHAR);
741 if (!buffer || len + sizeof(WCHAR) > *buflen)
743 len += sizeof(WCHAR);
744 set_last_error( ERROR_INSUFFICIENT_BUFFER );
746 else
748 strcpyW( buffer, request->status_text );
749 TRACE("returning string: %s\n", debugstr_w(buffer));
750 ret = TRUE;
752 *buflen = len;
753 return ret;
755 default:
756 if (attr >= sizeof(attribute_table)/sizeof(attribute_table[0]) || !attribute_table[attr])
758 FIXME("attribute %u not implemented\n", attr);
759 return FALSE;
761 TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
762 header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
763 break;
766 if (header_index >= 0)
768 header = &request->headers[header_index];
770 if (!header || (request_only && !header->is_request))
772 set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND );
773 return FALSE;
775 if (index) *index += 1;
776 if (level & WINHTTP_QUERY_FLAG_NUMBER)
778 if (!buffer || sizeof(int) > *buflen)
780 set_last_error( ERROR_INSUFFICIENT_BUFFER );
782 else
784 int *number = buffer;
785 *number = atoiW( header->value );
786 TRACE("returning number: %d\n", *number);
787 ret = TRUE;
789 *buflen = sizeof(int);
791 else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
793 SYSTEMTIME *st = buffer;
794 if (!buffer || sizeof(SYSTEMTIME) > *buflen)
796 set_last_error( ERROR_INSUFFICIENT_BUFFER );
798 else if ((ret = WinHttpTimeToSystemTime( header->value, st )))
800 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
801 st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
802 st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
804 *buflen = sizeof(SYSTEMTIME);
806 else if (header->value)
808 len = strlenW( header->value ) * sizeof(WCHAR);
809 if (!buffer || len + sizeof(WCHAR) > *buflen)
811 len += sizeof(WCHAR);
812 set_last_error( ERROR_INSUFFICIENT_BUFFER );
814 else
816 strcpyW( buffer, header->value );
817 TRACE("returning string: %s\n", debugstr_w(buffer));
818 ret = TRUE;
820 *buflen = len;
822 return ret;
825 /***********************************************************************
826 * WinHttpQueryHeaders (winhttp.@)
828 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
830 BOOL ret;
831 request_t *request;
833 TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index);
835 if (!(request = (request_t *)grab_object( hrequest )))
837 set_last_error( ERROR_INVALID_HANDLE );
838 return FALSE;
840 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
842 release_object( &request->hdr );
843 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
844 return FALSE;
847 ret = query_headers( request, level, name, buffer, buflen, index );
849 release_object( &request->hdr );
850 if (ret) set_last_error( ERROR_SUCCESS );
851 return ret;
854 static LPWSTR concatenate_string_list( LPCWSTR *list, int len )
856 LPCWSTR *t;
857 LPWSTR str;
859 for( t = list; *t ; t++ )
860 len += strlenW( *t );
861 len++;
863 str = heap_alloc( len * sizeof(WCHAR) );
864 if (!str) return NULL;
865 *str = 0;
867 for( t = list; *t ; t++ )
868 strcatW( str, *t );
870 return str;
873 static LPWSTR build_header_request_string( request_t *request, LPCWSTR verb,
874 LPCWSTR path, LPCWSTR version )
876 static const WCHAR crlf[] = {'\r','\n',0};
877 static const WCHAR space[] = { ' ',0 };
878 static const WCHAR colon[] = { ':',' ',0 };
879 static const WCHAR twocrlf[] = {'\r','\n','\r','\n', 0};
880 LPWSTR requestString;
881 DWORD len, n;
882 LPCWSTR *req;
883 UINT i;
884 LPWSTR p;
886 /* allocate space for an array of all the string pointers to be added */
887 len = (request->num_headers) * 4 + 10;
888 req = heap_alloc( len * sizeof(LPCWSTR) );
889 if (!req) return NULL;
891 /* add the verb, path and HTTP version string */
892 n = 0;
893 req[n++] = verb;
894 req[n++] = space;
895 req[n++] = path;
896 req[n++] = space;
897 req[n++] = version;
899 /* Append custom request headers */
900 for (i = 0; i < request->num_headers; i++)
902 if (request->headers[i].is_request)
904 req[n++] = crlf;
905 req[n++] = request->headers[i].field;
906 req[n++] = colon;
907 req[n++] = request->headers[i].value;
909 TRACE("Adding custom header %s (%s)\n",
910 debugstr_w(request->headers[i].field),
911 debugstr_w(request->headers[i].value));
915 if( n >= len )
916 ERR("oops. buffer overrun\n");
918 req[n] = NULL;
919 requestString = concatenate_string_list( req, 4 );
920 heap_free( req );
921 if (!requestString) return NULL;
924 * Set (header) termination string for request
925 * Make sure there are exactly two new lines at the end of the request
927 p = &requestString[strlenW(requestString)-1];
928 while ( (*p == '\n') || (*p == '\r') )
929 p--;
930 strcpyW( p+1, twocrlf );
932 return requestString;
935 static BOOL read_reply( request_t *request );
937 static BOOL secure_proxy_connect( request_t *request )
939 static const WCHAR verbConnect[] = {'C','O','N','N','E','C','T',0};
940 static const WCHAR fmt[] = {'%','s',':','%','u',0};
941 BOOL ret = FALSE;
942 LPWSTR path;
943 connect_t *connect = request->connect;
945 path = heap_alloc( (strlenW( connect->hostname ) + 13) * sizeof(WCHAR) );
946 if (path)
948 LPWSTR requestString;
950 sprintfW( path, fmt, connect->hostname, connect->hostport );
951 requestString = build_header_request_string( request, verbConnect,
952 path, http1_1 );
953 heap_free( path );
954 if (requestString)
956 LPSTR req_ascii = strdupWA( requestString );
958 heap_free( requestString );
959 if (req_ascii)
961 int len = strlen( req_ascii ), bytes_sent;
963 ret = netconn_send( &request->netconn, req_ascii, len, &bytes_sent );
964 heap_free( req_ascii );
965 if (ret)
966 ret = read_reply( request );
970 return ret;
973 #ifndef INET6_ADDRSTRLEN
974 #define INET6_ADDRSTRLEN 46
975 #endif
977 static WCHAR *addr_to_str( struct sockaddr *addr )
979 char buf[INET6_ADDRSTRLEN];
980 void *src;
982 switch (addr->sa_family)
984 case AF_INET:
985 src = &((struct sockaddr_in *)addr)->sin_addr;
986 break;
987 case AF_INET6:
988 src = &((struct sockaddr_in6 *)addr)->sin6_addr;
989 break;
990 default:
991 WARN("unsupported address family %d\n", addr->sa_family);
992 return NULL;
994 if (!inet_ntop( addr->sa_family, src, buf, sizeof(buf) )) return NULL;
995 return strdupAW( buf );
998 static BOOL open_connection( request_t *request )
1000 connect_t *connect;
1001 WCHAR *addressW = NULL;
1002 INTERNET_PORT port;
1003 socklen_t slen;
1004 struct sockaddr *saddr;
1005 DWORD len;
1007 if (netconn_connected( &request->netconn )) goto done;
1009 connect = request->connect;
1010 port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1011 saddr = (struct sockaddr *)&connect->sockaddr;
1012 slen = sizeof(struct sockaddr);
1014 if (!connect->resolved)
1016 len = strlenW( connect->servername ) + 1;
1017 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, len );
1019 if (!netconn_resolve( connect->servername, port, saddr, &slen, request->resolve_timeout )) return FALSE;
1020 connect->resolved = TRUE;
1022 if (!(addressW = addr_to_str( saddr ))) return FALSE;
1023 len = strlenW( addressW ) + 1;
1024 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1026 if (!addressW && !(addressW = addr_to_str( saddr ))) return FALSE;
1027 TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1029 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
1031 if (!netconn_create( &request->netconn, saddr->sa_family, SOCK_STREAM, 0 ))
1033 heap_free( addressW );
1034 return FALSE;
1036 netconn_set_timeout( &request->netconn, TRUE, request->send_timeout );
1037 netconn_set_timeout( &request->netconn, FALSE, request->recv_timeout );
1038 if (!netconn_connect( &request->netconn, saddr, slen, request->connect_timeout ))
1040 netconn_close( &request->netconn );
1041 heap_free( addressW );
1042 return FALSE;
1044 if (request->hdr.flags & WINHTTP_FLAG_SECURE)
1046 if (connect->session->proxy_server &&
1047 strcmpiW( connect->hostname, connect->servername ))
1049 if (!secure_proxy_connect( request ))
1051 heap_free( addressW );
1052 return FALSE;
1055 if (!netconn_secure_connect( &request->netconn, connect->hostname ))
1057 netconn_close( &request->netconn );
1058 heap_free( addressW );
1059 return FALSE;
1063 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 );
1065 done:
1066 request->read_pos = request->read_size = 0;
1067 request->read_chunked = FALSE;
1068 request->read_chunked_size = ~0u;
1069 request->read_chunked_eof = FALSE;
1070 heap_free( addressW );
1071 return TRUE;
1074 void close_connection( request_t *request )
1076 if (!netconn_connected( &request->netconn )) return;
1078 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1079 netconn_close( &request->netconn );
1080 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1083 static BOOL add_host_header( request_t *request, DWORD modifier )
1085 BOOL ret;
1086 DWORD len;
1087 WCHAR *host;
1088 static const WCHAR fmt[] = {'%','s',':','%','u',0};
1089 connect_t *connect = request->connect;
1090 INTERNET_PORT port;
1092 port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1094 if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1096 return process_header( request, attr_host, connect->hostname, modifier, TRUE );
1098 len = strlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1099 if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
1100 sprintfW( host, fmt, connect->hostname, port );
1101 ret = process_header( request, attr_host, host, modifier, TRUE );
1102 heap_free( host );
1103 return ret;
1106 static void clear_response_headers( request_t *request )
1108 unsigned int i;
1110 for (i = 0; i < request->num_headers; i++)
1112 if (!request->headers[i].field) continue;
1113 if (!request->headers[i].value) continue;
1114 if (request->headers[i].is_request) continue;
1115 delete_header( request, i );
1116 i--;
1120 static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
1121 DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
1123 static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0};
1124 static const WCHAR no_cache[] = {'n','o','-','c','a','c','h','e',0};
1125 static const WCHAR length_fmt[] = {'%','l','d',0};
1127 BOOL ret = FALSE;
1128 connect_t *connect = request->connect;
1129 session_t *session = connect->session;
1130 WCHAR *req = NULL;
1131 char *req_ascii;
1132 int bytes_sent;
1133 DWORD len, i, flags;
1135 clear_response_headers( request );
1137 flags = WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA;
1138 for (i = 0; i < request->num_accept_types; i++)
1140 process_header( request, attr_accept, request->accept_types[i], flags, TRUE );
1142 if (session->agent)
1143 process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
1145 if (connect->hostname)
1146 add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
1148 if (total_len || (request->verb && !strcmpW( request->verb, postW )))
1150 WCHAR length[21]; /* decimal long int + null */
1151 sprintfW( length, length_fmt, total_len );
1152 process_header( request, attr_content_length, length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
1154 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
1156 process_header( request, attr_connection, keep_alive, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
1158 if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
1160 process_header( request, attr_pragma, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
1161 process_header( request, attr_cache_control, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
1163 if (headers && !add_request_headers( request, headers, headers_len, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
1165 TRACE("failed to add request headers\n");
1166 return FALSE;
1168 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request ))
1170 WARN("failed to add cookie headers\n");
1171 return FALSE;
1174 if (context) request->hdr.context = context;
1176 if (!(ret = open_connection( request ))) goto end;
1177 if (!(req = build_request_string( request ))) goto end;
1179 if (!(req_ascii = strdupWA( req ))) goto end;
1180 TRACE("full request: %s\n", debugstr_a(req_ascii));
1181 len = strlen(req_ascii);
1183 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
1185 ret = netconn_send( &request->netconn, req_ascii, len, &bytes_sent );
1186 heap_free( req_ascii );
1187 if (!ret) goto end;
1189 if (optional_len)
1191 if (!netconn_send( &request->netconn, optional, optional_len, &bytes_sent )) goto end;
1192 request->optional = optional;
1193 request->optional_len = optional_len;
1194 len += optional_len;
1196 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
1198 end:
1199 if (async)
1201 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
1202 else
1204 WINHTTP_ASYNC_RESULT result;
1205 result.dwResult = API_SEND_REQUEST;
1206 result.dwError = get_last_error();
1207 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
1210 heap_free( req );
1211 return ret;
1214 static void task_send_request( task_header_t *task )
1216 send_request_t *s = (send_request_t *)task;
1217 send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
1218 heap_free( s->headers );
1221 /***********************************************************************
1222 * WinHttpSendRequest (winhttp.@)
1224 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len,
1225 LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
1227 BOOL ret;
1228 request_t *request;
1230 TRACE("%p, %s, 0x%x, %u, %u, %lx\n",
1231 hrequest, debugstr_w(headers), headers_len, optional_len, total_len, context);
1233 if (!(request = (request_t *)grab_object( hrequest )))
1235 set_last_error( ERROR_INVALID_HANDLE );
1236 return FALSE;
1238 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
1240 release_object( &request->hdr );
1241 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1242 return FALSE;
1245 if (headers && !headers_len) headers_len = strlenW( headers );
1247 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
1249 send_request_t *s;
1251 if (!(s = heap_alloc( sizeof(send_request_t) ))) return FALSE;
1252 s->hdr.request = request;
1253 s->hdr.proc = task_send_request;
1254 s->headers = strdupW( headers );
1255 s->headers_len = headers_len;
1256 s->optional = optional;
1257 s->optional_len = optional_len;
1258 s->total_len = total_len;
1259 s->context = context;
1261 addref_object( &request->hdr );
1262 ret = queue_task( (task_header_t *)s );
1264 else
1265 ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
1267 release_object( &request->hdr );
1268 if (ret) set_last_error( ERROR_SUCCESS );
1269 return ret;
1272 #define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0]))
1274 static const WCHAR basicW[] = {'B','a','s','i','c',0};
1275 static const WCHAR ntlmW[] = {'N','T','L','M',0};
1276 static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t',0};
1277 static const WCHAR digestW[] = {'D','i','g','e','s','t',0};
1278 static const WCHAR negotiateW[] = {'N','e','g','o','t','i','a','t','e',0};
1280 static const struct
1282 const WCHAR *str;
1283 unsigned int len;
1284 DWORD scheme;
1286 auth_schemes[] =
1288 { basicW, ARRAYSIZE(basicW) - 1, WINHTTP_AUTH_SCHEME_BASIC },
1289 { ntlmW, ARRAYSIZE(ntlmW) - 1, WINHTTP_AUTH_SCHEME_NTLM },
1290 { passportW, ARRAYSIZE(passportW) - 1, WINHTTP_AUTH_SCHEME_PASSPORT },
1291 { digestW, ARRAYSIZE(digestW) - 1, WINHTTP_AUTH_SCHEME_DIGEST },
1292 { negotiateW, ARRAYSIZE(negotiateW) - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
1294 static const unsigned int num_auth_schemes = sizeof(auth_schemes)/sizeof(auth_schemes[0]);
1296 static enum auth_scheme scheme_from_flag( DWORD flag )
1298 int i;
1300 for (i = 0; i < num_auth_schemes; i++) if (flag == auth_schemes[i].scheme) return i;
1301 return SCHEME_INVALID;
1304 static DWORD auth_scheme_from_header( WCHAR *header )
1306 unsigned int i;
1308 for (i = 0; i < num_auth_schemes; i++)
1310 if (!strncmpiW( header, auth_schemes[i].str, auth_schemes[i].len ) &&
1311 (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
1313 return 0;
1316 static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD supported, LPDWORD first )
1318 DWORD index = 0;
1319 BOOL ret = FALSE;
1321 for (;;)
1323 WCHAR *buffer;
1324 DWORD size, scheme;
1326 size = 0;
1327 query_headers( request, level, NULL, NULL, &size, &index );
1328 if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) break;
1330 index--;
1331 if (!(buffer = heap_alloc( size ))) return FALSE;
1332 if (!query_headers( request, level, NULL, buffer, &size, &index ))
1334 heap_free( buffer );
1335 return FALSE;
1337 scheme = auth_scheme_from_header( buffer );
1338 heap_free( buffer );
1339 if (!scheme) break;
1341 if (first && index == 1)
1342 *first = *supported = scheme;
1343 else
1344 *supported |= scheme;
1346 ret = TRUE;
1348 return ret;
1351 /***********************************************************************
1352 * WinHttpQueryAuthSchemes (winhttp.@)
1354 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
1356 BOOL ret = FALSE;
1357 request_t *request;
1359 TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
1361 if (!(request = (request_t *)grab_object( hrequest )))
1363 set_last_error( ERROR_INVALID_HANDLE );
1364 return FALSE;
1366 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
1368 release_object( &request->hdr );
1369 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1370 return FALSE;
1372 if (!supported || !first || !target)
1374 release_object( &request->hdr );
1375 set_last_error( ERROR_INVALID_PARAMETER );
1376 return FALSE;
1380 if (query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first ))
1382 *target = WINHTTP_AUTH_TARGET_SERVER;
1383 ret = TRUE;
1385 else if (query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first ))
1387 *target = WINHTTP_AUTH_TARGET_PROXY;
1388 ret = TRUE;
1391 release_object( &request->hdr );
1392 if (ret) set_last_error( ERROR_SUCCESS );
1393 return ret;
1396 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
1398 UINT n = 0, x;
1399 static const char base64enc[] =
1400 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1402 while (len > 0)
1404 /* first 6 bits, all from bin[0] */
1405 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
1406 x = (bin[0] & 3) << 4;
1408 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1409 if (len == 1)
1411 base64[n++] = base64enc[x];
1412 base64[n++] = '=';
1413 base64[n++] = '=';
1414 break;
1416 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
1417 x = (bin[1] & 0x0f) << 2;
1419 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1420 if (len == 2)
1422 base64[n++] = base64enc[x];
1423 base64[n++] = '=';
1424 break;
1426 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
1428 /* last 6 bits, all from bin [2] */
1429 base64[n++] = base64enc[bin[2] & 0x3f];
1430 bin += 3;
1431 len -= 3;
1433 base64[n] = 0;
1434 return n;
1437 static inline char decode_char( WCHAR c )
1439 if (c >= 'A' && c <= 'Z') return c - 'A';
1440 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
1441 if (c >= '0' && c <= '9') return c - '0' + 52;
1442 if (c == '+') return 62;
1443 if (c == '/') return 63;
1444 return 64;
1447 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
1449 unsigned int i = 0;
1450 char c0, c1, c2, c3;
1451 const WCHAR *p = base64;
1453 while (len > 4)
1455 if ((c0 = decode_char( p[0] )) > 63) return 0;
1456 if ((c1 = decode_char( p[1] )) > 63) return 0;
1457 if ((c2 = decode_char( p[2] )) > 63) return 0;
1458 if ((c3 = decode_char( p[3] )) > 63) return 0;
1460 if (buf)
1462 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1463 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1464 buf[i + 2] = (c2 << 6) | c3;
1466 len -= 4;
1467 i += 3;
1468 p += 4;
1470 if (p[2] == '=')
1472 if ((c0 = decode_char( p[0] )) > 63) return 0;
1473 if ((c1 = decode_char( p[1] )) > 63) return 0;
1475 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
1476 i++;
1478 else if (p[3] == '=')
1480 if ((c0 = decode_char( p[0] )) > 63) return 0;
1481 if ((c1 = decode_char( p[1] )) > 63) return 0;
1482 if ((c2 = decode_char( p[2] )) > 63) return 0;
1484 if (buf)
1486 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1487 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1489 i += 2;
1491 else
1493 if ((c0 = decode_char( p[0] )) > 63) return 0;
1494 if ((c1 = decode_char( p[1] )) > 63) return 0;
1495 if ((c2 = decode_char( p[2] )) > 63) return 0;
1496 if ((c3 = decode_char( p[3] )) > 63) return 0;
1498 if (buf)
1500 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1501 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1502 buf[i + 2] = (c2 << 6) | c3;
1504 i += 3;
1506 return i;
1509 static struct authinfo *alloc_authinfo(void)
1511 struct authinfo *ret;
1513 if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
1515 SecInvalidateHandle( &ret->cred );
1516 SecInvalidateHandle( &ret->ctx );
1517 memset( &ret->exp, 0, sizeof(ret->exp) );
1518 ret->scheme = 0;
1519 ret->attr = 0;
1520 ret->max_token = 0;
1521 ret->data = NULL;
1522 ret->data_len = 0;
1523 ret->finished = FALSE;
1524 return ret;
1527 void destroy_authinfo( struct authinfo *authinfo )
1529 if (!authinfo) return;
1531 if (SecIsValidHandle( &authinfo->ctx ))
1532 DeleteSecurityContext( &authinfo->ctx );
1533 if (SecIsValidHandle( &authinfo->cred ))
1534 FreeCredentialsHandle( &authinfo->cred );
1536 heap_free( authinfo->data );
1537 heap_free( authinfo );
1540 static BOOL get_authvalue( request_t *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
1542 DWORD size, index = 0;
1543 for (;;)
1545 size = len;
1546 if (!query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
1547 if (auth_scheme_from_header( buffer ) == scheme) break;
1549 return TRUE;
1552 static BOOL do_authorization( request_t *request, DWORD target, DWORD scheme_flag )
1554 struct authinfo *authinfo, **auth_ptr;
1555 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1556 const WCHAR *auth_target, *username, *password;
1557 WCHAR auth_value[2048], *auth_reply;
1558 DWORD len = sizeof(auth_value), len_scheme, flags;
1559 BOOL ret;
1561 if (scheme == SCHEME_INVALID) return FALSE;
1563 switch (target)
1565 case WINHTTP_AUTH_TARGET_SERVER:
1566 if (!get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len ))
1567 return FALSE;
1568 auth_ptr = &request->authinfo;
1569 auth_target = attr_authorization;
1570 if (request->creds[TARGET_SERVER][scheme].username)
1572 username = request->creds[TARGET_SERVER][scheme].username;
1573 password = request->creds[TARGET_SERVER][scheme].password;
1575 else
1577 username = request->connect->username;
1578 password = request->connect->password;
1580 break;
1582 case WINHTTP_AUTH_TARGET_PROXY:
1583 if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1584 return FALSE;
1585 auth_ptr = &request->proxy_authinfo;
1586 auth_target = attr_proxy_authorization;
1587 if (request->creds[TARGET_PROXY][scheme].username)
1589 username = request->creds[TARGET_PROXY][scheme].username;
1590 password = request->creds[TARGET_PROXY][scheme].password;
1592 else
1594 username = request->connect->session->proxy_username;
1595 password = request->connect->session->proxy_password;
1597 break;
1599 default:
1600 WARN("unknown target %x\n", target);
1601 return FALSE;
1603 authinfo = *auth_ptr;
1605 switch (scheme)
1607 case SCHEME_BASIC:
1609 int userlen, passlen;
1611 if (!username || !password) return FALSE;
1612 if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1614 userlen = WideCharToMultiByte( CP_UTF8, 0, username, strlenW( username ), NULL, 0, NULL, NULL );
1615 passlen = WideCharToMultiByte( CP_UTF8, 0, password, strlenW( password ), NULL, 0, NULL, NULL );
1617 authinfo->data_len = userlen + 1 + passlen;
1618 if (!(authinfo->data = heap_alloc( authinfo->data_len ))) return FALSE;
1620 WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1621 authinfo->data[userlen] = ':';
1622 WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1624 authinfo->scheme = SCHEME_BASIC;
1625 authinfo->finished = TRUE;
1626 break;
1628 case SCHEME_NTLM:
1629 case SCHEME_NEGOTIATE:
1631 SECURITY_STATUS status;
1632 SecBufferDesc out_desc, in_desc;
1633 SecBuffer out, in;
1634 ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1635 const WCHAR *p;
1636 BOOL first = FALSE;
1638 if (!authinfo)
1640 TimeStamp exp;
1641 SEC_WINNT_AUTH_IDENTITY_W id;
1642 WCHAR *domain, *user;
1644 if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1646 first = TRUE;
1647 domain = (WCHAR *)username;
1648 user = strchrW( username, '\\' );
1650 if (user) user++;
1651 else
1653 user = (WCHAR *)username;
1654 domain = NULL;
1656 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1657 id.User = user;
1658 id.UserLength = strlenW( user );
1659 id.Domain = domain;
1660 id.DomainLength = domain ? user - domain - 1 : 0;
1661 id.Password = (WCHAR *)password;
1662 id.PasswordLength = strlenW( password );
1664 status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1665 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1666 &authinfo->cred, &exp );
1667 if (status == SEC_E_OK)
1669 PSecPkgInfoW info;
1670 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1671 if (status == SEC_E_OK)
1673 authinfo->max_token = info->cbMaxToken;
1674 FreeContextBuffer( info );
1677 if (status != SEC_E_OK)
1679 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1680 debugstr_w(auth_schemes[scheme].str), status);
1681 heap_free( authinfo );
1682 return FALSE;
1684 authinfo->scheme = scheme;
1686 else if (authinfo->finished) return FALSE;
1688 if ((strlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1689 strncmpiW( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1691 ERR("authentication scheme changed from %s to %s\n",
1692 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1693 destroy_authinfo( authinfo );
1694 *auth_ptr = NULL;
1695 return FALSE;
1697 in.BufferType = SECBUFFER_TOKEN;
1698 in.cbBuffer = 0;
1699 in.pvBuffer = NULL;
1701 in_desc.ulVersion = 0;
1702 in_desc.cBuffers = 1;
1703 in_desc.pBuffers = &in;
1705 p = auth_value + auth_schemes[scheme].len;
1706 if (*p == ' ')
1708 int len = strlenW( ++p );
1709 in.cbBuffer = decode_base64( p, len, NULL );
1710 if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) {
1711 destroy_authinfo( authinfo );
1712 *auth_ptr = NULL;
1713 return FALSE;
1715 decode_base64( p, len, in.pvBuffer );
1717 out.BufferType = SECBUFFER_TOKEN;
1718 out.cbBuffer = authinfo->max_token;
1719 if (!(out.pvBuffer = heap_alloc( authinfo->max_token )))
1721 heap_free( in.pvBuffer );
1722 destroy_authinfo( authinfo );
1723 *auth_ptr = NULL;
1724 return FALSE;
1726 out_desc.ulVersion = 0;
1727 out_desc.cBuffers = 1;
1728 out_desc.pBuffers = &out;
1730 status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1731 first ? request->connect->servername : NULL, flags, 0,
1732 SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1733 &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1734 heap_free( in.pvBuffer );
1735 if (status == SEC_E_OK)
1737 heap_free( authinfo->data );
1738 authinfo->data = out.pvBuffer;
1739 authinfo->data_len = out.cbBuffer;
1740 authinfo->finished = TRUE;
1741 TRACE("sending last auth packet\n");
1743 else if (status == SEC_I_CONTINUE_NEEDED)
1745 heap_free( authinfo->data );
1746 authinfo->data = out.pvBuffer;
1747 authinfo->data_len = out.cbBuffer;
1748 TRACE("sending next auth packet\n");
1750 else
1752 ERR("InitializeSecurityContextW failed with error 0x%08x\n", status);
1753 heap_free( out.pvBuffer );
1754 destroy_authinfo( authinfo );
1755 *auth_ptr = NULL;
1756 return FALSE;
1758 break;
1760 default:
1761 ERR("invalid scheme %u\n", scheme);
1762 return FALSE;
1764 *auth_ptr = authinfo;
1766 len_scheme = auth_schemes[authinfo->scheme].len;
1767 len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1768 if (!(auth_reply = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1770 memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1771 auth_reply[len_scheme] = ' ';
1772 encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1774 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1775 ret = process_header( request, auth_target, auth_reply, flags, TRUE );
1776 heap_free( auth_reply );
1777 return ret;
1780 static BOOL set_credentials( request_t *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
1781 const WCHAR *password )
1783 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1785 if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
1787 set_last_error( ERROR_INVALID_PARAMETER );
1788 return FALSE;
1790 switch (target)
1792 case WINHTTP_AUTH_TARGET_SERVER:
1794 heap_free( request->creds[TARGET_SERVER][scheme].username );
1795 if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
1796 else if (!(request->creds[TARGET_SERVER][scheme].username = strdupW( username ))) return FALSE;
1798 heap_free( request->creds[TARGET_SERVER][scheme].password );
1799 if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
1800 else if (!(request->creds[TARGET_SERVER][scheme].password = strdupW( password ))) return FALSE;
1801 break;
1803 case WINHTTP_AUTH_TARGET_PROXY:
1805 heap_free( request->creds[TARGET_PROXY][scheme].username );
1806 if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
1807 else if (!(request->creds[TARGET_PROXY][scheme].username = strdupW( username ))) return FALSE;
1809 heap_free( request->creds[TARGET_PROXY][scheme].password );
1810 if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
1811 else if (!(request->creds[TARGET_PROXY][scheme].password = strdupW( password ))) return FALSE;
1812 break;
1814 default:
1815 WARN("unknown target %u\n", target);
1816 return FALSE;
1818 return TRUE;
1821 /***********************************************************************
1822 * WinHttpSetCredentials (winhttp.@)
1824 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, LPCWSTR username,
1825 LPCWSTR password, LPVOID params )
1827 BOOL ret;
1828 request_t *request;
1830 TRACE("%p, %x, 0x%08x, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params);
1832 if (!(request = (request_t *)grab_object( hrequest )))
1834 set_last_error( ERROR_INVALID_HANDLE );
1835 return FALSE;
1837 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
1839 release_object( &request->hdr );
1840 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1841 return FALSE;
1844 ret = set_credentials( request, target, scheme, username, password );
1846 release_object( &request->hdr );
1847 if (ret) set_last_error( ERROR_SUCCESS );
1848 return ret;
1851 static BOOL handle_authorization( request_t *request, DWORD status )
1853 DWORD i, schemes, first, level, target;
1855 switch (status)
1857 case HTTP_STATUS_DENIED:
1858 target = WINHTTP_AUTH_TARGET_SERVER;
1859 level = WINHTTP_QUERY_WWW_AUTHENTICATE;
1860 break;
1862 case HTTP_STATUS_PROXY_AUTH_REQ:
1863 target = WINHTTP_AUTH_TARGET_PROXY;
1864 level = WINHTTP_QUERY_PROXY_AUTHENTICATE;
1865 break;
1867 default:
1868 WARN("unhandled status %u\n", status);
1869 return FALSE;
1872 if (!query_auth_schemes( request, level, &schemes, &first )) return FALSE;
1873 if (do_authorization( request, target, first )) return TRUE;
1875 schemes &= ~first;
1876 for (i = 0; i < num_auth_schemes; i++)
1878 if (!(schemes & auth_schemes[i].scheme)) continue;
1879 if (do_authorization( request, target, auth_schemes[i].scheme )) return TRUE;
1881 return FALSE;
1884 /* set the request content length based on the headers */
1885 static DWORD set_content_length( request_t *request, DWORD status )
1887 WCHAR encoding[20];
1888 DWORD buflen = sizeof(request->content_length);
1890 if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED || !strcmpW( request->verb, headW ))
1891 request->content_length = 0;
1892 else
1894 if (!query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
1895 NULL, &request->content_length, &buflen, NULL ))
1896 request->content_length = ~0u;
1898 buflen = sizeof(encoding);
1899 if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
1900 !strcmpiW( encoding, chunkedW ))
1902 request->content_length = ~0u;
1903 request->read_chunked = TRUE;
1904 request->read_chunked_size = ~0u;
1905 request->read_chunked_eof = FALSE;
1908 request->content_read = 0;
1909 return request->content_length;
1912 /* read some more data into the read buffer */
1913 static BOOL read_more_data( request_t *request, int maxlen, BOOL notify )
1915 int len;
1916 BOOL ret;
1918 if (request->read_chunked_eof) return FALSE;
1920 if (request->read_size && request->read_pos)
1922 /* move existing data to the start of the buffer */
1923 memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1924 request->read_pos = 0;
1926 if (maxlen == -1) maxlen = sizeof(request->read_buf);
1928 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1930 ret = netconn_recv( &request->netconn, request->read_buf + request->read_size,
1931 maxlen - request->read_size, 0, &len );
1933 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1935 request->read_size += len;
1936 return ret;
1939 /* remove some amount of data from the read buffer */
1940 static void remove_data( request_t *request, int count )
1942 if (!(request->read_size -= count)) request->read_pos = 0;
1943 else request->read_pos += count;
1946 static BOOL read_line( request_t *request, char *buffer, DWORD *len )
1948 int count, bytes_read, pos = 0;
1950 for (;;)
1952 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1953 if (eol)
1955 count = eol - (request->read_buf + request->read_pos);
1956 bytes_read = count + 1;
1958 else count = bytes_read = request->read_size;
1960 count = min( count, *len - pos );
1961 memcpy( buffer + pos, request->read_buf + request->read_pos, count );
1962 pos += count;
1963 remove_data( request, bytes_read );
1964 if (eol) break;
1966 if (!read_more_data( request, -1, TRUE )) return FALSE;
1967 if (!request->read_size)
1969 *len = 0;
1970 TRACE("returning empty string\n");
1971 return FALSE;
1974 if (pos < *len)
1976 if (pos && buffer[pos - 1] == '\r') pos--;
1977 *len = pos + 1;
1979 buffer[*len - 1] = 0;
1980 TRACE("returning %s\n", debugstr_a(buffer));
1981 return TRUE;
1984 /* discard data contents until we reach end of line */
1985 static BOOL discard_eol( request_t *request, BOOL notify )
1989 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1990 if (eol)
1992 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1993 break;
1995 request->read_pos = request->read_size = 0; /* discard everything */
1996 if (!read_more_data( request, -1, notify )) return FALSE;
1997 } while (request->read_size);
1998 return TRUE;
2001 /* read the size of the next chunk */
2002 static BOOL start_next_chunk( request_t *request, BOOL notify )
2004 DWORD chunk_size = 0;
2006 assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
2008 if (request->read_chunked_eof) return FALSE;
2010 /* read terminator for the previous chunk */
2011 if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE;
2013 for (;;)
2015 while (request->read_size)
2017 char ch = request->read_buf[request->read_pos];
2018 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2019 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2020 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2021 else if (ch == ';' || ch == '\r' || ch == '\n')
2023 TRACE("reading %u byte chunk\n", chunk_size);
2025 if (request->content_length == ~0u) request->content_length = chunk_size;
2026 else request->content_length += chunk_size;
2028 request->read_chunked_size = chunk_size;
2029 if (!chunk_size) request->read_chunked_eof = TRUE;
2031 return discard_eol( request, notify );
2033 remove_data( request, 1 );
2035 if (!read_more_data( request, -1, notify )) return FALSE;
2036 if (!request->read_size)
2038 request->content_length = request->content_read = 0;
2039 request->read_chunked_size = 0;
2040 return TRUE;
2045 /* return the size of data available to be read immediately */
2046 static DWORD get_available_data( request_t *request )
2048 if (request->read_chunked) return min( request->read_chunked_size, request->read_size );
2049 return request->read_size;
2052 /* check if we have reached the end of the data to read */
2053 static BOOL end_of_read_data( request_t *request )
2055 if (!request->content_length) return TRUE;
2056 if (request->read_chunked) return request->read_chunked_eof;
2057 if (request->content_length == ~0u) return FALSE;
2058 return (request->content_length == request->content_read);
2061 static BOOL refill_buffer( request_t *request, BOOL notify )
2063 int len = sizeof(request->read_buf);
2065 if (request->read_chunked)
2067 if (request->read_chunked_eof) return FALSE;
2068 if (request->read_chunked_size == ~0u || !request->read_chunked_size)
2070 if (!start_next_chunk( request, notify )) return FALSE;
2072 len = min( len, request->read_chunked_size );
2074 else if (request->content_length != ~0u)
2076 len = min( len, request->content_length - request->content_read );
2079 if (len <= request->read_size) return TRUE;
2080 if (!read_more_data( request, len, notify )) return FALSE;
2081 if (!request->read_size) request->content_length = request->content_read = 0;
2082 return TRUE;
2085 #define MAX_REPLY_LEN 1460
2086 #define INITIAL_HEADER_BUFFER_LEN 512
2088 static BOOL read_reply( request_t *request )
2090 static const WCHAR crlf[] = {'\r','\n',0};
2092 char buffer[MAX_REPLY_LEN];
2093 DWORD buflen, len, offset, crlf_len = 2; /* strlenW(crlf) */
2094 char *status_code, *status_text;
2095 WCHAR *versionW, *status_textW, *raw_headers;
2096 WCHAR status_codeW[4]; /* sizeof("nnn") */
2098 if (!netconn_connected( &request->netconn )) return FALSE;
2102 buflen = MAX_REPLY_LEN;
2103 if (!read_line( request, buffer, &buflen )) return FALSE;
2105 /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2106 if (!(status_code = strchr( buffer, ' ' ))) return FALSE;
2107 status_code++;
2108 if (!(status_text = strchr( status_code, ' ' ))) return FALSE;
2109 if ((len = status_text - status_code) != sizeof("nnn") - 1) return FALSE;
2110 status_text++;
2112 TRACE("version [%s] status code [%s] status text [%s]\n",
2113 debugstr_an(buffer, status_code - buffer - 1),
2114 debugstr_an(status_code, len),
2115 debugstr_a(status_text));
2117 } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2119 /* we rely on the fact that the protocol is ascii */
2120 MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2121 status_codeW[len] = 0;
2122 if (!(process_header( request, attr_status, status_codeW,
2123 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE )))
2124 return FALSE;
2126 len = status_code - buffer;
2127 if (!(versionW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2128 MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2129 versionW[len - 1] = 0;
2131 heap_free( request->version );
2132 request->version = versionW;
2134 len = buflen - (status_text - buffer);
2135 if (!(status_textW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2136 MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2138 heap_free( request->status_text );
2139 request->status_text = status_textW;
2141 len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2142 if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
2143 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2144 memcpy( raw_headers + buflen - 1, crlf, sizeof(crlf) );
2146 heap_free( request->raw_headers );
2147 request->raw_headers = raw_headers;
2149 offset = buflen + crlf_len - 1;
2150 for (;;)
2152 header_t *header;
2154 buflen = MAX_REPLY_LEN;
2155 if (!read_line( request, buffer, &buflen )) return TRUE;
2156 if (!*buffer) buflen = 1;
2158 while (len - offset < buflen + crlf_len)
2160 WCHAR *tmp;
2161 len *= 2;
2162 if (!(tmp = heap_realloc( raw_headers, len * sizeof(WCHAR) ))) return FALSE;
2163 request->raw_headers = raw_headers = tmp;
2165 if (!*buffer)
2167 memcpy( raw_headers + offset, crlf, sizeof(crlf) );
2168 break;
2170 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2172 if (!(header = parse_header( raw_headers + offset ))) break;
2173 if (!(process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2175 free_header( header );
2176 break;
2178 free_header( header );
2179 memcpy( raw_headers + offset + buflen - 1, crlf, sizeof(crlf) );
2180 offset += buflen + crlf_len - 1;
2183 TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2184 return TRUE;
2187 static void finished_reading( request_t *request )
2189 static const WCHAR closeW[] = {'c','l','o','s','e',0};
2191 BOOL close = FALSE;
2192 WCHAR connection[20];
2193 DWORD size = sizeof(connection);
2195 if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
2196 else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
2197 query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
2199 if (!strcmpiW( connection, closeW )) close = TRUE;
2201 else if (!strcmpW( request->version, http1_0 )) close = TRUE;
2202 if (close) close_connection( request );
2205 static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
2207 int count, bytes_read = 0;
2209 if (end_of_read_data( request )) goto done;
2211 while (size)
2213 if (!(count = get_available_data( request )))
2215 if (!refill_buffer( request, async )) goto done;
2216 if (!(count = get_available_data( request ))) goto done;
2218 count = min( count, size );
2219 memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
2220 remove_data( request, count );
2221 if (request->read_chunked) request->read_chunked_size -= count;
2222 size -= count;
2223 bytes_read += count;
2224 request->content_read += count;
2225 if (end_of_read_data( request )) goto done;
2227 if (request->read_chunked && !request->read_chunked_size) refill_buffer( request, async );
2229 done:
2230 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
2232 if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
2233 if (read) *read = bytes_read;
2234 if (end_of_read_data( request )) finished_reading( request );
2235 return TRUE;
2238 /* read any content returned by the server so that the connection can be reused */
2239 static void drain_content( request_t *request )
2241 DWORD bytes_read;
2242 char buffer[2048];
2244 refill_buffer( request, FALSE );
2245 for (;;)
2247 if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
2251 static void record_cookies( request_t *request )
2253 unsigned int i;
2255 for (i = 0; i < request->num_headers; i++)
2257 header_t *set_cookie = &request->headers[i];
2258 if (!strcmpiW( set_cookie->field, attr_set_cookie ) && !set_cookie->is_request)
2260 set_cookies( request, set_cookie->value );
2265 static WCHAR *get_redirect_url( request_t *request, DWORD *len )
2267 DWORD size;
2268 WCHAR *ret;
2270 query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2271 if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
2272 if (!(ret = heap_alloc( size ))) return NULL;
2273 *len = size / sizeof(WCHAR);
2274 if (query_headers( request, WINHTTP_QUERY_LOCATION, NULL, ret, &size, NULL )) return ret;
2275 heap_free( ret );
2276 return NULL;
2279 static BOOL handle_redirect( request_t *request, DWORD status )
2281 BOOL ret = FALSE;
2282 DWORD len, len_url;
2283 URL_COMPONENTS uc;
2284 connect_t *connect = request->connect;
2285 INTERNET_PORT port;
2286 WCHAR *hostname = NULL, *location;
2287 int index;
2289 if (!(location = get_redirect_url( request, &len_url ))) return FALSE;
2291 memset( &uc, 0, sizeof(uc) );
2292 uc.dwStructSize = sizeof(uc);
2293 uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2295 if (!WinHttpCrackUrl( location, len_url, 0, &uc )) /* assume relative redirect */
2297 WCHAR *path, *p;
2299 if (location[0] == '/')
2301 len = strlenW( location );
2302 if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2303 strcpyW( path, location );
2305 else
2307 if ((p = strrchrW( request->path, '/' ))) *p = 0;
2308 len = strlenW( request->path ) + 1 + strlenW( location );
2309 if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2310 strcpyW( path, request->path );
2311 strcatW( path, slashW );
2312 strcatW( path, location );
2314 heap_free( request->path );
2315 request->path = path;
2317 drain_content( request );
2318 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
2320 else
2322 if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2324 if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP) goto end;
2325 TRACE("redirect from secure page to non-secure page\n");
2326 request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2328 else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2330 TRACE("redirect from non-secure page to secure page\n");
2331 request->hdr.flags |= WINHTTP_FLAG_SECURE;
2334 drain_content( request );
2335 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
2337 len = uc.dwHostNameLength;
2338 if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2339 memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2340 hostname[len] = 0;
2342 port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2343 if (strcmpiW( connect->hostname, hostname ) || connect->serverport != port)
2345 heap_free( connect->hostname );
2346 connect->hostname = hostname;
2347 connect->hostport = port;
2348 if (!(ret = set_server_for_hostname( connect, hostname, port ))) goto end;
2350 netconn_close( &request->netconn );
2351 if (!(ret = netconn_init( &request->netconn ))) goto end;
2352 request->read_pos = request->read_size = 0;
2353 request->read_chunked = FALSE;
2354 request->read_chunked_eof = FALSE;
2356 else heap_free( hostname );
2358 if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2359 if (!(ret = open_connection( request ))) goto end;
2361 heap_free( request->path );
2362 request->path = NULL;
2363 if (uc.dwUrlPathLength)
2365 len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2366 if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2367 strcpyW( request->path, uc.lpszUrlPath );
2369 else request->path = strdupW( slashW );
2372 /* remove content-type/length headers */
2373 if ((index = get_header_index( request, attr_content_type, 0, TRUE )) >= 0) delete_header( request, index );
2374 if ((index = get_header_index( request, attr_content_length, 0, TRUE )) >= 0 ) delete_header( request, index );
2376 if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !strcmpW( request->verb, postW ))
2378 heap_free( request->verb );
2379 request->verb = strdupW( getW );
2380 request->optional = NULL;
2381 request->optional_len = 0;
2383 ret = TRUE;
2385 end:
2386 heap_free( location );
2387 return ret;
2390 static BOOL receive_response( request_t *request, BOOL async )
2392 BOOL ret;
2393 DWORD size, query, status;
2395 for (;;)
2397 if (!(ret = read_reply( request )))
2399 set_last_error( ERROR_WINHTTP_INVALID_SERVER_RESPONSE );
2400 break;
2402 size = sizeof(DWORD);
2403 query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2404 if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
2406 set_content_length( request, status );
2408 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2410 if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2412 if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2413 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) break;
2415 if (!(ret = handle_redirect( request, status ))) break;
2417 /* recurse synchronously */
2418 if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2420 else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2422 if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
2424 if (!handle_authorization( request, status )) break;
2425 drain_content( request );
2427 /* recurse synchronously */
2428 if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2430 break;
2433 if (request->content_length) refill_buffer( request, FALSE );
2435 if (async)
2437 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
2438 else
2440 WINHTTP_ASYNC_RESULT result;
2441 result.dwResult = API_RECEIVE_RESPONSE;
2442 result.dwError = get_last_error();
2443 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2446 return ret;
2449 static void task_receive_response( task_header_t *task )
2451 receive_response_t *r = (receive_response_t *)task;
2452 receive_response( r->hdr.request, TRUE );
2455 /***********************************************************************
2456 * WinHttpReceiveResponse (winhttp.@)
2458 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
2460 BOOL ret;
2461 request_t *request;
2463 TRACE("%p, %p\n", hrequest, reserved);
2465 if (!(request = (request_t *)grab_object( hrequest )))
2467 set_last_error( ERROR_INVALID_HANDLE );
2468 return FALSE;
2470 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2472 release_object( &request->hdr );
2473 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2474 return FALSE;
2477 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2479 receive_response_t *r;
2481 if (!(r = heap_alloc( sizeof(receive_response_t) ))) return FALSE;
2482 r->hdr.request = request;
2483 r->hdr.proc = task_receive_response;
2485 addref_object( &request->hdr );
2486 ret = queue_task( (task_header_t *)r );
2488 else
2489 ret = receive_response( request, FALSE );
2491 release_object( &request->hdr );
2492 if (ret) set_last_error( ERROR_SUCCESS );
2493 return ret;
2496 static BOOL query_data_available( request_t *request, DWORD *available, BOOL async )
2498 DWORD count = 0;
2500 if (end_of_read_data( request )) goto done;
2502 count = get_available_data( request );
2503 if (!request->read_chunked)
2504 count += netconn_query_data_available( &request->netconn );
2505 if (!count)
2507 refill_buffer( request, async );
2508 count = get_available_data( request );
2509 if (!request->read_chunked)
2510 count += netconn_query_data_available( &request->netconn );
2513 done:
2514 if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
2515 TRACE("%u bytes available\n", count);
2516 if (available) *available = count;
2517 return TRUE;
2520 static void task_query_data_available( task_header_t *task )
2522 query_data_t *q = (query_data_t *)task;
2523 query_data_available( q->hdr.request, q->available, TRUE );
2526 /***********************************************************************
2527 * WinHttpQueryDataAvailable (winhttp.@)
2529 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
2531 BOOL ret;
2532 request_t *request;
2534 TRACE("%p, %p\n", hrequest, available);
2536 if (!(request = (request_t *)grab_object( hrequest )))
2538 set_last_error( ERROR_INVALID_HANDLE );
2539 return FALSE;
2541 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2543 release_object( &request->hdr );
2544 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2545 return FALSE;
2548 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2550 query_data_t *q;
2552 if (!(q = heap_alloc( sizeof(query_data_t) ))) return FALSE;
2553 q->hdr.request = request;
2554 q->hdr.proc = task_query_data_available;
2555 q->available = available;
2557 addref_object( &request->hdr );
2558 ret = queue_task( (task_header_t *)q );
2560 else
2561 ret = query_data_available( request, available, FALSE );
2563 release_object( &request->hdr );
2564 if (ret) set_last_error( ERROR_SUCCESS );
2565 return ret;
2568 static void task_read_data( task_header_t *task )
2570 read_data_t *r = (read_data_t *)task;
2571 read_data( r->hdr.request, r->buffer, r->to_read, r->read, TRUE );
2574 /***********************************************************************
2575 * WinHttpReadData (winhttp.@)
2577 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
2579 BOOL ret;
2580 request_t *request;
2582 TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
2584 if (!(request = (request_t *)grab_object( hrequest )))
2586 set_last_error( ERROR_INVALID_HANDLE );
2587 return FALSE;
2589 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2591 release_object( &request->hdr );
2592 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2593 return FALSE;
2596 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2598 read_data_t *r;
2600 if (!(r = heap_alloc( sizeof(read_data_t) ))) return FALSE;
2601 r->hdr.request = request;
2602 r->hdr.proc = task_read_data;
2603 r->buffer = buffer;
2604 r->to_read = to_read;
2605 r->read = read;
2607 addref_object( &request->hdr );
2608 ret = queue_task( (task_header_t *)r );
2610 else
2611 ret = read_data( request, buffer, to_read, read, FALSE );
2613 release_object( &request->hdr );
2614 if (ret) set_last_error( ERROR_SUCCESS );
2615 return ret;
2618 static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDWORD written, BOOL async )
2620 BOOL ret;
2621 int num_bytes;
2623 ret = netconn_send( &request->netconn, buffer, to_write, &num_bytes );
2625 if (async)
2627 if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) );
2628 else
2630 WINHTTP_ASYNC_RESULT result;
2631 result.dwResult = API_WRITE_DATA;
2632 result.dwError = get_last_error();
2633 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2636 if (ret && written) *written = num_bytes;
2637 return ret;
2640 static void task_write_data( task_header_t *task )
2642 write_data_t *w = (write_data_t *)task;
2643 write_data( w->hdr.request, w->buffer, w->to_write, w->written, TRUE );
2646 /***********************************************************************
2647 * WinHttpWriteData (winhttp.@)
2649 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written )
2651 BOOL ret;
2652 request_t *request;
2654 TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written);
2656 if (!(request = (request_t *)grab_object( hrequest )))
2658 set_last_error( ERROR_INVALID_HANDLE );
2659 return FALSE;
2661 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2663 release_object( &request->hdr );
2664 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2665 return FALSE;
2668 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2670 write_data_t *w;
2672 if (!(w = heap_alloc( sizeof(write_data_t) ))) return FALSE;
2673 w->hdr.request = request;
2674 w->hdr.proc = task_write_data;
2675 w->buffer = buffer;
2676 w->to_write = to_write;
2677 w->written = written;
2679 addref_object( &request->hdr );
2680 ret = queue_task( (task_header_t *)w );
2682 else
2683 ret = write_data( request, buffer, to_write, written, FALSE );
2685 release_object( &request->hdr );
2686 if (ret) set_last_error( ERROR_SUCCESS );
2687 return ret;
2690 enum request_state
2692 REQUEST_STATE_UNINITIALIZED,
2693 REQUEST_STATE_INITIALIZED,
2694 REQUEST_STATE_CANCELLED,
2695 REQUEST_STATE_OPEN,
2696 REQUEST_STATE_SENT,
2697 REQUEST_STATE_RESPONSE_RECEIVED
2700 struct winhttp_request
2702 IWinHttpRequest IWinHttpRequest_iface;
2703 LONG refs;
2704 CRITICAL_SECTION cs;
2705 enum request_state state;
2706 HINTERNET hsession;
2707 HINTERNET hconnect;
2708 HINTERNET hrequest;
2709 VARIANT data;
2710 WCHAR *verb;
2711 HANDLE thread;
2712 HANDLE wait;
2713 HANDLE cancel;
2714 char *buffer;
2715 DWORD offset;
2716 DWORD bytes_available;
2717 DWORD bytes_read;
2718 DWORD error;
2719 DWORD logon_policy;
2720 DWORD disable_feature;
2721 LONG resolve_timeout;
2722 LONG connect_timeout;
2723 LONG send_timeout;
2724 LONG receive_timeout;
2725 WINHTTP_PROXY_INFO proxy;
2726 BOOL async;
2727 UINT url_codepage;
2730 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
2732 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
2735 static ULONG WINAPI winhttp_request_AddRef(
2736 IWinHttpRequest *iface )
2738 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2739 return InterlockedIncrement( &request->refs );
2742 /* critical section must be held */
2743 static void cancel_request( struct winhttp_request *request )
2745 if (request->state <= REQUEST_STATE_CANCELLED) return;
2747 SetEvent( request->cancel );
2748 LeaveCriticalSection( &request->cs );
2749 WaitForSingleObject( request->thread, INFINITE );
2750 EnterCriticalSection( &request->cs );
2752 request->state = REQUEST_STATE_CANCELLED;
2754 CloseHandle( request->thread );
2755 request->thread = NULL;
2756 CloseHandle( request->wait );
2757 request->wait = NULL;
2758 CloseHandle( request->cancel );
2759 request->cancel = NULL;
2762 /* critical section must be held */
2763 static void free_request( struct winhttp_request *request )
2765 if (request->state < REQUEST_STATE_INITIALIZED) return;
2766 WinHttpCloseHandle( request->hrequest );
2767 WinHttpCloseHandle( request->hconnect );
2768 WinHttpCloseHandle( request->hsession );
2769 CloseHandle( request->thread );
2770 CloseHandle( request->wait );
2771 CloseHandle( request->cancel );
2772 heap_free( request->proxy.lpszProxy );
2773 heap_free( request->proxy.lpszProxyBypass );
2774 heap_free( request->buffer );
2775 heap_free( request->verb );
2776 VariantClear( &request->data );
2779 static ULONG WINAPI winhttp_request_Release(
2780 IWinHttpRequest *iface )
2782 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2783 LONG refs = InterlockedDecrement( &request->refs );
2784 if (!refs)
2786 TRACE("destroying %p\n", request);
2788 EnterCriticalSection( &request->cs );
2789 cancel_request( request );
2790 free_request( request );
2791 LeaveCriticalSection( &request->cs );
2792 request->cs.DebugInfo->Spare[0] = 0;
2793 DeleteCriticalSection( &request->cs );
2794 heap_free( request );
2796 return refs;
2799 static HRESULT WINAPI winhttp_request_QueryInterface(
2800 IWinHttpRequest *iface,
2801 REFIID riid,
2802 void **obj )
2804 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2806 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
2808 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
2809 IsEqualGUID( riid, &IID_IDispatch ) ||
2810 IsEqualGUID( riid, &IID_IUnknown ))
2812 *obj = iface;
2814 else
2816 FIXME("interface %s not implemented\n", debugstr_guid(riid));
2817 return E_NOINTERFACE;
2819 IWinHttpRequest_AddRef( iface );
2820 return S_OK;
2823 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
2824 IWinHttpRequest *iface,
2825 UINT *count )
2827 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2829 TRACE("%p, %p\n", request, count);
2830 *count = 1;
2831 return S_OK;
2834 enum type_id
2836 IWinHttpRequest_tid,
2837 last_tid
2840 static ITypeLib *winhttp_typelib;
2841 static ITypeInfo *winhttp_typeinfo[last_tid];
2843 static REFIID winhttp_tid_id[] =
2845 &IID_IWinHttpRequest
2848 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
2850 HRESULT hr;
2852 if (!winhttp_typelib)
2854 ITypeLib *typelib;
2856 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
2857 if (FAILED(hr))
2859 ERR("LoadRegTypeLib failed: %08x\n", hr);
2860 return hr;
2862 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
2863 ITypeLib_Release( typelib );
2865 if (!winhttp_typeinfo[tid])
2867 ITypeInfo *typeinfo;
2869 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
2870 if (FAILED(hr))
2872 ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(winhttp_tid_id[tid]), hr);
2873 return hr;
2875 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
2876 ITypeInfo_Release( typeinfo );
2878 *ret = winhttp_typeinfo[tid];
2879 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
2880 return S_OK;
2883 void release_typelib(void)
2885 unsigned i;
2887 for (i = 0; i < sizeof(winhttp_typeinfo)/sizeof(*winhttp_typeinfo); i++)
2888 if (winhttp_typeinfo[i])
2889 ITypeInfo_Release(winhttp_typeinfo[i]);
2891 if (winhttp_typelib)
2892 ITypeLib_Release(winhttp_typelib);
2895 static HRESULT WINAPI winhttp_request_GetTypeInfo(
2896 IWinHttpRequest *iface,
2897 UINT index,
2898 LCID lcid,
2899 ITypeInfo **info )
2901 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2902 TRACE("%p, %u, %u, %p\n", request, index, lcid, info);
2904 return get_typeinfo( IWinHttpRequest_tid, info );
2907 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
2908 IWinHttpRequest *iface,
2909 REFIID riid,
2910 LPOLESTR *names,
2911 UINT count,
2912 LCID lcid,
2913 DISPID *dispid )
2915 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2916 ITypeInfo *typeinfo;
2917 HRESULT hr;
2919 TRACE("%p, %s, %p, %u, %u, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid);
2921 if (!names || !count || !dispid) return E_INVALIDARG;
2923 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
2924 if (SUCCEEDED(hr))
2926 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
2927 ITypeInfo_Release( typeinfo );
2929 return hr;
2932 static HRESULT WINAPI winhttp_request_Invoke(
2933 IWinHttpRequest *iface,
2934 DISPID member,
2935 REFIID riid,
2936 LCID lcid,
2937 WORD flags,
2938 DISPPARAMS *params,
2939 VARIANT *result,
2940 EXCEPINFO *excep_info,
2941 UINT *arg_err )
2943 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
2944 ITypeInfo *typeinfo;
2945 HRESULT hr;
2947 TRACE("%p, %d, %s, %d, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
2948 lcid, flags, params, result, excep_info, arg_err);
2950 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
2952 if (member == DISPID_HTTPREQUEST_OPTION)
2954 VARIANT ret_value, option;
2955 UINT err_pos;
2957 if (!result) result = &ret_value;
2958 if (!arg_err) arg_err = &err_pos;
2960 VariantInit( &option );
2961 VariantInit( result );
2963 if (!flags) return S_OK;
2965 if (flags == DISPATCH_PROPERTYPUT)
2967 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
2968 if (FAILED(hr)) return hr;
2970 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
2971 if (FAILED(hr))
2972 WARN("put_Option(%d) failed: %x\n", V_I4( &option ), hr);
2973 return hr;
2975 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
2977 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
2978 if (FAILED(hr)) return hr;
2980 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
2981 if (FAILED(hr))
2982 WARN("get_Option(%d) failed: %x\n", V_I4( &option ), hr);
2983 return hr;
2986 FIXME("unsupported flags %x\n", flags);
2987 return E_NOTIMPL;
2990 /* fallback to standard implementation */
2992 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
2993 if (SUCCEEDED(hr))
2995 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
2996 params, result, excep_info, arg_err );
2997 ITypeInfo_Release( typeinfo );
2999 return hr;
3002 static HRESULT WINAPI winhttp_request_SetProxy(
3003 IWinHttpRequest *iface,
3004 HTTPREQUEST_PROXY_SETTING proxy_setting,
3005 VARIANT proxy_server,
3006 VARIANT bypass_list )
3008 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3009 DWORD err = ERROR_SUCCESS;
3011 TRACE("%p, %u, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
3012 debugstr_variant(&bypass_list));
3014 EnterCriticalSection( &request->cs );
3015 switch (proxy_setting)
3017 case HTTPREQUEST_PROXYSETTING_DEFAULT:
3018 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
3019 heap_free( request->proxy.lpszProxy );
3020 heap_free( request->proxy.lpszProxyBypass );
3021 request->proxy.lpszProxy = NULL;
3022 request->proxy.lpszProxyBypass = NULL;
3023 break;
3025 case HTTPREQUEST_PROXYSETTING_DIRECT:
3026 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
3027 heap_free( request->proxy.lpszProxy );
3028 heap_free( request->proxy.lpszProxyBypass );
3029 request->proxy.lpszProxy = NULL;
3030 request->proxy.lpszProxyBypass = NULL;
3031 break;
3033 case HTTPREQUEST_PROXYSETTING_PROXY:
3034 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
3035 if (V_VT( &proxy_server ) == VT_BSTR)
3037 heap_free( request->proxy.lpszProxy );
3038 request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) );
3040 if (V_VT( &bypass_list ) == VT_BSTR)
3042 heap_free( request->proxy.lpszProxyBypass );
3043 request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) );
3045 break;
3047 default:
3048 err = ERROR_INVALID_PARAMETER;
3049 break;
3051 LeaveCriticalSection( &request->cs );
3052 return HRESULT_FROM_WIN32( err );
3055 static HRESULT WINAPI winhttp_request_SetCredentials(
3056 IWinHttpRequest *iface,
3057 BSTR username,
3058 BSTR password,
3059 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
3061 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3062 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
3063 DWORD err = ERROR_SUCCESS;
3065 TRACE("%p, %s, %p, 0x%08x\n", request, debugstr_w(username), password, flags);
3067 EnterCriticalSection( &request->cs );
3068 if (request->state < REQUEST_STATE_OPEN)
3070 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
3071 goto done;
3073 switch (flags)
3075 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
3076 target = WINHTTP_AUTH_TARGET_SERVER;
3077 break;
3078 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
3079 target = WINHTTP_AUTH_TARGET_PROXY;
3080 break;
3081 default:
3082 err = ERROR_INVALID_PARAMETER;
3083 goto done;
3085 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
3087 err = get_last_error();
3089 done:
3090 LeaveCriticalSection( &request->cs );
3091 return HRESULT_FROM_WIN32( err );
3094 static void initialize_request( struct winhttp_request *request )
3096 request->hrequest = NULL;
3097 request->hconnect = NULL;
3098 request->hsession = NULL;
3099 request->thread = NULL;
3100 request->wait = NULL;
3101 request->cancel = NULL;
3102 request->buffer = NULL;
3103 request->verb = NULL;
3104 request->offset = 0;
3105 request->bytes_available = 0;
3106 request->bytes_read = 0;
3107 request->error = ERROR_SUCCESS;
3108 request->async = FALSE;
3109 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
3110 request->disable_feature = 0;
3111 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
3112 request->proxy.lpszProxy = NULL;
3113 request->proxy.lpszProxyBypass = NULL;
3114 request->resolve_timeout = 0;
3115 request->connect_timeout = 60000;
3116 request->send_timeout = 30000;
3117 request->receive_timeout = 30000;
3118 request->url_codepage = CP_UTF8;
3119 VariantInit( &request->data );
3120 request->state = REQUEST_STATE_INITIALIZED;
3123 static void reset_request( struct winhttp_request *request )
3125 cancel_request( request );
3126 WinHttpCloseHandle( request->hrequest );
3127 request->hrequest = NULL;
3128 WinHttpCloseHandle( request->hconnect );
3129 request->hconnect = NULL;
3130 heap_free( request->buffer );
3131 request->buffer = NULL;
3132 heap_free( request->verb );
3133 request->verb = NULL;
3134 request->offset = 0;
3135 request->bytes_available = 0;
3136 request->bytes_read = 0;
3137 request->error = ERROR_SUCCESS;
3138 request->async = FALSE;
3139 request->url_codepage = CP_UTF8;
3140 VariantClear( &request->data );
3141 request->state = REQUEST_STATE_INITIALIZED;
3144 static HRESULT WINAPI winhttp_request_Open(
3145 IWinHttpRequest *iface,
3146 BSTR method,
3147 BSTR url,
3148 VARIANT async )
3150 static const WCHAR typeW[] = {'*','/','*',0};
3151 static const WCHAR *acceptW[] = {typeW, NULL};
3152 static const WCHAR httpsW[] = {'h','t','t','p','s'};
3153 static const WCHAR user_agentW[] = {
3154 'M','o','z','i','l','l','a','/','4','.','0',' ','(','c','o','m','p','a','t','i','b','l','e',';',' ',
3155 'W','i','n','3','2',';',' ','W','i','n','H','t','t','p','.','W','i','n','H','t','t','p',
3156 'R','e','q','u','e','s','t','.','5',')',0};
3157 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3158 URL_COMPONENTS uc;
3159 WCHAR *hostname, *path = NULL, *verb = NULL;
3160 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
3162 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
3163 debugstr_variant(&async));
3165 if (!method || !url) return E_INVALIDARG;
3167 memset( &uc, 0, sizeof(uc) );
3168 uc.dwStructSize = sizeof(uc);
3169 uc.dwSchemeLength = ~0u;
3170 uc.dwHostNameLength = ~0u;
3171 uc.dwUrlPathLength = ~0u;
3172 uc.dwExtraInfoLength = ~0u;
3173 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( get_last_error() );
3175 EnterCriticalSection( &request->cs );
3176 if (request->state < REQUEST_STATE_INITIALIZED) initialize_request( request );
3177 else reset_request( request );
3179 if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
3180 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
3181 hostname[uc.dwHostNameLength] = 0;
3183 if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
3184 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
3185 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
3187 if (!(verb = strdupW( method ))) goto error;
3188 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
3189 else request->async = FALSE;
3191 if (!request->hsession)
3193 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
3194 WINHTTP_FLAG_ASYNC )))
3196 err = get_last_error();
3197 goto error;
3199 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
3201 WinHttpCloseHandle( request->hsession );
3202 request->hsession = NULL;
3203 err = get_last_error();
3204 goto error;
3207 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
3209 err = get_last_error();
3210 goto error;
3213 len = sizeof(httpsW) / sizeof(WCHAR);
3214 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
3216 flags |= WINHTTP_FLAG_SECURE;
3218 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
3220 err = get_last_error();
3221 goto error;
3223 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
3225 request->state = REQUEST_STATE_OPEN;
3226 request->verb = verb;
3227 heap_free( hostname );
3228 heap_free( path );
3229 LeaveCriticalSection( &request->cs );
3230 return S_OK;
3232 error:
3233 WinHttpCloseHandle( request->hconnect );
3234 request->hconnect = NULL;
3235 heap_free( hostname );
3236 heap_free( path );
3237 heap_free( verb );
3238 LeaveCriticalSection( &request->cs );
3239 return HRESULT_FROM_WIN32( err );
3242 static HRESULT WINAPI winhttp_request_SetRequestHeader(
3243 IWinHttpRequest *iface,
3244 BSTR header,
3245 BSTR value )
3247 static const WCHAR fmtW[] = {'%','s',':',' ','%','s','\r','\n',0};
3248 static const WCHAR emptyW[] = {0};
3249 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3250 DWORD len, err = ERROR_SUCCESS;
3251 WCHAR *str;
3253 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
3255 if (!header) return E_INVALIDARG;
3257 EnterCriticalSection( &request->cs );
3258 if (request->state < REQUEST_STATE_OPEN)
3260 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
3261 goto done;
3263 if (request->state >= REQUEST_STATE_SENT)
3265 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
3266 goto done;
3268 len = strlenW( header ) + 4;
3269 if (value) len += strlenW( value );
3270 if (!(str = heap_alloc( (len + 1) * sizeof(WCHAR) )))
3272 err = ERROR_OUTOFMEMORY;
3273 goto done;
3275 sprintfW( str, fmtW, header, value ? value : emptyW );
3276 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
3277 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
3279 err = get_last_error();
3281 heap_free( str );
3283 done:
3284 LeaveCriticalSection( &request->cs );
3285 return HRESULT_FROM_WIN32( err );
3288 static HRESULT WINAPI winhttp_request_GetResponseHeader(
3289 IWinHttpRequest *iface,
3290 BSTR header,
3291 BSTR *value )
3293 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3294 DWORD size, err = ERROR_SUCCESS;
3296 TRACE("%p, %p\n", request, header);
3298 EnterCriticalSection( &request->cs );
3299 if (request->state < REQUEST_STATE_SENT)
3301 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3302 goto done;
3304 if (!header || !value)
3306 err = ERROR_INVALID_PARAMETER;
3307 goto done;
3309 size = 0;
3310 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
3312 err = get_last_error();
3313 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3315 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
3317 err = ERROR_OUTOFMEMORY;
3318 goto done;
3320 err = ERROR_SUCCESS;
3321 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
3323 err = get_last_error();
3324 SysFreeString( *value );
3326 done:
3327 LeaveCriticalSection( &request->cs );
3328 return HRESULT_FROM_WIN32( err );
3331 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
3332 IWinHttpRequest *iface,
3333 BSTR *headers )
3335 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3336 DWORD size, err = ERROR_SUCCESS;
3338 TRACE("%p, %p\n", request, headers);
3340 if (!headers) return E_INVALIDARG;
3342 EnterCriticalSection( &request->cs );
3343 if (request->state < REQUEST_STATE_SENT)
3345 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3346 goto done;
3348 size = 0;
3349 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
3351 err = get_last_error();
3352 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3354 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
3356 err = ERROR_OUTOFMEMORY;
3357 goto done;
3359 err = ERROR_SUCCESS;
3360 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
3362 err = get_last_error();
3363 SysFreeString( *headers );
3365 done:
3366 LeaveCriticalSection( &request->cs );
3367 return HRESULT_FROM_WIN32( err );
3370 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
3372 struct winhttp_request *request = (struct winhttp_request *)context;
3374 switch (status)
3376 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
3377 request->bytes_available = *(DWORD *)buffer;
3378 request->error = ERROR_SUCCESS;
3379 break;
3380 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
3381 request->bytes_read = size;
3382 request->error = ERROR_SUCCESS;
3383 break;
3384 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
3386 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
3387 request->error = result->dwError;
3388 break;
3390 default: break;
3392 SetEvent( request->wait );
3395 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
3397 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
3398 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
3401 static DWORD wait_for_completion( struct winhttp_request *request )
3403 HANDLE handles[2] = { request->wait, request->cancel };
3405 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
3407 case WAIT_OBJECT_0:
3408 break;
3409 case WAIT_OBJECT_0 + 1:
3410 request->error = ERROR_CANCELLED;
3411 break;
3412 default:
3413 request->error = get_last_error();
3414 break;
3416 return request->error;
3419 static HRESULT request_receive( struct winhttp_request *request )
3421 DWORD err, size, buflen = 4096;
3423 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
3424 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
3426 return HRESULT_FROM_WIN32( get_last_error() );
3428 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
3429 if (!strcmpW( request->verb, headW ))
3431 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
3432 return S_OK;
3434 if (!(request->buffer = heap_alloc( buflen ))) return E_OUTOFMEMORY;
3435 request->buffer[0] = 0;
3436 size = 0;
3439 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
3440 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
3442 err = get_last_error();
3443 goto error;
3445 if ((err = wait_for_completion( request ))) goto error;
3446 if (!request->bytes_available) break;
3447 size += request->bytes_available;
3448 if (buflen < size)
3450 char *tmp;
3451 while (buflen < size) buflen *= 2;
3452 if (!(tmp = heap_realloc( request->buffer, buflen )))
3454 err = ERROR_OUTOFMEMORY;
3455 goto error;
3457 request->buffer = tmp;
3459 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
3460 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
3461 request->bytes_available, &request->bytes_read ))
3463 err = get_last_error();
3464 goto error;
3466 if ((err = wait_for_completion( request ))) goto error;
3467 request->offset += request->bytes_read;
3468 } while (request->bytes_read);
3470 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
3471 return S_OK;
3473 error:
3474 heap_free( request->buffer );
3475 request->buffer = NULL;
3476 return HRESULT_FROM_WIN32( err );
3479 static DWORD request_set_parameters( struct winhttp_request *request )
3481 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
3482 sizeof(request->proxy) )) return get_last_error();
3484 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
3485 sizeof(request->logon_policy) )) return get_last_error();
3487 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
3488 sizeof(request->disable_feature) )) return get_last_error();
3490 if (!WinHttpSetTimeouts( request->hrequest,
3491 request->resolve_timeout,
3492 request->connect_timeout,
3493 request->send_timeout,
3494 request->receive_timeout )) return get_last_error();
3495 return ERROR_SUCCESS;
3498 static void request_set_utf8_content_type( struct winhttp_request *request )
3500 static const WCHAR fmtW[] = {'%','s',':',' ','%','s',0};
3501 static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n',0};
3502 static const WCHAR charset_utf8W[] = {'c','h','a','r','s','e','t','=','u','t','f','-','8',0};
3503 WCHAR headerW[64];
3504 int len;
3506 len = sprintfW( headerW, fmtW, attr_content_type, text_plainW );
3507 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
3508 len = sprintfW( headerW, fmtW, attr_content_type, charset_utf8W );
3509 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
3512 static HRESULT request_send( struct winhttp_request *request )
3514 SAFEARRAY *sa = NULL;
3515 VARIANT data;
3516 char *ptr = NULL;
3517 LONG size = 0;
3518 HRESULT hr;
3519 DWORD err;
3521 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
3522 if (strcmpW( request->verb, getW ))
3524 VariantInit( &data );
3525 if (V_VT( &request->data ) == VT_BSTR)
3527 UINT cp = CP_ACP;
3528 const WCHAR *str = V_BSTR( &request->data );
3529 int i, len = strlenW( str );
3531 for (i = 0; i < len; i++)
3533 if (str[i] > 127)
3535 cp = CP_UTF8;
3536 break;
3539 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
3540 if (!(ptr = heap_alloc( size ))) return E_OUTOFMEMORY;
3541 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
3542 if (cp == CP_UTF8) request_set_utf8_content_type( request );
3544 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
3546 sa = V_ARRAY( &data );
3547 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
3548 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
3550 SafeArrayUnaccessData( sa );
3551 return hr;
3553 size++;
3556 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT );
3557 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
3559 err = get_last_error();
3560 goto error;
3562 if ((err = wait_for_completion( request ))) goto error;
3563 if (sa) SafeArrayUnaccessData( sa );
3564 else heap_free( ptr );
3565 request->state = REQUEST_STATE_SENT;
3566 return S_OK;
3568 error:
3569 if (sa) SafeArrayUnaccessData( sa );
3570 else heap_free( ptr );
3571 return HRESULT_FROM_WIN32( err );
3574 static HRESULT request_send_and_receive( struct winhttp_request *request )
3576 HRESULT hr = request_send( request );
3577 if (hr == S_OK) hr = request_receive( request );
3578 return hr;
3581 static DWORD CALLBACK send_and_receive_proc( void *arg )
3583 struct winhttp_request *request = (struct winhttp_request *)arg;
3584 return request_send_and_receive( request );
3587 /* critical section must be held */
3588 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
3590 HANDLE thread = request->thread;
3591 DWORD err, ret;
3593 LeaveCriticalSection( &request->cs );
3594 while ((err = MsgWaitForMultipleObjects( 1, &thread, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
3596 MSG msg;
3597 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
3599 TranslateMessage( &msg );
3600 DispatchMessageW( &msg );
3603 switch (err)
3605 case WAIT_OBJECT_0:
3606 ret = ERROR_SUCCESS;
3607 break;
3608 case WAIT_TIMEOUT:
3609 ret = ERROR_TIMEOUT;
3610 break;
3611 case WAIT_FAILED:
3612 default:
3613 ret = get_last_error();
3614 break;
3616 EnterCriticalSection( &request->cs );
3617 return ret;
3620 static HRESULT WINAPI winhttp_request_Send(
3621 IWinHttpRequest *iface,
3622 VARIANT body )
3624 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3625 HRESULT hr;
3627 TRACE("%p, %s\n", request, debugstr_variant(&body));
3629 EnterCriticalSection( &request->cs );
3630 if (request->state < REQUEST_STATE_OPEN)
3632 LeaveCriticalSection( &request->cs );
3633 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
3635 if (request->state >= REQUEST_STATE_SENT)
3637 LeaveCriticalSection( &request->cs );
3638 return S_OK;
3640 VariantClear( &request->data );
3641 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
3643 LeaveCriticalSection( &request->cs );
3644 return hr;
3646 if (!(request->thread = CreateThread( NULL, 0, send_and_receive_proc, request, 0, NULL )))
3648 LeaveCriticalSection( &request->cs );
3649 return HRESULT_FROM_WIN32( get_last_error() );
3651 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
3652 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
3653 if (!request->async)
3655 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
3657 LeaveCriticalSection( &request->cs );
3658 return hr;
3661 static HRESULT WINAPI winhttp_request_get_Status(
3662 IWinHttpRequest *iface,
3663 LONG *status )
3665 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3666 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
3668 TRACE("%p, %p\n", request, status);
3670 if (!status) return E_INVALIDARG;
3672 EnterCriticalSection( &request->cs );
3673 if (request->state < REQUEST_STATE_SENT)
3675 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3676 goto done;
3678 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
3679 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
3681 err = get_last_error();
3682 goto done;
3684 *status = status_code;
3686 done:
3687 LeaveCriticalSection( &request->cs );
3688 return HRESULT_FROM_WIN32( err );
3691 static HRESULT WINAPI winhttp_request_get_StatusText(
3692 IWinHttpRequest *iface,
3693 BSTR *status )
3695 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3696 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
3698 TRACE("%p, %p\n", request, status);
3700 if (!status) return E_INVALIDARG;
3702 EnterCriticalSection( &request->cs );
3703 if (request->state < REQUEST_STATE_SENT)
3705 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3706 goto done;
3708 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
3710 err = get_last_error();
3711 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
3713 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
3715 err = ERROR_OUTOFMEMORY;
3716 goto done;
3718 index = 0;
3719 err = ERROR_SUCCESS;
3720 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
3722 err = get_last_error();
3723 SysFreeString( *status );
3725 done:
3726 LeaveCriticalSection( &request->cs );
3727 return HRESULT_FROM_WIN32( err );
3730 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
3732 static const WCHAR utf8W[] = {'u','t','f','-','8',0};
3733 static const WCHAR charsetW[] = {'c','h','a','r','s','e','t',0};
3734 WCHAR *buffer, *p;
3735 DWORD size;
3737 *codepage = CP_ACP;
3738 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
3739 get_last_error() == ERROR_INSUFFICIENT_BUFFER)
3741 if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY;
3742 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
3744 return get_last_error();
3746 if ((p = strstrW( buffer, charsetW )))
3748 p += strlenW( charsetW );
3749 while (*p == ' ') p++;
3750 if (*p++ == '=')
3752 while (*p == ' ') p++;
3753 if (!strcmpiW( p, utf8W )) *codepage = CP_UTF8;
3756 heap_free( buffer );
3758 return ERROR_SUCCESS;
3761 static HRESULT WINAPI winhttp_request_get_ResponseText(
3762 IWinHttpRequest *iface,
3763 BSTR *body )
3765 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3766 UINT codepage;
3767 DWORD err = ERROR_SUCCESS;
3768 int len;
3770 TRACE("%p, %p\n", request, body);
3772 if (!body) return E_INVALIDARG;
3774 EnterCriticalSection( &request->cs );
3775 if (request->state < REQUEST_STATE_SENT)
3777 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3778 goto done;
3780 if ((err = request_get_codepage( request, &codepage ))) goto done;
3781 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
3782 if (!(*body = SysAllocStringLen( NULL, len )))
3784 err = ERROR_OUTOFMEMORY;
3785 goto done;
3787 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
3788 (*body)[len] = 0;
3790 done:
3791 LeaveCriticalSection( &request->cs );
3792 return HRESULT_FROM_WIN32( err );
3795 static HRESULT WINAPI winhttp_request_get_ResponseBody(
3796 IWinHttpRequest *iface,
3797 VARIANT *body )
3799 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
3800 SAFEARRAY *sa;
3801 HRESULT hr;
3802 DWORD err = ERROR_SUCCESS;
3803 char *ptr;
3805 TRACE("%p, %p\n", request, body);
3807 if (!body) return E_INVALIDARG;
3809 EnterCriticalSection( &request->cs );
3810 if (request->state < REQUEST_STATE_SENT)
3812 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
3813 goto done;
3815 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
3817 err = ERROR_OUTOFMEMORY;
3818 goto done;
3820 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
3822 SafeArrayDestroy( sa );
3823 LeaveCriticalSection( &request->cs );
3824 return hr;
3826 memcpy( ptr, request->buffer, request->offset );
3827 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
3829 SafeArrayDestroy( sa );
3830 LeaveCriticalSection( &request->cs );
3831 return hr;
3833 V_VT( body ) = VT_ARRAY|VT_UI1;
3834 V_ARRAY( body ) = sa;
3836 done:
3837 LeaveCriticalSection( &request->cs );
3838 return HRESULT_FROM_WIN32( err );
3841 struct stream
3843 IStream IStream_iface;
3844 LONG refs;
3845 char *data;
3846 ULARGE_INTEGER pos, size;
3849 static inline struct stream *impl_from_IStream( IStream *iface )
3851 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
3854 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
3856 struct stream *stream = impl_from_IStream( iface );
3858 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
3860 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
3862 *obj = iface;
3864 else
3866 FIXME("interface %s not implemented\n", debugstr_guid(riid));
3867 return E_NOINTERFACE;
3869 IStream_AddRef( iface );
3870 return S_OK;
3873 static ULONG WINAPI stream_AddRef( IStream *iface )
3875 struct stream *stream = impl_from_IStream( iface );
3876 return InterlockedIncrement( &stream->refs );
3879 static ULONG WINAPI stream_Release( IStream *iface )
3881 struct stream *stream = impl_from_IStream( iface );
3882 LONG refs = InterlockedDecrement( &stream->refs );
3883 if (!refs)
3885 heap_free( stream->data );
3886 heap_free( stream );
3888 return refs;
3891 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
3893 struct stream *stream = impl_from_IStream( iface );
3894 ULONG size;
3896 if (stream->pos.QuadPart >= stream->size.QuadPart)
3898 *read = 0;
3899 return S_FALSE;
3902 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
3903 memcpy( buf, stream->data + stream->pos.QuadPart, size );
3904 stream->pos.QuadPart += size;
3905 *read = size;
3907 return S_OK;
3910 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
3912 FIXME("\n");
3913 return E_NOTIMPL;
3916 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
3918 struct stream *stream = impl_from_IStream( iface );
3920 if (origin == STREAM_SEEK_SET)
3921 stream->pos.QuadPart = move.QuadPart;
3922 else if (origin == STREAM_SEEK_CUR)
3923 stream->pos.QuadPart += move.QuadPart;
3924 else if (origin == STREAM_SEEK_END)
3925 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
3927 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
3928 return S_OK;
3931 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
3933 FIXME("\n");
3934 return E_NOTIMPL;
3937 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
3938 ULARGE_INTEGER *written )
3940 FIXME("\n");
3941 return E_NOTIMPL;
3944 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
3946 FIXME("\n");
3947 return E_NOTIMPL;
3950 static HRESULT WINAPI stream_Revert( IStream *iface )
3952 FIXME("\n");
3953 return E_NOTIMPL;
3956 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
3958 FIXME("\n");
3959 return E_NOTIMPL;
3962 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
3964 FIXME("\n");
3965 return E_NOTIMPL;
3968 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
3970 FIXME("\n");
3971 return E_NOTIMPL;
3974 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
3976 FIXME("\n");
3977 return E_NOTIMPL;
3980 static const IStreamVtbl stream_vtbl =
3982 stream_QueryInterface,
3983 stream_AddRef,
3984 stream_Release,
3985 stream_Read,
3986 stream_Write,
3987 stream_Seek,
3988 stream_SetSize,
3989 stream_CopyTo,
3990 stream_Commit,
3991 stream_Revert,
3992 stream_LockRegion,
3993 stream_UnlockRegion,
3994 stream_Stat,
3995 stream_Clone
3998 static HRESULT WINAPI winhttp_request_get_ResponseStream(
3999 IWinHttpRequest *iface,
4000 VARIANT *body )
4002 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4003 DWORD err = ERROR_SUCCESS;
4004 struct stream *stream;
4006 TRACE("%p, %p\n", request, body);
4008 if (!body) return E_INVALIDARG;
4010 EnterCriticalSection( &request->cs );
4011 if (request->state < REQUEST_STATE_SENT)
4013 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4014 goto done;
4016 if (!(stream = heap_alloc( sizeof(*stream) )))
4018 err = ERROR_OUTOFMEMORY;
4019 goto done;
4021 stream->IStream_iface.lpVtbl = &stream_vtbl;
4022 stream->refs = 1;
4023 if (!(stream->data = heap_alloc( request->offset )))
4025 heap_free( stream );
4026 err = ERROR_OUTOFMEMORY;
4027 goto done;
4029 memcpy( stream->data, request->buffer, request->offset );
4030 stream->pos.QuadPart = 0;
4031 stream->size.QuadPart = request->offset;
4032 V_VT( body ) = VT_UNKNOWN;
4033 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
4035 done:
4036 LeaveCriticalSection( &request->cs );
4037 return HRESULT_FROM_WIN32( err );
4040 static HRESULT WINAPI winhttp_request_get_Option(
4041 IWinHttpRequest *iface,
4042 WinHttpRequestOption option,
4043 VARIANT *value )
4045 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4046 HRESULT hr = S_OK;
4048 TRACE("%p, %u, %p\n", request, option, value);
4050 EnterCriticalSection( &request->cs );
4051 switch (option)
4053 case WinHttpRequestOption_URLCodePage:
4054 V_VT( value ) = VT_I4;
4055 V_I4( value ) = request->url_codepage;
4056 break;
4057 default:
4058 FIXME("unimplemented option %u\n", option);
4059 hr = E_NOTIMPL;
4060 break;
4062 LeaveCriticalSection( &request->cs );
4063 return hr;
4066 static HRESULT WINAPI winhttp_request_put_Option(
4067 IWinHttpRequest *iface,
4068 WinHttpRequestOption option,
4069 VARIANT value )
4071 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4072 HRESULT hr = S_OK;
4074 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
4076 EnterCriticalSection( &request->cs );
4077 switch (option)
4079 case WinHttpRequestOption_EnableRedirects:
4081 if (V_BOOL( &value ))
4082 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
4083 else
4084 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
4085 break;
4087 case WinHttpRequestOption_URLCodePage:
4089 static const WCHAR utf8W[] = {'u','t','f','-','8',0};
4090 VARIANT cp;
4092 VariantInit( &cp );
4093 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
4094 if (SUCCEEDED( hr ))
4096 request->url_codepage = V_UI4( &cp );
4097 TRACE("URL codepage: %u\n", request->url_codepage);
4099 else if (V_VT( &value ) == VT_BSTR && !strcmpiW( V_BSTR( &value ), utf8W ))
4101 TRACE("URL codepage: UTF-8\n");
4102 request->url_codepage = CP_UTF8;
4103 hr = S_OK;
4105 else
4106 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
4107 break;
4109 default:
4110 FIXME("unimplemented option %u\n", option);
4111 hr = E_NOTIMPL;
4112 break;
4114 LeaveCriticalSection( &request->cs );
4115 return hr;
4118 static HRESULT WINAPI winhttp_request_WaitForResponse(
4119 IWinHttpRequest *iface,
4120 VARIANT timeout,
4121 VARIANT_BOOL *succeeded )
4123 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4124 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
4126 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
4128 EnterCriticalSection( &request->cs );
4129 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
4131 LeaveCriticalSection( &request->cs );
4132 return S_OK;
4134 switch ((err = request_wait( request, msecs )))
4136 case ERROR_TIMEOUT:
4137 if (succeeded) *succeeded = VARIANT_FALSE;
4138 err = ERROR_SUCCESS;
4139 break;
4141 case ERROR_SUCCESS:
4142 if (succeeded) *succeeded = VARIANT_TRUE;
4143 break;
4145 default: break;
4147 LeaveCriticalSection( &request->cs );
4148 return HRESULT_FROM_WIN32( err );
4151 static HRESULT WINAPI winhttp_request_Abort(
4152 IWinHttpRequest *iface )
4154 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4156 TRACE("%p\n", request);
4158 EnterCriticalSection( &request->cs );
4159 cancel_request( request );
4160 LeaveCriticalSection( &request->cs );
4161 return S_OK;
4164 static HRESULT WINAPI winhttp_request_SetTimeouts(
4165 IWinHttpRequest *iface,
4166 LONG resolve_timeout,
4167 LONG connect_timeout,
4168 LONG send_timeout,
4169 LONG receive_timeout )
4171 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4173 TRACE("%p, %d, %d, %d, %d\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout);
4175 EnterCriticalSection( &request->cs );
4176 request->resolve_timeout = resolve_timeout;
4177 request->connect_timeout = connect_timeout;
4178 request->send_timeout = send_timeout;
4179 request->receive_timeout = receive_timeout;
4180 LeaveCriticalSection( &request->cs );
4181 return S_OK;
4184 static HRESULT WINAPI winhttp_request_SetClientCertificate(
4185 IWinHttpRequest *iface,
4186 BSTR certificate )
4188 FIXME("\n");
4189 return E_NOTIMPL;
4192 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
4193 IWinHttpRequest *iface,
4194 WinHttpRequestAutoLogonPolicy policy )
4196 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4197 HRESULT hr = S_OK;
4199 TRACE("%p, %u\n", request, policy );
4201 EnterCriticalSection( &request->cs );
4202 switch (policy)
4204 case AutoLogonPolicy_Always:
4205 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
4206 break;
4207 case AutoLogonPolicy_OnlyIfBypassProxy:
4208 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
4209 break;
4210 case AutoLogonPolicy_Never:
4211 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
4212 break;
4213 default: hr = E_INVALIDARG;
4214 break;
4216 LeaveCriticalSection( &request->cs );
4217 return hr;
4220 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
4222 winhttp_request_QueryInterface,
4223 winhttp_request_AddRef,
4224 winhttp_request_Release,
4225 winhttp_request_GetTypeInfoCount,
4226 winhttp_request_GetTypeInfo,
4227 winhttp_request_GetIDsOfNames,
4228 winhttp_request_Invoke,
4229 winhttp_request_SetProxy,
4230 winhttp_request_SetCredentials,
4231 winhttp_request_Open,
4232 winhttp_request_SetRequestHeader,
4233 winhttp_request_GetResponseHeader,
4234 winhttp_request_GetAllResponseHeaders,
4235 winhttp_request_Send,
4236 winhttp_request_get_Status,
4237 winhttp_request_get_StatusText,
4238 winhttp_request_get_ResponseText,
4239 winhttp_request_get_ResponseBody,
4240 winhttp_request_get_ResponseStream,
4241 winhttp_request_get_Option,
4242 winhttp_request_put_Option,
4243 winhttp_request_WaitForResponse,
4244 winhttp_request_Abort,
4245 winhttp_request_SetTimeouts,
4246 winhttp_request_SetClientCertificate,
4247 winhttp_request_SetAutoLogonPolicy
4250 HRESULT WinHttpRequest_create( void **obj )
4252 struct winhttp_request *request;
4254 TRACE("%p\n", obj);
4256 if (!(request = heap_alloc( sizeof(*request) ))) return E_OUTOFMEMORY;
4257 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
4258 request->refs = 1;
4259 request->state = REQUEST_STATE_UNINITIALIZED;
4260 request->proxy.lpszProxy = NULL;
4261 request->proxy.lpszProxyBypass = NULL;
4262 request->url_codepage = CP_UTF8;
4263 InitializeCriticalSection( &request->cs );
4264 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
4266 *obj = &request->IWinHttpRequest_iface;
4267 TRACE("returning iface %p\n", *obj);
4268 return S_OK;