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