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