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