1 #ifdef __REACTOS__ 2 #include "precomp.h" 3 #include "zlib.h" 4 #else/* 5 * Wininet - HTTP Implementation 6 * 7 * Copyright 1999 Corel Corporation 8 * Copyright 2002 CodeWeavers Inc. 9 * Copyright 2002 TransGaming Technologies Inc. 10 * Copyright 2004 Mike McCormack for CodeWeavers 11 * Copyright 2005 Aric Stewart for CodeWeavers 12 * Copyright 2006 Robert Shearman for CodeWeavers 13 * Copyright 2011 Jacek Caban for CodeWeavers 14 * 15 * Ulrich Czekalla 16 * David Hammerton 17 * 18 * This library is free software; you can redistribute it and/or 19 * modify it under the terms of the GNU Lesser General Public 20 * License as published by the Free Software Foundation; either 21 * version 2.1 of the License, or (at your option) any later version. 22 * 23 * This library is distributed in the hope that it will be useful, 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 26 * Lesser General Public License for more details. 27 * 28 * You should have received a copy of the GNU Lesser General Public 29 * License along with this library; if not, write to the Free Software 30 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 31 */ 32 33 #include <stdlib.h> 34 35 #include "winsock2.h" 36 #include "ws2ipdef.h" 37 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <time.h> 41 #include <assert.h> 42 #include <errno.h> 43 #include <limits.h> 44 45 #include "windef.h" 46 #include "winbase.h" 47 #include "wininet.h" 48 #include "winerror.h" 49 #include "winternl.h" 50 #define NO_SHLWAPI_STREAM 51 #define NO_SHLWAPI_REG 52 #define NO_SHLWAPI_GDI 53 #include "shlwapi.h" 54 #include "sspi.h" 55 #include "wincrypt.h" 56 #include "winuser.h" 57 58 #include "internet.h" 59 #include "zlib.h" 60 #include "resource.h" 61 #include "wine/debug.h" 62 #include "wine/exception.h" 63 #endif /* defined(__REACTOS__) */ 64 65 WINE_DEFAULT_DEBUG_CHANNEL(wininet); 66 67 #define HTTP_ADDHDR_FLAG_ADD 0x20000000 68 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000 69 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000 70 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000 71 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000 72 #define HTTP_ADDHDR_FLAG_REQ 0x02000000 73 74 #define COLLECT_TIME 60000 75 76 struct HttpAuthInfo 77 { 78 LPWSTR scheme; 79 CredHandle cred; 80 CtxtHandle ctx; 81 TimeStamp exp; 82 ULONG attr; 83 ULONG max_token; 84 void *auth_data; 85 unsigned int auth_data_len; 86 BOOL finished; /* finished authenticating */ 87 }; 88 89 90 typedef struct _basicAuthorizationData 91 { 92 struct list entry; 93 94 LPWSTR host; 95 LPWSTR realm; 96 LPSTR authorization; 97 UINT authorizationLen; 98 } basicAuthorizationData; 99 100 typedef struct _authorizationData 101 { 102 struct list entry; 103 104 LPWSTR host; 105 LPWSTR scheme; 106 LPWSTR domain; 107 UINT domain_len; 108 LPWSTR user; 109 UINT user_len; 110 LPWSTR password; 111 UINT password_len; 112 } authorizationData; 113 114 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache); 115 static struct list authorizationCache = LIST_INIT(authorizationCache); 116 117 static CRITICAL_SECTION authcache_cs; 118 static CRITICAL_SECTION_DEBUG critsect_debug = 119 { 120 0, 0, &authcache_cs, 121 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 122 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") } 123 }; 124 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; 125 126 static DWORD HTTP_GetResponseHeaders(http_request_t *req, INT *len); 127 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier); 128 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer); 129 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr); 130 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request); 131 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index); 132 static LPWSTR HTTP_build_req( LPCWSTR *list, int len ); 133 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD); 134 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin); 135 static DWORD drain_content(http_request_t*,BOOL); 136 137 static CRITICAL_SECTION connection_pool_cs; 138 static CRITICAL_SECTION_DEBUG connection_pool_debug = 139 { 140 0, 0, &connection_pool_cs, 141 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList }, 142 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") } 143 }; 144 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 }; 145 146 static struct list connection_pool = LIST_INIT(connection_pool); 147 static BOOL collector_running; 148 149 void server_addref(server_t *server) 150 { 151 InterlockedIncrement(&server->ref); 152 } 153 154 void server_release(server_t *server) 155 { 156 if(InterlockedDecrement(&server->ref)) 157 return; 158 159 #ifdef __REACTOS__ 160 EnterCriticalSection(&connection_pool_cs); 161 #endif 162 list_remove(&server->entry); 163 #ifdef __REACTOS__ 164 LeaveCriticalSection(&connection_pool_cs); 165 #endif 166 167 if(server->cert_chain) 168 CertFreeCertificateChain(server->cert_chain); 169 heap_free(server->name); 170 heap_free(server->scheme_host_port); 171 heap_free(server); 172 } 173 174 static BOOL process_host_port(server_t *server) 175 { 176 BOOL default_port; 177 size_t name_len, len; 178 WCHAR *buf; 179 180 name_len = lstrlenW(server->name); 181 len = name_len + 10 /* strlen("://:<port>") */ + ARRAY_SIZE(L"https"); 182 buf = heap_alloc( len * sizeof(WCHAR) ); 183 if(!buf) 184 return FALSE; 185 186 swprintf(buf, len, L"%s://%s:%u", server->is_https ? L"https" : L"http", server->name, server->port); 187 server->scheme_host_port = buf; 188 189 server->host_port = server->scheme_host_port + 7 /* strlen("http://") */; 190 if(server->is_https) 191 server->host_port++; 192 193 default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT); 194 server->canon_host_port = default_port ? server->name : server->host_port; 195 return TRUE; 196 } 197 198 server_t *get_server(substr_t name, INTERNET_PORT port, BOOL is_https, BOOL do_create) 199 { 200 server_t *iter, *server = NULL; 201 202 EnterCriticalSection(&connection_pool_cs); 203 204 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) { 205 if(iter->port == port && name.len == lstrlenW(iter->name) && !wcsnicmp(iter->name, name.str, name.len) 206 && iter->is_https == is_https) { 207 server = iter; 208 server_addref(server); 209 break; 210 } 211 } 212 213 if(!server && do_create) { 214 server = heap_alloc_zero(sizeof(*server)); 215 if(server) { 216 server->ref = 2; /* list reference and return */ 217 server->port = port; 218 server->is_https = is_https; 219 list_init(&server->conn_pool); 220 server->name = heap_strndupW(name.str, name.len); 221 if(server->name && process_host_port(server)) { 222 list_add_head(&connection_pool, &server->entry); 223 }else { 224 heap_free(server); 225 server = NULL; 226 } 227 } 228 } 229 230 LeaveCriticalSection(&connection_pool_cs); 231 232 return server; 233 } 234 235 BOOL collect_connections(collect_type_t collect_type) 236 { 237 netconn_t *netconn, *netconn_safe; 238 server_t *server, *server_safe; 239 BOOL remaining = FALSE; 240 DWORD64 now; 241 242 #ifdef __REACTOS__ 243 now = GetTickCount(); 244 #else 245 now = GetTickCount64(); 246 #endif 247 248 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) { 249 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) { 250 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) { 251 TRACE("freeing %p\n", netconn); 252 list_remove(&netconn->pool_entry); 253 free_netconn(netconn); 254 }else { 255 remaining = TRUE; 256 } 257 } 258 259 if(collect_type == COLLECT_CLEANUP) { 260 list_remove(&server->entry); 261 list_init(&server->entry); 262 server_release(server); 263 } 264 } 265 266 return remaining; 267 } 268 269 static DWORD WINAPI collect_connections_proc(void *arg) 270 { 271 BOOL remaining_conns; 272 273 do { 274 /* FIXME: Use more sophisticated method */ 275 Sleep(5000); 276 277 EnterCriticalSection(&connection_pool_cs); 278 279 remaining_conns = collect_connections(COLLECT_TIMEOUT); 280 if(!remaining_conns) 281 collector_running = FALSE; 282 283 LeaveCriticalSection(&connection_pool_cs); 284 }while(remaining_conns); 285 286 FreeLibraryAndExitThread(WININET_hModule, 0); 287 } 288 289 /*********************************************************************** 290 * HTTP_GetHeader (internal) 291 * 292 * Headers section must be held 293 */ 294 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head) 295 { 296 int HeaderIndex = 0; 297 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE); 298 if (HeaderIndex == -1) 299 return NULL; 300 else 301 return &req->custHeaders[HeaderIndex]; 302 } 303 304 static WCHAR *get_host_header( http_request_t *req ) 305 { 306 HTTPHEADERW *header; 307 WCHAR *ret = NULL; 308 309 EnterCriticalSection( &req->headers_section ); 310 if ((header = HTTP_GetHeader( req, L"Host" ))) ret = heap_strdupW( header->lpszValue ); 311 else ret = heap_strdupW( req->server->canon_host_port ); 312 LeaveCriticalSection( &req->headers_section ); 313 return ret; 314 } 315 316 struct data_stream_vtbl_t { 317 BOOL (*end_of_data)(data_stream_t*,http_request_t*); 318 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,BOOL); 319 DWORD (*drain_content)(data_stream_t*,http_request_t*,BOOL); 320 void (*destroy)(data_stream_t*); 321 }; 322 323 typedef struct { 324 data_stream_t data_stream; 325 326 BYTE buf[READ_BUFFER_SIZE]; 327 DWORD buf_size; 328 DWORD buf_pos; 329 DWORD chunk_size; 330 331 enum { 332 CHUNKED_STREAM_STATE_READING_CHUNK_SIZE, 333 CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE, 334 CHUNKED_STREAM_STATE_READING_CHUNK, 335 CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA, 336 CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END, 337 CHUNKED_STREAM_STATE_END_OF_STREAM, 338 CHUNKED_STREAM_STATE_ERROR 339 } state; 340 } chunked_stream_t; 341 342 static inline void destroy_data_stream(data_stream_t *stream) 343 { 344 stream->vtbl->destroy(stream); 345 } 346 347 static void reset_data_stream(http_request_t *req) 348 { 349 destroy_data_stream(req->data_stream); 350 req->data_stream = &req->netconn_stream.data_stream; 351 req->read_pos = req->read_size = req->netconn_stream.content_read = 0; 352 req->read_gzip = FALSE; 353 } 354 355 static void remove_header( http_request_t *request, const WCHAR *str, BOOL from_request ) 356 { 357 int index; 358 EnterCriticalSection( &request->headers_section ); 359 index = HTTP_GetCustomHeaderIndex( request, str, 0, from_request ); 360 if (index != -1) HTTP_DeleteCustomHeader( request, index ); 361 LeaveCriticalSection( &request->headers_section ); 362 } 363 364 typedef struct { 365 data_stream_t stream; 366 data_stream_t *parent_stream; 367 z_stream zstream; 368 BYTE buf[READ_BUFFER_SIZE]; 369 DWORD buf_size; 370 DWORD buf_pos; 371 BOOL end_of_data; 372 } gzip_stream_t; 373 374 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req) 375 { 376 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream; 377 return gzip_stream->end_of_data 378 || (!gzip_stream->buf_size && gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req)); 379 } 380 381 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size, 382 DWORD *read, BOOL allow_blocking) 383 { 384 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream; 385 z_stream *zstream = &gzip_stream->zstream; 386 DWORD current_read, ret_read = 0; 387 int zres; 388 DWORD res = ERROR_SUCCESS; 389 390 TRACE("(%d %x)\n", size, allow_blocking); 391 392 while(size && !gzip_stream->end_of_data) { 393 if(!gzip_stream->buf_size) { 394 if(gzip_stream->buf_pos) { 395 if(gzip_stream->buf_size) 396 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size); 397 gzip_stream->buf_pos = 0; 398 } 399 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size, 400 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, allow_blocking); 401 if(res != ERROR_SUCCESS) 402 break; 403 404 gzip_stream->buf_size += current_read; 405 if(!current_read) { 406 WARN("unexpected end of data\n"); 407 gzip_stream->end_of_data = TRUE; 408 break; 409 } 410 } 411 412 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos; 413 zstream->avail_in = gzip_stream->buf_size; 414 zstream->next_out = buf+ret_read; 415 zstream->avail_out = size; 416 zres = inflate(&gzip_stream->zstream, 0); 417 current_read = size - zstream->avail_out; 418 size -= current_read; 419 ret_read += current_read; 420 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos); 421 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf; 422 if(zres == Z_STREAM_END) { 423 TRACE("end of data\n"); 424 gzip_stream->end_of_data = TRUE; 425 inflateEnd(zstream); 426 }else if(zres != Z_OK) { 427 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg)); 428 if(!ret_read) 429 res = ERROR_INTERNET_DECODING_FAILED; 430 break; 431 } 432 433 if(ret_read) 434 allow_blocking = FALSE; 435 } 436 437 TRACE("read %u bytes\n", ret_read); 438 if(ret_read) 439 res = ERROR_SUCCESS; 440 *read = ret_read; 441 return res; 442 } 443 444 static DWORD gzip_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking) 445 { 446 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream; 447 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req, allow_blocking); 448 } 449 450 static void gzip_destroy(data_stream_t *stream) 451 { 452 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream; 453 454 destroy_data_stream(gzip_stream->parent_stream); 455 456 if(!gzip_stream->end_of_data) 457 inflateEnd(&gzip_stream->zstream); 458 heap_free(gzip_stream); 459 } 460 461 static const data_stream_vtbl_t gzip_stream_vtbl = { 462 gzip_end_of_data, 463 gzip_read, 464 gzip_drain_content, 465 gzip_destroy 466 }; 467 468 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size) 469 { 470 return heap_alloc(items*size); 471 } 472 473 static void wininet_zfree(voidpf opaque, voidpf address) 474 { 475 heap_free(address); 476 } 477 478 static DWORD init_gzip_stream(http_request_t *req, BOOL is_gzip) 479 { 480 gzip_stream_t *gzip_stream; 481 int zres; 482 483 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t)); 484 if(!gzip_stream) 485 return ERROR_OUTOFMEMORY; 486 487 gzip_stream->stream.vtbl = &gzip_stream_vtbl; 488 gzip_stream->zstream.zalloc = wininet_zalloc; 489 gzip_stream->zstream.zfree = wininet_zfree; 490 491 zres = inflateInit2(&gzip_stream->zstream, is_gzip ? 0x1f : -15); 492 if(zres != Z_OK) { 493 ERR("inflateInit failed: %d\n", zres); 494 heap_free(gzip_stream); 495 return ERROR_OUTOFMEMORY; 496 } 497 498 remove_header(req, L"Content-Length", FALSE); 499 500 if(req->read_size) { 501 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size); 502 gzip_stream->buf_size = req->read_size; 503 req->read_pos = req->read_size = 0; 504 } 505 506 req->read_gzip = TRUE; 507 gzip_stream->parent_stream = req->data_stream; 508 req->data_stream = &gzip_stream->stream; 509 return ERROR_SUCCESS; 510 } 511 512 /*********************************************************************** 513 * HTTP_FreeTokens (internal) 514 * 515 * Frees table of pointers. 516 */ 517 static void HTTP_FreeTokens(LPWSTR * token_array) 518 { 519 int i; 520 for (i = 0; token_array[i]; i++) heap_free(token_array[i]); 521 heap_free(token_array); 522 } 523 524 static void HTTP_FixURL(http_request_t *request) 525 { 526 /* If we don't have a path we set it to root */ 527 if (NULL == request->path) 528 request->path = heap_strdupW(L"/"); 529 else /* remove \r and \n*/ 530 { 531 int nLen = lstrlenW(request->path); 532 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n'))) 533 { 534 nLen--; 535 request->path[nLen]='\0'; 536 } 537 /* Replace '\' with '/' */ 538 while (nLen>0) { 539 nLen--; 540 if (request->path[nLen] == '\\') request->path[nLen]='/'; 541 } 542 } 543 544 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE, 545 request->path, lstrlenW(request->path), L"http://", lstrlenW(L"http://") ) 546 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */ 547 { 548 WCHAR *fixurl = heap_alloc((lstrlenW(request->path) + 2)*sizeof(WCHAR)); 549 *fixurl = '/'; 550 lstrcpyW(fixurl + 1, request->path); 551 heap_free( request->path ); 552 request->path = fixurl; 553 } 554 } 555 556 static WCHAR* build_request_header(http_request_t *request, const WCHAR *verb, 557 const WCHAR *path, const WCHAR *version, BOOL use_cr) 558 { 559 LPWSTR requestString; 560 DWORD len, n; 561 LPCWSTR *req; 562 UINT i; 563 564 EnterCriticalSection( &request->headers_section ); 565 566 /* allocate space for an array of all the string pointers to be added */ 567 len = request->nCustHeaders * 5 + 10; 568 if (!(req = heap_alloc( len * sizeof(const WCHAR *) ))) 569 { 570 LeaveCriticalSection( &request->headers_section ); 571 return NULL; 572 } 573 574 /* add the verb, path and HTTP version string */ 575 n = 0; 576 req[n++] = verb; 577 req[n++] = L" "; 578 req[n++] = path; 579 req[n++] = L" "; 580 req[n++] = version; 581 if (use_cr) 582 req[n++] = L"\r"; 583 req[n++] = L"\n"; 584 585 /* Append custom request headers */ 586 for (i = 0; i < request->nCustHeaders; i++) 587 { 588 if (request->custHeaders[i].wFlags & HDR_ISREQUEST) 589 { 590 req[n++] = request->custHeaders[i].lpszField; 591 req[n++] = L": "; 592 req[n++] = request->custHeaders[i].lpszValue; 593 if (use_cr) 594 req[n++] = L"\r"; 595 req[n++] = L"\n"; 596 597 TRACE("Adding custom header %s (%s)\n", 598 debugstr_w(request->custHeaders[i].lpszField), 599 debugstr_w(request->custHeaders[i].lpszValue)); 600 } 601 } 602 if (use_cr) 603 req[n++] = L"\r"; 604 req[n++] = L"\n"; 605 req[n] = NULL; 606 607 requestString = HTTP_build_req( req, 4 ); 608 heap_free( req ); 609 LeaveCriticalSection( &request->headers_section ); 610 return requestString; 611 } 612 613 static WCHAR* build_response_header(http_request_t *request, BOOL use_cr) 614 { 615 const WCHAR **req; 616 WCHAR *ret, buf[14]; 617 DWORD i, n = 0; 618 619 EnterCriticalSection( &request->headers_section ); 620 621 if (!(req = heap_alloc( (request->nCustHeaders * 5 + 8) * sizeof(WCHAR *) ))) 622 { 623 LeaveCriticalSection( &request->headers_section ); 624 return NULL; 625 } 626 627 if (request->status_code) 628 { 629 req[n++] = request->version; 630 swprintf(buf, ARRAY_SIZE(buf), L" %u ", request->status_code); 631 req[n++] = buf; 632 req[n++] = request->statusText; 633 if (use_cr) 634 req[n++] = L"\r"; 635 req[n++] = L"\n"; 636 } 637 638 for(i = 0; i < request->nCustHeaders; i++) 639 { 640 if(!(request->custHeaders[i].wFlags & HDR_ISREQUEST) 641 && wcscmp(request->custHeaders[i].lpszField, L"Status")) 642 { 643 req[n++] = request->custHeaders[i].lpszField; 644 req[n++] = L": "; 645 req[n++] = request->custHeaders[i].lpszValue; 646 if(use_cr) 647 req[n++] = L"\r"; 648 req[n++] = L"\n"; 649 650 TRACE("Adding custom header %s (%s)\n", 651 debugstr_w(request->custHeaders[i].lpszField), 652 debugstr_w(request->custHeaders[i].lpszValue)); 653 } 654 } 655 if(use_cr) 656 req[n++] = L"\r"; 657 req[n++] = L"\n"; 658 req[n] = NULL; 659 660 ret = HTTP_build_req(req, 0); 661 heap_free(req); 662 LeaveCriticalSection( &request->headers_section ); 663 return ret; 664 } 665 666 static void HTTP_ProcessCookies( http_request_t *request ) 667 { 668 int HeaderIndex; 669 int numCookies = 0; 670 LPHTTPHEADERW setCookieHeader; 671 WCHAR *path, *tmp; 672 673 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) 674 return; 675 676 path = heap_strdupW(request->path); 677 if (!path) 678 return; 679 680 tmp = wcsrchr(path, '/'); 681 if (tmp && tmp[1]) tmp[1] = 0; 682 683 EnterCriticalSection( &request->headers_section ); 684 685 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, L"Set-Cookie", numCookies++, FALSE)) != -1) 686 { 687 const WCHAR *data; 688 substr_t name; 689 690 setCookieHeader = &request->custHeaders[HeaderIndex]; 691 692 if (!setCookieHeader->lpszValue) 693 continue; 694 695 data = wcschr(setCookieHeader->lpszValue, '='); 696 if(!data) 697 continue; 698 699 name = substr(setCookieHeader->lpszValue, data - setCookieHeader->lpszValue); 700 data++; 701 set_cookie(substrz(request->server->name), substrz(path), name, substrz(data), INTERNET_COOKIE_HTTPONLY); 702 } 703 704 LeaveCriticalSection( &request->headers_section ); 705 heap_free(path); 706 } 707 708 static void strip_spaces(LPWSTR start) 709 { 710 LPWSTR str = start; 711 LPWSTR end; 712 713 while (*str == ' ') 714 str++; 715 716 if (str != start) 717 memmove(start, str, sizeof(WCHAR) * (lstrlenW(str) + 1)); 718 719 end = start + lstrlenW(start) - 1; 720 while (end >= start && *end == ' ') 721 { 722 *end = '\0'; 723 end--; 724 } 725 } 726 727 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm ) 728 { 729 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */ 730 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */ 731 BOOL is_basic; 732 is_basic = !wcsnicmp(pszAuthValue, szBasic, ARRAY_SIZE(szBasic)) && 733 ((pszAuthValue[ARRAY_SIZE(szBasic)] == ' ') || !pszAuthValue[ARRAY_SIZE(szBasic)]); 734 if (is_basic && pszRealm) 735 { 736 LPCWSTR token; 737 LPCWSTR ptr = &pszAuthValue[ARRAY_SIZE(szBasic)]; 738 LPCWSTR realm; 739 ptr++; 740 *pszRealm=NULL; 741 token = wcschr(ptr,'='); 742 if (!token) 743 return TRUE; 744 realm = ptr; 745 while (*realm == ' ') 746 realm++; 747 if(!wcsnicmp(realm, szRealm, ARRAY_SIZE(szRealm)) && 748 (realm[ARRAY_SIZE(szRealm)] == ' ' || realm[ARRAY_SIZE(szRealm)] == '=')) 749 { 750 token++; 751 while (*token == ' ') 752 token++; 753 if (*token == '\0') 754 return TRUE; 755 *pszRealm = heap_strdupW(token); 756 strip_spaces(*pszRealm); 757 } 758 } 759 760 return is_basic; 761 } 762 763 static void destroy_authinfo( struct HttpAuthInfo *authinfo ) 764 { 765 if (!authinfo) return; 766 767 if (SecIsValidHandle(&authinfo->ctx)) 768 DeleteSecurityContext(&authinfo->ctx); 769 if (SecIsValidHandle(&authinfo->cred)) 770 FreeCredentialsHandle(&authinfo->cred); 771 772 heap_free(authinfo->auth_data); 773 heap_free(authinfo->scheme); 774 heap_free(authinfo); 775 } 776 777 static UINT retrieve_cached_basic_authorization(http_request_t *req, const WCHAR *host, const WCHAR *realm, char **auth_data) 778 { 779 basicAuthorizationData *ad; 780 UINT rc = 0; 781 782 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm)); 783 784 EnterCriticalSection(&authcache_cs); 785 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry) 786 { 787 if (!wcsicmp(host, ad->host) && (!realm || !wcscmp(realm, ad->realm))) 788 { 789 char *colon; 790 DWORD length; 791 792 TRACE("Authorization found in cache\n"); 793 *auth_data = heap_alloc(ad->authorizationLen); 794 memcpy(*auth_data,ad->authorization,ad->authorizationLen); 795 rc = ad->authorizationLen; 796 797 /* update session username and password to reflect current credentials */ 798 colon = strchr(ad->authorization, ':'); 799 length = colon - ad->authorization; 800 801 heap_free(req->session->userName); 802 heap_free(req->session->password); 803 804 req->session->userName = heap_strndupAtoW(ad->authorization, length, &length); 805 length++; 806 req->session->password = heap_strndupAtoW(&ad->authorization[length], ad->authorizationLen - length, &length); 807 break; 808 } 809 } 810 LeaveCriticalSection(&authcache_cs); 811 return rc; 812 } 813 814 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len) 815 { 816 struct list *cursor; 817 basicAuthorizationData* ad = NULL; 818 819 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len)); 820 821 EnterCriticalSection(&authcache_cs); 822 LIST_FOR_EACH(cursor, &basicAuthorizationCache) 823 { 824 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry); 825 if (!wcsicmp(host,check->host) && !wcscmp(realm,check->realm)) 826 { 827 ad = check; 828 break; 829 } 830 } 831 832 if (ad) 833 { 834 TRACE("Found match in cache, replacing\n"); 835 heap_free(ad->authorization); 836 ad->authorization = heap_alloc(auth_data_len); 837 memcpy(ad->authorization, auth_data, auth_data_len); 838 ad->authorizationLen = auth_data_len; 839 } 840 else 841 { 842 ad = heap_alloc(sizeof(basicAuthorizationData)); 843 ad->host = heap_strdupW(host); 844 ad->realm = heap_strdupW(realm); 845 ad->authorization = heap_alloc(auth_data_len); 846 memcpy(ad->authorization, auth_data, auth_data_len); 847 ad->authorizationLen = auth_data_len; 848 list_add_head(&basicAuthorizationCache,&ad->entry); 849 TRACE("authorization cached\n"); 850 } 851 LeaveCriticalSection(&authcache_cs); 852 } 853 854 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme, 855 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity) 856 { 857 authorizationData *ad; 858 859 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme)); 860 861 EnterCriticalSection(&authcache_cs); 862 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) { 863 if(!wcsicmp(host, ad->host) && !wcsicmp(scheme, ad->scheme)) { 864 TRACE("Authorization found in cache\n"); 865 866 nt_auth_identity->User = heap_strdupW(ad->user); 867 nt_auth_identity->Password = heap_strdupW(ad->password); 868 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len); 869 if(!nt_auth_identity->User || !nt_auth_identity->Password || 870 (!nt_auth_identity->Domain && ad->domain_len)) { 871 heap_free(nt_auth_identity->User); 872 heap_free(nt_auth_identity->Password); 873 heap_free(nt_auth_identity->Domain); 874 break; 875 } 876 877 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; 878 nt_auth_identity->UserLength = ad->user_len; 879 nt_auth_identity->PasswordLength = ad->password_len; 880 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len); 881 nt_auth_identity->DomainLength = ad->domain_len; 882 LeaveCriticalSection(&authcache_cs); 883 return TRUE; 884 } 885 } 886 LeaveCriticalSection(&authcache_cs); 887 888 return FALSE; 889 } 890 891 static void cache_authorization(LPWSTR host, LPWSTR scheme, 892 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity) 893 { 894 authorizationData *ad; 895 BOOL found = FALSE; 896 897 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme)); 898 899 EnterCriticalSection(&authcache_cs); 900 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) 901 if(!wcsicmp(host, ad->host) && !wcsicmp(scheme, ad->scheme)) { 902 found = TRUE; 903 break; 904 } 905 906 if(found) { 907 heap_free(ad->user); 908 heap_free(ad->password); 909 heap_free(ad->domain); 910 } else { 911 ad = heap_alloc(sizeof(authorizationData)); 912 if(!ad) { 913 LeaveCriticalSection(&authcache_cs); 914 return; 915 } 916 917 ad->host = heap_strdupW(host); 918 ad->scheme = heap_strdupW(scheme); 919 list_add_head(&authorizationCache, &ad->entry); 920 } 921 922 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength); 923 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength); 924 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength); 925 ad->user_len = nt_auth_identity->UserLength; 926 ad->password_len = nt_auth_identity->PasswordLength; 927 ad->domain_len = nt_auth_identity->DomainLength; 928 929 if(!ad->host || !ad->scheme || !ad->user || !ad->password 930 || (nt_auth_identity->Domain && !ad->domain)) { 931 heap_free(ad->host); 932 heap_free(ad->scheme); 933 heap_free(ad->user); 934 heap_free(ad->password); 935 heap_free(ad->domain); 936 list_remove(&ad->entry); 937 heap_free(ad); 938 } 939 940 LeaveCriticalSection(&authcache_cs); 941 } 942 943 void free_authorization_cache(void) 944 { 945 authorizationData *ad, *sa_safe; 946 basicAuthorizationData *basic, *basic_safe; 947 948 EnterCriticalSection(&authcache_cs); 949 950 LIST_FOR_EACH_ENTRY_SAFE(basic, basic_safe, &basicAuthorizationCache, basicAuthorizationData, entry) 951 { 952 heap_free(basic->host); 953 heap_free(basic->realm); 954 heap_free(basic->authorization); 955 956 list_remove(&basic->entry); 957 heap_free(basic); 958 } 959 960 LIST_FOR_EACH_ENTRY_SAFE(ad, sa_safe, &authorizationCache, authorizationData, entry) 961 { 962 heap_free(ad->host); 963 heap_free(ad->scheme); 964 heap_free(ad->user); 965 heap_free(ad->password); 966 heap_free(ad->domain); 967 list_remove(&ad->entry); 968 heap_free(ad); 969 } 970 971 LeaveCriticalSection(&authcache_cs); 972 } 973 974 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue, 975 struct HttpAuthInfo **ppAuthInfo, 976 LPWSTR domain_and_username, LPWSTR password, 977 LPWSTR host ) 978 { 979 SECURITY_STATUS sec_status; 980 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo; 981 BOOL first = FALSE; 982 LPWSTR szRealm = NULL; 983 984 TRACE("%s\n", debugstr_w(pszAuthValue)); 985 986 if (!pAuthInfo) 987 { 988 TimeStamp exp; 989 990 first = TRUE; 991 pAuthInfo = heap_alloc(sizeof(*pAuthInfo)); 992 if (!pAuthInfo) 993 return FALSE; 994 995 SecInvalidateHandle(&pAuthInfo->cred); 996 SecInvalidateHandle(&pAuthInfo->ctx); 997 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp)); 998 pAuthInfo->attr = 0; 999 pAuthInfo->auth_data = NULL; 1000 pAuthInfo->auth_data_len = 0; 1001 pAuthInfo->finished = FALSE; 1002 1003 if (is_basic_auth_value(pszAuthValue,NULL)) 1004 { 1005 pAuthInfo->scheme = heap_strdupW(L"Basic"); 1006 if (!pAuthInfo->scheme) 1007 { 1008 heap_free(pAuthInfo); 1009 return FALSE; 1010 } 1011 } 1012 else 1013 { 1014 PVOID pAuthData; 1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity; 1016 1017 pAuthInfo->scheme = heap_strdupW(pszAuthValue); 1018 if (!pAuthInfo->scheme) 1019 { 1020 heap_free(pAuthInfo); 1021 return FALSE; 1022 } 1023 1024 if (domain_and_username) 1025 { 1026 WCHAR *user = wcschr(domain_and_username, '\\'); 1027 WCHAR *domain = domain_and_username; 1028 1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */ 1030 1031 pAuthData = &nt_auth_identity; 1032 1033 if (user) user++; 1034 else 1035 { 1036 user = domain_and_username; 1037 domain = NULL; 1038 } 1039 1040 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; 1041 nt_auth_identity.User = user; 1042 nt_auth_identity.UserLength = lstrlenW(nt_auth_identity.User); 1043 nt_auth_identity.Domain = domain; 1044 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0; 1045 nt_auth_identity.Password = password; 1046 nt_auth_identity.PasswordLength = lstrlenW(nt_auth_identity.Password); 1047 1048 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity); 1049 } 1050 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity)) 1051 pAuthData = &nt_auth_identity; 1052 else 1053 /* use default credentials */ 1054 pAuthData = NULL; 1055 1056 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme, 1057 SECPKG_CRED_OUTBOUND, NULL, 1058 pAuthData, NULL, 1059 NULL, &pAuthInfo->cred, 1060 &exp); 1061 1062 if(pAuthData && !domain_and_username) { 1063 heap_free(nt_auth_identity.User); 1064 heap_free(nt_auth_identity.Domain); 1065 heap_free(nt_auth_identity.Password); 1066 } 1067 1068 if (sec_status == SEC_E_OK) 1069 { 1070 PSecPkgInfoW sec_pkg_info; 1071 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info); 1072 if (sec_status == SEC_E_OK) 1073 { 1074 pAuthInfo->max_token = sec_pkg_info->cbMaxToken; 1075 FreeContextBuffer(sec_pkg_info); 1076 } 1077 } 1078 if (sec_status != SEC_E_OK) 1079 { 1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n", 1081 debugstr_w(pAuthInfo->scheme), sec_status); 1082 heap_free(pAuthInfo->scheme); 1083 heap_free(pAuthInfo); 1084 return FALSE; 1085 } 1086 } 1087 *ppAuthInfo = pAuthInfo; 1088 } 1089 else if (pAuthInfo->finished) 1090 return FALSE; 1091 1092 if ((lstrlenW(pszAuthValue) < lstrlenW(pAuthInfo->scheme)) || 1093 wcsnicmp(pszAuthValue, pAuthInfo->scheme, lstrlenW(pAuthInfo->scheme))) 1094 { 1095 ERR("authentication scheme changed from %s to %s\n", 1096 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue)); 1097 return FALSE; 1098 } 1099 1100 if (is_basic_auth_value(pszAuthValue,&szRealm)) 1101 { 1102 int userlen; 1103 int passlen; 1104 char *auth_data = NULL; 1105 UINT auth_data_len = 0; 1106 1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm)); 1108 1109 if (!domain_and_username) 1110 { 1111 if (host && szRealm) 1112 auth_data_len = retrieve_cached_basic_authorization(request, host, szRealm,&auth_data); 1113 if (auth_data_len == 0) 1114 { 1115 heap_free(szRealm); 1116 return FALSE; 1117 } 1118 } 1119 else 1120 { 1121 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL); 1122 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL); 1123 1124 /* length includes a nul terminator, which will be re-used for the ':' */ 1125 auth_data = heap_alloc(userlen + 1 + passlen); 1126 if (!auth_data) 1127 { 1128 heap_free(szRealm); 1129 return FALSE; 1130 } 1131 1132 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL); 1133 auth_data[userlen] = ':'; 1134 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL); 1135 auth_data_len = userlen + 1 + passlen; 1136 if (host && szRealm) 1137 cache_basic_authorization(host, szRealm, auth_data, auth_data_len); 1138 } 1139 1140 pAuthInfo->auth_data = auth_data; 1141 pAuthInfo->auth_data_len = auth_data_len; 1142 pAuthInfo->finished = TRUE; 1143 heap_free(szRealm); 1144 return TRUE; 1145 } 1146 else 1147 { 1148 LPCWSTR pszAuthData; 1149 SecBufferDesc out_desc, in_desc; 1150 SecBuffer out, in; 1151 unsigned char *buffer; 1152 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE | 1153 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE; 1154 1155 in.BufferType = SECBUFFER_TOKEN; 1156 in.cbBuffer = 0; 1157 in.pvBuffer = NULL; 1158 1159 in_desc.ulVersion = 0; 1160 in_desc.cBuffers = 1; 1161 in_desc.pBuffers = ∈ 1162 1163 pszAuthData = pszAuthValue + lstrlenW(pAuthInfo->scheme); 1164 if (*pszAuthData == ' ') 1165 { 1166 pszAuthData++; 1167 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL); 1168 in.pvBuffer = heap_alloc(in.cbBuffer); 1169 HTTP_DecodeBase64(pszAuthData, in.pvBuffer); 1170 } 1171 1172 buffer = heap_alloc(pAuthInfo->max_token); 1173 1174 out.BufferType = SECBUFFER_TOKEN; 1175 out.cbBuffer = pAuthInfo->max_token; 1176 out.pvBuffer = buffer; 1177 1178 out_desc.ulVersion = 0; 1179 out_desc.cBuffers = 1; 1180 out_desc.pBuffers = &out; 1181 1182 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL, 1183 first ? NULL : &pAuthInfo->ctx, 1184 first ? request->server->name : NULL, 1185 context_req, 0, SECURITY_NETWORK_DREP, 1186 in.pvBuffer ? &in_desc : NULL, 1187 0, &pAuthInfo->ctx, &out_desc, 1188 &pAuthInfo->attr, &pAuthInfo->exp); 1189 if (sec_status == SEC_E_OK) 1190 { 1191 pAuthInfo->finished = TRUE; 1192 pAuthInfo->auth_data = out.pvBuffer; 1193 pAuthInfo->auth_data_len = out.cbBuffer; 1194 TRACE("sending last auth packet\n"); 1195 } 1196 else if (sec_status == SEC_I_CONTINUE_NEEDED) 1197 { 1198 pAuthInfo->auth_data = out.pvBuffer; 1199 pAuthInfo->auth_data_len = out.cbBuffer; 1200 TRACE("sending next auth packet\n"); 1201 } 1202 else 1203 { 1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status); 1205 heap_free(out.pvBuffer); 1206 destroy_authinfo(pAuthInfo); 1207 *ppAuthInfo = NULL; 1208 return FALSE; 1209 } 1210 } 1211 1212 return TRUE; 1213 } 1214 1215 /*********************************************************************** 1216 * HTTP_HttpAddRequestHeadersW (internal) 1217 */ 1218 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request, 1219 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier) 1220 { 1221 LPWSTR lpszStart; 1222 LPWSTR lpszEnd; 1223 LPWSTR buffer; 1224 DWORD len, res = ERROR_HTTP_INVALID_HEADER; 1225 1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength)); 1227 1228 if( dwHeaderLength == ~0U ) 1229 len = lstrlenW(lpszHeader); 1230 else 1231 len = dwHeaderLength; 1232 buffer = heap_alloc(sizeof(WCHAR)*(len+1)); 1233 lstrcpynW( buffer, lpszHeader, len + 1); 1234 1235 lpszStart = buffer; 1236 1237 do 1238 { 1239 LPWSTR * pFieldAndValue; 1240 1241 lpszEnd = lpszStart; 1242 1243 while (*lpszEnd != '\0') 1244 { 1245 if (*lpszEnd == '\r' || *lpszEnd == '\n') 1246 break; 1247 lpszEnd++; 1248 } 1249 1250 if (*lpszStart == '\0') 1251 break; 1252 1253 if (*lpszEnd == '\r' || *lpszEnd == '\n') 1254 { 1255 *lpszEnd = '\0'; 1256 lpszEnd++; /* Jump over newline */ 1257 } 1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart)); 1259 if (*lpszStart == '\0') 1260 { 1261 /* Skip 0-length headers */ 1262 lpszStart = lpszEnd; 1263 res = ERROR_SUCCESS; 1264 continue; 1265 } 1266 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart); 1267 if (pFieldAndValue) 1268 { 1269 res = HTTP_ProcessHeader(request, pFieldAndValue[0], 1270 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ); 1271 HTTP_FreeTokens(pFieldAndValue); 1272 } 1273 1274 lpszStart = lpszEnd; 1275 } while (res == ERROR_SUCCESS); 1276 1277 heap_free(buffer); 1278 return res; 1279 } 1280 1281 /*********************************************************************** 1282 * HttpAddRequestHeadersW (WININET.@) 1283 * 1284 * Adds one or more HTTP header to the request handler 1285 * 1286 * NOTE 1287 * On Windows if dwHeaderLength includes the trailing '\0', then 1288 * HttpAddRequestHeadersW() adds it too. However this results in an 1289 * invalid HTTP header which is rejected by some servers so we probably 1290 * don't need to match Windows on that point. 1291 * 1292 * RETURNS 1293 * TRUE on success 1294 * FALSE on failure 1295 * 1296 */ 1297 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest, 1298 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier) 1299 { 1300 http_request_t *request; 1301 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 1302 1303 TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier); 1304 1305 if (!lpszHeader) 1306 return TRUE; 1307 1308 request = (http_request_t*) get_handle_object( hHttpRequest ); 1309 if (request && request->hdr.htype == WH_HHTTPREQ) 1310 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier ); 1311 if( request ) 1312 WININET_Release( &request->hdr ); 1313 1314 if(res != ERROR_SUCCESS) 1315 SetLastError(res); 1316 return res == ERROR_SUCCESS; 1317 } 1318 1319 /*********************************************************************** 1320 * HttpAddRequestHeadersA (WININET.@) 1321 * 1322 * Adds one or more HTTP header to the request handler 1323 * 1324 * RETURNS 1325 * TRUE on success 1326 * FALSE on failure 1327 * 1328 */ 1329 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest, 1330 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier) 1331 { 1332 WCHAR *headers = NULL; 1333 BOOL r; 1334 1335 TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier); 1336 1337 if(lpszHeader) 1338 headers = heap_strndupAtoW(lpszHeader, dwHeaderLength, &dwHeaderLength); 1339 1340 r = HttpAddRequestHeadersW(hHttpRequest, headers, dwHeaderLength, dwModifier); 1341 1342 heap_free(headers); 1343 return r; 1344 } 1345 1346 static void free_accept_types( WCHAR **accept_types ) 1347 { 1348 WCHAR *ptr, **types = accept_types; 1349 1350 if (!types) return; 1351 while ((ptr = *types)) 1352 { 1353 heap_free( ptr ); 1354 types++; 1355 } 1356 heap_free( accept_types ); 1357 } 1358 1359 static WCHAR **convert_accept_types( const char **accept_types ) 1360 { 1361 unsigned int count; 1362 const char **types = accept_types; 1363 WCHAR **typesW; 1364 BOOL invalid_pointer = FALSE; 1365 1366 if (!types) return NULL; 1367 count = 0; 1368 while (*types) 1369 { 1370 __TRY 1371 { 1372 /* find out how many there are */ 1373 if (*types && **types) 1374 { 1375 TRACE("accept type: %s\n", debugstr_a(*types)); 1376 count++; 1377 } 1378 } 1379 __EXCEPT_PAGE_FAULT 1380 { 1381 WARN("invalid accept type pointer\n"); 1382 invalid_pointer = TRUE; 1383 } 1384 __ENDTRY; 1385 types++; 1386 } 1387 if (invalid_pointer) return NULL; 1388 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL; 1389 count = 0; 1390 types = accept_types; 1391 while (*types) 1392 { 1393 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types ); 1394 types++; 1395 } 1396 typesW[count] = NULL; 1397 return typesW; 1398 } 1399 1400 /*********************************************************************** 1401 * HttpOpenRequestA (WININET.@) 1402 * 1403 * Open a HTTP request handle 1404 * 1405 * RETURNS 1406 * HINTERNET a HTTP request handle on success 1407 * NULL on failure 1408 * 1409 */ 1410 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession, 1411 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion, 1412 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes, 1413 DWORD dwFlags, DWORD_PTR dwContext) 1414 { 1415 LPWSTR szVerb = NULL, szObjectName = NULL; 1416 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL; 1417 HINTERNET rc = NULL; 1418 1419 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession, 1420 debugstr_a(lpszVerb), debugstr_a(lpszObjectName), 1421 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes, 1422 dwFlags, dwContext); 1423 1424 if (lpszVerb) 1425 { 1426 szVerb = heap_strdupAtoW(lpszVerb); 1427 if ( !szVerb ) 1428 goto end; 1429 } 1430 1431 if (lpszObjectName) 1432 { 1433 szObjectName = heap_strdupAtoW(lpszObjectName); 1434 if ( !szObjectName ) 1435 goto end; 1436 } 1437 1438 if (lpszVersion) 1439 { 1440 szVersion = heap_strdupAtoW(lpszVersion); 1441 if ( !szVersion ) 1442 goto end; 1443 } 1444 1445 if (lpszReferrer) 1446 { 1447 szReferrer = heap_strdupAtoW(lpszReferrer); 1448 if ( !szReferrer ) 1449 goto end; 1450 } 1451 1452 szAcceptTypes = convert_accept_types( lpszAcceptTypes ); 1453 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer, 1454 (const WCHAR **)szAcceptTypes, dwFlags, dwContext); 1455 1456 end: 1457 free_accept_types(szAcceptTypes); 1458 heap_free(szReferrer); 1459 heap_free(szVersion); 1460 heap_free(szObjectName); 1461 heap_free(szVerb); 1462 return rc; 1463 } 1464 1465 /*********************************************************************** 1466 * HTTP_EncodeBase64 1467 */ 1468 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 ) 1469 { 1470 UINT n = 0, x; 1471 static const CHAR HTTP_Base64Enc[] = 1472 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 1473 1474 while( len > 0 ) 1475 { 1476 /* first 6 bits, all from bin[0] */ 1477 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2]; 1478 x = (bin[0] & 3) << 4; 1479 1480 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */ 1481 if( len == 1 ) 1482 { 1483 base64[n++] = HTTP_Base64Enc[x]; 1484 base64[n++] = '='; 1485 base64[n++] = '='; 1486 break; 1487 } 1488 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ]; 1489 x = ( bin[1] & 0x0f ) << 2; 1490 1491 /* next 6 bits 4 from bin[1] and 2 from bin[2] */ 1492 if( len == 2 ) 1493 { 1494 base64[n++] = HTTP_Base64Enc[x]; 1495 base64[n++] = '='; 1496 break; 1497 } 1498 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ]; 1499 1500 /* last 6 bits, all from bin [2] */ 1501 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ]; 1502 bin += 3; 1503 len -= 3; 1504 } 1505 base64[n] = 0; 1506 return n; 1507 } 1508 1509 static const signed char HTTP_Base64Dec[] = 1510 { 1511 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */ 1512 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */ 1513 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */ 1514 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */ 1515 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */ 1516 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */ 1517 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */ 1518 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70 */ 1519 }; 1520 1521 /*********************************************************************** 1522 * HTTP_DecodeBase64 1523 */ 1524 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin ) 1525 { 1526 unsigned int n = 0; 1527 1528 while(*base64) 1529 { 1530 signed char in[4]; 1531 1532 if (base64[0] >= ARRAY_SIZE(HTTP_Base64Dec) || 1533 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) || 1534 base64[1] >= ARRAY_SIZE(HTTP_Base64Dec) || 1535 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1)) 1536 { 1537 WARN("invalid base64: %s\n", debugstr_w(base64)); 1538 return 0; 1539 } 1540 if (bin) 1541 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4); 1542 n++; 1543 1544 if ((base64[2] == '=') && (base64[3] == '=')) 1545 break; 1546 if (base64[2] > ARRAY_SIZE(HTTP_Base64Dec) || 1547 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1)) 1548 { 1549 WARN("invalid base64: %s\n", debugstr_w(&base64[2])); 1550 return 0; 1551 } 1552 if (bin) 1553 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2); 1554 n++; 1555 1556 if (base64[3] == '=') 1557 break; 1558 if (base64[3] > ARRAY_SIZE(HTTP_Base64Dec) || 1559 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1)) 1560 { 1561 WARN("invalid base64: %s\n", debugstr_w(&base64[3])); 1562 return 0; 1563 } 1564 if (bin) 1565 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]); 1566 n++; 1567 1568 base64 += 4; 1569 } 1570 1571 return n; 1572 } 1573 1574 static WCHAR *encode_auth_data( const WCHAR *scheme, const char *data, UINT data_len ) 1575 { 1576 WCHAR *ret; 1577 UINT len, scheme_len = lstrlenW( scheme ); 1578 1579 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */ 1580 len = scheme_len + 1 + ((data_len + 2) * 4) / 3; 1581 if (!(ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL; 1582 memcpy( ret, scheme, scheme_len * sizeof(WCHAR) ); 1583 ret[scheme_len] = ' '; 1584 HTTP_EncodeBase64( data, data_len, ret + scheme_len + 1 ); 1585 return ret; 1586 } 1587 1588 1589 /*********************************************************************** 1590 * HTTP_InsertAuthorization 1591 * 1592 * Insert or delete the authorization field in the request header. 1593 */ 1594 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header ) 1595 { 1596 WCHAR *host, *authorization = NULL; 1597 1598 if (pAuthInfo) 1599 { 1600 if (pAuthInfo->auth_data_len) 1601 { 1602 if (!(authorization = encode_auth_data(pAuthInfo->scheme, pAuthInfo->auth_data, pAuthInfo->auth_data_len))) 1603 return FALSE; 1604 1605 /* clear the data as it isn't valid now that it has been sent to the 1606 * server, unless it's Basic authentication which doesn't do 1607 * connection tracking */ 1608 if (wcsicmp(pAuthInfo->scheme, L"Basic")) 1609 { 1610 heap_free(pAuthInfo->auth_data); 1611 pAuthInfo->auth_data = NULL; 1612 pAuthInfo->auth_data_len = 0; 1613 } 1614 } 1615 1616 TRACE("Inserting authorization: %s\n", debugstr_w(authorization)); 1617 1618 HTTP_ProcessHeader(request, header, authorization, 1619 HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD); 1620 heap_free(authorization); 1621 } 1622 else 1623 { 1624 UINT data_len; 1625 char *data; 1626 1627 /* Don't use cached credentials when a username or Authorization was specified */ 1628 if ((request->session->userName && request->session->userName[0]) || wcscmp(header, L"Authorization")) 1629 return TRUE; 1630 1631 if (!(host = get_host_header(request))) 1632 return TRUE; 1633 1634 if ((data_len = retrieve_cached_basic_authorization(request, host, NULL, &data))) 1635 { 1636 TRACE("Found cached basic authorization for %s\n", debugstr_w(host)); 1637 1638 if (!(authorization = encode_auth_data(L"Basic", data, data_len))) 1639 { 1640 heap_free(data); 1641 heap_free(host); 1642 return FALSE; 1643 } 1644 1645 TRACE("Inserting authorization: %s\n", debugstr_w(authorization)); 1646 1647 HTTP_ProcessHeader(request, header, authorization, 1648 HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD); 1649 heap_free(data); 1650 heap_free(authorization); 1651 } 1652 heap_free(host); 1653 } 1654 return TRUE; 1655 } 1656 1657 static WCHAR *build_proxy_path_url(http_request_t *req) 1658 { 1659 DWORD size, len; 1660 WCHAR *url; 1661 1662 len = lstrlenW(req->server->scheme_host_port); 1663 size = len + lstrlenW(req->path) + 1; 1664 if(*req->path != '/') 1665 size++; 1666 url = heap_alloc(size * sizeof(WCHAR)); 1667 if(!url) 1668 return NULL; 1669 1670 memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR)); 1671 if(*req->path != '/') 1672 url[len++] = '/'; 1673 1674 lstrcpyW(url+len, req->path); 1675 1676 TRACE("url=%s\n", debugstr_w(url)); 1677 return url; 1678 } 1679 1680 static BOOL HTTP_DomainMatches(LPCWSTR server, substr_t domain) 1681 { 1682 const WCHAR *dot, *ptr; 1683 int len; 1684 1685 if(domain.len == ARRAY_SIZE(L"<local>")-1 && !wcsnicmp(domain.str, L"<local>", domain.len) && !wcschr(server, '.' )) 1686 return TRUE; 1687 1688 if(domain.len && *domain.str != '*') 1689 return domain.len == lstrlenW(server) && !wcsnicmp(server, domain.str, domain.len); 1690 1691 if(domain.len < 2 || domain.str[1] != '.') 1692 return FALSE; 1693 1694 /* For a hostname to match a wildcard, the last domain must match 1695 * the wildcard exactly. E.g. if the wildcard is *.a.b, and the 1696 * hostname is www.foo.a.b, it matches, but a.b does not. 1697 */ 1698 dot = wcschr(server, '.'); 1699 if(!dot) 1700 return FALSE; 1701 1702 len = lstrlenW(dot + 1); 1703 if(len < domain.len - 2) 1704 return FALSE; 1705 1706 /* The server's domain is longer than the wildcard, so it 1707 * could be a subdomain. Compare the last portion of the 1708 * server's domain. 1709 */ 1710 ptr = dot + 1 + len - domain.len + 2; 1711 if(!wcsnicmp(ptr, domain.str+2, domain.len-2)) 1712 /* This is only a match if the preceding character is 1713 * a '.', i.e. that it is a matching domain. E.g. 1714 * if domain is '*.b.c' and server is 'www.ab.c' they 1715 * do not match. 1716 */ 1717 return *(ptr - 1) == '.'; 1718 1719 return len == domain.len-2 && !wcsnicmp(dot + 1, domain.str + 2, len); 1720 } 1721 1722 static BOOL HTTP_ShouldBypassProxy(appinfo_t *lpwai, LPCWSTR server) 1723 { 1724 LPCWSTR ptr; 1725 BOOL ret = FALSE; 1726 1727 if (!lpwai->proxyBypass) return FALSE; 1728 ptr = lpwai->proxyBypass; 1729 while(1) { 1730 LPCWSTR tmp = ptr; 1731 1732 ptr = wcschr( ptr, ';' ); 1733 if (!ptr) 1734 ptr = wcschr( tmp, ' ' ); 1735 if (!ptr) 1736 ptr = tmp + lstrlenW(tmp); 1737 ret = HTTP_DomainMatches( server, substr(tmp, ptr-tmp) ); 1738 if (ret || !*ptr) 1739 break; 1740 ptr++; 1741 } 1742 return ret; 1743 } 1744 1745 /*********************************************************************** 1746 * HTTP_DealWithProxy 1747 */ 1748 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request) 1749 { 1750 static WCHAR szNul[] = L""; 1751 URL_COMPONENTSW UrlComponents = { sizeof(UrlComponents) }; 1752 server_t *new_server = NULL; 1753 WCHAR *proxy; 1754 1755 proxy = INTERNET_FindProxyForProtocol(hIC->proxy, L"http"); 1756 if(!proxy) 1757 return FALSE; 1758 if(CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, 1759 proxy, lstrlenW(L"http://"), L"http://", lstrlenW(L"http://"))) { 1760 WCHAR *proxy_url = heap_alloc(lstrlenW(proxy)*sizeof(WCHAR) + sizeof(L"http://")); 1761 if(!proxy_url) { 1762 heap_free(proxy); 1763 return FALSE; 1764 } 1765 lstrcpyW(proxy_url, L"http://"); 1766 lstrcatW(proxy_url, proxy); 1767 heap_free(proxy); 1768 proxy = proxy_url; 1769 } 1770 1771 UrlComponents.dwHostNameLength = 1; 1772 if(InternetCrackUrlW(proxy, 0, 0, &UrlComponents) && UrlComponents.dwHostNameLength) { 1773 if( !request->path ) 1774 request->path = szNul; 1775 1776 new_server = get_server(substr(UrlComponents.lpszHostName, UrlComponents.dwHostNameLength), 1777 UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE); 1778 } 1779 heap_free(proxy); 1780 if(!new_server) 1781 return FALSE; 1782 1783 request->proxy = new_server; 1784 1785 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port); 1786 return TRUE; 1787 } 1788 1789 static DWORD HTTP_ResolveName(http_request_t *request) 1790 { 1791 server_t *server = request->proxy ? request->proxy : request->server; 1792 int addr_len; 1793 1794 if(server->addr_len) 1795 return ERROR_SUCCESS; 1796 1797 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 1798 INTERNET_STATUS_RESOLVING_NAME, 1799 server->name, 1800 (lstrlenW(server->name)+1) * sizeof(WCHAR)); 1801 1802 addr_len = sizeof(server->addr); 1803 if (!GetAddress(server->name, server->port, (SOCKADDR*)&server->addr, &addr_len, server->addr_str)) 1804 return ERROR_INTERNET_NAME_NOT_RESOLVED; 1805 1806 server->addr_len = addr_len; 1807 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 1808 INTERNET_STATUS_NAME_RESOLVED, 1809 server->addr_str, strlen(server->addr_str)+1); 1810 1811 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str); 1812 return ERROR_SUCCESS; 1813 } 1814 1815 static WCHAR *compose_request_url(http_request_t *req) 1816 { 1817 const WCHAR *host, *scheme; 1818 WCHAR *buf, *ptr; 1819 size_t len; 1820 1821 host = req->server->canon_host_port; 1822 1823 if (req->server->is_https) 1824 scheme = L"https://"; 1825 else 1826 scheme = L"http://"; 1827 1828 len = lstrlenW(scheme) + lstrlenW(host) + (req->path[0] != '/' ? 1 : 0) + lstrlenW(req->path); 1829 ptr = buf = heap_alloc((len+1) * sizeof(WCHAR)); 1830 if(buf) { 1831 lstrcpyW(ptr, scheme); 1832 ptr += lstrlenW(ptr); 1833 1834 lstrcpyW(ptr, host); 1835 ptr += lstrlenW(ptr); 1836 1837 if(req->path[0] != '/') 1838 *ptr++ = '/'; 1839 1840 lstrcpyW(ptr, req->path); 1841 ptr += lstrlenW(ptr); 1842 *ptr = 0; 1843 } 1844 1845 return buf; 1846 } 1847 1848 1849 /*********************************************************************** 1850 * HTTPREQ_Destroy (internal) 1851 * 1852 * Deallocate request handle 1853 * 1854 */ 1855 static void HTTPREQ_Destroy(object_header_t *hdr) 1856 { 1857 http_request_t *request = (http_request_t*) hdr; 1858 DWORD i; 1859 1860 TRACE("\n"); 1861 1862 if(request->hCacheFile) 1863 CloseHandle(request->hCacheFile); 1864 if(request->req_file) 1865 req_file_release(request->req_file); 1866 1867 request->headers_section.DebugInfo->Spare[0] = 0; 1868 DeleteCriticalSection( &request->headers_section ); 1869 request->read_section.DebugInfo->Spare[0] = 0; 1870 DeleteCriticalSection( &request->read_section ); 1871 WININET_Release(&request->session->hdr); 1872 1873 destroy_authinfo(request->authInfo); 1874 destroy_authinfo(request->proxyAuthInfo); 1875 1876 if(request->server) 1877 server_release(request->server); 1878 if(request->proxy) 1879 server_release(request->proxy); 1880 1881 heap_free(request->path); 1882 heap_free(request->verb); 1883 heap_free(request->version); 1884 heap_free(request->statusText); 1885 1886 for (i = 0; i < request->nCustHeaders; i++) 1887 { 1888 heap_free(request->custHeaders[i].lpszField); 1889 heap_free(request->custHeaders[i].lpszValue); 1890 } 1891 destroy_data_stream(request->data_stream); 1892 heap_free(request->custHeaders); 1893 } 1894 1895 static void http_release_netconn(http_request_t *req, BOOL reuse) 1896 { 1897 TRACE("%p %p %x\n",req, req->netconn, reuse); 1898 1899 if(!is_valid_netconn(req->netconn)) 1900 return; 1901 1902 #ifndef __REACTOS__ 1903 if(reuse && req->netconn->keep_alive) { 1904 BOOL run_collector; 1905 1906 EnterCriticalSection(&connection_pool_cs); 1907 1908 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry); 1909 req->netconn->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME; 1910 req->netconn = NULL; 1911 1912 run_collector = !collector_running; 1913 collector_running = TRUE; 1914 1915 LeaveCriticalSection(&connection_pool_cs); 1916 1917 if(run_collector) { 1918 HANDLE thread = NULL; 1919 HMODULE module; 1920 1921 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module); 1922 if(module) 1923 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL); 1924 if(!thread) { 1925 EnterCriticalSection(&connection_pool_cs); 1926 collector_running = FALSE; 1927 LeaveCriticalSection(&connection_pool_cs); 1928 1929 if(module) 1930 FreeLibrary(module); 1931 } 1932 else 1933 CloseHandle(thread); 1934 } 1935 return; 1936 } 1937 #else 1938 /* Silence unused function warning */ 1939 (void)collect_connections_proc; 1940 #endif 1941 1942 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, 1943 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0); 1944 1945 close_netconn(req->netconn); 1946 1947 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, 1948 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0); 1949 } 1950 1951 static BOOL HTTP_KeepAlive(http_request_t *request) 1952 { 1953 WCHAR szVersion[10]; 1954 WCHAR szConnectionResponse[20]; 1955 DWORD dwBufferSize = sizeof(szVersion); 1956 BOOL keepalive = FALSE; 1957 1958 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that 1959 * the connection is keep-alive by default */ 1960 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS 1961 && !wcsicmp(szVersion, L"HTTP/1.1")) 1962 { 1963 keepalive = TRUE; 1964 } 1965 1966 dwBufferSize = sizeof(szConnectionResponse); 1967 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS 1968 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS) 1969 { 1970 keepalive = !wcsicmp(szConnectionResponse, L"Keep-Alive"); 1971 } 1972 1973 return keepalive; 1974 } 1975 1976 static void HTTPREQ_CloseConnection(object_header_t *hdr) 1977 { 1978 http_request_t *req = (http_request_t*)hdr; 1979 1980 http_release_netconn(req, drain_content(req, FALSE) == ERROR_SUCCESS); 1981 } 1982 1983 static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL unicode) 1984 { 1985 int len; 1986 if (unicode) 1987 { 1988 WCHAR *buf = buffer; 1989 1990 if (str) len = lstrlenW(str); 1991 else len = 0; 1992 if (*size < (len + 1) * sizeof(WCHAR)) 1993 { 1994 *size = (len + 1) * sizeof(WCHAR); 1995 return ERROR_INSUFFICIENT_BUFFER; 1996 } 1997 if (str) lstrcpyW(buf, str); 1998 else buf[0] = 0; 1999 2000 *size = len; 2001 return ERROR_SUCCESS; 2002 } 2003 else 2004 { 2005 char *buf = buffer; 2006 2007 if (str) len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); 2008 else len = 1; 2009 if (*size < len) 2010 { 2011 *size = len; 2012 return ERROR_INSUFFICIENT_BUFFER; 2013 } 2014 if (str) WideCharToMultiByte(CP_ACP, 0, str, -1, buf, *size, NULL, NULL); 2015 else buf[0] = 0; 2016 2017 *size = len - 1; 2018 return ERROR_SUCCESS; 2019 } 2020 } 2021 2022 static DWORD get_security_cert_struct(http_request_t *req, INTERNET_CERTIFICATE_INFOA *info) 2023 { 2024 PCCERT_CONTEXT context; 2025 DWORD len; 2026 2027 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn); 2028 if(!context) 2029 return ERROR_NOT_SUPPORTED; 2030 2031 memset(info, 0, sizeof(*info)); 2032 info->ftExpiry = context->pCertInfo->NotAfter; 2033 info->ftStart = context->pCertInfo->NotBefore; 2034 len = CertNameToStrA(context->dwCertEncodingType, 2035 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0); 2036 info->lpszSubjectInfo = LocalAlloc(0, len); 2037 if(info->lpszSubjectInfo) 2038 CertNameToStrA(context->dwCertEncodingType, 2039 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, 2040 info->lpszSubjectInfo, len); 2041 len = CertNameToStrA(context->dwCertEncodingType, 2042 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0); 2043 info->lpszIssuerInfo = LocalAlloc(0, len); 2044 if(info->lpszIssuerInfo) 2045 CertNameToStrA(context->dwCertEncodingType, 2046 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, 2047 info->lpszIssuerInfo, len); 2048 info->dwKeySize = NETCON_GetCipherStrength(req->netconn); 2049 2050 CertFreeCertificateContext(context); 2051 return ERROR_SUCCESS; 2052 } 2053 2054 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode) 2055 { 2056 http_request_t *req = (http_request_t*)hdr; 2057 2058 switch(option) { 2059 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO: 2060 { 2061 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer; 2062 2063 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n"); 2064 2065 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO)) 2066 return ERROR_INSUFFICIENT_BUFFER; 2067 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO); 2068 /* FIXME: can't get a SOCKET from our connection since we don't use 2069 * winsock 2070 */ 2071 info->Socket = 0; 2072 /* FIXME: get source port from req->netConnection */ 2073 info->SourcePort = 0; 2074 info->DestPort = req->server->port; 2075 info->Flags = 0; 2076 if (HTTP_KeepAlive(req)) 2077 info->Flags |= IDSI_FLAG_KEEP_ALIVE; 2078 if (req->proxy) 2079 info->Flags |= IDSI_FLAG_PROXY; 2080 if (is_valid_netconn(req->netconn) && req->netconn->secure) 2081 info->Flags |= IDSI_FLAG_SECURE; 2082 2083 return ERROR_SUCCESS; 2084 } 2085 2086 case 98: 2087 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n"); 2088 /* fall through */ 2089 case INTERNET_OPTION_SECURITY_FLAGS: 2090 { 2091 DWORD flags; 2092 2093 if (*size < sizeof(ULONG)) 2094 return ERROR_INSUFFICIENT_BUFFER; 2095 2096 *size = sizeof(DWORD); 2097 flags = is_valid_netconn(req->netconn) ? req->netconn->security_flags : req->security_flags | req->server->security_flags; 2098 *(DWORD *)buffer = flags; 2099 2100 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags); 2101 return ERROR_SUCCESS; 2102 } 2103 2104 case INTERNET_OPTION_HANDLE_TYPE: 2105 TRACE("INTERNET_OPTION_HANDLE_TYPE\n"); 2106 2107 if (*size < sizeof(ULONG)) 2108 return ERROR_INSUFFICIENT_BUFFER; 2109 2110 *size = sizeof(DWORD); 2111 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST; 2112 return ERROR_SUCCESS; 2113 2114 case INTERNET_OPTION_URL: { 2115 WCHAR *url; 2116 DWORD res; 2117 2118 TRACE("INTERNET_OPTION_URL\n"); 2119 2120 url = compose_request_url(req); 2121 if(!url) 2122 return ERROR_OUTOFMEMORY; 2123 2124 res = str_to_buffer(url, buffer, size, unicode); 2125 heap_free(url); 2126 return res; 2127 } 2128 case INTERNET_OPTION_USER_AGENT: 2129 return str_to_buffer(req->session->appInfo->agent, buffer, size, unicode); 2130 case INTERNET_OPTION_USERNAME: 2131 return str_to_buffer(req->session->userName, buffer, size, unicode); 2132 case INTERNET_OPTION_PASSWORD: 2133 return str_to_buffer(req->session->password, buffer, size, unicode); 2134 case INTERNET_OPTION_PROXY_USERNAME: 2135 return str_to_buffer(req->session->appInfo->proxyUsername, buffer, size, unicode); 2136 case INTERNET_OPTION_PROXY_PASSWORD: 2137 return str_to_buffer(req->session->appInfo->proxyPassword, buffer, size, unicode); 2138 2139 case INTERNET_OPTION_CACHE_TIMESTAMPS: { 2140 INTERNET_CACHE_ENTRY_INFOW *info; 2141 INTERNET_CACHE_TIMESTAMPS *ts = buffer; 2142 DWORD nbytes, error; 2143 BOOL ret; 2144 2145 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n"); 2146 2147 if(!req->req_file) 2148 return ERROR_FILE_NOT_FOUND; 2149 2150 if (*size < sizeof(*ts)) 2151 { 2152 *size = sizeof(*ts); 2153 return ERROR_INSUFFICIENT_BUFFER; 2154 } 2155 2156 nbytes = 0; 2157 ret = GetUrlCacheEntryInfoW(req->req_file->url, NULL, &nbytes); 2158 error = GetLastError(); 2159 if (!ret && error == ERROR_INSUFFICIENT_BUFFER) 2160 { 2161 if (!(info = heap_alloc(nbytes))) 2162 return ERROR_OUTOFMEMORY; 2163 2164 GetUrlCacheEntryInfoW(req->req_file->url, info, &nbytes); 2165 2166 ts->ftExpires = info->ExpireTime; 2167 ts->ftLastModified = info->LastModifiedTime; 2168 2169 heap_free(info); 2170 *size = sizeof(*ts); 2171 return ERROR_SUCCESS; 2172 } 2173 return error; 2174 } 2175 2176 case INTERNET_OPTION_DATAFILE_NAME: { 2177 DWORD req_size; 2178 2179 TRACE("INTERNET_OPTION_DATAFILE_NAME\n"); 2180 2181 if(!req->req_file) { 2182 *size = 0; 2183 return ERROR_INTERNET_ITEM_NOT_FOUND; 2184 } 2185 2186 if(unicode) { 2187 req_size = (lstrlenW(req->req_file->file_name)+1) * sizeof(WCHAR); 2188 if(*size < req_size) 2189 return ERROR_INSUFFICIENT_BUFFER; 2190 2191 *size = req_size; 2192 memcpy(buffer, req->req_file->file_name, *size); 2193 return ERROR_SUCCESS; 2194 }else { 2195 req_size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name, -1, NULL, 0, NULL, NULL); 2196 if (req_size > *size) 2197 return ERROR_INSUFFICIENT_BUFFER; 2198 2199 *size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name, 2200 -1, buffer, *size, NULL, NULL); 2201 return ERROR_SUCCESS; 2202 } 2203 } 2204 2205 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: { 2206 if(!req->netconn) 2207 return ERROR_INTERNET_INVALID_OPERATION; 2208 2209 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) { 2210 *size = sizeof(INTERNET_CERTIFICATE_INFOA); 2211 return ERROR_INSUFFICIENT_BUFFER; 2212 } 2213 2214 return get_security_cert_struct(req, (INTERNET_CERTIFICATE_INFOA*)buffer); 2215 } 2216 case INTERNET_OPTION_SECURITY_CERTIFICATE: { 2217 DWORD err; 2218 int needed; 2219 char subject[64]; 2220 char issuer[64]; 2221 char effective[64]; 2222 char expiration[64]; 2223 char protocol[64]; 2224 char signature[64]; 2225 char encryption[64]; 2226 char privacy[64]; 2227 char bits[16]; 2228 char strength[16]; 2229 char start_date[32]; 2230 char start_time[32]; 2231 char expiry_date[32]; 2232 char expiry_time[32]; 2233 SYSTEMTIME start, expiry; 2234 INTERNET_CERTIFICATE_INFOA info; 2235 2236 if(!size) 2237 return ERROR_INVALID_PARAMETER; 2238 2239 if(!req->netconn) { 2240 *size = 0; 2241 return ERROR_INTERNET_INVALID_OPERATION; 2242 } 2243 2244 if(!buffer) { 2245 *size = 1; 2246 return ERROR_INSUFFICIENT_BUFFER; 2247 } 2248 2249 if((err = get_security_cert_struct(req, &info))) 2250 return err; 2251 2252 LoadStringA(WININET_hModule, IDS_CERT_SUBJECT, subject, sizeof(subject)); 2253 LoadStringA(WININET_hModule, IDS_CERT_ISSUER, issuer, sizeof(issuer)); 2254 LoadStringA(WININET_hModule, IDS_CERT_EFFECTIVE, effective, sizeof(effective)); 2255 LoadStringA(WININET_hModule, IDS_CERT_EXPIRATION, expiration, sizeof(expiration)); 2256 LoadStringA(WININET_hModule, IDS_CERT_PROTOCOL, protocol, sizeof(protocol)); 2257 LoadStringA(WININET_hModule, IDS_CERT_SIGNATURE, signature, sizeof(signature)); 2258 LoadStringA(WININET_hModule, IDS_CERT_ENCRYPTION, encryption, sizeof(encryption)); 2259 LoadStringA(WININET_hModule, IDS_CERT_PRIVACY, privacy, sizeof(privacy)); 2260 LoadStringA(WININET_hModule, info.dwKeySize >= 128 ? IDS_CERT_HIGH : IDS_CERT_LOW, 2261 strength, sizeof(strength)); 2262 LoadStringA(WININET_hModule, IDS_CERT_BITS, bits, sizeof(bits)); 2263 2264 FileTimeToSystemTime(&info.ftStart, &start); 2265 FileTimeToSystemTime(&info.ftExpiry, &expiry); 2266 GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_date, sizeof(start_date)); 2267 GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_time, sizeof(start_time)); 2268 GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_date, sizeof(expiry_date)); 2269 GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_time, sizeof(expiry_time)); 2270 2271 needed = _scprintf("%s:\r\n%s\r\n" 2272 "%s:\r\n%s\r\n" 2273 "%s:\t%s %s\r\n" 2274 "%s:\t%s %s\r\n" 2275 "%s:\t(null)\r\n" 2276 "%s:\t(null)\r\n" 2277 "%s:\t(null)\r\n" 2278 "%s:\t%s (%u %s)", 2279 subject, info.lpszSubjectInfo, 2280 issuer, info.lpszIssuerInfo, 2281 effective, start_date, start_time, 2282 expiration, expiry_date, expiry_time, 2283 protocol, signature, encryption, 2284 privacy, strength, info.dwKeySize, bits); 2285 2286 if(needed < *size) { 2287 err = ERROR_SUCCESS; 2288 *size = snprintf(buffer, *size, 2289 "%s:\r\n%s\r\n" 2290 "%s:\r\n%s\r\n" 2291 "%s:\t%s %s\r\n" 2292 "%s:\t%s %s\r\n" 2293 "%s:\t(null)\r\n" 2294 "%s:\t(null)\r\n" 2295 "%s:\t(null)\r\n" 2296 "%s:\t%s (%u %s)", 2297 subject, info.lpszSubjectInfo, 2298 issuer, info.lpszIssuerInfo, 2299 effective, start_date, start_time, 2300 expiration, expiry_date, expiry_time, 2301 protocol, signature, encryption, 2302 privacy, strength, info.dwKeySize, bits); 2303 }else { 2304 err = ERROR_INSUFFICIENT_BUFFER; 2305 *size = 1; 2306 } 2307 2308 LocalFree(info.lpszSubjectInfo); 2309 LocalFree(info.lpszIssuerInfo); 2310 LocalFree(info.lpszProtocolName); 2311 LocalFree(info.lpszSignatureAlgName); 2312 LocalFree(info.lpszEncryptionAlgName); 2313 return err; 2314 } 2315 case INTERNET_OPTION_CONNECT_TIMEOUT: 2316 if (*size < sizeof(DWORD)) 2317 return ERROR_INSUFFICIENT_BUFFER; 2318 2319 *size = sizeof(DWORD); 2320 *(DWORD *)buffer = req->connect_timeout; 2321 return ERROR_SUCCESS; 2322 case INTERNET_OPTION_REQUEST_FLAGS: { 2323 DWORD flags = 0; 2324 2325 if (*size < sizeof(DWORD)) 2326 return ERROR_INSUFFICIENT_BUFFER; 2327 2328 /* FIXME: Add support for: 2329 * INTERNET_REQFLAG_FROM_CACHE 2330 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED 2331 */ 2332 2333 if(req->proxy) 2334 flags |= INTERNET_REQFLAG_VIA_PROXY; 2335 if(!req->status_code) 2336 flags |= INTERNET_REQFLAG_NO_HEADERS; 2337 2338 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags); 2339 2340 *size = sizeof(DWORD); 2341 *(DWORD*)buffer = flags; 2342 return ERROR_SUCCESS; 2343 } 2344 case INTERNET_OPTION_ERROR_MASK: 2345 TRACE("INTERNET_OPTION_ERROR_MASK\n"); 2346 2347 if (*size < sizeof(ULONG)) 2348 return ERROR_INSUFFICIENT_BUFFER; 2349 2350 *(ULONG*)buffer = hdr->ErrorMask; 2351 *size = sizeof(ULONG); 2352 return ERROR_SUCCESS; 2353 } 2354 2355 return INET_QueryOption(hdr, option, buffer, size, unicode); 2356 } 2357 2358 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size) 2359 { 2360 http_request_t *req = (http_request_t*)hdr; 2361 2362 switch(option) { 2363 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */ 2364 TRACE("Undocumented option 99\n"); 2365 2366 if (!buffer || size != sizeof(DWORD)) 2367 return ERROR_INVALID_PARAMETER; 2368 if(*(DWORD*)buffer & ~SECURITY_SET_MASK) 2369 return ERROR_INTERNET_OPTION_NOT_SETTABLE; 2370 2371 /* fall through */ 2372 case INTERNET_OPTION_SECURITY_FLAGS: 2373 { 2374 DWORD flags; 2375 2376 if (!buffer || size != sizeof(DWORD)) 2377 return ERROR_INVALID_PARAMETER; 2378 flags = *(DWORD *)buffer; 2379 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags); 2380 flags &= SECURITY_SET_MASK; 2381 req->security_flags |= flags; 2382 if(is_valid_netconn(req->netconn)) 2383 req->netconn->security_flags |= flags; 2384 return ERROR_SUCCESS; 2385 } 2386 case INTERNET_OPTION_CONNECT_TIMEOUT: 2387 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER; 2388 req->connect_timeout = *(DWORD *)buffer; 2389 return ERROR_SUCCESS; 2390 2391 case INTERNET_OPTION_SEND_TIMEOUT: 2392 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER; 2393 req->send_timeout = *(DWORD *)buffer; 2394 return ERROR_SUCCESS; 2395 2396 case INTERNET_OPTION_RECEIVE_TIMEOUT: 2397 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER; 2398 req->receive_timeout = *(DWORD *)buffer; 2399 return ERROR_SUCCESS; 2400 2401 case INTERNET_OPTION_USERNAME: 2402 heap_free(req->session->userName); 2403 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 2404 return ERROR_SUCCESS; 2405 2406 case INTERNET_OPTION_PASSWORD: 2407 heap_free(req->session->password); 2408 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 2409 return ERROR_SUCCESS; 2410 2411 case INTERNET_OPTION_PROXY_USERNAME: 2412 heap_free(req->session->appInfo->proxyUsername); 2413 if (!(req->session->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 2414 return ERROR_SUCCESS; 2415 2416 case INTERNET_OPTION_PROXY_PASSWORD: 2417 heap_free(req->session->appInfo->proxyPassword); 2418 if (!(req->session->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 2419 return ERROR_SUCCESS; 2420 2421 } 2422 2423 return INET_SetOption(hdr, option, buffer, size); 2424 } 2425 2426 static void commit_cache_entry(http_request_t *req) 2427 { 2428 WCHAR *header; 2429 DWORD header_len; 2430 BOOL res; 2431 2432 TRACE("%p\n", req); 2433 2434 CloseHandle(req->hCacheFile); 2435 req->hCacheFile = NULL; 2436 2437 header = build_response_header(req, TRUE); 2438 header_len = (header ? lstrlenW(header) : 0); 2439 res = CommitUrlCacheEntryW(req->req_file->url, req->req_file->file_name, req->expires, 2440 req->last_modified, NORMAL_CACHE_ENTRY, 2441 header, header_len, NULL, 0); 2442 if(res) 2443 req->req_file->is_committed = TRUE; 2444 else 2445 WARN("CommitUrlCacheEntry failed: %u\n", GetLastError()); 2446 heap_free(header); 2447 } 2448 2449 static void create_cache_entry(http_request_t *req) 2450 { 2451 WCHAR file_name[MAX_PATH+1]; 2452 WCHAR *url; 2453 BOOL b = TRUE; 2454 2455 /* FIXME: We should free previous cache file earlier */ 2456 if(req->req_file) { 2457 req_file_release(req->req_file); 2458 req->req_file = NULL; 2459 } 2460 if(req->hCacheFile) { 2461 CloseHandle(req->hCacheFile); 2462 req->hCacheFile = NULL; 2463 } 2464 2465 if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) 2466 b = FALSE; 2467 2468 if(b) { 2469 int header_idx; 2470 2471 EnterCriticalSection( &req->headers_section ); 2472 2473 header_idx = HTTP_GetCustomHeaderIndex(req, L"Cache-Control", 0, FALSE); 2474 if(header_idx != -1) { 2475 WCHAR *ptr; 2476 2477 for(ptr=req->custHeaders[header_idx].lpszValue; *ptr; ) { 2478 WCHAR *end; 2479 2480 while(*ptr==' ' || *ptr=='\t') 2481 ptr++; 2482 2483 end = wcschr(ptr, ','); 2484 if(!end) 2485 end = ptr + lstrlenW(ptr); 2486 2487 if(!wcsnicmp(ptr, L"no-cache", ARRAY_SIZE(L"no-cache")-1) 2488 || !wcsnicmp(ptr, L"no-store", ARRAY_SIZE(L"no-store")-1)) { 2489 b = FALSE; 2490 break; 2491 } 2492 2493 ptr = end; 2494 if(*ptr == ',') 2495 ptr++; 2496 } 2497 } 2498 2499 LeaveCriticalSection( &req->headers_section ); 2500 } 2501 2502 if(!b) { 2503 if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE)) 2504 return; 2505 2506 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n"); 2507 } 2508 2509 url = compose_request_url(req); 2510 if(!url) { 2511 WARN("Could not get URL\n"); 2512 return; 2513 } 2514 2515 b = CreateUrlCacheEntryW(url, req->contentLength == ~0 ? 0 : req->contentLength, NULL, file_name, 0); 2516 if(!b) { 2517 WARN("Could not create cache entry: %08x\n", GetLastError()); 2518 return; 2519 } 2520 2521 create_req_file(file_name, &req->req_file); 2522 req->req_file->url = url; 2523 2524 req->hCacheFile = CreateFileW(file_name, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 2525 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 2526 if(req->hCacheFile == INVALID_HANDLE_VALUE) { 2527 WARN("Could not create file: %u\n", GetLastError()); 2528 req->hCacheFile = NULL; 2529 return; 2530 } 2531 2532 if(req->read_size) { 2533 DWORD written; 2534 2535 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL); 2536 if(!b) 2537 FIXME("WriteFile failed: %u\n", GetLastError()); 2538 2539 if(req->data_stream->vtbl->end_of_data(req->data_stream, req)) 2540 commit_cache_entry(req); 2541 } 2542 } 2543 2544 /* read some more data into the read buffer (the read section must be held) */ 2545 static DWORD read_more_data( http_request_t *req, int maxlen ) 2546 { 2547 DWORD res; 2548 int len; 2549 2550 if (req->read_pos) 2551 { 2552 /* move existing data to the start of the buffer */ 2553 if(req->read_size) 2554 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size ); 2555 req->read_pos = 0; 2556 } 2557 2558 if (maxlen == -1) maxlen = sizeof(req->read_buf); 2559 2560 res = NETCON_recv( req->netconn, req->read_buf + req->read_size, 2561 maxlen - req->read_size, TRUE, &len ); 2562 if(res == ERROR_SUCCESS) 2563 req->read_size += len; 2564 2565 return res; 2566 } 2567 2568 /* remove some amount of data from the read buffer (the read section must be held) */ 2569 static void remove_data( http_request_t *req, int count ) 2570 { 2571 if (!(req->read_size -= count)) req->read_pos = 0; 2572 else req->read_pos += count; 2573 } 2574 2575 static DWORD read_line( http_request_t *req, LPSTR buffer, DWORD *len ) 2576 { 2577 int count, bytes_read, pos = 0; 2578 DWORD res; 2579 2580 EnterCriticalSection( &req->read_section ); 2581 for (;;) 2582 { 2583 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size ); 2584 2585 if (eol) 2586 { 2587 count = eol - (req->read_buf + req->read_pos); 2588 bytes_read = count + 1; 2589 } 2590 else count = bytes_read = req->read_size; 2591 2592 count = min( count, *len - pos ); 2593 memcpy( buffer + pos, req->read_buf + req->read_pos, count ); 2594 pos += count; 2595 remove_data( req, bytes_read ); 2596 if (eol) break; 2597 2598 if ((res = read_more_data( req, -1 ))) 2599 { 2600 WARN( "read failed %u\n", res ); 2601 LeaveCriticalSection( &req->read_section ); 2602 return res; 2603 } 2604 if (!req->read_size) 2605 { 2606 *len = 0; 2607 TRACE( "returning empty string\n" ); 2608 LeaveCriticalSection( &req->read_section ); 2609 return ERROR_SUCCESS; 2610 } 2611 } 2612 LeaveCriticalSection( &req->read_section ); 2613 2614 if (pos < *len) 2615 { 2616 if (pos && buffer[pos - 1] == '\r') pos--; 2617 *len = pos + 1; 2618 } 2619 buffer[*len - 1] = 0; 2620 TRACE( "returning %s\n", debugstr_a(buffer)); 2621 return ERROR_SUCCESS; 2622 } 2623 2624 /* check if we have reached the end of the data to read (the read section must be held) */ 2625 static BOOL end_of_read_data( http_request_t *req ) 2626 { 2627 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req); 2628 } 2629 2630 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, BOOL allow_blocking) 2631 { 2632 DWORD res; 2633 2634 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, allow_blocking); 2635 if(res != ERROR_SUCCESS) 2636 *read = 0; 2637 assert(*read <= size); 2638 2639 if(req->hCacheFile) { 2640 if(*read) { 2641 BOOL bres; 2642 DWORD written; 2643 2644 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL); 2645 if(!bres) 2646 FIXME("WriteFile failed: %u\n", GetLastError()); 2647 } 2648 2649 if((res == ERROR_SUCCESS && !*read) || req->data_stream->vtbl->end_of_data(req->data_stream, req)) 2650 commit_cache_entry(req); 2651 } 2652 2653 return res; 2654 } 2655 2656 /* fetch some more data into the read buffer (the read section must be held) */ 2657 static DWORD refill_read_buffer(http_request_t *req, BOOL allow_blocking, DWORD *read_bytes) 2658 { 2659 DWORD res, read=0; 2660 2661 if(req->read_size == sizeof(req->read_buf)) 2662 return ERROR_SUCCESS; 2663 2664 if(req->read_pos) { 2665 if(req->read_size) 2666 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size); 2667 req->read_pos = 0; 2668 } 2669 2670 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size, 2671 &read, allow_blocking); 2672 if(res != ERROR_SUCCESS) 2673 return res; 2674 2675 req->read_size += read; 2676 2677 TRACE("read %u bytes, read_size %u\n", read, req->read_size); 2678 if(read_bytes) 2679 *read_bytes = read; 2680 return res; 2681 } 2682 2683 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req) 2684 { 2685 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream; 2686 return netconn_stream->content_read == netconn_stream->content_length || !is_valid_netconn(req->netconn); 2687 } 2688 2689 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size, 2690 DWORD *read, BOOL allow_blocking) 2691 { 2692 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream; 2693 DWORD res = ERROR_SUCCESS; 2694 int ret = 0; 2695 2696 size = min(size, netconn_stream->content_length-netconn_stream->content_read); 2697 2698 if(size && is_valid_netconn(req->netconn)) { 2699 res = NETCON_recv(req->netconn, buf, size, allow_blocking, &ret); 2700 if(res == ERROR_SUCCESS) { 2701 if(!ret) 2702 netconn_stream->content_length = netconn_stream->content_read; 2703 netconn_stream->content_read += ret; 2704 } 2705 } 2706 2707 TRACE("res %u read %u bytes\n", res, ret); 2708 *read = ret; 2709 return res; 2710 } 2711 2712 static DWORD netconn_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking) 2713 { 2714 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream; 2715 BYTE buf[1024]; 2716 int len, res; 2717 size_t size; 2718 2719 if(netconn_stream->content_length == ~0) 2720 return WSAEISCONN; 2721 2722 while(netconn_stream->content_read < netconn_stream->content_length) { 2723 size = min(sizeof(buf), netconn_stream->content_length-netconn_stream->content_read); 2724 res = NETCON_recv(req->netconn, buf, size, allow_blocking, &len); 2725 if(res) 2726 return res; 2727 if(!len) 2728 return WSAECONNABORTED; 2729 2730 netconn_stream->content_read += len; 2731 } 2732 2733 return ERROR_SUCCESS; 2734 } 2735 2736 static void netconn_destroy(data_stream_t *stream) 2737 { 2738 } 2739 2740 static const data_stream_vtbl_t netconn_stream_vtbl = { 2741 netconn_end_of_data, 2742 netconn_read, 2743 netconn_drain_content, 2744 netconn_destroy 2745 }; 2746 2747 static char next_chunked_data_char(chunked_stream_t *stream) 2748 { 2749 assert(stream->buf_size); 2750 2751 stream->buf_size--; 2752 return stream->buf[stream->buf_pos++]; 2753 } 2754 2755 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req) 2756 { 2757 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream; 2758 switch(chunked_stream->state) { 2759 case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END: 2760 case CHUNKED_STREAM_STATE_END_OF_STREAM: 2761 case CHUNKED_STREAM_STATE_ERROR: 2762 return TRUE; 2763 default: 2764 return FALSE; 2765 } 2766 } 2767 2768 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size, 2769 DWORD *read, BOOL allow_blocking) 2770 { 2771 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream; 2772 DWORD ret_read = 0, res = ERROR_SUCCESS; 2773 BOOL continue_read = TRUE; 2774 int read_bytes; 2775 char ch; 2776 2777 do { 2778 TRACE("state %d\n", chunked_stream->state); 2779 2780 /* Ensure that we have data in the buffer for states that need it. */ 2781 if(!chunked_stream->buf_size) { 2782 BOOL blocking_read = allow_blocking; 2783 2784 switch(chunked_stream->state) { 2785 case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END: 2786 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE: 2787 /* never allow blocking after 0 chunk size */ 2788 if(!chunked_stream->chunk_size) 2789 blocking_read = FALSE; 2790 /* fall through */ 2791 case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE: 2792 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA: 2793 chunked_stream->buf_pos = 0; 2794 res = NETCON_recv(req->netconn, chunked_stream->buf, sizeof(chunked_stream->buf), blocking_read, &read_bytes); 2795 if(res == ERROR_SUCCESS && read_bytes) { 2796 chunked_stream->buf_size += read_bytes; 2797 }else if(res == WSAEWOULDBLOCK) { 2798 if(ret_read || allow_blocking) 2799 res = ERROR_SUCCESS; 2800 continue_read = FALSE; 2801 continue; 2802 }else { 2803 chunked_stream->state = CHUNKED_STREAM_STATE_ERROR; 2804 } 2805 break; 2806 default: 2807 break; 2808 } 2809 } 2810 2811 switch(chunked_stream->state) { 2812 case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE: 2813 ch = next_chunked_data_char(chunked_stream); 2814 2815 if(ch >= '0' && ch <= '9') { 2816 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - '0'; 2817 }else if(ch >= 'a' && ch <= 'f') { 2818 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'a' + 10; 2819 }else if (ch >= 'A' && ch <= 'F') { 2820 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'A' + 10; 2821 }else if (ch == ';' || ch == '\r' || ch == '\n') { 2822 TRACE("reading %u byte chunk\n", chunked_stream->chunk_size); 2823 chunked_stream->buf_size++; 2824 chunked_stream->buf_pos--; 2825 if(req->contentLength == ~0) req->contentLength = chunked_stream->chunk_size; 2826 else req->contentLength += chunked_stream->chunk_size; 2827 chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE; 2828 } 2829 break; 2830 2831 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE: 2832 ch = next_chunked_data_char(chunked_stream); 2833 if(ch == '\n') 2834 chunked_stream->state = chunked_stream->chunk_size 2835 ? CHUNKED_STREAM_STATE_READING_CHUNK 2836 : CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END; 2837 else if(ch != '\r') 2838 WARN("unexpected char '%c'\n", ch); 2839 break; 2840 2841 case CHUNKED_STREAM_STATE_READING_CHUNK: 2842 assert(chunked_stream->chunk_size); 2843 if(!size) { 2844 continue_read = FALSE; 2845 break; 2846 } 2847 read_bytes = min(size, chunked_stream->chunk_size); 2848 2849 if(chunked_stream->buf_size) { 2850 if(read_bytes > chunked_stream->buf_size) 2851 read_bytes = chunked_stream->buf_size; 2852 2853 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes); 2854 chunked_stream->buf_pos += read_bytes; 2855 chunked_stream->buf_size -= read_bytes; 2856 }else { 2857 res = NETCON_recv(req->netconn, (char*)buf+ret_read, read_bytes, 2858 allow_blocking, (int*)&read_bytes); 2859 if(res != ERROR_SUCCESS) { 2860 continue_read = FALSE; 2861 break; 2862 } 2863 2864 if(!read_bytes) { 2865 chunked_stream->state = CHUNKED_STREAM_STATE_ERROR; 2866 continue; 2867 } 2868 } 2869 2870 chunked_stream->chunk_size -= read_bytes; 2871 size -= read_bytes; 2872 ret_read += read_bytes; 2873 if(!chunked_stream->chunk_size) 2874 chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA; 2875 allow_blocking = FALSE; 2876 break; 2877 2878 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA: 2879 ch = next_chunked_data_char(chunked_stream); 2880 if(ch == '\n') 2881 chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE; 2882 else if(ch != '\r') 2883 WARN("unexpected char '%c'\n", ch); 2884 break; 2885 2886 case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END: 2887 ch = next_chunked_data_char(chunked_stream); 2888 if(ch == '\n') 2889 chunked_stream->state = CHUNKED_STREAM_STATE_END_OF_STREAM; 2890 else if(ch != '\r') 2891 WARN("unexpected char '%c'\n", ch); 2892 break; 2893 2894 case CHUNKED_STREAM_STATE_END_OF_STREAM: 2895 case CHUNKED_STREAM_STATE_ERROR: 2896 continue_read = FALSE; 2897 break; 2898 } 2899 } while(continue_read); 2900 2901 if(ret_read) 2902 res = ERROR_SUCCESS; 2903 if(res != ERROR_SUCCESS) 2904 return res; 2905 2906 TRACE("read %d bytes\n", ret_read); 2907 *read = ret_read; 2908 return ERROR_SUCCESS; 2909 } 2910 2911 static DWORD chunked_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking) 2912 { 2913 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream; 2914 BYTE buf[1024]; 2915 DWORD size, res; 2916 2917 while(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM 2918 && chunked_stream->state != CHUNKED_STREAM_STATE_ERROR) { 2919 res = chunked_read(stream, req, buf, sizeof(buf), &size, allow_blocking); 2920 if(res != ERROR_SUCCESS) 2921 return res; 2922 } 2923 2924 if(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM) 2925 return ERROR_NO_DATA; 2926 return ERROR_SUCCESS; 2927 } 2928 2929 static void chunked_destroy(data_stream_t *stream) 2930 { 2931 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream; 2932 heap_free(chunked_stream); 2933 } 2934 2935 static const data_stream_vtbl_t chunked_stream_vtbl = { 2936 chunked_end_of_data, 2937 chunked_read, 2938 chunked_drain_content, 2939 chunked_destroy 2940 }; 2941 2942 /* set the request content length based on the headers */ 2943 static DWORD set_content_length(http_request_t *request) 2944 { 2945 WCHAR contentLength[32]; 2946 WCHAR encoding[20]; 2947 DWORD size; 2948 2949 if(request->status_code == HTTP_STATUS_NO_CONTENT || !wcscmp(request->verb, L"HEAD")) { 2950 request->contentLength = request->netconn_stream.content_length = 0; 2951 return ERROR_SUCCESS; 2952 } 2953 2954 size = sizeof(contentLength); 2955 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONTENT_LENGTH, 2956 contentLength, &size, NULL) != ERROR_SUCCESS || 2957 !StrToInt64ExW(contentLength, STIF_DEFAULT, (LONGLONG*)&request->contentLength)) { 2958 request->contentLength = ~0; 2959 } 2960 2961 request->netconn_stream.content_length = request->contentLength; 2962 request->netconn_stream.content_read = request->read_size; 2963 2964 size = sizeof(encoding); 2965 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS && 2966 !wcsicmp(encoding, L"chunked")) 2967 { 2968 chunked_stream_t *chunked_stream; 2969 2970 chunked_stream = heap_alloc(sizeof(*chunked_stream)); 2971 if(!chunked_stream) 2972 return ERROR_OUTOFMEMORY; 2973 2974 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl; 2975 chunked_stream->buf_size = chunked_stream->buf_pos = 0; 2976 chunked_stream->chunk_size = 0; 2977 chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE; 2978 2979 if(request->read_size) { 2980 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size); 2981 chunked_stream->buf_size = request->read_size; 2982 request->read_size = request->read_pos = 0; 2983 } 2984 2985 request->data_stream = &chunked_stream->data_stream; 2986 request->contentLength = ~0; 2987 } 2988 2989 if(request->hdr.decoding) { 2990 int encoding_idx; 2991 2992 EnterCriticalSection( &request->headers_section ); 2993 2994 encoding_idx = HTTP_GetCustomHeaderIndex(request, L"Content-Encoding", 0, FALSE); 2995 if(encoding_idx != -1) { 2996 if(!wcsicmp(request->custHeaders[encoding_idx].lpszValue, L"gzip")) { 2997 HTTP_DeleteCustomHeader(request, encoding_idx); 2998 LeaveCriticalSection( &request->headers_section ); 2999 return init_gzip_stream(request, TRUE); 3000 } 3001 if(!wcsicmp(request->custHeaders[encoding_idx].lpszValue, L"deflate")) { 3002 HTTP_DeleteCustomHeader(request, encoding_idx); 3003 LeaveCriticalSection( &request->headers_section ); 3004 return init_gzip_stream(request, FALSE); 3005 } 3006 } 3007 3008 LeaveCriticalSection( &request->headers_section ); 3009 } 3010 3011 return ERROR_SUCCESS; 3012 } 3013 3014 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error) 3015 { 3016 INTERNET_ASYNC_RESULT iar; 3017 3018 iar.dwResult = result; 3019 iar.dwError = error; 3020 3021 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, 3022 sizeof(INTERNET_ASYNC_RESULT)); 3023 } 3024 3025 static void HTTP_ReceiveRequestData(http_request_t *req) 3026 { 3027 DWORD res, read = 0; 3028 3029 TRACE("%p\n", req); 3030 3031 EnterCriticalSection( &req->read_section ); 3032 3033 res = refill_read_buffer(req, FALSE, &read); 3034 if(res == ERROR_SUCCESS) 3035 read += req->read_size; 3036 3037 LeaveCriticalSection( &req->read_section ); 3038 3039 if(res != WSAEWOULDBLOCK && (res != ERROR_SUCCESS || !read)) { 3040 WARN("res %u read %u, closing connection\n", res, read); 3041 http_release_netconn(req, FALSE); 3042 } 3043 3044 if(res != ERROR_SUCCESS && res != WSAEWOULDBLOCK) { 3045 send_request_complete(req, 0, res); 3046 return; 3047 } 3048 3049 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, 0); 3050 } 3051 3052 /* read data from the http connection (the read section must be held) */ 3053 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL allow_blocking) 3054 { 3055 DWORD current_read = 0, ret_read = 0; 3056 DWORD res = ERROR_SUCCESS; 3057 3058 EnterCriticalSection( &req->read_section ); 3059 3060 if(req->read_size) { 3061 ret_read = min(size, req->read_size); 3062 memcpy(buffer, req->read_buf+req->read_pos, ret_read); 3063 req->read_size -= ret_read; 3064 req->read_pos += ret_read; 3065 allow_blocking = FALSE; 3066 } 3067 3068 if(ret_read < size) { 3069 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, allow_blocking); 3070 if(res == ERROR_SUCCESS) 3071 ret_read += current_read; 3072 else if(res == WSAEWOULDBLOCK && ret_read) 3073 res = ERROR_SUCCESS; 3074 } 3075 3076 LeaveCriticalSection( &req->read_section ); 3077 3078 *read = ret_read; 3079 TRACE( "retrieved %u bytes (res %u)\n", ret_read, res ); 3080 3081 if(res != WSAEWOULDBLOCK) { 3082 if(res != ERROR_SUCCESS) 3083 http_release_netconn(req, FALSE); 3084 else if(!ret_read && drain_content(req, FALSE) == ERROR_SUCCESS) 3085 http_release_netconn(req, TRUE); 3086 } 3087 3088 return res; 3089 } 3090 3091 static DWORD drain_content(http_request_t *req, BOOL blocking) 3092 { 3093 DWORD res; 3094 3095 TRACE("%p\n", req->netconn); 3096 3097 if(!is_valid_netconn(req->netconn)) 3098 return ERROR_NO_DATA; 3099 3100 if(!wcscmp(req->verb, L"HEAD")) 3101 return ERROR_SUCCESS; 3102 3103 EnterCriticalSection( &req->read_section ); 3104 res = req->data_stream->vtbl->drain_content(req->data_stream, req, blocking); 3105 LeaveCriticalSection( &req->read_section ); 3106 return res; 3107 } 3108 3109 typedef struct { 3110 task_header_t hdr; 3111 void *buf; 3112 DWORD size; 3113 DWORD read_pos; 3114 DWORD *ret_read; 3115 } read_file_task_t; 3116 3117 static void async_read_file_proc(task_header_t *hdr) 3118 { 3119 read_file_task_t *task = (read_file_task_t*)hdr; 3120 http_request_t *req = (http_request_t*)task->hdr.hdr; 3121 DWORD res = ERROR_SUCCESS, read = task->read_pos, complete_arg = 0; 3122 3123 TRACE("req %p buf %p size %u read_pos %u ret_read %p\n", req, task->buf, task->size, task->read_pos, task->ret_read); 3124 3125 if(task->buf) { 3126 DWORD read_bytes; 3127 while (read < task->size) { 3128 res = HTTPREQ_Read(req, (char*)task->buf + read, task->size - read, &read_bytes, TRUE); 3129 if (res != ERROR_SUCCESS || !read_bytes) 3130 break; 3131 read += read_bytes; 3132 } 3133 }else { 3134 EnterCriticalSection(&req->read_section); 3135 res = refill_read_buffer(req, TRUE, &read); 3136 LeaveCriticalSection(&req->read_section); 3137 3138 if(task->ret_read) 3139 complete_arg = read; /* QueryDataAvailable reports read bytes in request complete notification */ 3140 if(res != ERROR_SUCCESS || !read) 3141 http_release_netconn(req, drain_content(req, FALSE) == ERROR_SUCCESS); 3142 } 3143 3144 TRACE("res %u read %u\n", res, read); 3145 3146 if(task->ret_read) 3147 *task->ret_read = read; 3148 3149 /* FIXME: We should report bytes transferred before decoding content. */ 3150 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, &read, sizeof(read)); 3151 3152 if(res != ERROR_SUCCESS) 3153 complete_arg = res; 3154 send_request_complete(req, res == ERROR_SUCCESS, complete_arg); 3155 } 3156 3157 static DWORD async_read(http_request_t *req, void *buf, DWORD size, DWORD read_pos, DWORD *ret_read) 3158 { 3159 read_file_task_t *task; 3160 3161 task = alloc_async_task(&req->hdr, async_read_file_proc, sizeof(*task)); 3162 if(!task) 3163 return ERROR_OUTOFMEMORY; 3164 3165 task->buf = buf; 3166 task->size = size; 3167 task->read_pos = read_pos; 3168 task->ret_read = ret_read; 3169 3170 INTERNET_AsyncCall(&task->hdr); 3171 return ERROR_IO_PENDING; 3172 } 3173 3174 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read, 3175 DWORD flags, DWORD_PTR context) 3176 { 3177 http_request_t *req = (http_request_t*)hdr; 3178 DWORD res = ERROR_SUCCESS, read = 0, cread, error = ERROR_SUCCESS; 3179 BOOL allow_blocking, notify_received = FALSE; 3180 3181 TRACE("(%p %p %u %x)\n", req, buf, size, flags); 3182 3183 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT)) 3184 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT)); 3185 3186 allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC); 3187 3188 if(allow_blocking || TryEnterCriticalSection(&req->read_section)) { 3189 if(allow_blocking) 3190 EnterCriticalSection(&req->read_section); 3191 if(hdr->dwError == ERROR_SUCCESS) 3192 hdr->dwError = INTERNET_HANDLE_IN_USE; 3193 else if(hdr->dwError == INTERNET_HANDLE_IN_USE) 3194 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR; 3195 3196 if(req->read_size) { 3197 read = min(size, req->read_size); 3198 memcpy(buf, req->read_buf + req->read_pos, read); 3199 req->read_size -= read; 3200 req->read_pos += read; 3201 } 3202 3203 if(read < size && (!read || !(flags & IRF_NO_WAIT)) && !end_of_read_data(req)) { 3204 LeaveCriticalSection(&req->read_section); 3205 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); 3206 EnterCriticalSection( &req->read_section ); 3207 notify_received = TRUE; 3208 3209 while(read < size) { 3210 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, allow_blocking); 3211 read += cread; 3212 if (res != ERROR_SUCCESS || !cread) 3213 break; 3214 } 3215 } 3216 3217 if(hdr->dwError == INTERNET_HANDLE_IN_USE) 3218 hdr->dwError = ERROR_SUCCESS; 3219 else 3220 error = hdr->dwError; 3221 3222 LeaveCriticalSection( &req->read_section ); 3223 }else { 3224 res = WSAEWOULDBLOCK; 3225 } 3226 3227 if(res == WSAEWOULDBLOCK) { 3228 if(!(flags & IRF_NO_WAIT)) 3229 return async_read(req, buf, size, read, ret_read); 3230 if(!read) 3231 return async_read(req, NULL, 0, 0, NULL); 3232 res = ERROR_SUCCESS; 3233 } 3234 3235 *ret_read = read; 3236 if (res != ERROR_SUCCESS) 3237 return res; 3238 3239 if(notify_received) 3240 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, 3241 &read, sizeof(read)); 3242 return error; 3243 } 3244 3245 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written) 3246 { 3247 DWORD res; 3248 http_request_t *request = (http_request_t*)hdr; 3249 3250 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); 3251 3252 *written = 0; 3253 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written); 3254 if (res == ERROR_SUCCESS) 3255 request->bytesWritten += *written; 3256 3257 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD)); 3258 return res; 3259 } 3260 3261 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx) 3262 { 3263 http_request_t *req = (http_request_t*)hdr; 3264 DWORD res = ERROR_SUCCESS, avail = 0, error = ERROR_SUCCESS; 3265 BOOL allow_blocking, notify_received = FALSE; 3266 3267 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx); 3268 3269 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT)) 3270 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT)); 3271 3272 *available = 0; 3273 allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC); 3274 3275 if(allow_blocking || TryEnterCriticalSection(&req->read_section)) { 3276 if(allow_blocking) 3277 EnterCriticalSection(&req->read_section); 3278 if(hdr->dwError == ERROR_SUCCESS) 3279 hdr->dwError = INTERNET_HANDLE_IN_USE; 3280 else if(hdr->dwError == INTERNET_HANDLE_IN_USE) 3281 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR; 3282 3283 avail = req->read_size; 3284 3285 if(!avail && !end_of_read_data(req)) { 3286 LeaveCriticalSection(&req->read_section); 3287 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); 3288 EnterCriticalSection( &req->read_section ); 3289 notify_received = TRUE; 3290 3291 res = refill_read_buffer(req, allow_blocking, &avail); 3292 } 3293 3294 if(hdr->dwError == INTERNET_HANDLE_IN_USE) 3295 hdr->dwError = ERROR_SUCCESS; 3296 else 3297 error = hdr->dwError; 3298 3299 LeaveCriticalSection( &req->read_section ); 3300 }else { 3301 res = WSAEWOULDBLOCK; 3302 } 3303 3304 if(res == WSAEWOULDBLOCK) 3305 return async_read(req, NULL, 0, 0, available); 3306 3307 if (res != ERROR_SUCCESS) 3308 return res; 3309 3310 *available = avail; 3311 if(notify_received) 3312 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, 3313 &avail, sizeof(avail)); 3314 return error; 3315 } 3316 3317 static DWORD HTTPREQ_LockRequestFile(object_header_t *hdr, req_file_t **ret) 3318 { 3319 http_request_t *req = (http_request_t*)hdr; 3320 3321 TRACE("(%p)\n", req); 3322 3323 if(!req->req_file) { 3324 WARN("No cache file name available\n"); 3325 return ERROR_FILE_NOT_FOUND; 3326 } 3327 3328 *ret = req_file_addref(req->req_file); 3329 return ERROR_SUCCESS; 3330 } 3331 3332 static const object_vtbl_t HTTPREQVtbl = { 3333 HTTPREQ_Destroy, 3334 HTTPREQ_CloseConnection, 3335 HTTPREQ_QueryOption, 3336 HTTPREQ_SetOption, 3337 HTTPREQ_ReadFile, 3338 HTTPREQ_WriteFile, 3339 HTTPREQ_QueryDataAvailable, 3340 NULL, 3341 HTTPREQ_LockRequestFile 3342 }; 3343 3344 /*********************************************************************** 3345 * HTTP_HttpOpenRequestW (internal) 3346 * 3347 * Open a HTTP request handle 3348 * 3349 * RETURNS 3350 * HINTERNET a HTTP request handle on success 3351 * NULL on failure 3352 * 3353 */ 3354 static DWORD HTTP_HttpOpenRequestW(http_session_t *session, 3355 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion, 3356 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes, 3357 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret) 3358 { 3359 appinfo_t *hIC = session->appInfo; 3360 http_request_t *request; 3361 DWORD port, len; 3362 3363 TRACE("-->\n"); 3364 3365 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t)); 3366 if(!request) 3367 return ERROR_OUTOFMEMORY; 3368 3369 request->hdr.htype = WH_HHTTPREQ; 3370 request->hdr.dwFlags = dwFlags; 3371 request->hdr.dwContext = dwContext; 3372 request->hdr.decoding = session->hdr.decoding; 3373 request->contentLength = ~0; 3374 3375 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl; 3376 request->data_stream = &request->netconn_stream.data_stream; 3377 request->connect_timeout = session->connect_timeout; 3378 request->send_timeout = session->send_timeout; 3379 request->receive_timeout = session->receive_timeout; 3380 3381 InitializeCriticalSection( &request->headers_section ); 3382 request->headers_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.headers_section"); 3383 3384 InitializeCriticalSection( &request->read_section ); 3385 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section"); 3386 3387 WININET_AddRef( &session->hdr ); 3388 request->session = session; 3389 list_add_head( &session->hdr.children, &request->hdr.entry ); 3390 3391 port = session->hostPort; 3392 if (port == INTERNET_INVALID_PORT_NUMBER) 3393 port = (session->hdr.dwFlags & INTERNET_FLAG_SECURE) ? 3394 INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT; 3395 3396 request->server = get_server(substrz(session->hostName), port, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE); 3397 if(!request->server) { 3398 WININET_Release(&request->hdr); 3399 return ERROR_OUTOFMEMORY; 3400 } 3401 3402 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID) 3403 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID; 3404 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID) 3405 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; 3406 3407 if (lpszObjectName && *lpszObjectName) { 3408 HRESULT rc; 3409 WCHAR dummy; 3410 3411 len = 1; 3412 rc = UrlCanonicalizeW(lpszObjectName, &dummy, &len, URL_ESCAPE_SPACES_ONLY); 3413 if (rc != E_POINTER) 3414 len = lstrlenW(lpszObjectName)+1; 3415 request->path = heap_alloc(len*sizeof(WCHAR)); 3416 rc = UrlCanonicalizeW(lpszObjectName, request->path, &len, 3417 URL_ESCAPE_SPACES_ONLY); 3418 if (rc != S_OK) 3419 { 3420 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc); 3421 lstrcpyW(request->path,lpszObjectName); 3422 } 3423 }else { 3424 request->path = heap_strdupW(L"/"); 3425 } 3426 3427 if (lpszReferrer && *lpszReferrer) 3428 HTTP_ProcessHeader(request, L"Referer", lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ); 3429 3430 if (lpszAcceptTypes) 3431 { 3432 int i; 3433 for (i = 0; lpszAcceptTypes[i]; i++) 3434 { 3435 if (!*lpszAcceptTypes[i]) continue; 3436 HTTP_ProcessHeader(request, L"Accept", lpszAcceptTypes[i], 3437 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA | 3438 HTTP_ADDHDR_FLAG_REQ | 3439 (i == 0 ? (HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD) : 0)); 3440 } 3441 } 3442 3443 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : L"GET"); 3444 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : L"HTTP/1.1"); 3445 3446 if (hIC->proxy && hIC->proxy[0] && !HTTP_ShouldBypassProxy(hIC, session->hostName)) 3447 HTTP_DealWithProxy( hIC, session, request ); 3448 3449 INTERNET_SendCallback(&session->hdr, dwContext, 3450 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet, 3451 sizeof(HINTERNET)); 3452 3453 TRACE("<-- (%p)\n", request); 3454 3455 *ret = request->hdr.hInternet; 3456 return ERROR_SUCCESS; 3457 } 3458 3459 /*********************************************************************** 3460 * HttpOpenRequestW (WININET.@) 3461 * 3462 * Open a HTTP request handle 3463 * 3464 * RETURNS 3465 * HINTERNET a HTTP request handle on success 3466 * NULL on failure 3467 * 3468 */ 3469 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession, 3470 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion, 3471 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes, 3472 DWORD dwFlags, DWORD_PTR dwContext) 3473 { 3474 http_session_t *session; 3475 HINTERNET handle = NULL; 3476 DWORD res; 3477 3478 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession, 3479 debugstr_w(lpszVerb), debugstr_w(lpszObjectName), 3480 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes, 3481 dwFlags, dwContext); 3482 if(lpszAcceptTypes!=NULL) 3483 { 3484 int i; 3485 for(i=0;lpszAcceptTypes[i]!=NULL;i++) 3486 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i])); 3487 } 3488 3489 session = (http_session_t*) get_handle_object( hHttpSession ); 3490 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION) 3491 { 3492 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 3493 goto lend; 3494 } 3495 3496 /* 3497 * My tests seem to show that the windows version does not 3498 * become asynchronous until after this point. And anyhow 3499 * if this call was asynchronous then how would you get the 3500 * necessary HINTERNET pointer returned by this function. 3501 * 3502 */ 3503 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName, 3504 lpszVersion, lpszReferrer, lpszAcceptTypes, 3505 dwFlags, dwContext, &handle); 3506 lend: 3507 if( session ) 3508 WININET_Release( &session->hdr ); 3509 TRACE("returning %p\n", handle); 3510 if(res != ERROR_SUCCESS) 3511 SetLastError(res); 3512 return handle; 3513 } 3514 3515 static const LPCWSTR header_lookup[] = { 3516 L"Mime-Version", /* HTTP_QUERY_MIME_VERSION = 0 */ 3517 L"Content-Type", /* HTTP_QUERY_CONTENT_TYPE = 1 */ 3518 L"Content-Transfer-Encoding", /* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */ 3519 L"Content-ID", /* HTTP_QUERY_CONTENT_ID = 3 */ 3520 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */ 3521 L"Content-Length", /* HTTP_QUERY_CONTENT_LENGTH = 5 */ 3522 L"Content-Language", /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */ 3523 L"Allow", /* HTTP_QUERY_ALLOW = 7 */ 3524 L"Public", /* HTTP_QUERY_PUBLIC = 8 */ 3525 L"Date", /* HTTP_QUERY_DATE = 9 */ 3526 L"Expires", /* HTTP_QUERY_EXPIRES = 10 */ 3527 L"Last-Modified", /* HTTP_QUERY_LAST_MODIFIED = 11 */ 3528 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */ 3529 L"URI", /* HTTP_QUERY_URI = 13 */ 3530 L"From", /* HTTP_QUERY_DERIVED_FROM = 14 */ 3531 NULL, /* HTTP_QUERY_COST = 15 */ 3532 NULL, /* HTTP_QUERY_LINK = 16 */ 3533 L"Pragma", /* HTTP_QUERY_PRAGMA = 17 */ 3534 NULL, /* HTTP_QUERY_VERSION = 18 */ 3535 L"Status", /* HTTP_QUERY_STATUS_CODE = 19 */ 3536 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */ 3537 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */ 3538 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */ 3539 L"Connection", /* HTTP_QUERY_CONNECTION = 23 */ 3540 L"Accept", /* HTTP_QUERY_ACCEPT = 24 */ 3541 L"Accept-Charset", /* HTTP_QUERY_ACCEPT_CHARSET = 25 */ 3542 L"Accept-Encoding", /* HTTP_QUERY_ACCEPT_ENCODING = 26 */ 3543 L"Accept-Language", /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */ 3544 L"Authorization", /* HTTP_QUERY_AUTHORIZATION = 28 */ 3545 L"Content-Encoding", /* HTTP_QUERY_CONTENT_ENCODING = 29 */ 3546 NULL, /* HTTP_QUERY_FORWARDED = 30 */ 3547 NULL, /* HTTP_QUERY_FROM = 31 */ 3548 L"If-Modified-Since", /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */ 3549 L"Location", /* HTTP_QUERY_LOCATION = 33 */ 3550 NULL, /* HTTP_QUERY_ORIG_URI = 34 */ 3551 L"Referer", /* HTTP_QUERY_REFERER = 35 */ 3552 L"Retry-After", /* HTTP_QUERY_RETRY_AFTER = 36 */ 3553 L"Server", /* HTTP_QUERY_SERVER = 37 */ 3554 NULL, /* HTTP_TITLE = 38 */ 3555 L"User-Agent", /* HTTP_QUERY_USER_AGENT = 39 */ 3556 L"WWW-Authenticate", /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */ 3557 L"Proxy-Authenticate", /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */ 3558 L"Accept-Ranges", /* HTTP_QUERY_ACCEPT_RANGES = 42 */ 3559 L"Set-Cookie", /* HTTP_QUERY_SET_COOKIE = 43 */ 3560 L"Cookie", /* HTTP_QUERY_COOKIE = 44 */ 3561 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */ 3562 NULL, /* HTTP_QUERY_REFRESH = 46 */ 3563 L"Content-Disposition", /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */ 3564 L"Age", /* HTTP_QUERY_AGE = 48 */ 3565 L"Cache-Control", /* HTTP_QUERY_CACHE_CONTROL = 49 */ 3566 L"Content-Base", /* HTTP_QUERY_CONTENT_BASE = 50 */ 3567 L"Content-Location", /* HTTP_QUERY_CONTENT_LOCATION = 51 */ 3568 L"Content-MD5", /* HTTP_QUERY_CONTENT_MD5 = 52 */ 3569 L"Content-Range", /* HTTP_QUERY_CONTENT_RANGE = 53 */ 3570 L"ETag", /* HTTP_QUERY_ETAG = 54 */ 3571 L"Host", /* HTTP_QUERY_HOST = 55 */ 3572 L"If-Match", /* HTTP_QUERY_IF_MATCH = 56 */ 3573 L"If-None-Match", /* HTTP_QUERY_IF_NONE_MATCH = 57 */ 3574 L"If-Range", /* HTTP_QUERY_IF_RANGE = 58 */ 3575 L"If-Unmodified-Since", /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */ 3576 L"Max-Forwards", /* HTTP_QUERY_MAX_FORWARDS = 60 */ 3577 L"Proxy-Authorization", /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */ 3578 L"Range", /* HTTP_QUERY_RANGE = 62 */ 3579 L"Transfer-Encoding", /* HTTP_QUERY_TRANSFER_ENCODING = 63 */ 3580 L"Upgrade", /* HTTP_QUERY_UPGRADE = 64 */ 3581 L"Vary", /* HTTP_QUERY_VARY = 65 */ 3582 L"Via", /* HTTP_QUERY_VIA = 66 */ 3583 L"Warning", /* HTTP_QUERY_WARNING = 67 */ 3584 L"Expect", /* HTTP_QUERY_EXPECT = 68 */ 3585 L"Proxy-Connection", /* HTTP_QUERY_PROXY_CONNECTION = 69 */ 3586 L"Unless-Modified-Since", /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */ 3587 }; 3588 3589 /*********************************************************************** 3590 * HTTP_HttpQueryInfoW (internal) 3591 */ 3592 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel, 3593 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex) 3594 { 3595 LPHTTPHEADERW lphttpHdr = NULL; 3596 BOOL request_only = !!(dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS); 3597 INT requested_index = lpdwIndex ? *lpdwIndex : 0; 3598 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK); 3599 INT index = -1; 3600 3601 EnterCriticalSection( &request->headers_section ); 3602 3603 /* Find requested header structure */ 3604 switch (level) 3605 { 3606 case HTTP_QUERY_CUSTOM: 3607 if (!lpBuffer) 3608 { 3609 LeaveCriticalSection( &request->headers_section ); 3610 return ERROR_INVALID_PARAMETER; 3611 } 3612 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only); 3613 break; 3614 case HTTP_QUERY_RAW_HEADERS_CRLF: 3615 { 3616 LPWSTR headers; 3617 DWORD len = 0; 3618 DWORD res = ERROR_INVALID_PARAMETER; 3619 3620 if (request_only) 3621 headers = build_request_header(request, request->verb, request->path, request->version, TRUE); 3622 else 3623 headers = build_response_header(request, TRUE); 3624 if (!headers) 3625 { 3626 LeaveCriticalSection( &request->headers_section ); 3627 return ERROR_OUTOFMEMORY; 3628 } 3629 3630 len = lstrlenW(headers) * sizeof(WCHAR); 3631 if (len + sizeof(WCHAR) > *lpdwBufferLength) 3632 { 3633 len += sizeof(WCHAR); 3634 res = ERROR_INSUFFICIENT_BUFFER; 3635 } 3636 else if (lpBuffer) 3637 { 3638 memcpy(lpBuffer, headers, len + sizeof(WCHAR)); 3639 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR))); 3640 res = ERROR_SUCCESS; 3641 } 3642 *lpdwBufferLength = len; 3643 3644 heap_free(headers); 3645 LeaveCriticalSection( &request->headers_section ); 3646 return res; 3647 } 3648 case HTTP_QUERY_RAW_HEADERS: 3649 { 3650 LPWSTR headers; 3651 DWORD len; 3652 3653 if (request_only) 3654 headers = build_request_header(request, request->verb, request->path, request->version, FALSE); 3655 else 3656 headers = build_response_header(request, FALSE); 3657 3658 if (!headers) 3659 { 3660 LeaveCriticalSection( &request->headers_section ); 3661 return ERROR_OUTOFMEMORY; 3662 } 3663 3664 len = lstrlenW(headers) * sizeof(WCHAR); 3665 if (len > *lpdwBufferLength) 3666 { 3667 *lpdwBufferLength = len; 3668 heap_free(headers); 3669 LeaveCriticalSection( &request->headers_section ); 3670 return ERROR_INSUFFICIENT_BUFFER; 3671 } 3672 3673 if (lpBuffer) 3674 { 3675 DWORD i; 3676 3677 TRACE("returning data: %s\n", debugstr_wn(headers, len / sizeof(WCHAR))); 3678 3679 for (i = 0; i < len / sizeof(WCHAR); i++) 3680 { 3681 if (headers[i] == '\n') 3682 headers[i] = 0; 3683 } 3684 memcpy(lpBuffer, headers, len); 3685 } 3686 *lpdwBufferLength = len - sizeof(WCHAR); 3687 3688 heap_free(headers); 3689 LeaveCriticalSection( &request->headers_section ); 3690 return ERROR_SUCCESS; 3691 } 3692 case HTTP_QUERY_STATUS_TEXT: 3693 if (request->statusText) 3694 { 3695 DWORD len = lstrlenW(request->statusText); 3696 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR)) 3697 { 3698 *lpdwBufferLength = (len + 1) * sizeof(WCHAR); 3699 LeaveCriticalSection( &request->headers_section ); 3700 return ERROR_INSUFFICIENT_BUFFER; 3701 } 3702 if (lpBuffer) 3703 { 3704 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR)); 3705 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len)); 3706 } 3707 *lpdwBufferLength = len * sizeof(WCHAR); 3708 LeaveCriticalSection( &request->headers_section ); 3709 return ERROR_SUCCESS; 3710 } 3711 break; 3712 case HTTP_QUERY_VERSION: 3713 if (request->version) 3714 { 3715 DWORD len = lstrlenW(request->version); 3716 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR)) 3717 { 3718 *lpdwBufferLength = (len + 1) * sizeof(WCHAR); 3719 LeaveCriticalSection( &request->headers_section ); 3720 return ERROR_INSUFFICIENT_BUFFER; 3721 } 3722 if (lpBuffer) 3723 { 3724 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR)); 3725 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len)); 3726 } 3727 *lpdwBufferLength = len * sizeof(WCHAR); 3728 LeaveCriticalSection( &request->headers_section ); 3729 return ERROR_SUCCESS; 3730 } 3731 break; 3732 case HTTP_QUERY_CONTENT_ENCODING: 3733 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level], 3734 requested_index,request_only); 3735 break; 3736 case HTTP_QUERY_STATUS_CODE: { 3737 DWORD res = ERROR_SUCCESS; 3738 3739 if(request_only) 3740 { 3741 LeaveCriticalSection( &request->headers_section ); 3742 return ERROR_HTTP_INVALID_QUERY_REQUEST; 3743 } 3744 3745 if(requested_index) 3746 break; 3747 3748 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) { 3749 if(*lpdwBufferLength >= sizeof(DWORD)) 3750 *(DWORD*)lpBuffer = request->status_code; 3751 else 3752 res = ERROR_INSUFFICIENT_BUFFER; 3753 *lpdwBufferLength = sizeof(DWORD); 3754 }else { 3755 WCHAR buf[12]; 3756 DWORD size; 3757 3758 size = swprintf(buf, ARRAY_SIZE(buf), L"%u", request->status_code) * sizeof(WCHAR); 3759 3760 if(size <= *lpdwBufferLength) { 3761 memcpy(lpBuffer, buf, size+sizeof(WCHAR)); 3762 }else { 3763 size += sizeof(WCHAR); 3764 res = ERROR_INSUFFICIENT_BUFFER; 3765 } 3766 3767 *lpdwBufferLength = size; 3768 } 3769 LeaveCriticalSection( &request->headers_section ); 3770 return res; 3771 } 3772 default: 3773 assert (ARRAY_SIZE(header_lookup) == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1)); 3774 3775 if (level < ARRAY_SIZE(header_lookup) && header_lookup[level]) 3776 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level], 3777 requested_index,request_only); 3778 } 3779 3780 if (index >= 0) 3781 lphttpHdr = &request->custHeaders[index]; 3782 3783 /* Ensure header satisfies requested attributes */ 3784 if (!lphttpHdr || 3785 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) && 3786 (~lphttpHdr->wFlags & HDR_ISREQUEST))) 3787 { 3788 LeaveCriticalSection( &request->headers_section ); 3789 return ERROR_HTTP_HEADER_NOT_FOUND; 3790 } 3791 3792 /* coalesce value to requested type */ 3793 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer) 3794 { 3795 unsigned long value; 3796 3797 if (*lpdwBufferLength != sizeof(DWORD)) 3798 { 3799 LeaveCriticalSection( &request->headers_section ); 3800 return ERROR_HTTP_INVALID_HEADER; 3801 } 3802 3803 errno = 0; 3804 value = wcstoul( lphttpHdr->lpszValue, NULL, 10 ); 3805 if (value > UINT_MAX || (value == ULONG_MAX && errno == ERANGE)) 3806 { 3807 LeaveCriticalSection( &request->headers_section ); 3808 return ERROR_HTTP_INVALID_HEADER; 3809 } 3810 3811 *(DWORD *)lpBuffer = value; 3812 TRACE(" returning number: %u\n", *(DWORD *)lpBuffer); 3813 } 3814 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer) 3815 { 3816 time_t tmpTime; 3817 struct tm tmpTM; 3818 SYSTEMTIME *STHook; 3819 3820 tmpTime = ConvertTimeString(lphttpHdr->lpszValue); 3821 3822 tmpTM = *gmtime(&tmpTime); 3823 STHook = (SYSTEMTIME *)lpBuffer; 3824 STHook->wDay = tmpTM.tm_mday; 3825 STHook->wHour = tmpTM.tm_hour; 3826 STHook->wMilliseconds = 0; 3827 STHook->wMinute = tmpTM.tm_min; 3828 STHook->wDayOfWeek = tmpTM.tm_wday; 3829 STHook->wMonth = tmpTM.tm_mon + 1; 3830 STHook->wSecond = tmpTM.tm_sec; 3831 STHook->wYear = 1900+tmpTM.tm_year; 3832 3833 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n", 3834 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek, 3835 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds); 3836 } 3837 else if (lphttpHdr->lpszValue) 3838 { 3839 DWORD len = (lstrlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR); 3840 3841 if (len > *lpdwBufferLength) 3842 { 3843 *lpdwBufferLength = len; 3844 LeaveCriticalSection( &request->headers_section ); 3845 return ERROR_INSUFFICIENT_BUFFER; 3846 } 3847 if (lpBuffer) 3848 { 3849 memcpy(lpBuffer, lphttpHdr->lpszValue, len); 3850 TRACE("! returning string: %s\n", debugstr_w(lpBuffer)); 3851 } 3852 *lpdwBufferLength = len - sizeof(WCHAR); 3853 } 3854 if (lpdwIndex) (*lpdwIndex)++; 3855 3856 LeaveCriticalSection( &request->headers_section ); 3857 return ERROR_SUCCESS; 3858 } 3859 3860 /*********************************************************************** 3861 * HttpQueryInfoW (WININET.@) 3862 * 3863 * Queries for information about an HTTP request 3864 * 3865 * RETURNS 3866 * TRUE on success 3867 * FALSE on failure 3868 * 3869 */ 3870 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel, 3871 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex) 3872 { 3873 http_request_t *request; 3874 DWORD res; 3875 3876 if (TRACE_ON(wininet)) { 3877 #define FE(x) { x, #x } 3878 static const wininet_flag_info query_flags[] = { 3879 FE(HTTP_QUERY_MIME_VERSION), 3880 FE(HTTP_QUERY_CONTENT_TYPE), 3881 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING), 3882 FE(HTTP_QUERY_CONTENT_ID), 3883 FE(HTTP_QUERY_CONTENT_DESCRIPTION), 3884 FE(HTTP_QUERY_CONTENT_LENGTH), 3885 FE(HTTP_QUERY_CONTENT_LANGUAGE), 3886 FE(HTTP_QUERY_ALLOW), 3887 FE(HTTP_QUERY_PUBLIC), 3888 FE(HTTP_QUERY_DATE), 3889 FE(HTTP_QUERY_EXPIRES), 3890 FE(HTTP_QUERY_LAST_MODIFIED), 3891 FE(HTTP_QUERY_MESSAGE_ID), 3892 FE(HTTP_QUERY_URI), 3893 FE(HTTP_QUERY_DERIVED_FROM), 3894 FE(HTTP_QUERY_COST), 3895 FE(HTTP_QUERY_LINK), 3896 FE(HTTP_QUERY_PRAGMA), 3897 FE(HTTP_QUERY_VERSION), 3898 FE(HTTP_QUERY_STATUS_CODE), 3899 FE(HTTP_QUERY_STATUS_TEXT), 3900 FE(HTTP_QUERY_RAW_HEADERS), 3901 FE(HTTP_QUERY_RAW_HEADERS_CRLF), 3902 FE(HTTP_QUERY_CONNECTION), 3903 FE(HTTP_QUERY_ACCEPT), 3904 FE(HTTP_QUERY_ACCEPT_CHARSET), 3905 FE(HTTP_QUERY_ACCEPT_ENCODING), 3906 FE(HTTP_QUERY_ACCEPT_LANGUAGE), 3907 FE(HTTP_QUERY_AUTHORIZATION), 3908 FE(HTTP_QUERY_CONTENT_ENCODING), 3909 FE(HTTP_QUERY_FORWARDED), 3910 FE(HTTP_QUERY_FROM), 3911 FE(HTTP_QUERY_IF_MODIFIED_SINCE), 3912 FE(HTTP_QUERY_LOCATION), 3913 FE(HTTP_QUERY_ORIG_URI), 3914 FE(HTTP_QUERY_REFERER), 3915 FE(HTTP_QUERY_RETRY_AFTER), 3916 FE(HTTP_QUERY_SERVER), 3917 FE(HTTP_QUERY_TITLE), 3918 FE(HTTP_QUERY_USER_AGENT), 3919 FE(HTTP_QUERY_WWW_AUTHENTICATE), 3920 FE(HTTP_QUERY_PROXY_AUTHENTICATE), 3921 FE(HTTP_QUERY_ACCEPT_RANGES), 3922 FE(HTTP_QUERY_SET_COOKIE), 3923 FE(HTTP_QUERY_COOKIE), 3924 FE(HTTP_QUERY_REQUEST_METHOD), 3925 FE(HTTP_QUERY_REFRESH), 3926 FE(HTTP_QUERY_CONTENT_DISPOSITION), 3927 FE(HTTP_QUERY_AGE), 3928 FE(HTTP_QUERY_CACHE_CONTROL), 3929 FE(HTTP_QUERY_CONTENT_BASE), 3930 FE(HTTP_QUERY_CONTENT_LOCATION), 3931 FE(HTTP_QUERY_CONTENT_MD5), 3932 FE(HTTP_QUERY_CONTENT_RANGE), 3933 FE(HTTP_QUERY_ETAG), 3934 FE(HTTP_QUERY_HOST), 3935 FE(HTTP_QUERY_IF_MATCH), 3936 FE(HTTP_QUERY_IF_NONE_MATCH), 3937 FE(HTTP_QUERY_IF_RANGE), 3938 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE), 3939 FE(HTTP_QUERY_MAX_FORWARDS), 3940 FE(HTTP_QUERY_PROXY_AUTHORIZATION), 3941 FE(HTTP_QUERY_RANGE), 3942 FE(HTTP_QUERY_TRANSFER_ENCODING), 3943 FE(HTTP_QUERY_UPGRADE), 3944 FE(HTTP_QUERY_VARY), 3945 FE(HTTP_QUERY_VIA), 3946 FE(HTTP_QUERY_WARNING), 3947 FE(HTTP_QUERY_CUSTOM) 3948 }; 3949 static const wininet_flag_info modifier_flags[] = { 3950 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS), 3951 FE(HTTP_QUERY_FLAG_SYSTEMTIME), 3952 FE(HTTP_QUERY_FLAG_NUMBER), 3953 FE(HTTP_QUERY_FLAG_COALESCE) 3954 }; 3955 #undef FE 3956 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK; 3957 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK; 3958 DWORD i; 3959 3960 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info); 3961 TRACE(" Attribute:"); 3962 for (i = 0; i < ARRAY_SIZE(query_flags); i++) { 3963 if (query_flags[i].val == info) { 3964 TRACE(" %s", query_flags[i].name); 3965 break; 3966 } 3967 } 3968 if (i == ARRAY_SIZE(query_flags)) { 3969 TRACE(" Unknown (%08x)", info); 3970 } 3971 3972 TRACE(" Modifier:"); 3973 for (i = 0; i < ARRAY_SIZE(modifier_flags); i++) { 3974 if (modifier_flags[i].val & info_mod) { 3975 TRACE(" %s", modifier_flags[i].name); 3976 info_mod &= ~ modifier_flags[i].val; 3977 } 3978 } 3979 3980 if (info_mod) { 3981 TRACE(" Unknown (%08x)", info_mod); 3982 } 3983 TRACE("\n"); 3984 } 3985 3986 request = (http_request_t*) get_handle_object( hHttpRequest ); 3987 if (NULL == request || request->hdr.htype != WH_HHTTPREQ) 3988 { 3989 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 3990 goto lend; 3991 } 3992 3993 if (lpBuffer == NULL) 3994 *lpdwBufferLength = 0; 3995 res = HTTP_HttpQueryInfoW( request, dwInfoLevel, 3996 lpBuffer, lpdwBufferLength, lpdwIndex); 3997 3998 lend: 3999 if( request ) 4000 WININET_Release( &request->hdr ); 4001 4002 TRACE("%u <--\n", res); 4003 4004 SetLastError(res); 4005 return res == ERROR_SUCCESS; 4006 } 4007 4008 /*********************************************************************** 4009 * HttpQueryInfoA (WININET.@) 4010 * 4011 * Queries for information about an HTTP request 4012 * 4013 * RETURNS 4014 * TRUE on success 4015 * FALSE on failure 4016 * 4017 */ 4018 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel, 4019 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex) 4020 { 4021 BOOL result; 4022 DWORD len; 4023 WCHAR* bufferW; 4024 4025 TRACE("%p %x\n", hHttpRequest, dwInfoLevel); 4026 4027 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) || 4028 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME)) 4029 { 4030 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer, 4031 lpdwBufferLength, lpdwIndex ); 4032 } 4033 4034 if (lpBuffer) 4035 { 4036 DWORD alloclen; 4037 len = (*lpdwBufferLength)*sizeof(WCHAR); 4038 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM) 4039 { 4040 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR); 4041 if (alloclen < len) 4042 alloclen = len; 4043 } 4044 else 4045 alloclen = len; 4046 bufferW = heap_alloc(alloclen); 4047 /* buffer is in/out because of HTTP_QUERY_CUSTOM */ 4048 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM) 4049 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) ); 4050 } else 4051 { 4052 bufferW = NULL; 4053 len = 0; 4054 } 4055 4056 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW, 4057 &len, lpdwIndex ); 4058 if( result ) 4059 { 4060 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1, 4061 lpBuffer, *lpdwBufferLength, NULL, NULL ); 4062 *lpdwBufferLength = len - 1; 4063 4064 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer)); 4065 } 4066 else 4067 /* since the strings being returned from HttpQueryInfoW should be 4068 * only ASCII characters, it is reasonable to assume that all of 4069 * the Unicode characters can be reduced to a single byte */ 4070 *lpdwBufferLength = len / sizeof(WCHAR); 4071 4072 heap_free( bufferW ); 4073 return result; 4074 } 4075 4076 static WCHAR *get_redirect_url(http_request_t *request) 4077 { 4078 static WCHAR szHttp[] = L"http"; 4079 static WCHAR szHttps[] = L"https"; 4080 http_session_t *session = request->session; 4081 URL_COMPONENTSW urlComponents = { sizeof(urlComponents) }; 4082 WCHAR *orig_url = NULL, *redirect_url = NULL, *combined_url = NULL; 4083 DWORD url_length = 0, res; 4084 BOOL b; 4085 4086 url_length = 0; 4087 res = HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, redirect_url, &url_length, NULL); 4088 if(res == ERROR_INSUFFICIENT_BUFFER) { 4089 redirect_url = heap_alloc(url_length); 4090 res = HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, redirect_url, &url_length, NULL); 4091 } 4092 if(res != ERROR_SUCCESS) { 4093 heap_free(redirect_url); 4094 return NULL; 4095 } 4096 4097 urlComponents.dwSchemeLength = 1; 4098 b = InternetCrackUrlW(redirect_url, url_length / sizeof(WCHAR), 0, &urlComponents); 4099 if(b && urlComponents.dwSchemeLength && 4100 urlComponents.nScheme != INTERNET_SCHEME_HTTP && urlComponents.nScheme != INTERNET_SCHEME_HTTPS) { 4101 TRACE("redirect to non-http URL\n"); 4102 return NULL; 4103 } 4104 4105 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp; 4106 urlComponents.dwSchemeLength = 0; 4107 urlComponents.lpszHostName = request->server->name; 4108 urlComponents.nPort = request->server->port; 4109 urlComponents.lpszUserName = session->userName; 4110 urlComponents.lpszUrlPath = request->path; 4111 4112 b = InternetCreateUrlW(&urlComponents, 0, NULL, &url_length); 4113 if(!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 4114 orig_url = heap_alloc(url_length); 4115 4116 /* convert from bytes to characters */ 4117 url_length = url_length / sizeof(WCHAR) - 1; 4118 b = InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length); 4119 } 4120 4121 if(b) { 4122 url_length = 0; 4123 b = InternetCombineUrlW(orig_url, redirect_url, NULL, &url_length, ICU_ENCODE_SPACES_ONLY); 4124 if(!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 4125 combined_url = heap_alloc(url_length * sizeof(WCHAR)); 4126 b = InternetCombineUrlW(orig_url, redirect_url, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY); 4127 if(!b) { 4128 heap_free(combined_url); 4129 combined_url = NULL; 4130 } 4131 } 4132 } 4133 4134 heap_free(orig_url); 4135 heap_free(redirect_url); 4136 return combined_url; 4137 } 4138 4139 4140 /*********************************************************************** 4141 * HTTP_HandleRedirect (internal) 4142 */ 4143 static DWORD HTTP_HandleRedirect(http_request_t *request, WCHAR *url) 4144 { 4145 URL_COMPONENTSW urlComponents = { sizeof(urlComponents) }; 4146 http_session_t *session = request->session; 4147 size_t url_len = lstrlenW(url); 4148 4149 if(url[0] == '/') 4150 { 4151 /* if it's an absolute path, keep the same session info */ 4152 urlComponents.lpszUrlPath = url; 4153 urlComponents.dwUrlPathLength = url_len; 4154 } 4155 else 4156 { 4157 urlComponents.dwHostNameLength = 1; 4158 urlComponents.dwUserNameLength = 1; 4159 urlComponents.dwUrlPathLength = 1; 4160 if(!InternetCrackUrlW(url, url_len, 0, &urlComponents)) 4161 return INTERNET_GetLastError(); 4162 4163 if(!urlComponents.dwHostNameLength) 4164 return ERROR_INTERNET_INVALID_URL; 4165 } 4166 4167 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT, 4168 url, (url_len + 1) * sizeof(WCHAR)); 4169 4170 if(urlComponents.dwHostNameLength) { 4171 BOOL custom_port = FALSE; 4172 substr_t host; 4173 4174 if(urlComponents.nScheme == INTERNET_SCHEME_HTTP) { 4175 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) { 4176 TRACE("redirect from secure page to non-secure page\n"); 4177 /* FIXME: warn about from secure redirect to non-secure page */ 4178 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE; 4179 } 4180 4181 custom_port = urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT; 4182 }else if(urlComponents.nScheme == INTERNET_SCHEME_HTTPS) { 4183 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) { 4184 TRACE("redirect from non-secure page to secure page\n"); 4185 /* FIXME: notify about redirect to secure page */ 4186 request->hdr.dwFlags |= INTERNET_FLAG_SECURE; 4187 } 4188 4189 custom_port = urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT; 4190 } 4191 4192 heap_free(session->hostName); 4193 4194 session->hostName = heap_strndupW(urlComponents.lpszHostName, urlComponents.dwHostNameLength); 4195 session->hostPort = urlComponents.nPort; 4196 4197 heap_free(session->userName); 4198 session->userName = NULL; 4199 if (urlComponents.dwUserNameLength) 4200 session->userName = heap_strndupW(urlComponents.lpszUserName, urlComponents.dwUserNameLength); 4201 4202 reset_data_stream(request); 4203 4204 host = substr(urlComponents.lpszHostName, urlComponents.dwHostNameLength); 4205 4206 if(host.len != lstrlenW(request->server->name) || wcsnicmp(request->server->name, host.str, host.len) 4207 || request->server->port != urlComponents.nPort) { 4208 server_t *new_server; 4209 4210 new_server = get_server(host, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE); 4211 server_release(request->server); 4212 request->server = new_server; 4213 } 4214 4215 if (custom_port) 4216 HTTP_ProcessHeader(request, L"Host", request->server->host_port, 4217 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ); 4218 else 4219 HTTP_ProcessHeader(request, L"Host", request->server->name, 4220 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ); 4221 } 4222 4223 heap_free(request->path); 4224 request->path = NULL; 4225 if(urlComponents.dwUrlPathLength) 4226 { 4227 DWORD needed = 1; 4228 HRESULT rc; 4229 WCHAR dummy[] = L""; 4230 WCHAR *path; 4231 4232 path = heap_strndupW(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength); 4233 rc = UrlEscapeW(path, dummy, &needed, URL_ESCAPE_SPACES_ONLY); 4234 if (rc != E_POINTER) 4235 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc); 4236 request->path = heap_alloc(needed*sizeof(WCHAR)); 4237 rc = UrlEscapeW(path, request->path, &needed, 4238 URL_ESCAPE_SPACES_ONLY); 4239 if (rc != S_OK) 4240 { 4241 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc); 4242 lstrcpyW(request->path, path); 4243 } 4244 heap_free(path); 4245 } 4246 4247 /* Remove custom content-type/length headers on redirects. */ 4248 remove_header(request, L"Content-Type", TRUE); 4249 remove_header(request, L"Content-Length", TRUE); 4250 4251 return ERROR_SUCCESS; 4252 } 4253 4254 /*********************************************************************** 4255 * HTTP_build_req (internal) 4256 * 4257 * concatenate all the strings in the request together 4258 */ 4259 static LPWSTR HTTP_build_req( LPCWSTR *list, int len ) 4260 { 4261 LPCWSTR *t; 4262 LPWSTR str; 4263 4264 for( t = list; *t ; t++ ) 4265 len += lstrlenW( *t ); 4266 len++; 4267 4268 str = heap_alloc(len*sizeof(WCHAR)); 4269 *str = 0; 4270 4271 for( t = list; *t ; t++ ) 4272 lstrcatW( str, *t ); 4273 4274 return str; 4275 } 4276 4277 static void HTTP_InsertCookies(http_request_t *request) 4278 { 4279 WCHAR *cookies; 4280 DWORD res; 4281 4282 res = get_cookie_header(request->server->name, request->path, &cookies); 4283 if(res != ERROR_SUCCESS || !cookies) 4284 return; 4285 4286 HTTP_HttpAddRequestHeadersW(request, cookies, lstrlenW(cookies), 4287 HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD); 4288 heap_free(cookies); 4289 } 4290 4291 static WORD HTTP_ParseWkday(LPCWSTR day) 4292 { 4293 static const WCHAR days[7][4] = {L"sun", 4294 L"mon", 4295 L"tue", 4296 L"wed", 4297 L"thu", 4298 L"fri", 4299 L"sat"}; 4300 unsigned int i; 4301 for (i = 0; i < ARRAY_SIZE(days); i++) 4302 if (!wcsicmp(day, days[i])) 4303 return i; 4304 4305 /* Invalid */ 4306 return 7; 4307 } 4308 4309 static WORD HTTP_ParseMonth(LPCWSTR month) 4310 { 4311 if (!wcsicmp(month, L"jan")) return 1; 4312 if (!wcsicmp(month, L"feb")) return 2; 4313 if (!wcsicmp(month, L"mar")) return 3; 4314 if (!wcsicmp(month, L"apr")) return 4; 4315 if (!wcsicmp(month, L"may")) return 5; 4316 if (!wcsicmp(month, L"jun")) return 6; 4317 if (!wcsicmp(month, L"jul")) return 7; 4318 if (!wcsicmp(month, L"aug")) return 8; 4319 if (!wcsicmp(month, L"sep")) return 9; 4320 if (!wcsicmp(month, L"oct")) return 10; 4321 if (!wcsicmp(month, L"nov")) return 11; 4322 if (!wcsicmp(month, L"dec")) return 12; 4323 /* Invalid */ 4324 return 0; 4325 } 4326 4327 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS, 4328 * optionally preceded by whitespace. 4329 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of 4330 * st, and sets *str to the first character after the time format. 4331 */ 4332 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str) 4333 { 4334 LPCWSTR ptr = *str; 4335 WCHAR *nextPtr; 4336 unsigned long num; 4337 4338 while (iswspace(*ptr)) 4339 ptr++; 4340 4341 num = wcstoul(ptr, &nextPtr, 10); 4342 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':') 4343 { 4344 ERR("unexpected time format %s\n", debugstr_w(ptr)); 4345 return FALSE; 4346 } 4347 if (num > 23) 4348 { 4349 ERR("unexpected hour in time format %s\n", debugstr_w(ptr)); 4350 return FALSE; 4351 } 4352 ptr = nextPtr + 1; 4353 st->wHour = (WORD)num; 4354 num = wcstoul(ptr, &nextPtr, 10); 4355 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':') 4356 { 4357 ERR("unexpected time format %s\n", debugstr_w(ptr)); 4358 return FALSE; 4359 } 4360 if (num > 59) 4361 { 4362 ERR("unexpected minute in time format %s\n", debugstr_w(ptr)); 4363 return FALSE; 4364 } 4365 ptr = nextPtr + 1; 4366 st->wMinute = (WORD)num; 4367 num = wcstoul(ptr, &nextPtr, 10); 4368 if (!nextPtr || nextPtr <= ptr) 4369 { 4370 ERR("unexpected time format %s\n", debugstr_w(ptr)); 4371 return FALSE; 4372 } 4373 if (num > 59) 4374 { 4375 ERR("unexpected second in time format %s\n", debugstr_w(ptr)); 4376 return FALSE; 4377 } 4378 *str = nextPtr; 4379 st->wSecond = (WORD)num; 4380 return TRUE; 4381 } 4382 4383 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft) 4384 { 4385 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr; 4386 LPCWSTR ptr; 4387 SYSTEMTIME st = { 0 }; 4388 unsigned long num; 4389 4390 for (ptr = value, dayPtr = day; *ptr && !iswspace(*ptr) && 4391 dayPtr - day < ARRAY_SIZE(day) - 1; ptr++, dayPtr++) 4392 *dayPtr = *ptr; 4393 *dayPtr = 0; 4394 st.wDayOfWeek = HTTP_ParseWkday(day); 4395 if (st.wDayOfWeek >= 7) 4396 { 4397 ERR("unexpected weekday %s\n", debugstr_w(day)); 4398 return FALSE; 4399 } 4400 4401 while (iswspace(*ptr)) 4402 ptr++; 4403 4404 for (monthPtr = month; !iswspace(*ptr) && monthPtr - month < ARRAY_SIZE(month) - 1; 4405 monthPtr++, ptr++) 4406 *monthPtr = *ptr; 4407 *monthPtr = 0; 4408 st.wMonth = HTTP_ParseMonth(month); 4409 if (!st.wMonth || st.wMonth > 12) 4410 { 4411 ERR("unexpected month %s\n", debugstr_w(month)); 4412 return FALSE; 4413 } 4414 4415 while (iswspace(*ptr)) 4416 ptr++; 4417 4418 num = wcstoul(ptr, &nextPtr, 10); 4419 if (!nextPtr || nextPtr <= ptr || !num || num > 31) 4420 { 4421 ERR("unexpected day %s\n", debugstr_w(ptr)); 4422 return FALSE; 4423 } 4424 ptr = nextPtr; 4425 st.wDay = (WORD)num; 4426 4427 while (iswspace(*ptr)) 4428 ptr++; 4429 4430 if (!HTTP_ParseTime(&st, &ptr)) 4431 return FALSE; 4432 4433 while (iswspace(*ptr)) 4434 ptr++; 4435 4436 num = wcstoul(ptr, &nextPtr, 10); 4437 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827) 4438 { 4439 ERR("unexpected year %s\n", debugstr_w(ptr)); 4440 return FALSE; 4441 } 4442 ptr = nextPtr; 4443 st.wYear = (WORD)num; 4444 4445 while (iswspace(*ptr)) 4446 ptr++; 4447 4448 /* asctime() doesn't report a timezone, but some web servers do, so accept 4449 * with or without GMT. 4450 */ 4451 if (*ptr && wcscmp(ptr, L"GMT")) 4452 { 4453 ERR("unexpected timezone %s\n", debugstr_w(ptr)); 4454 return FALSE; 4455 } 4456 return SystemTimeToFileTime(&st, ft); 4457 } 4458 4459 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft) 4460 { 4461 WCHAR *nextPtr, day[4], month[4], *monthPtr; 4462 LPCWSTR ptr; 4463 unsigned long num; 4464 SYSTEMTIME st = { 0 }; 4465 4466 ptr = wcschr(value, ','); 4467 if (!ptr) 4468 return FALSE; 4469 if (ptr - value != 3) 4470 { 4471 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value)); 4472 return FALSE; 4473 } 4474 memcpy(day, value, (ptr - value) * sizeof(WCHAR)); 4475 day[3] = 0; 4476 st.wDayOfWeek = HTTP_ParseWkday(day); 4477 if (st.wDayOfWeek > 6) 4478 { 4479 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value)); 4480 return FALSE; 4481 } 4482 ptr++; 4483 4484 while (iswspace(*ptr)) 4485 ptr++; 4486 4487 num = wcstoul(ptr, &nextPtr, 10); 4488 if (!nextPtr || nextPtr <= ptr || !num || num > 31) 4489 { 4490 WARN("unexpected day %s\n", debugstr_w(value)); 4491 return FALSE; 4492 } 4493 ptr = nextPtr; 4494 st.wDay = (WORD)num; 4495 4496 while (iswspace(*ptr)) 4497 ptr++; 4498 4499 for (monthPtr = month; !iswspace(*ptr) && monthPtr - month < ARRAY_SIZE(month) - 1; 4500 monthPtr++, ptr++) 4501 *monthPtr = *ptr; 4502 *monthPtr = 0; 4503 st.wMonth = HTTP_ParseMonth(month); 4504 if (!st.wMonth || st.wMonth > 12) 4505 { 4506 WARN("unexpected month %s\n", debugstr_w(month)); 4507 return FALSE; 4508 } 4509 4510 while (iswspace(*ptr)) 4511 ptr++; 4512 4513 num = wcstoul(ptr, &nextPtr, 10); 4514 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827) 4515 { 4516 ERR("unexpected year %s\n", debugstr_w(value)); 4517 return FALSE; 4518 } 4519 ptr = nextPtr; 4520 st.wYear = (WORD)num; 4521 4522 if (!HTTP_ParseTime(&st, &ptr)) 4523 return FALSE; 4524 4525 while (iswspace(*ptr)) 4526 ptr++; 4527 4528 if (wcscmp(ptr, L"GMT")) 4529 { 4530 ERR("unexpected time zone %s\n", debugstr_w(ptr)); 4531 return FALSE; 4532 } 4533 return SystemTimeToFileTime(&st, ft); 4534 } 4535 4536 static WORD HTTP_ParseWeekday(LPCWSTR day) 4537 { 4538 static const WCHAR days[7][10] = {L"sunday", 4539 L"monday", 4540 L"tuesday", 4541 L"wednesday", 4542 L"thursday", 4543 L"friday", 4544 L"saturday"}; 4545 unsigned int i; 4546 for (i = 0; i < ARRAY_SIZE(days); i++) 4547 if (!wcsicmp(day, days[i])) 4548 return i; 4549 4550 /* Invalid */ 4551 return 7; 4552 } 4553 4554 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft) 4555 { 4556 WCHAR *nextPtr, day[10], month[4], *monthPtr; 4557 LPCWSTR ptr; 4558 unsigned long num; 4559 SYSTEMTIME st = { 0 }; 4560 4561 ptr = wcschr(value, ','); 4562 if (!ptr) 4563 return FALSE; 4564 if (ptr - value == 3) 4565 { 4566 memcpy(day, value, (ptr - value) * sizeof(WCHAR)); 4567 day[3] = 0; 4568 st.wDayOfWeek = HTTP_ParseWkday(day); 4569 if (st.wDayOfWeek > 6) 4570 { 4571 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value)); 4572 return FALSE; 4573 } 4574 } 4575 else if (ptr - value < ARRAY_SIZE(day)) 4576 { 4577 memcpy(day, value, (ptr - value) * sizeof(WCHAR)); 4578 day[ptr - value + 1] = 0; 4579 st.wDayOfWeek = HTTP_ParseWeekday(day); 4580 if (st.wDayOfWeek > 6) 4581 { 4582 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value)); 4583 return FALSE; 4584 } 4585 } 4586 else 4587 { 4588 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value)); 4589 return FALSE; 4590 } 4591 ptr++; 4592 4593 while (iswspace(*ptr)) 4594 ptr++; 4595 4596 num = wcstoul(ptr, &nextPtr, 10); 4597 if (!nextPtr || nextPtr <= ptr || !num || num > 31) 4598 { 4599 ERR("unexpected day %s\n", debugstr_w(value)); 4600 return FALSE; 4601 } 4602 ptr = nextPtr; 4603 st.wDay = (WORD)num; 4604 4605 if (*ptr != '-') 4606 { 4607 ERR("unexpected month format %s\n", debugstr_w(ptr)); 4608 return FALSE; 4609 } 4610 ptr++; 4611 4612 for (monthPtr = month; *ptr != '-' && monthPtr - month < ARRAY_SIZE(month) - 1; 4613 monthPtr++, ptr++) 4614 *monthPtr = *ptr; 4615 *monthPtr = 0; 4616 st.wMonth = HTTP_ParseMonth(month); 4617 if (!st.wMonth || st.wMonth > 12) 4618 { 4619 ERR("unexpected month %s\n", debugstr_w(month)); 4620 return FALSE; 4621 } 4622 4623 if (*ptr != '-') 4624 { 4625 ERR("unexpected year format %s\n", debugstr_w(ptr)); 4626 return FALSE; 4627 } 4628 ptr++; 4629 4630 num = wcstoul(ptr, &nextPtr, 10); 4631 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827) 4632 { 4633 ERR("unexpected year %s\n", debugstr_w(value)); 4634 return FALSE; 4635 } 4636 ptr = nextPtr; 4637 st.wYear = (WORD)num; 4638 4639 if (!HTTP_ParseTime(&st, &ptr)) 4640 return FALSE; 4641 4642 while (iswspace(*ptr)) 4643 ptr++; 4644 4645 if (wcscmp(ptr, L"GMT")) 4646 { 4647 ERR("unexpected time zone %s\n", debugstr_w(ptr)); 4648 return FALSE; 4649 } 4650 return SystemTimeToFileTime(&st, ft); 4651 } 4652 4653 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft) 4654 { 4655 BOOL ret; 4656 4657 if (!wcscmp(value, L"0")) 4658 { 4659 ft->dwLowDateTime = ft->dwHighDateTime = 0; 4660 ret = TRUE; 4661 } 4662 else if (wcschr(value, ',')) 4663 { 4664 ret = HTTP_ParseRfc1123Date(value, ft); 4665 if (!ret) 4666 { 4667 ret = HTTP_ParseRfc850Date(value, ft); 4668 if (!ret) 4669 ERR("unexpected date format %s\n", debugstr_w(value)); 4670 } 4671 } 4672 else 4673 { 4674 ret = HTTP_ParseDateAsAsctime(value, ft); 4675 if (!ret) 4676 ERR("unexpected date format %s\n", debugstr_w(value)); 4677 } 4678 return ret; 4679 } 4680 4681 static void HTTP_ProcessExpires(http_request_t *request) 4682 { 4683 BOOL expirationFound = FALSE; 4684 int headerIndex; 4685 4686 EnterCriticalSection( &request->headers_section ); 4687 4688 /* Look for a Cache-Control header with a max-age directive, as it takes 4689 * precedence over the Expires header. 4690 */ 4691 headerIndex = HTTP_GetCustomHeaderIndex(request, L"Cache-Control", 0, FALSE); 4692 if (headerIndex != -1) 4693 { 4694 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex]; 4695 LPWSTR ptr; 4696 4697 for (ptr = ccHeader->lpszValue; ptr && *ptr; ) 4698 { 4699 LPWSTR comma = wcschr(ptr, ','), end, equal; 4700 4701 if (comma) 4702 end = comma; 4703 else 4704 end = ptr + lstrlenW(ptr); 4705 for (equal = end - 1; equal > ptr && *equal != '='; equal--) 4706 ; 4707 if (*equal == '=') 4708 { 4709 if (!wcsnicmp(ptr, L"max-age", equal - ptr - 1)) 4710 { 4711 LPWSTR nextPtr; 4712 unsigned long age; 4713 4714 age = wcstoul(equal + 1, &nextPtr, 10); 4715 if (nextPtr > equal + 1) 4716 { 4717 LARGE_INTEGER ft; 4718 4719 NtQuerySystemTime( &ft ); 4720 /* Age is in seconds, FILETIME resolution is in 4721 * 100 nanosecond intervals. 4722 */ 4723 ft.QuadPart += age * (ULONGLONG)1000000; 4724 request->expires.dwLowDateTime = ft.u.LowPart; 4725 request->expires.dwHighDateTime = ft.u.HighPart; 4726 expirationFound = TRUE; 4727 } 4728 } 4729 } 4730 if (comma) 4731 { 4732 ptr = comma + 1; 4733 while (iswspace(*ptr)) 4734 ptr++; 4735 } 4736 else 4737 ptr = NULL; 4738 } 4739 } 4740 if (!expirationFound) 4741 { 4742 headerIndex = HTTP_GetCustomHeaderIndex(request, L"Expires", 0, FALSE); 4743 if (headerIndex != -1) 4744 { 4745 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex]; 4746 FILETIME ft; 4747 4748 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft)) 4749 { 4750 expirationFound = TRUE; 4751 request->expires = ft; 4752 } 4753 } 4754 } 4755 if (!expirationFound) 4756 { 4757 LARGE_INTEGER t; 4758 4759 /* With no known age, default to 10 minutes until expiration. */ 4760 NtQuerySystemTime( &t ); 4761 t.QuadPart += 10 * 60 * (ULONGLONG)10000000; 4762 request->expires.dwLowDateTime = t.u.LowPart; 4763 request->expires.dwHighDateTime = t.u.HighPart; 4764 } 4765 4766 LeaveCriticalSection( &request->headers_section ); 4767 } 4768 4769 static void HTTP_ProcessLastModified(http_request_t *request) 4770 { 4771 int headerIndex; 4772 4773 EnterCriticalSection( &request->headers_section ); 4774 4775 headerIndex = HTTP_GetCustomHeaderIndex(request, L"Last-Modified", 0, FALSE); 4776 if (headerIndex != -1) 4777 { 4778 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex]; 4779 FILETIME ft; 4780 4781 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft)) 4782 request->last_modified = ft; 4783 } 4784 4785 LeaveCriticalSection( &request->headers_section ); 4786 } 4787 4788 static void http_process_keep_alive(http_request_t *req) 4789 { 4790 int index; 4791 4792 EnterCriticalSection( &req->headers_section ); 4793 4794 if ((index = HTTP_GetCustomHeaderIndex(req, L"Connection", 0, FALSE)) != -1) 4795 req->netconn->keep_alive = !wcsicmp(req->custHeaders[index].lpszValue, L"Keep-Alive"); 4796 else if ((index = HTTP_GetCustomHeaderIndex(req, L"Proxy-Connection", 0, FALSE)) != -1) 4797 req->netconn->keep_alive = !wcsicmp(req->custHeaders[index].lpszValue, L"Keep-Alive"); 4798 else 4799 req->netconn->keep_alive = !wcsicmp(req->version, L"HTTP/1.1"); 4800 4801 LeaveCriticalSection( &req->headers_section ); 4802 } 4803 4804 static DWORD open_http_connection(http_request_t *request, BOOL *reusing) 4805 { 4806 netconn_t *netconn = NULL; 4807 DWORD res; 4808 4809 if (request->netconn) 4810 { 4811 if (NETCON_is_alive(request->netconn) && drain_content(request, TRUE) == ERROR_SUCCESS) 4812 { 4813 reset_data_stream(request); 4814 *reusing = TRUE; 4815 return ERROR_SUCCESS; 4816 } 4817 4818 TRACE("freeing netconn\n"); 4819 free_netconn(request->netconn); 4820 request->netconn = NULL; 4821 } 4822 4823 reset_data_stream(request); 4824 4825 res = HTTP_ResolveName(request); 4826 if(res != ERROR_SUCCESS) 4827 return res; 4828 4829 EnterCriticalSection(&connection_pool_cs); 4830 4831 while(!list_empty(&request->server->conn_pool)) { 4832 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry); 4833 list_remove(&netconn->pool_entry); 4834 4835 if(is_valid_netconn(netconn) && NETCON_is_alive(netconn)) 4836 break; 4837 4838 TRACE("connection %p closed during idle\n", netconn); 4839 free_netconn(netconn); 4840 netconn = NULL; 4841 } 4842 4843 LeaveCriticalSection(&connection_pool_cs); 4844 4845 if(netconn) { 4846 TRACE("<-- reusing %p netconn\n", netconn); 4847 request->netconn = netconn; 4848 *reusing = TRUE; 4849 return ERROR_SUCCESS; 4850 } 4851 4852 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name), 4853 request->proxy ? debugstr_w(request->proxy->name) : "(null)"); 4854 4855 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 4856 INTERNET_STATUS_CONNECTING_TO_SERVER, 4857 request->server->addr_str, 4858 strlen(request->server->addr_str)+1); 4859 4860 res = create_netconn(request->proxy ? request->proxy : request->server, request->security_flags, 4861 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0, 4862 request->connect_timeout, &netconn); 4863 if(res != ERROR_SUCCESS) { 4864 ERR("create_netconn failed: %u\n", res); 4865 return res; 4866 } 4867 4868 request->netconn = netconn; 4869 4870 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 4871 INTERNET_STATUS_CONNECTED_TO_SERVER, 4872 request->server->addr_str, strlen(request->server->addr_str)+1); 4873 4874 *reusing = FALSE; 4875 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn); 4876 return ERROR_SUCCESS; 4877 } 4878 4879 static char *build_ascii_request( const WCHAR *str, void *data, DWORD data_len, DWORD *out_len ) 4880 { 4881 int len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); 4882 char *ret; 4883 4884 if (!(ret = heap_alloc( len + data_len ))) return NULL; 4885 WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); 4886 if (data_len) memcpy( ret + len - 1, data, data_len ); 4887 *out_len = len + data_len - 1; 4888 ret[*out_len] = 0; 4889 return ret; 4890 } 4891 4892 static void set_content_length_header( http_request_t *request, DWORD len, DWORD flags ) 4893 { 4894 WCHAR buf[ARRAY_SIZE(L"Content-Length: %u\r\n") + 10]; 4895 4896 swprintf( buf, ARRAY_SIZE(buf), L"Content-Length: %u\r\n", len ); 4897 HTTP_HttpAddRequestHeadersW( request, buf, ~0u, flags ); 4898 } 4899 4900 /*********************************************************************** 4901 * HTTP_HttpSendRequestW (internal) 4902 * 4903 * Sends the specified request to the HTTP server 4904 * 4905 * RETURNS 4906 * ERROR_SUCCESS on success 4907 * win32 error code on failure 4908 * 4909 */ 4910 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders, 4911 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength, 4912 DWORD dwContentLength, BOOL bEndRequest) 4913 { 4914 BOOL redirected = FALSE, secure_proxy_connect = FALSE, loop_next; 4915 WCHAR *request_header = NULL; 4916 INT responseLen, cnt; 4917 DWORD res; 4918 4919 TRACE("--> %p\n", request); 4920 4921 assert(request->hdr.htype == WH_HHTTPREQ); 4922 4923 /* if the verb is NULL default to GET */ 4924 if (!request->verb) 4925 request->verb = heap_strdupW(L"GET"); 4926 4927 HTTP_ProcessHeader(request, L"Host", request->server->canon_host_port, 4928 HTTP_ADDREQ_FLAG_ADD_IF_NEW | HTTP_ADDHDR_FLAG_REQ); 4929 4930 if (dwContentLength || wcscmp(request->verb, L"GET")) 4931 { 4932 set_content_length_header(request, dwContentLength, HTTP_ADDREQ_FLAG_ADD_IF_NEW); 4933 request->bytesToWrite = dwContentLength; 4934 } 4935 if (request->session->appInfo->agent) 4936 { 4937 WCHAR *agent_header; 4938 int len; 4939 4940 len = lstrlenW(request->session->appInfo->agent) + lstrlenW(L"User-Agent: %s\r\n"); 4941 agent_header = heap_alloc(len * sizeof(WCHAR)); 4942 swprintf(agent_header, len, L"User-Agent: %s\r\n", request->session->appInfo->agent); 4943 4944 HTTP_HttpAddRequestHeadersW(request, agent_header, lstrlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW); 4945 heap_free(agent_header); 4946 } 4947 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE) 4948 { 4949 HTTP_HttpAddRequestHeadersW(request, L"Pragma: no-cache\r\n", 4950 lstrlenW(L"Pragma: no-cache\r\n"), HTTP_ADDREQ_FLAG_ADD_IF_NEW); 4951 } 4952 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && wcscmp(request->verb, L"GET")) 4953 { 4954 HTTP_HttpAddRequestHeadersW(request, L"Cache-Control: no-cache\r\n", 4955 lstrlenW(L"Cache-Control: no-cache\r\n"), HTTP_ADDREQ_FLAG_ADD_IF_NEW); 4956 } 4957 4958 /* add the headers the caller supplied */ 4959 if( lpszHeaders && dwHeaderLength ) 4960 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE); 4961 4962 do 4963 { 4964 DWORD len, data_len = dwOptionalLength; 4965 BOOL reusing_connection; 4966 char *ascii_req; 4967 4968 loop_next = FALSE; 4969 4970 if(redirected) { 4971 request->contentLength = ~0; 4972 request->bytesToWrite = 0; 4973 } 4974 4975 if (TRACE_ON(wininet)) 4976 { 4977 HTTPHEADERW *host; 4978 4979 EnterCriticalSection( &request->headers_section ); 4980 host = HTTP_GetHeader(request, L"Host"); 4981 TRACE("Going to url %s %s\n", debugstr_w(host->lpszValue), debugstr_w(request->path)); 4982 LeaveCriticalSection( &request->headers_section ); 4983 } 4984 4985 HTTP_FixURL(request); 4986 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION) 4987 { 4988 HTTP_ProcessHeader(request, L"Connection", L"Keep-Alive", 4989 HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD); 4990 } 4991 HTTP_InsertAuthorization(request, request->authInfo, L"Authorization"); 4992 HTTP_InsertAuthorization(request, request->proxyAuthInfo, L"Proxy-Authorization"); 4993 4994 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)) 4995 HTTP_InsertCookies(request); 4996 4997 res = open_http_connection(request, &reusing_connection); 4998 if (res != ERROR_SUCCESS) 4999 break; 5000 5001 if (!reusing_connection && (request->hdr.dwFlags & INTERNET_FLAG_SECURE)) 5002 { 5003 if (request->proxy) secure_proxy_connect = TRUE; 5004 else 5005 { 5006 res = NETCON_secure_connect(request->netconn, request->server); 5007 if (res != ERROR_SUCCESS) 5008 { 5009 WARN("failed to upgrade to secure connection\n"); 5010 http_release_netconn(request, FALSE); 5011 break; 5012 } 5013 } 5014 } 5015 if (secure_proxy_connect) 5016 { 5017 const WCHAR *target = request->server->host_port; 5018 5019 if (HTTP_GetCustomHeaderIndex(request, L"Content-Length", 0, TRUE) >= 0) 5020 set_content_length_header(request, 0, HTTP_ADDREQ_FLAG_REPLACE); 5021 5022 request_header = build_request_header(request, L"CONNECT", target, L"HTTP/1.1", TRUE); 5023 } 5024 else if (request->proxy && !(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) 5025 { 5026 WCHAR *url = build_proxy_path_url(request); 5027 request_header = build_request_header(request, request->verb, url, request->version, TRUE); 5028 heap_free(url); 5029 } 5030 else 5031 { 5032 if (request->proxy && HTTP_GetCustomHeaderIndex(request, L"Content-Length", 0, TRUE) >= 0) 5033 set_content_length_header(request, dwContentLength, HTTP_ADDREQ_FLAG_REPLACE); 5034 5035 request_header = build_request_header(request, request->verb, request->path, request->version, TRUE); 5036 } 5037 5038 TRACE("Request header -> %s\n", debugstr_w(request_header) ); 5039 5040 /* send the request as ASCII, tack on the optional data */ 5041 if (!lpOptional || redirected || secure_proxy_connect) 5042 data_len = 0; 5043 5044 ascii_req = build_ascii_request(request_header, lpOptional, data_len, &len); 5045 heap_free(request_header); 5046 TRACE("full request -> %s\n", debugstr_a(ascii_req) ); 5047 5048 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 5049 INTERNET_STATUS_SENDING_REQUEST, NULL, 0); 5050 5051 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout ); 5052 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt); 5053 heap_free( ascii_req ); 5054 if(res != ERROR_SUCCESS) { 5055 TRACE("send failed: %u\n", res); 5056 if(!reusing_connection) 5057 break; 5058 http_release_netconn(request, FALSE); 5059 loop_next = TRUE; 5060 continue; 5061 } 5062 5063 request->bytesWritten = data_len; 5064 5065 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 5066 INTERNET_STATUS_REQUEST_SENT, 5067 &len, sizeof(DWORD)); 5068 5069 if (bEndRequest) 5070 { 5071 DWORD dwBufferSize; 5072 5073 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 5074 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); 5075 5076 if (HTTP_GetResponseHeaders(request, &responseLen)) 5077 { 5078 http_release_netconn(request, FALSE); 5079 res = ERROR_INTERNET_CONNECTION_ABORTED; 5080 goto lend; 5081 } 5082 /* FIXME: We should know that connection is closed before sending 5083 * headers. Otherwise wrong callbacks are executed */ 5084 if(!responseLen && reusing_connection) { 5085 TRACE("Connection closed by server, reconnecting\n"); 5086 http_release_netconn(request, FALSE); 5087 loop_next = TRUE; 5088 continue; 5089 } 5090 5091 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 5092 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, 5093 sizeof(DWORD)); 5094 5095 http_process_keep_alive(request); 5096 HTTP_ProcessCookies(request); 5097 HTTP_ProcessExpires(request); 5098 HTTP_ProcessLastModified(request); 5099 5100 res = set_content_length(request); 5101 if(res != ERROR_SUCCESS) 5102 goto lend; 5103 if(!request->contentLength && !secure_proxy_connect) 5104 http_release_netconn(request, TRUE); 5105 5106 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen) 5107 { 5108 WCHAR *new_url; 5109 5110 switch(request->status_code) { 5111 case HTTP_STATUS_REDIRECT: 5112 case HTTP_STATUS_MOVED: 5113 case HTTP_STATUS_REDIRECT_KEEP_VERB: 5114 case HTTP_STATUS_REDIRECT_METHOD: 5115 new_url = get_redirect_url(request); 5116 if(!new_url) 5117 break; 5118 5119 if (wcscmp(request->verb, L"GET") && wcscmp(request->verb, L"HEAD") && 5120 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB) 5121 { 5122 heap_free(request->verb); 5123 request->verb = heap_strdupW(L"GET"); 5124 } 5125 http_release_netconn(request, drain_content(request, FALSE) == ERROR_SUCCESS); 5126 res = HTTP_HandleRedirect(request, new_url); 5127 heap_free(new_url); 5128 if (res == ERROR_SUCCESS) 5129 loop_next = TRUE; 5130 redirected = TRUE; 5131 } 5132 } 5133 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS) 5134 { 5135 WCHAR szAuthValue[2048]; 5136 dwBufferSize=2048; 5137 if (request->status_code == HTTP_STATUS_DENIED) 5138 { 5139 WCHAR *host = heap_strdupW( request->server->canon_host_port ); 5140 DWORD dwIndex = 0; 5141 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS) 5142 { 5143 if (HTTP_DoAuthorization(request, szAuthValue, 5144 &request->authInfo, 5145 request->session->userName, 5146 request->session->password, host)) 5147 { 5148 if (drain_content(request, TRUE) != ERROR_SUCCESS) 5149 { 5150 FIXME("Could not drain content\n"); 5151 http_release_netconn(request, FALSE); 5152 } 5153 loop_next = TRUE; 5154 break; 5155 } 5156 } 5157 heap_free( host ); 5158 5159 if(!loop_next) { 5160 TRACE("Cleaning wrong authorization data\n"); 5161 destroy_authinfo(request->authInfo); 5162 request->authInfo = NULL; 5163 } 5164 } 5165 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ) 5166 { 5167 DWORD dwIndex = 0; 5168 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS) 5169 { 5170 if (HTTP_DoAuthorization(request, szAuthValue, 5171 &request->proxyAuthInfo, 5172 request->session->appInfo->proxyUsername, 5173 request->session->appInfo->proxyPassword, 5174 NULL)) 5175 { 5176 if (drain_content(request, TRUE) != ERROR_SUCCESS) 5177 { 5178 FIXME("Could not drain content\n"); 5179 http_release_netconn(request, FALSE); 5180 } 5181 loop_next = TRUE; 5182 break; 5183 } 5184 } 5185 5186 if(!loop_next) { 5187 TRACE("Cleaning wrong proxy authorization data\n"); 5188 destroy_authinfo(request->proxyAuthInfo); 5189 request->proxyAuthInfo = NULL; 5190 } 5191 } 5192 } 5193 if (secure_proxy_connect && request->status_code == HTTP_STATUS_OK) 5194 { 5195 res = NETCON_secure_connect(request->netconn, request->server); 5196 if (res != ERROR_SUCCESS) 5197 { 5198 WARN("failed to upgrade to secure proxy connection\n"); 5199 http_release_netconn( request, FALSE ); 5200 break; 5201 } 5202 remove_header(request, L"Proxy-Authorization", TRUE); 5203 destroy_authinfo(request->proxyAuthInfo); 5204 request->proxyAuthInfo = NULL; 5205 request->contentLength = 0; 5206 request->netconn_stream.content_length = 0; 5207 5208 secure_proxy_connect = FALSE; 5209 loop_next = TRUE; 5210 } 5211 } 5212 else 5213 res = ERROR_SUCCESS; 5214 } 5215 while (loop_next); 5216 5217 lend: 5218 /* TODO: send notification for P3P header */ 5219 5220 if(res == ERROR_SUCCESS) 5221 create_cache_entry(request); 5222 5223 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC) 5224 { 5225 if (res == ERROR_SUCCESS) { 5226 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite) 5227 HTTP_ReceiveRequestData(request); 5228 else 5229 send_request_complete(request, 5230 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0); 5231 }else { 5232 send_request_complete(request, 0, res); 5233 } 5234 } 5235 5236 TRACE("<--\n"); 5237 return res; 5238 } 5239 5240 typedef struct { 5241 task_header_t hdr; 5242 WCHAR *headers; 5243 DWORD headers_len; 5244 void *optional; 5245 DWORD optional_len; 5246 DWORD content_len; 5247 BOOL end_request; 5248 } send_request_task_t; 5249 5250 /*********************************************************************** 5251 * 5252 * Helper functions for the HttpSendRequest(Ex) functions 5253 * 5254 */ 5255 static void AsyncHttpSendRequestProc(task_header_t *hdr) 5256 { 5257 send_request_task_t *task = (send_request_task_t*)hdr; 5258 http_request_t *request = (http_request_t*)task->hdr.hdr; 5259 5260 TRACE("%p\n", request); 5261 5262 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional, 5263 task->optional_len, task->content_len, task->end_request); 5264 5265 heap_free(task->headers); 5266 } 5267 5268 5269 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext) 5270 { 5271 INT responseLen; 5272 DWORD res = ERROR_SUCCESS; 5273 5274 if(!is_valid_netconn(request->netconn)) { 5275 WARN("Not connected\n"); 5276 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED); 5277 return ERROR_INTERNET_OPERATION_CANCELLED; 5278 } 5279 5280 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 5281 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); 5282 5283 if (HTTP_GetResponseHeaders(request, &responseLen) || !responseLen) 5284 res = ERROR_HTTP_HEADER_NOT_FOUND; 5285 5286 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, 5287 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD)); 5288 5289 /* process cookies here. Is this right? */ 5290 http_process_keep_alive(request); 5291 HTTP_ProcessCookies(request); 5292 HTTP_ProcessExpires(request); 5293 HTTP_ProcessLastModified(request); 5294 5295 if ((res = set_content_length(request)) == ERROR_SUCCESS) { 5296 if(!request->contentLength) 5297 http_release_netconn(request, TRUE); 5298 } 5299 5300 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT)) 5301 { 5302 switch(request->status_code) { 5303 case HTTP_STATUS_REDIRECT: 5304 case HTTP_STATUS_MOVED: 5305 case HTTP_STATUS_REDIRECT_METHOD: 5306 case HTTP_STATUS_REDIRECT_KEEP_VERB: { 5307 WCHAR *new_url; 5308 5309 new_url = get_redirect_url(request); 5310 if(!new_url) 5311 break; 5312 5313 if (wcscmp(request->verb, L"GET") && wcscmp(request->verb, L"HEAD") && 5314 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB) 5315 { 5316 heap_free(request->verb); 5317 request->verb = heap_strdupW(L"GET"); 5318 } 5319 http_release_netconn(request, drain_content(request, FALSE) == ERROR_SUCCESS); 5320 res = HTTP_HandleRedirect(request, new_url); 5321 heap_free(new_url); 5322 if (res == ERROR_SUCCESS) 5323 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE); 5324 } 5325 } 5326 } 5327 5328 if(res == ERROR_SUCCESS) 5329 create_cache_entry(request); 5330 5331 if (res == ERROR_SUCCESS && request->contentLength) 5332 HTTP_ReceiveRequestData(request); 5333 else 5334 send_request_complete(request, res == ERROR_SUCCESS, res); 5335 5336 return res; 5337 } 5338 5339 /*********************************************************************** 5340 * HttpEndRequestA (WININET.@) 5341 * 5342 * Ends an HTTP request that was started by HttpSendRequestEx 5343 * 5344 * RETURNS 5345 * TRUE if successful 5346 * FALSE on failure 5347 * 5348 */ 5349 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest, 5350 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext) 5351 { 5352 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext); 5353 5354 if (lpBuffersOut) 5355 { 5356 SetLastError(ERROR_INVALID_PARAMETER); 5357 return FALSE; 5358 } 5359 5360 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext); 5361 } 5362 5363 typedef struct { 5364 task_header_t hdr; 5365 DWORD flags; 5366 DWORD context; 5367 } end_request_task_t; 5368 5369 static void AsyncHttpEndRequestProc(task_header_t *hdr) 5370 { 5371 end_request_task_t *task = (end_request_task_t*)hdr; 5372 http_request_t *req = (http_request_t*)task->hdr.hdr; 5373 5374 TRACE("%p\n", req); 5375 5376 HTTP_HttpEndRequestW(req, task->flags, task->context); 5377 } 5378 5379 /*********************************************************************** 5380 * HttpEndRequestW (WININET.@) 5381 * 5382 * Ends an HTTP request that was started by HttpSendRequestEx 5383 * 5384 * RETURNS 5385 * TRUE if successful 5386 * FALSE on failure 5387 * 5388 */ 5389 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest, 5390 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext) 5391 { 5392 http_request_t *request; 5393 DWORD res; 5394 5395 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext); 5396 5397 if (lpBuffersOut) 5398 { 5399 SetLastError(ERROR_INVALID_PARAMETER); 5400 return FALSE; 5401 } 5402 5403 request = (http_request_t*) get_handle_object( hRequest ); 5404 5405 if (NULL == request || request->hdr.htype != WH_HHTTPREQ) 5406 { 5407 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); 5408 if (request) 5409 WININET_Release( &request->hdr ); 5410 return FALSE; 5411 } 5412 request->hdr.dwFlags |= dwFlags; 5413 5414 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC) 5415 { 5416 end_request_task_t *task; 5417 5418 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task)); 5419 task->flags = dwFlags; 5420 task->context = dwContext; 5421 5422 INTERNET_AsyncCall(&task->hdr); 5423 res = ERROR_IO_PENDING; 5424 } 5425 else 5426 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext); 5427 5428 WININET_Release( &request->hdr ); 5429 TRACE("%u <--\n", res); 5430 if(res != ERROR_SUCCESS) 5431 SetLastError(res); 5432 return res == ERROR_SUCCESS; 5433 } 5434 5435 /*********************************************************************** 5436 * HttpSendRequestExA (WININET.@) 5437 * 5438 * Sends the specified request to the HTTP server and allows chunked 5439 * transfers. 5440 * 5441 * RETURNS 5442 * Success: TRUE 5443 * Failure: FALSE, call GetLastError() for more information. 5444 */ 5445 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest, 5446 LPINTERNET_BUFFERSA lpBuffersIn, 5447 LPINTERNET_BUFFERSA lpBuffersOut, 5448 DWORD dwFlags, DWORD_PTR dwContext) 5449 { 5450 INTERNET_BUFFERSW BuffersInW; 5451 BOOL rc = FALSE; 5452 DWORD headerlen; 5453 LPWSTR header = NULL; 5454 5455 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn, 5456 lpBuffersOut, dwFlags, dwContext); 5457 5458 if (lpBuffersIn) 5459 { 5460 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW); 5461 if (lpBuffersIn->lpcszHeader) 5462 { 5463 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader, 5464 lpBuffersIn->dwHeadersLength,0,0); 5465 header = heap_alloc(headerlen*sizeof(WCHAR)); 5466 if (!(BuffersInW.lpcszHeader = header)) 5467 { 5468 SetLastError(ERROR_OUTOFMEMORY); 5469 return FALSE; 5470 } 5471 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0, 5472 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength, 5473 header, headerlen); 5474 } 5475 else 5476 BuffersInW.lpcszHeader = NULL; 5477 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal; 5478 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer; 5479 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength; 5480 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal; 5481 BuffersInW.Next = NULL; 5482 } 5483 5484 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext); 5485 5486 heap_free(header); 5487 return rc; 5488 } 5489 5490 /*********************************************************************** 5491 * HttpSendRequestExW (WININET.@) 5492 * 5493 * Sends the specified request to the HTTP server and allows chunked 5494 * transfers 5495 * 5496 * RETURNS 5497 * Success: TRUE 5498 * Failure: FALSE, call GetLastError() for more information. 5499 */ 5500 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest, 5501 LPINTERNET_BUFFERSW lpBuffersIn, 5502 LPINTERNET_BUFFERSW lpBuffersOut, 5503 DWORD dwFlags, DWORD_PTR dwContext) 5504 { 5505 http_request_t *request; 5506 http_session_t *session; 5507 appinfo_t *hIC; 5508 DWORD res; 5509 5510 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn, 5511 lpBuffersOut, dwFlags, dwContext); 5512 5513 request = (http_request_t*) get_handle_object( hRequest ); 5514 5515 if (NULL == request || request->hdr.htype != WH_HHTTPREQ) 5516 { 5517 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 5518 goto lend; 5519 } 5520 5521 session = request->session; 5522 assert(session->hdr.htype == WH_HHTTPSESSION); 5523 hIC = session->appInfo; 5524 assert(hIC->hdr.htype == WH_HINIT); 5525 5526 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) 5527 { 5528 send_request_task_t *task; 5529 5530 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task)); 5531 if (lpBuffersIn) 5532 { 5533 DWORD size = 0; 5534 5535 if (lpBuffersIn->lpcszHeader) 5536 { 5537 if (lpBuffersIn->dwHeadersLength == ~0u) 5538 size = (lstrlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR); 5539 else 5540 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR); 5541 5542 task->headers = heap_alloc(size); 5543 memcpy(task->headers, lpBuffersIn->lpcszHeader, size); 5544 } 5545 else task->headers = NULL; 5546 5547 task->headers_len = size / sizeof(WCHAR); 5548 task->optional = lpBuffersIn->lpvBuffer; 5549 task->optional_len = lpBuffersIn->dwBufferLength; 5550 task->content_len = lpBuffersIn->dwBufferTotal; 5551 } 5552 else 5553 { 5554 task->headers = NULL; 5555 task->headers_len = 0; 5556 task->optional = NULL; 5557 task->optional_len = 0; 5558 task->content_len = 0; 5559 } 5560 5561 task->end_request = FALSE; 5562 5563 INTERNET_AsyncCall(&task->hdr); 5564 res = ERROR_IO_PENDING; 5565 } 5566 else 5567 { 5568 if (lpBuffersIn) 5569 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength, 5570 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength, 5571 lpBuffersIn->dwBufferTotal, FALSE); 5572 else 5573 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE); 5574 } 5575 5576 lend: 5577 if ( request ) 5578 WININET_Release( &request->hdr ); 5579 5580 TRACE("<---\n"); 5581 SetLastError(res); 5582 return res == ERROR_SUCCESS; 5583 } 5584 5585 /*********************************************************************** 5586 * HttpSendRequestW (WININET.@) 5587 * 5588 * Sends the specified request to the HTTP server 5589 * 5590 * RETURNS 5591 * TRUE on success 5592 * FALSE on failure 5593 * 5594 */ 5595 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders, 5596 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength) 5597 { 5598 http_request_t *request; 5599 http_session_t *session = NULL; 5600 appinfo_t *hIC = NULL; 5601 DWORD res = ERROR_SUCCESS; 5602 5603 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest, 5604 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength); 5605 5606 request = (http_request_t*) get_handle_object( hHttpRequest ); 5607 if (NULL == request || request->hdr.htype != WH_HHTTPREQ) 5608 { 5609 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 5610 goto lend; 5611 } 5612 5613 session = request->session; 5614 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION) 5615 { 5616 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 5617 goto lend; 5618 } 5619 5620 hIC = session->appInfo; 5621 if (NULL == hIC || hIC->hdr.htype != WH_HINIT) 5622 { 5623 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; 5624 goto lend; 5625 } 5626 5627 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) 5628 { 5629 send_request_task_t *task; 5630 5631 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task)); 5632 if (lpszHeaders) 5633 { 5634 DWORD size; 5635 5636 if (dwHeaderLength == ~0u) size = (lstrlenW(lpszHeaders) + 1) * sizeof(WCHAR); 5637 else size = dwHeaderLength * sizeof(WCHAR); 5638 5639 task->headers = heap_alloc(size); 5640 memcpy(task->headers, lpszHeaders, size); 5641 } 5642 else 5643 task->headers = NULL; 5644 task->headers_len = dwHeaderLength; 5645 task->optional = lpOptional; 5646 task->optional_len = dwOptionalLength; 5647 task->content_len = dwOptionalLength; 5648 task->end_request = TRUE; 5649 5650 INTERNET_AsyncCall(&task->hdr); 5651 res = ERROR_IO_PENDING; 5652 } 5653 else 5654 { 5655 res = HTTP_HttpSendRequestW(request, lpszHeaders, 5656 dwHeaderLength, lpOptional, dwOptionalLength, 5657 dwOptionalLength, TRUE); 5658 } 5659 lend: 5660 if( request ) 5661 WININET_Release( &request->hdr ); 5662 5663 SetLastError(res); 5664 return res == ERROR_SUCCESS; 5665 } 5666 5667 /*********************************************************************** 5668 * HttpSendRequestA (WININET.@) 5669 * 5670 * Sends the specified request to the HTTP server 5671 * 5672 * RETURNS 5673 * TRUE on success 5674 * FALSE on failure 5675 * 5676 */ 5677 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders, 5678 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength) 5679 { 5680 BOOL result; 5681 LPWSTR szHeaders=NULL; 5682 DWORD nLen=dwHeaderLength; 5683 if(lpszHeaders!=NULL) 5684 { 5685 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0); 5686 szHeaders = heap_alloc(nLen*sizeof(WCHAR)); 5687 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen); 5688 } 5689 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength); 5690 heap_free(szHeaders); 5691 return result; 5692 } 5693 5694 /*********************************************************************** 5695 * HTTPSESSION_Destroy (internal) 5696 * 5697 * Deallocate session handle 5698 * 5699 */ 5700 static void HTTPSESSION_Destroy(object_header_t *hdr) 5701 { 5702 http_session_t *session = (http_session_t*) hdr; 5703 5704 TRACE("%p\n", session); 5705 5706 WININET_Release(&session->appInfo->hdr); 5707 5708 heap_free(session->hostName); 5709 heap_free(session->password); 5710 heap_free(session->userName); 5711 } 5712 5713 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode) 5714 { 5715 http_session_t *ses = (http_session_t *)hdr; 5716 5717 switch(option) { 5718 case INTERNET_OPTION_HANDLE_TYPE: 5719 TRACE("INTERNET_OPTION_HANDLE_TYPE\n"); 5720 5721 if (*size < sizeof(ULONG)) 5722 return ERROR_INSUFFICIENT_BUFFER; 5723 5724 *size = sizeof(DWORD); 5725 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP; 5726 return ERROR_SUCCESS; 5727 case INTERNET_OPTION_CONNECT_TIMEOUT: 5728 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n"); 5729 5730 if (*size < sizeof(DWORD)) 5731 return ERROR_INSUFFICIENT_BUFFER; 5732 5733 *size = sizeof(DWORD); 5734 *(DWORD *)buffer = ses->connect_timeout; 5735 return ERROR_SUCCESS; 5736 5737 case INTERNET_OPTION_SEND_TIMEOUT: 5738 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n"); 5739 5740 if (*size < sizeof(DWORD)) 5741 return ERROR_INSUFFICIENT_BUFFER; 5742 5743 *size = sizeof(DWORD); 5744 *(DWORD *)buffer = ses->send_timeout; 5745 return ERROR_SUCCESS; 5746 5747 case INTERNET_OPTION_RECEIVE_TIMEOUT: 5748 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n"); 5749 5750 if (*size < sizeof(DWORD)) 5751 return ERROR_INSUFFICIENT_BUFFER; 5752 5753 *size = sizeof(DWORD); 5754 *(DWORD *)buffer = ses->receive_timeout; 5755 return ERROR_SUCCESS; 5756 } 5757 5758 return INET_QueryOption(hdr, option, buffer, size, unicode); 5759 } 5760 5761 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size) 5762 { 5763 http_session_t *ses = (http_session_t*)hdr; 5764 5765 switch(option) { 5766 case INTERNET_OPTION_USERNAME: 5767 { 5768 heap_free(ses->userName); 5769 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 5770 return ERROR_SUCCESS; 5771 } 5772 case INTERNET_OPTION_PASSWORD: 5773 { 5774 heap_free(ses->password); 5775 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 5776 return ERROR_SUCCESS; 5777 } 5778 case INTERNET_OPTION_PROXY_USERNAME: 5779 { 5780 heap_free(ses->appInfo->proxyUsername); 5781 if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 5782 return ERROR_SUCCESS; 5783 } 5784 case INTERNET_OPTION_PROXY_PASSWORD: 5785 { 5786 heap_free(ses->appInfo->proxyPassword); 5787 if (!(ses->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY; 5788 return ERROR_SUCCESS; 5789 } 5790 case INTERNET_OPTION_CONNECT_TIMEOUT: 5791 { 5792 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER; 5793 ses->connect_timeout = *(DWORD *)buffer; 5794 return ERROR_SUCCESS; 5795 } 5796 case INTERNET_OPTION_SEND_TIMEOUT: 5797 { 5798 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER; 5799 ses->send_timeout = *(DWORD *)buffer; 5800 return ERROR_SUCCESS; 5801 } 5802 case INTERNET_OPTION_RECEIVE_TIMEOUT: 5803 { 5804 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER; 5805 ses->receive_timeout = *(DWORD *)buffer; 5806 return ERROR_SUCCESS; 5807 } 5808 default: break; 5809 } 5810 5811 return INET_SetOption(hdr, option, buffer, size); 5812 } 5813 5814 static const object_vtbl_t HTTPSESSIONVtbl = { 5815 HTTPSESSION_Destroy, 5816 NULL, 5817 HTTPSESSION_QueryOption, 5818 HTTPSESSION_SetOption, 5819 NULL, 5820 NULL, 5821 NULL, 5822 NULL 5823 }; 5824 5825 5826 /*********************************************************************** 5827 * HTTP_Connect (internal) 5828 * 5829 * Create http session handle 5830 * 5831 * RETURNS 5832 * HINTERNET a session handle on success 5833 * NULL on failure 5834 * 5835 */ 5836 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, 5837 INTERNET_PORT serverPort, LPCWSTR lpszUserName, 5838 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext, 5839 DWORD dwInternalFlags, HINTERNET *ret) 5840 { 5841 http_session_t *session = NULL; 5842 5843 TRACE("-->\n"); 5844 5845 if (!lpszServerName || !lpszServerName[0]) 5846 return ERROR_INVALID_PARAMETER; 5847 5848 assert( hIC->hdr.htype == WH_HINIT ); 5849 5850 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t)); 5851 if (!session) 5852 return ERROR_OUTOFMEMORY; 5853 5854 /* 5855 * According to my tests. The name is not resolved until a request is sent 5856 */ 5857 5858 session->hdr.htype = WH_HHTTPSESSION; 5859 session->hdr.dwFlags = dwFlags; 5860 session->hdr.dwContext = dwContext; 5861 session->hdr.dwInternalFlags |= dwInternalFlags; 5862 session->hdr.decoding = hIC->hdr.decoding; 5863 5864 WININET_AddRef( &hIC->hdr ); 5865 session->appInfo = hIC; 5866 list_add_head( &hIC->hdr.children, &session->hdr.entry ); 5867 5868 session->hostName = heap_strdupW(lpszServerName); 5869 if (lpszUserName && lpszUserName[0]) 5870 session->userName = heap_strdupW(lpszUserName); 5871 session->password = heap_strdupW(lpszPassword); 5872 session->hostPort = serverPort; 5873 session->connect_timeout = hIC->connect_timeout; 5874 session->send_timeout = 0; 5875 session->receive_timeout = 0; 5876 5877 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */ 5878 if (!(session->hdr.dwInternalFlags & INET_OPENURL)) 5879 { 5880 INTERNET_SendCallback(&hIC->hdr, dwContext, 5881 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet, 5882 sizeof(HINTERNET)); 5883 } 5884 5885 /* 5886 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on 5887 * windows 5888 */ 5889 5890 TRACE("%p --> %p\n", hIC, session); 5891 5892 *ret = session->hdr.hInternet; 5893 return ERROR_SUCCESS; 5894 } 5895 5896 /*********************************************************************** 5897 * HTTP_clear_response_headers (internal) 5898 * 5899 * clear out any old response headers 5900 */ 5901 static void HTTP_clear_response_headers( http_request_t *request ) 5902 { 5903 DWORD i; 5904 5905 EnterCriticalSection( &request->headers_section ); 5906 5907 for( i=0; i<request->nCustHeaders; i++) 5908 { 5909 if( !request->custHeaders[i].lpszField ) 5910 continue; 5911 if( !request->custHeaders[i].lpszValue ) 5912 continue; 5913 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST ) 5914 continue; 5915 HTTP_DeleteCustomHeader( request, i ); 5916 i--; 5917 } 5918 5919 LeaveCriticalSection( &request->headers_section ); 5920 } 5921 5922 /*********************************************************************** 5923 * HTTP_GetResponseHeaders (internal) 5924 * 5925 * Read server response 5926 * 5927 * RETURNS 5928 * 5929 * TRUE on success 5930 * FALSE on error 5931 */ 5932 static DWORD HTTP_GetResponseHeaders(http_request_t *request, INT *len) 5933 { 5934 INT cbreaks = 0; 5935 WCHAR buffer[MAX_REPLY_LEN]; 5936 DWORD buflen = MAX_REPLY_LEN; 5937 INT rc = 0; 5938 char bufferA[MAX_REPLY_LEN]; 5939 LPWSTR status_code = NULL, status_text = NULL; 5940 DWORD res = ERROR_HTTP_INVALID_SERVER_RESPONSE; 5941 BOOL codeHundred = FALSE; 5942 5943 TRACE("-->\n"); 5944 5945 if(!is_valid_netconn(request->netconn)) 5946 goto lend; 5947 5948 /* clear old response headers (eg. from a redirect response) */ 5949 HTTP_clear_response_headers( request ); 5950 5951 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout ); 5952 do { 5953 /* 5954 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code. 5955 */ 5956 buflen = MAX_REPLY_LEN; 5957 if ((res = read_line(request, bufferA, &buflen))) 5958 goto lend; 5959 5960 if (!buflen) goto lend; 5961 5962 rc += buflen; 5963 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN ); 5964 /* check is this a status code line? */ 5965 if (!wcsncmp(buffer, L"HTTP/1.0", 4)) 5966 { 5967 /* split the version from the status code */ 5968 status_code = wcschr( buffer, ' ' ); 5969 if( !status_code ) 5970 goto lend; 5971 *status_code++=0; 5972 5973 /* split the status code from the status text */ 5974 status_text = wcschr( status_code, ' ' ); 5975 if( status_text ) 5976 *status_text++=0; 5977 5978 request->status_code = wcstol(status_code, NULL, 10); 5979 5980 TRACE("version [%s] status code [%s] status text [%s]\n", 5981 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) ); 5982 5983 codeHundred = request->status_code == HTTP_STATUS_CONTINUE; 5984 } 5985 else if (!codeHundred) 5986 { 5987 WARN("No status line at head of response (%s)\n", debugstr_w(buffer)); 5988 5989 heap_free(request->version); 5990 heap_free(request->statusText); 5991 5992 request->status_code = HTTP_STATUS_OK; 5993 request->version = heap_strdupW(L"HTTP/1.0"); 5994 request->statusText = heap_strdupW(L"OK"); 5995 5996 goto lend; 5997 } 5998 } while (codeHundred); 5999 6000 /* Add status code */ 6001 HTTP_ProcessHeader(request, L"Status", status_code, 6002 HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD); 6003 6004 heap_free(request->version); 6005 heap_free(request->statusText); 6006 6007 request->version = heap_strdupW(buffer); 6008 request->statusText = heap_strdupW(status_text ? status_text : L""); 6009 6010 /* Restore the spaces */ 6011 *(status_code-1) = ' '; 6012 if (status_text) 6013 *(status_text-1) = ' '; 6014 6015 /* Parse each response line */ 6016 do 6017 { 6018 buflen = MAX_REPLY_LEN; 6019 if (!read_line(request, bufferA, &buflen) && buflen) 6020 { 6021 LPWSTR * pFieldAndValue; 6022 6023 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA)); 6024 6025 if (!bufferA[0]) break; 6026 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN ); 6027 6028 pFieldAndValue = HTTP_InterpretHttpHeader(buffer); 6029 if (pFieldAndValue) 6030 { 6031 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1], 6032 HTTP_ADDREQ_FLAG_ADD ); 6033 HTTP_FreeTokens(pFieldAndValue); 6034 } 6035 } 6036 else 6037 { 6038 cbreaks++; 6039 if (cbreaks >= 2) 6040 break; 6041 } 6042 }while(1); 6043 6044 res = ERROR_SUCCESS; 6045 6046 lend: 6047 6048 *len = rc; 6049 TRACE("<--\n"); 6050 return res; 6051 } 6052 6053 /*********************************************************************** 6054 * HTTP_InterpretHttpHeader (internal) 6055 * 6056 * Parse server response 6057 * 6058 * RETURNS 6059 * 6060 * Pointer to array of field, value, NULL on success. 6061 * NULL on error. 6062 */ 6063 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer) 6064 { 6065 LPWSTR * pTokenPair; 6066 LPWSTR pszColon; 6067 INT len; 6068 6069 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3); 6070 6071 pszColon = wcschr(buffer, ':'); 6072 /* must have two tokens */ 6073 if (!pszColon) 6074 { 6075 HTTP_FreeTokens(pTokenPair); 6076 if (buffer[0]) 6077 TRACE("No ':' in line: %s\n", debugstr_w(buffer)); 6078 return NULL; 6079 } 6080 6081 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR)); 6082 if (!pTokenPair[0]) 6083 { 6084 HTTP_FreeTokens(pTokenPair); 6085 return NULL; 6086 } 6087 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR)); 6088 pTokenPair[0][pszColon - buffer] = '\0'; 6089 6090 /* skip colon */ 6091 pszColon++; 6092 len = lstrlenW(pszColon); 6093 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR)); 6094 if (!pTokenPair[1]) 6095 { 6096 HTTP_FreeTokens(pTokenPair); 6097 return NULL; 6098 } 6099 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR)); 6100 6101 strip_spaces(pTokenPair[0]); 6102 strip_spaces(pTokenPair[1]); 6103 6104 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1])); 6105 return pTokenPair; 6106 } 6107 6108 /*********************************************************************** 6109 * HTTP_ProcessHeader (internal) 6110 * 6111 * Stuff header into header tables according to <dwModifier> 6112 * 6113 */ 6114 6115 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON) 6116 6117 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier) 6118 { 6119 LPHTTPHEADERW lphttpHdr; 6120 INT index; 6121 BOOL request_only = !!(dwModifier & HTTP_ADDHDR_FLAG_REQ); 6122 DWORD res = ERROR_SUCCESS; 6123 6124 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier); 6125 6126 EnterCriticalSection( &request->headers_section ); 6127 6128 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only); 6129 if (index >= 0) 6130 { 6131 lphttpHdr = &request->custHeaders[index]; 6132 6133 /* replace existing header if FLAG_REPLACE is given */ 6134 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE) 6135 { 6136 HTTP_DeleteCustomHeader( request, index ); 6137 6138 if (value && value[0]) 6139 { 6140 HTTPHEADERW hdr; 6141 6142 hdr.lpszField = (LPWSTR)field; 6143 hdr.lpszValue = (LPWSTR)value; 6144 hdr.wFlags = hdr.wCount = 0; 6145 6146 if (dwModifier & HTTP_ADDHDR_FLAG_REQ) 6147 hdr.wFlags |= HDR_ISREQUEST; 6148 6149 res = HTTP_InsertCustomHeader( request, &hdr ); 6150 } 6151 6152 goto out; 6153 } 6154 6155 /* do not add new header if FLAG_ADD_IF_NEW is set */ 6156 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW) 6157 { 6158 res = ERROR_HTTP_INVALID_HEADER; /* FIXME */ 6159 goto out; 6160 } 6161 6162 /* handle appending to existing header */ 6163 if (dwModifier & COALESCEFLAGS) 6164 { 6165 LPWSTR lpsztmp; 6166 WCHAR ch = 0; 6167 INT len = 0; 6168 INT origlen = lstrlenW(lphttpHdr->lpszValue); 6169 INT valuelen = lstrlenW(value); 6170 6171 /* FIXME: Should it really clear HDR_ISREQUEST? */ 6172 if (dwModifier & HTTP_ADDHDR_FLAG_REQ) 6173 lphttpHdr->wFlags |= HDR_ISREQUEST; 6174 else 6175 lphttpHdr->wFlags &= ~HDR_ISREQUEST; 6176 6177 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA) 6178 { 6179 ch = ','; 6180 lphttpHdr->wFlags |= HDR_COMMADELIMITED; 6181 } 6182 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON) 6183 { 6184 ch = ';'; 6185 lphttpHdr->wFlags |= HDR_COMMADELIMITED; 6186 } 6187 6188 len = origlen + valuelen + ((ch > 0) ? 2 : 0); 6189 6190 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR)); 6191 if (lpsztmp) 6192 { 6193 lphttpHdr->lpszValue = lpsztmp; 6194 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */ 6195 if (ch > 0) 6196 { 6197 lphttpHdr->lpszValue[origlen] = ch; 6198 origlen++; 6199 lphttpHdr->lpszValue[origlen] = ' '; 6200 origlen++; 6201 } 6202 6203 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR)); 6204 lphttpHdr->lpszValue[len] = '\0'; 6205 } 6206 else 6207 { 6208 WARN("heap_realloc (%d bytes) failed\n",len+1); 6209 res = ERROR_OUTOFMEMORY; 6210 } 6211 6212 goto out; 6213 } 6214 } 6215 6216 /* FIXME: What about other combinations? */ 6217 if ((dwModifier & ~HTTP_ADDHDR_FLAG_REQ) == HTTP_ADDHDR_FLAG_REPLACE) 6218 { 6219 res = ERROR_HTTP_HEADER_NOT_FOUND; 6220 goto out; 6221 } 6222 6223 /* FIXME: What if value == ""? */ 6224 if (value) 6225 { 6226 HTTPHEADERW hdr; 6227 6228 hdr.lpszField = (LPWSTR)field; 6229 hdr.lpszValue = (LPWSTR)value; 6230 hdr.wFlags = hdr.wCount = 0; 6231 6232 if (dwModifier & HTTP_ADDHDR_FLAG_REQ) 6233 hdr.wFlags |= HDR_ISREQUEST; 6234 6235 res = HTTP_InsertCustomHeader( request, &hdr ); 6236 goto out; 6237 } 6238 6239 /* FIXME: What if value == NULL? */ 6240 out: 6241 TRACE("<-- %d\n", res); 6242 LeaveCriticalSection( &request->headers_section ); 6243 return res; 6244 } 6245 6246 /*********************************************************************** 6247 * HTTP_GetCustomHeaderIndex (internal) 6248 * 6249 * Return index of custom header from header array 6250 * Headers section must be held 6251 */ 6252 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField, 6253 int requested_index, BOOL request_only) 6254 { 6255 DWORD index; 6256 6257 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only); 6258 6259 for (index = 0; index < request->nCustHeaders; index++) 6260 { 6261 if (wcsicmp(request->custHeaders[index].lpszField, lpszField)) 6262 continue; 6263 6264 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST)) 6265 continue; 6266 6267 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST)) 6268 continue; 6269 6270 if (requested_index == 0) 6271 break; 6272 requested_index --; 6273 } 6274 6275 if (index >= request->nCustHeaders) 6276 index = -1; 6277 6278 TRACE("Return: %d\n", index); 6279 return index; 6280 } 6281 6282 6283 /*********************************************************************** 6284 * HTTP_InsertCustomHeader (internal) 6285 * 6286 * Insert header into array 6287 * Headers section must be held 6288 */ 6289 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr) 6290 { 6291 INT count; 6292 LPHTTPHEADERW lph = NULL; 6293 6294 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue)); 6295 count = request->nCustHeaders + 1; 6296 if (count > 1) 6297 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count); 6298 else 6299 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count); 6300 6301 if (!lph) 6302 return ERROR_OUTOFMEMORY; 6303 6304 request->custHeaders = lph; 6305 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField); 6306 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue); 6307 request->custHeaders[count-1].wFlags = lpHdr->wFlags; 6308 request->custHeaders[count-1].wCount= lpHdr->wCount; 6309 request->nCustHeaders++; 6310 6311 return ERROR_SUCCESS; 6312 } 6313 6314 6315 /*********************************************************************** 6316 * HTTP_DeleteCustomHeader (internal) 6317 * 6318 * Delete header from array 6319 * If this function is called, the index may change. 6320 * Headers section must be held 6321 */ 6322 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index) 6323 { 6324 if( request->nCustHeaders <= 0 ) 6325 return FALSE; 6326 if( index >= request->nCustHeaders ) 6327 return FALSE; 6328 request->nCustHeaders--; 6329 6330 heap_free(request->custHeaders[index].lpszField); 6331 heap_free(request->custHeaders[index].lpszValue); 6332 6333 memmove( &request->custHeaders[index], &request->custHeaders[index+1], 6334 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) ); 6335 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) ); 6336 6337 return TRUE; 6338 } 6339 6340 6341 /*********************************************************************** 6342 * IsHostInProxyBypassList (@) 6343 * 6344 * Undocumented 6345 * 6346 */ 6347 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length) 6348 { 6349 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length); 6350 return FALSE; 6351 } 6352