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