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