2 * Wininet - HTTP Implementation
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
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.
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.
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
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
69 #include "cryptuiapi.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
78 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK[] = {'O','K',0};
81 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf[] = {'\r','\n', 0};
91 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge[] = { 'A','g','e',0 };
97 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
104 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
105 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
106 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
107 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
108 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
109 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 };
110 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
111 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
112 static const WCHAR szDate[] = { 'D','a','t','e',0 };
113 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
114 static const WCHAR szETag[] = { 'E','T','a','g',0 };
115 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
116 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
117 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
118 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
120 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
121 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
122 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
123 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
124 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
125 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
126 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
127 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
128 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
129 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
130 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
131 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
132 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
133 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
134 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
135 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
136 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 };
137 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
138 static const WCHAR szURI[] = { 'U','R','I',0 };
139 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
140 static const WCHAR szVary[] = { 'V','a','r','y',0 };
141 static const WCHAR szVia[] = { 'V','i','a',0 };
142 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
143 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define COLLECT_TIME 60000
159 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
170 unsigned int auth_data_len;
171 BOOL finished; /* finished authenticating */
175 typedef struct _basicAuthorizationData
182 UINT authorizationLen;
183 } basicAuthorizationData;
185 typedef struct _authorizationData
199 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
200 static struct list authorizationCache = LIST_INIT(authorizationCache);
202 static CRITICAL_SECTION authcache_cs;
203 static CRITICAL_SECTION_DEBUG critsect_debug =
206 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
207 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
209 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222 static BOOL drain_content(http_request_t*,BOOL);
224 static CRITICAL_SECTION connection_pool_cs;
225 static CRITICAL_SECTION_DEBUG connection_pool_debug =
227 0, 0, &connection_pool_cs,
228 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
229 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
231 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
233 static struct list connection_pool = LIST_INIT(connection_pool);
234 static BOOL collector_running;
236 void server_addref(server_t *server)
238 InterlockedIncrement(&server->ref);
241 void server_release(server_t *server)
243 if(InterlockedDecrement(&server->ref))
246 list_remove(&server->entry);
248 if(server->cert_chain)
249 CertFreeCertificateChain(server->cert_chain);
250 heap_free(server->name);
251 heap_free(server->scheme_host_port);
255 static BOOL process_host_port(server_t *server)
261 static const WCHAR httpW[] = {'h','t','t','p',0};
262 static const WCHAR httpsW[] = {'h','t','t','p','s',0};
263 static const WCHAR formatW[] = {'%','s',':','/','/','%','s',':','%','u',0};
265 name_len = strlenW(server->name);
266 buf = heap_alloc((name_len + 10 /* strlen("://:<port>") */)*sizeof(WCHAR) + sizeof(httpsW));
270 sprintfW(buf, formatW, server->is_https ? httpsW : httpW, server->name, server->port);
271 server->scheme_host_port = buf;
273 server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
277 default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
278 server->canon_host_port = default_port ? server->name : server->host_port;
282 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
284 server_t *iter, *server = NULL;
286 if(port == INTERNET_INVALID_PORT_NUMBER)
287 port = is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
289 EnterCriticalSection(&connection_pool_cs);
291 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
292 if(iter->port == port && !strcmpW(iter->name, name) && iter->is_https == is_https) {
294 server_addref(server);
299 if(!server && do_create) {
300 server = heap_alloc_zero(sizeof(*server));
302 server->ref = 2; /* list reference and return */
304 server->is_https = is_https;
305 list_init(&server->conn_pool);
306 server->name = heap_strdupW(name);
307 if(server->name && process_host_port(server)) {
308 list_add_head(&connection_pool, &server->entry);
316 LeaveCriticalSection(&connection_pool_cs);
321 BOOL collect_connections(collect_type_t collect_type)
323 netconn_t *netconn, *netconn_safe;
324 server_t *server, *server_safe;
325 BOOL remaining = FALSE;
328 now = GetTickCount64();
330 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
331 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
332 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
333 TRACE("freeing %p\n", netconn);
334 list_remove(&netconn->pool_entry);
335 free_netconn(netconn);
341 if(collect_type == COLLECT_CLEANUP) {
342 list_remove(&server->entry);
343 list_init(&server->entry);
344 server_release(server);
351 static DWORD WINAPI collect_connections_proc(void *arg)
353 BOOL remaining_conns;
356 /* FIXME: Use more sophisticated method */
359 EnterCriticalSection(&connection_pool_cs);
361 remaining_conns = collect_connections(COLLECT_TIMEOUT);
363 collector_running = FALSE;
365 LeaveCriticalSection(&connection_pool_cs);
366 }while(remaining_conns);
368 FreeLibraryAndExitThread(WININET_hModule, 0);
371 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
374 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
375 if (HeaderIndex == -1)
378 return &req->custHeaders[HeaderIndex];
387 struct data_stream_vtbl_t {
388 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
389 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
390 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
391 BOOL (*drain_content)(data_stream_t*,http_request_t*);
392 void (*destroy)(data_stream_t*);
396 data_stream_t data_stream;
398 BYTE buf[READ_BUFFER_SIZE];
404 static inline void destroy_data_stream(data_stream_t *stream)
406 stream->vtbl->destroy(stream);
409 static void reset_data_stream(http_request_t *req)
411 destroy_data_stream(req->data_stream);
412 req->data_stream = &req->netconn_stream.data_stream;
413 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
414 req->read_chunked = req->read_gzip = FALSE;
420 data_stream_t stream;
421 data_stream_t *parent_stream;
423 BYTE buf[READ_BUFFER_SIZE];
429 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
431 /* Allow reading only from read buffer */
435 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
437 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
438 return gzip_stream->end_of_data;
441 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
442 DWORD *read, read_mode_t read_mode)
444 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
445 z_stream *zstream = &gzip_stream->zstream;
446 DWORD current_read, ret_read = 0;
449 DWORD res = ERROR_SUCCESS;
451 while(size && !gzip_stream->end_of_data) {
452 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
454 if(gzip_stream->buf_size <= 64 && !end) {
455 if(gzip_stream->buf_pos) {
456 if(gzip_stream->buf_size)
457 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
458 gzip_stream->buf_pos = 0;
460 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
461 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
462 gzip_stream->buf_size += current_read;
463 if(res != ERROR_SUCCESS)
465 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
466 if(!current_read && !end) {
467 if(read_mode != READMODE_NOBLOCK) {
468 WARN("unexpected end of data\n");
469 gzip_stream->end_of_data = TRUE;
473 if(gzip_stream->buf_size <= 64 && !end)
477 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
478 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
479 zstream->next_out = buf+ret_read;
480 zstream->avail_out = size;
481 zres = inflate(&gzip_stream->zstream, 0);
482 current_read = size - zstream->avail_out;
483 size -= current_read;
484 ret_read += current_read;
485 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
486 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
487 if(zres == Z_STREAM_END) {
488 TRACE("end of data\n");
489 gzip_stream->end_of_data = TRUE;
491 }else if(zres != Z_OK) {
492 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
494 res = ERROR_INTERNET_DECODING_FAILED;
498 if(ret_read && read_mode == READMODE_ASYNC)
499 read_mode = READMODE_NOBLOCK;
502 TRACE("read %u bytes\n", ret_read);
507 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
509 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
510 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
513 static void gzip_destroy(data_stream_t *stream)
515 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
517 destroy_data_stream(gzip_stream->parent_stream);
519 if(!gzip_stream->end_of_data)
520 inflateEnd(&gzip_stream->zstream);
521 heap_free(gzip_stream);
524 static const data_stream_vtbl_t gzip_stream_vtbl = {
532 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
534 return heap_alloc(items*size);
537 static void wininet_zfree(voidpf opaque, voidpf address)
542 static DWORD init_gzip_stream(http_request_t *req)
544 gzip_stream_t *gzip_stream;
547 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
549 return ERROR_OUTOFMEMORY;
551 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
552 gzip_stream->zstream.zalloc = wininet_zalloc;
553 gzip_stream->zstream.zfree = wininet_zfree;
555 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
557 ERR("inflateInit failed: %d\n", zres);
558 heap_free(gzip_stream);
559 return ERROR_OUTOFMEMORY;
562 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
564 HTTP_DeleteCustomHeader(req, index);
567 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
568 gzip_stream->buf_size = req->read_size;
569 req->read_pos = req->read_size = 0;
572 req->read_gzip = TRUE;
573 gzip_stream->parent_stream = req->data_stream;
574 req->data_stream = &gzip_stream->stream;
575 return ERROR_SUCCESS;
580 static DWORD init_gzip_stream(http_request_t *req)
582 ERR("gzip stream not supported, missing zlib.\n");
583 return ERROR_SUCCESS;
588 /***********************************************************************
589 * HTTP_Tokenize (internal)
591 * Tokenize a string, allocating memory for the tokens.
593 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
595 LPWSTR * token_array;
602 /* empty string has no tokens */
606 for (i = 0; string[i]; i++)
608 if (!strncmpW(string+i, token_string, strlenW(token_string)))
612 /* we want to skip over separators, but not the null terminator */
613 for (j = 0; j < strlenW(token_string) - 1; j++)
621 /* add 1 for terminating NULL */
622 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
623 token_array[tokens] = NULL;
626 for (i = 0; i < tokens; i++)
629 next_token = strstrW(string, token_string);
630 if (!next_token) next_token = string+strlenW(string);
631 len = next_token - string;
632 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
633 memcpy(token_array[i], string, len*sizeof(WCHAR));
634 token_array[i][len] = '\0';
635 string = next_token+strlenW(token_string);
640 /***********************************************************************
641 * HTTP_FreeTokens (internal)
643 * Frees memory returned from HTTP_Tokenize.
645 static void HTTP_FreeTokens(LPWSTR * token_array)
648 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
649 heap_free(token_array);
652 static void HTTP_FixURL(http_request_t *request)
654 static const WCHAR szSlash[] = { '/',0 };
655 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
657 /* If we don't have a path we set it to root */
658 if (NULL == request->path)
659 request->path = heap_strdupW(szSlash);
660 else /* remove \r and \n*/
662 int nLen = strlenW(request->path);
663 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
666 request->path[nLen]='\0';
668 /* Replace '\' with '/' */
671 if (request->path[nLen] == '\\') request->path[nLen]='/';
675 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
676 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
677 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
679 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
681 strcpyW(fixurl + 1, request->path);
682 heap_free( request->path );
683 request->path = fixurl;
687 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
689 LPWSTR requestString;
695 static const WCHAR szSpace[] = { ' ',0 };
696 static const WCHAR szColon[] = { ':',' ',0 };
697 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
699 /* allocate space for an array of all the string pointers to be added */
700 len = (request->nCustHeaders)*4 + 10;
701 req = heap_alloc(len*sizeof(LPCWSTR));
703 /* add the verb, path and HTTP version string */
711 /* Append custom request headers */
712 for (i = 0; i < request->nCustHeaders; i++)
714 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
717 req[n++] = request->custHeaders[i].lpszField;
719 req[n++] = request->custHeaders[i].lpszValue;
721 TRACE("Adding custom header %s (%s)\n",
722 debugstr_w(request->custHeaders[i].lpszField),
723 debugstr_w(request->custHeaders[i].lpszValue));
728 ERR("oops. buffer overrun\n");
731 requestString = HTTP_build_req( req, 4 );
735 * Set (header) termination string for request
736 * Make sure there's exactly two new lines at the end of the request
738 p = &requestString[strlenW(requestString)-1];
739 while ( (*p == '\n') || (*p == '\r') )
741 strcpyW( p+1, sztwocrlf );
743 return requestString;
746 static void HTTP_ProcessCookies( http_request_t *request )
750 LPHTTPHEADERW setCookieHeader;
752 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
755 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
761 setCookieHeader = &request->custHeaders[HeaderIndex];
763 if (!setCookieHeader->lpszValue)
766 host = HTTP_GetHeader(request, hostW);
770 data = strchrW(setCookieHeader->lpszValue, '=');
774 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
779 set_cookie(host->lpszValue, request->path, name, data);
784 static void strip_spaces(LPWSTR start)
789 while (*str == ' ' && *str != '\0')
793 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
795 end = start + strlenW(start) - 1;
796 while (end >= start && *end == ' ')
803 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
805 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
806 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
808 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
809 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
810 if (is_basic && pszRealm)
813 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
817 token = strchrW(ptr,'=');
821 while (*realm == ' ' && *realm != '\0')
823 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
824 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
827 while (*token == ' ' && *token != '\0')
831 *pszRealm = heap_strdupW(token);
832 strip_spaces(*pszRealm);
839 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
841 if (!authinfo) return;
843 if (SecIsValidHandle(&authinfo->ctx))
844 DeleteSecurityContext(&authinfo->ctx);
845 if (SecIsValidHandle(&authinfo->cred))
846 FreeCredentialsHandle(&authinfo->cred);
848 heap_free(authinfo->auth_data);
849 heap_free(authinfo->scheme);
853 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
855 basicAuthorizationData *ad;
858 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
860 EnterCriticalSection(&authcache_cs);
861 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
863 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
865 TRACE("Authorization found in cache\n");
866 *auth_data = heap_alloc(ad->authorizationLen);
867 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
868 rc = ad->authorizationLen;
872 LeaveCriticalSection(&authcache_cs);
876 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
879 basicAuthorizationData* ad = NULL;
881 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
883 EnterCriticalSection(&authcache_cs);
884 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
886 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
887 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
896 TRACE("Found match in cache, replacing\n");
897 heap_free(ad->authorization);
898 ad->authorization = heap_alloc(auth_data_len);
899 memcpy(ad->authorization, auth_data, auth_data_len);
900 ad->authorizationLen = auth_data_len;
904 ad = heap_alloc(sizeof(basicAuthorizationData));
905 ad->host = heap_strdupW(host);
906 ad->realm = heap_strdupW(realm);
907 ad->authorization = heap_alloc(auth_data_len);
908 memcpy(ad->authorization, auth_data, auth_data_len);
909 ad->authorizationLen = auth_data_len;
910 list_add_head(&basicAuthorizationCache,&ad->entry);
911 TRACE("authorization cached\n");
913 LeaveCriticalSection(&authcache_cs);
916 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
917 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
919 authorizationData *ad;
921 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
923 EnterCriticalSection(&authcache_cs);
924 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
925 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
926 TRACE("Authorization found in cache\n");
928 nt_auth_identity->User = heap_strdupW(ad->user);
929 nt_auth_identity->Password = heap_strdupW(ad->password);
930 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
931 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
932 (!nt_auth_identity->Domain && ad->domain_len)) {
933 heap_free(nt_auth_identity->User);
934 heap_free(nt_auth_identity->Password);
935 heap_free(nt_auth_identity->Domain);
939 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
940 nt_auth_identity->UserLength = ad->user_len;
941 nt_auth_identity->PasswordLength = ad->password_len;
942 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
943 nt_auth_identity->DomainLength = ad->domain_len;
944 LeaveCriticalSection(&authcache_cs);
948 LeaveCriticalSection(&authcache_cs);
953 static void cache_authorization(LPWSTR host, LPWSTR scheme,
954 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
956 authorizationData *ad;
959 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
961 EnterCriticalSection(&authcache_cs);
962 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
963 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
970 heap_free(ad->password);
971 heap_free(ad->domain);
973 ad = heap_alloc(sizeof(authorizationData));
975 LeaveCriticalSection(&authcache_cs);
979 ad->host = heap_strdupW(host);
980 ad->scheme = heap_strdupW(scheme);
981 list_add_head(&authorizationCache, &ad->entry);
984 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
985 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
986 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
987 ad->user_len = nt_auth_identity->UserLength;
988 ad->password_len = nt_auth_identity->PasswordLength;
989 ad->domain_len = nt_auth_identity->DomainLength;
991 if(!ad->host || !ad->scheme || !ad->user || !ad->password
992 || (nt_auth_identity->Domain && !ad->domain)) {
994 heap_free(ad->scheme);
996 heap_free(ad->password);
997 heap_free(ad->domain);
998 list_remove(&ad->entry);
1002 LeaveCriticalSection(&authcache_cs);
1005 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
1006 struct HttpAuthInfo **ppAuthInfo,
1007 LPWSTR domain_and_username, LPWSTR password,
1010 SECURITY_STATUS sec_status;
1011 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
1013 LPWSTR szRealm = NULL;
1015 TRACE("%s\n", debugstr_w(pszAuthValue));
1022 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1026 SecInvalidateHandle(&pAuthInfo->cred);
1027 SecInvalidateHandle(&pAuthInfo->ctx);
1028 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1029 pAuthInfo->attr = 0;
1030 pAuthInfo->auth_data = NULL;
1031 pAuthInfo->auth_data_len = 0;
1032 pAuthInfo->finished = FALSE;
1034 if (is_basic_auth_value(pszAuthValue,NULL))
1036 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1037 pAuthInfo->scheme = heap_strdupW(szBasic);
1038 if (!pAuthInfo->scheme)
1040 heap_free(pAuthInfo);
1047 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1049 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1050 if (!pAuthInfo->scheme)
1052 heap_free(pAuthInfo);
1056 if (domain_and_username)
1058 WCHAR *user = strchrW(domain_and_username, '\\');
1059 WCHAR *domain = domain_and_username;
1061 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1063 pAuthData = &nt_auth_identity;
1068 user = domain_and_username;
1072 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1073 nt_auth_identity.User = user;
1074 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1075 nt_auth_identity.Domain = domain;
1076 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1077 nt_auth_identity.Password = password;
1078 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1080 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1082 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1083 pAuthData = &nt_auth_identity;
1085 /* use default credentials */
1088 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1089 SECPKG_CRED_OUTBOUND, NULL,
1091 NULL, &pAuthInfo->cred,
1094 if(pAuthData && !domain_and_username) {
1095 heap_free(nt_auth_identity.User);
1096 heap_free(nt_auth_identity.Domain);
1097 heap_free(nt_auth_identity.Password);
1100 if (sec_status == SEC_E_OK)
1102 PSecPkgInfoW sec_pkg_info;
1103 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1104 if (sec_status == SEC_E_OK)
1106 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1107 FreeContextBuffer(sec_pkg_info);
1110 if (sec_status != SEC_E_OK)
1112 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1113 debugstr_w(pAuthInfo->scheme), sec_status);
1114 heap_free(pAuthInfo->scheme);
1115 heap_free(pAuthInfo);
1119 *ppAuthInfo = pAuthInfo;
1121 else if (pAuthInfo->finished)
1124 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1125 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1127 ERR("authentication scheme changed from %s to %s\n",
1128 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1132 if (is_basic_auth_value(pszAuthValue,&szRealm))
1136 char *auth_data = NULL;
1137 UINT auth_data_len = 0;
1139 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1141 if (!domain_and_username)
1143 if (host && szRealm)
1144 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1145 if (auth_data_len == 0)
1153 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1154 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1156 /* length includes a nul terminator, which will be re-used for the ':' */
1157 auth_data = heap_alloc(userlen + 1 + passlen);
1164 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1165 auth_data[userlen] = ':';
1166 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1167 auth_data_len = userlen + 1 + passlen;
1168 if (host && szRealm)
1169 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1172 pAuthInfo->auth_data = auth_data;
1173 pAuthInfo->auth_data_len = auth_data_len;
1174 pAuthInfo->finished = TRUE;
1180 LPCWSTR pszAuthData;
1181 SecBufferDesc out_desc, in_desc;
1183 unsigned char *buffer;
1184 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1185 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1187 in.BufferType = SECBUFFER_TOKEN;
1191 in_desc.ulVersion = 0;
1192 in_desc.cBuffers = 1;
1193 in_desc.pBuffers = ∈
1195 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1196 if (*pszAuthData == ' ')
1199 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1200 in.pvBuffer = heap_alloc(in.cbBuffer);
1201 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1204 buffer = heap_alloc(pAuthInfo->max_token);
1206 out.BufferType = SECBUFFER_TOKEN;
1207 out.cbBuffer = pAuthInfo->max_token;
1208 out.pvBuffer = buffer;
1210 out_desc.ulVersion = 0;
1211 out_desc.cBuffers = 1;
1212 out_desc.pBuffers = &out;
1214 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1215 first ? NULL : &pAuthInfo->ctx,
1216 first ? request->server->name : NULL,
1217 context_req, 0, SECURITY_NETWORK_DREP,
1218 in.pvBuffer ? &in_desc : NULL,
1219 0, &pAuthInfo->ctx, &out_desc,
1220 &pAuthInfo->attr, &pAuthInfo->exp);
1221 if (sec_status == SEC_E_OK)
1223 pAuthInfo->finished = TRUE;
1224 pAuthInfo->auth_data = out.pvBuffer;
1225 pAuthInfo->auth_data_len = out.cbBuffer;
1226 TRACE("sending last auth packet\n");
1228 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1230 pAuthInfo->auth_data = out.pvBuffer;
1231 pAuthInfo->auth_data_len = out.cbBuffer;
1232 TRACE("sending next auth packet\n");
1236 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1237 heap_free(out.pvBuffer);
1238 destroy_authinfo(pAuthInfo);
1247 /***********************************************************************
1248 * HTTP_HttpAddRequestHeadersW (internal)
1250 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1251 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1256 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1258 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1260 if( dwHeaderLength == ~0U )
1261 len = strlenW(lpszHeader);
1263 len = dwHeaderLength;
1264 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1265 lstrcpynW( buffer, lpszHeader, len + 1);
1271 LPWSTR * pFieldAndValue;
1273 lpszEnd = lpszStart;
1275 while (*lpszEnd != '\0')
1277 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1282 if (*lpszStart == '\0')
1285 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1288 lpszEnd++; /* Jump over newline */
1290 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1291 if (*lpszStart == '\0')
1293 /* Skip 0-length headers */
1294 lpszStart = lpszEnd;
1295 res = ERROR_SUCCESS;
1298 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1301 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1302 if (res == ERROR_SUCCESS)
1303 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1304 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1305 HTTP_FreeTokens(pFieldAndValue);
1308 lpszStart = lpszEnd;
1309 } while (res == ERROR_SUCCESS);
1315 /***********************************************************************
1316 * HttpAddRequestHeadersW (WININET.@)
1318 * Adds one or more HTTP header to the request handler
1321 * On Windows if dwHeaderLength includes the trailing '\0', then
1322 * HttpAddRequestHeadersW() adds it too. However this results in an
1323 * invalid HTTP header which is rejected by some servers so we probably
1324 * don't need to match Windows on that point.
1331 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1332 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1334 http_request_t *request;
1335 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1337 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1342 request = (http_request_t*) get_handle_object( hHttpRequest );
1343 if (request && request->hdr.htype == WH_HHTTPREQ)
1344 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1346 WININET_Release( &request->hdr );
1348 if(res != ERROR_SUCCESS)
1350 return res == ERROR_SUCCESS;
1353 /***********************************************************************
1354 * HttpAddRequestHeadersA (WININET.@)
1356 * Adds one or more HTTP header to the request handler
1363 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1364 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1370 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1372 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1373 hdr = heap_alloc(len*sizeof(WCHAR));
1374 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1375 if( dwHeaderLength != ~0U )
1376 dwHeaderLength = len;
1378 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1384 static void free_accept_types( WCHAR **accept_types )
1386 WCHAR *ptr, **types = accept_types;
1389 while ((ptr = *types))
1394 heap_free( accept_types );
1397 static WCHAR **convert_accept_types( const char **accept_types )
1400 const char **types = accept_types;
1402 BOOL invalid_pointer = FALSE;
1404 if (!types) return NULL;
1410 /* find out how many there are */
1411 if (*types && **types)
1413 TRACE("accept type: %s\n", debugstr_a(*types));
1419 WARN("invalid accept type pointer\n");
1420 invalid_pointer = TRUE;
1425 if (invalid_pointer) return NULL;
1426 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1428 types = accept_types;
1431 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1434 typesW[count] = NULL;
1438 /***********************************************************************
1439 * HttpOpenRequestA (WININET.@)
1441 * Open a HTTP request handle
1444 * HINTERNET a HTTP request handle on success
1448 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1449 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1450 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1451 DWORD dwFlags, DWORD_PTR dwContext)
1453 LPWSTR szVerb = NULL, szObjectName = NULL;
1454 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1455 HINTERNET rc = FALSE;
1457 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1458 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1459 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1460 dwFlags, dwContext);
1464 szVerb = heap_strdupAtoW(lpszVerb);
1471 szObjectName = heap_strdupAtoW(lpszObjectName);
1472 if ( !szObjectName )
1478 szVersion = heap_strdupAtoW(lpszVersion);
1485 szReferrer = heap_strdupAtoW(lpszReferrer);
1490 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1491 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1492 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1495 free_accept_types(szAcceptTypes);
1496 heap_free(szReferrer);
1497 heap_free(szVersion);
1498 heap_free(szObjectName);
1503 /***********************************************************************
1506 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1509 static const CHAR HTTP_Base64Enc[] =
1510 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1514 /* first 6 bits, all from bin[0] */
1515 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1516 x = (bin[0] & 3) << 4;
1518 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1521 base64[n++] = HTTP_Base64Enc[x];
1526 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1527 x = ( bin[1] & 0x0f ) << 2;
1529 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1532 base64[n++] = HTTP_Base64Enc[x];
1536 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1538 /* last 6 bits, all from bin [2] */
1539 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1547 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1548 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1549 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1550 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1551 static const signed char HTTP_Base64Dec[256] =
1553 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1554 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1555 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1556 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1557 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1558 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1559 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1560 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1561 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1562 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1563 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1564 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1565 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1566 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1567 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1568 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1569 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1570 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1571 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1572 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1573 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1574 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1575 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1576 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1577 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1578 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1582 /***********************************************************************
1585 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1593 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1594 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1595 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1596 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1598 WARN("invalid base64: %s\n", debugstr_w(base64));
1602 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1605 if ((base64[2] == '=') && (base64[3] == '='))
1607 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1608 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1610 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1614 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1617 if (base64[3] == '=')
1619 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1620 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1622 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1626 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1635 /***********************************************************************
1636 * HTTP_InsertAuthorization
1638 * Insert or delete the authorization field in the request header.
1640 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1644 static const WCHAR wszSpace[] = {' ',0};
1645 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1647 WCHAR *authorization = NULL;
1649 if (pAuthInfo->auth_data_len)
1651 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1652 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1653 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1657 strcpyW(authorization, pAuthInfo->scheme);
1658 strcatW(authorization, wszSpace);
1659 HTTP_EncodeBase64(pAuthInfo->auth_data,
1660 pAuthInfo->auth_data_len,
1661 authorization+strlenW(authorization));
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))
1668 heap_free(pAuthInfo->auth_data);
1669 pAuthInfo->auth_data = NULL;
1670 pAuthInfo->auth_data_len = 0;
1674 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1676 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1677 heap_free(authorization);
1682 static WCHAR *build_proxy_path_url(http_request_t *req)
1687 len = strlenW(req->server->scheme_host_port);
1688 size = len + strlenW(req->path) + 1;
1689 if(*req->path != '/')
1691 url = heap_alloc(size * sizeof(WCHAR));
1695 memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
1696 if(*req->path != '/')
1699 strcpyW(url+len, req->path);
1701 TRACE("url=%s\n", debugstr_w(url));
1705 /***********************************************************************
1706 * HTTP_DealWithProxy
1708 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1710 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1711 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1712 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1713 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1714 static WCHAR szNul[] = { 0 };
1715 URL_COMPONENTSW UrlComponents;
1716 server_t *new_server;
1717 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1718 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1719 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1721 memset( &UrlComponents, 0, sizeof UrlComponents );
1722 UrlComponents.dwStructSize = sizeof UrlComponents;
1723 UrlComponents.lpszHostName = buf;
1724 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1726 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1728 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1729 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1730 sprintfW(proxy, szFormat, protoProxy);
1732 strcpyW(proxy, protoProxy);
1733 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1735 if( UrlComponents.dwHostNameLength == 0 )
1738 if( !request->path )
1739 request->path = szNul;
1741 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
1745 request->proxy = new_server;
1747 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1751 static DWORD HTTP_ResolveName(http_request_t *request)
1753 server_t *server = request->proxy ? request->proxy : request->server;
1757 if(server->addr_len)
1758 return ERROR_SUCCESS;
1760 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1761 INTERNET_STATUS_RESOLVING_NAME,
1763 (strlenW(server->name)+1) * sizeof(WCHAR));
1765 addr_len = sizeof(server->addr);
1766 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1767 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1769 switch(server->addr.ss_family) {
1771 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1774 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1777 WARN("unsupported family %d\n", server->addr.ss_family);
1778 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1781 server->addr_len = addr_len;
1782 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1783 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1784 INTERNET_STATUS_NAME_RESOLVED,
1785 server->addr_str, strlen(server->addr_str)+1);
1787 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1788 return ERROR_SUCCESS;
1791 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1793 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1794 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1795 static const WCHAR slash[] = { '/',0 };
1796 LPHTTPHEADERW host_header;
1799 host_header = HTTP_GetHeader(req, hostW);
1803 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1807 strcpyW(buf, scheme);
1808 strcatW(buf, host_header->lpszValue);
1809 if (req->path[0] != '/')
1810 strcatW(buf, slash);
1811 strcatW(buf, req->path);
1816 /***********************************************************************
1817 * HTTPREQ_Destroy (internal)
1819 * Deallocate request handle
1822 static void HTTPREQ_Destroy(object_header_t *hdr)
1824 http_request_t *request = (http_request_t*) hdr;
1829 if(request->hCacheFile) {
1830 CloseHandle(request->hCacheFile);
1831 DeleteFileW(request->cacheFile);
1833 heap_free(request->cacheFile);
1835 request->read_section.DebugInfo->Spare[0] = 0;
1836 DeleteCriticalSection( &request->read_section );
1837 WININET_Release(&request->session->hdr);
1839 destroy_authinfo(request->authInfo);
1840 destroy_authinfo(request->proxyAuthInfo);
1843 server_release(request->server);
1845 server_release(request->proxy);
1847 heap_free(request->path);
1848 heap_free(request->verb);
1849 heap_free(request->rawHeaders);
1850 heap_free(request->version);
1851 heap_free(request->statusText);
1853 for (i = 0; i < request->nCustHeaders; i++)
1855 heap_free(request->custHeaders[i].lpszField);
1856 heap_free(request->custHeaders[i].lpszValue);
1858 destroy_data_stream(request->data_stream);
1859 heap_free(request->custHeaders);
1862 static void http_release_netconn(http_request_t *req, BOOL reuse)
1864 TRACE("%p %p\n",req, req->netconn);
1869 if(reuse && req->netconn->keep_alive) {
1872 EnterCriticalSection(&connection_pool_cs);
1874 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1875 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1876 req->netconn = NULL;
1878 run_collector = !collector_running;
1879 collector_running = TRUE;
1881 LeaveCriticalSection(&connection_pool_cs);
1884 HANDLE thread = NULL;
1887 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1889 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1891 EnterCriticalSection(&connection_pool_cs);
1892 collector_running = FALSE;
1893 LeaveCriticalSection(&connection_pool_cs);
1896 FreeLibrary(module);
1899 CloseHandle(thread);
1904 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1905 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1907 free_netconn(req->netconn);
1908 req->netconn = NULL;
1910 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1911 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1914 static BOOL HTTP_KeepAlive(http_request_t *request)
1916 WCHAR szVersion[10];
1917 WCHAR szConnectionResponse[20];
1918 DWORD dwBufferSize = sizeof(szVersion);
1919 BOOL keepalive = FALSE;
1921 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1922 * the connection is keep-alive by default */
1923 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1924 && !strcmpiW(szVersion, g_szHttp1_1))
1929 dwBufferSize = sizeof(szConnectionResponse);
1930 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1931 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1933 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1939 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1941 http_request_t *req = (http_request_t*)hdr;
1943 http_release_netconn(req, drain_content(req, FALSE));
1946 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1948 http_request_t *req = (http_request_t*)hdr;
1951 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1953 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1955 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1957 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1958 return ERROR_INSUFFICIENT_BUFFER;
1959 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1960 /* FIXME: can't get a SOCKET from our connection since we don't use
1964 /* FIXME: get source port from req->netConnection */
1965 info->SourcePort = 0;
1966 info->DestPort = req->server->port;
1968 if (HTTP_KeepAlive(req))
1969 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1971 info->Flags |= IDSI_FLAG_PROXY;
1972 if (req->netconn->useSSL)
1973 info->Flags |= IDSI_FLAG_SECURE;
1975 return ERROR_SUCCESS;
1979 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1981 case INTERNET_OPTION_SECURITY_FLAGS:
1985 if (*size < sizeof(ULONG))
1986 return ERROR_INSUFFICIENT_BUFFER;
1988 *size = sizeof(DWORD);
1989 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1990 *(DWORD *)buffer = flags;
1992 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1993 return ERROR_SUCCESS;
1996 case INTERNET_OPTION_HANDLE_TYPE:
1997 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1999 if (*size < sizeof(ULONG))
2000 return ERROR_INSUFFICIENT_BUFFER;
2002 *size = sizeof(DWORD);
2003 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2004 return ERROR_SUCCESS;
2006 case INTERNET_OPTION_URL: {
2007 WCHAR url[INTERNET_MAX_URL_LENGTH];
2012 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2014 TRACE("INTERNET_OPTION_URL\n");
2016 host = HTTP_GetHeader(req, hostW);
2017 strcpyW(url, httpW);
2018 strcatW(url, host->lpszValue);
2019 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2021 strcatW(url, req->path);
2023 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2026 len = (strlenW(url)+1) * sizeof(WCHAR);
2028 return ERROR_INSUFFICIENT_BUFFER;
2031 strcpyW(buffer, url);
2032 return ERROR_SUCCESS;
2034 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2036 return ERROR_INSUFFICIENT_BUFFER;
2039 return ERROR_SUCCESS;
2043 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2044 INTERNET_CACHE_ENTRY_INFOW *info;
2045 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2046 WCHAR url[INTERNET_MAX_URL_LENGTH];
2047 DWORD nbytes, error;
2050 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2052 if (*size < sizeof(*ts))
2054 *size = sizeof(*ts);
2055 return ERROR_INSUFFICIENT_BUFFER;
2058 HTTP_GetRequestURL(req, url);
2059 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2060 error = GetLastError();
2061 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2063 if (!(info = heap_alloc(nbytes)))
2064 return ERROR_OUTOFMEMORY;
2066 GetUrlCacheEntryInfoW(url, info, &nbytes);
2068 ts->ftExpires = info->ExpireTime;
2069 ts->ftLastModified = info->LastModifiedTime;
2072 *size = sizeof(*ts);
2073 return ERROR_SUCCESS;
2078 case INTERNET_OPTION_DATAFILE_NAME: {
2081 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2083 if(!req->cacheFile) {
2085 return ERROR_INTERNET_ITEM_NOT_FOUND;
2089 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2090 if(*size < req_size)
2091 return ERROR_INSUFFICIENT_BUFFER;
2094 memcpy(buffer, req->cacheFile, *size);
2095 return ERROR_SUCCESS;
2097 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2098 if (req_size > *size)
2099 return ERROR_INSUFFICIENT_BUFFER;
2101 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2102 -1, buffer, *size, NULL, NULL);
2103 return ERROR_SUCCESS;
2107 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2108 PCCERT_CONTEXT context;
2110 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2111 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2112 return ERROR_INSUFFICIENT_BUFFER;
2115 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2117 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2120 memset(info, 0, sizeof(*info));
2121 info->ftExpiry = context->pCertInfo->NotAfter;
2122 info->ftStart = context->pCertInfo->NotBefore;
2123 len = CertNameToStrA(context->dwCertEncodingType,
2124 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2125 info->lpszSubjectInfo = LocalAlloc(0, len);
2126 if(info->lpszSubjectInfo)
2127 CertNameToStrA(context->dwCertEncodingType,
2128 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2129 info->lpszSubjectInfo, len);
2130 len = CertNameToStrA(context->dwCertEncodingType,
2131 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2132 info->lpszIssuerInfo = LocalAlloc(0, len);
2133 if(info->lpszIssuerInfo)
2134 CertNameToStrA(context->dwCertEncodingType,
2135 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2136 info->lpszIssuerInfo, len);
2137 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2138 CertFreeCertificateContext(context);
2139 return ERROR_SUCCESS;
2141 return ERROR_NOT_SUPPORTED;
2143 case INTERNET_OPTION_CONNECT_TIMEOUT:
2144 if (*size < sizeof(DWORD))
2145 return ERROR_INSUFFICIENT_BUFFER;
2147 *size = sizeof(DWORD);
2148 *(DWORD *)buffer = req->connect_timeout;
2149 return ERROR_SUCCESS;
2150 case INTERNET_OPTION_REQUEST_FLAGS: {
2153 if (*size < sizeof(DWORD))
2154 return ERROR_INSUFFICIENT_BUFFER;
2156 /* FIXME: Add support for:
2157 * INTERNET_REQFLAG_FROM_CACHE
2158 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2162 flags |= INTERNET_REQFLAG_VIA_PROXY;
2163 if(!req->rawHeaders)
2164 flags |= INTERNET_REQFLAG_NO_HEADERS;
2166 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2168 *size = sizeof(DWORD);
2169 *(DWORD*)buffer = flags;
2170 return ERROR_SUCCESS;
2174 return INET_QueryOption(hdr, option, buffer, size, unicode);
2177 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2179 http_request_t *req = (http_request_t*)hdr;
2182 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2183 TRACE("Undocumented option 99\n");
2185 if (!buffer || size != sizeof(DWORD))
2186 return ERROR_INVALID_PARAMETER;
2187 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2188 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2191 case INTERNET_OPTION_SECURITY_FLAGS:
2195 if (!buffer || size != sizeof(DWORD))
2196 return ERROR_INVALID_PARAMETER;
2197 flags = *(DWORD *)buffer;
2198 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2199 flags &= SECURITY_SET_MASK;
2200 req->security_flags |= flags;
2202 req->netconn->security_flags |= flags;
2203 return ERROR_SUCCESS;
2205 case INTERNET_OPTION_CONNECT_TIMEOUT:
2206 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2207 req->connect_timeout = *(DWORD *)buffer;
2208 return ERROR_SUCCESS;
2210 case INTERNET_OPTION_SEND_TIMEOUT:
2211 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2212 req->send_timeout = *(DWORD *)buffer;
2213 return ERROR_SUCCESS;
2215 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2216 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2217 req->receive_timeout = *(DWORD *)buffer;
2218 return ERROR_SUCCESS;
2220 case INTERNET_OPTION_USERNAME:
2221 heap_free(req->session->userName);
2222 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2223 return ERROR_SUCCESS;
2225 case INTERNET_OPTION_PASSWORD:
2226 heap_free(req->session->password);
2227 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2228 return ERROR_SUCCESS;
2229 case INTERNET_OPTION_HTTP_DECODING:
2230 if(size != sizeof(BOOL))
2231 return ERROR_INVALID_PARAMETER;
2232 req->decoding = *(BOOL*)buffer;
2233 return ERROR_SUCCESS;
2236 return INET_SetOption(hdr, option, buffer, size);
2239 static void commit_cache_entry(http_request_t *req)
2241 WCHAR url[INTERNET_MAX_URL_LENGTH];
2245 CloseHandle(req->hCacheFile);
2246 req->hCacheFile = NULL;
2248 if(HTTP_GetRequestURL(req, url)) {
2251 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2252 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2253 req->last_modified, NORMAL_CACHE_ENTRY,
2254 req->rawHeaders, headersLen, NULL, 0);
2258 static void create_cache_entry(http_request_t *req)
2260 WCHAR url[INTERNET_MAX_URL_LENGTH];
2261 WCHAR file_name[MAX_PATH+1];
2264 /* FIXME: We should free previous cache file earlier */
2265 heap_free(req->cacheFile);
2266 CloseHandle(req->hCacheFile);
2267 req->hCacheFile = NULL;
2269 b = HTTP_GetRequestURL(req, url);
2271 WARN("Could not get URL\n");
2275 b = CreateUrlCacheEntryW(url, req->contentLength, NULL, file_name, 0);
2277 WARN("Could not create cache entry: %08x\n", GetLastError());
2281 req->cacheFile = heap_strdupW(file_name);
2282 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2283 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2284 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2285 WARN("Could not create file: %u\n", GetLastError());
2286 req->hCacheFile = NULL;
2290 if(req->read_size) {
2293 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2295 FIXME("WriteFile failed: %u\n", GetLastError());
2297 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2298 commit_cache_entry(req);
2302 /* read some more data into the read buffer (the read section must be held) */
2303 static DWORD read_more_data( http_request_t *req, int maxlen )
2310 /* move existing data to the start of the buffer */
2312 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2316 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2318 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2319 maxlen - req->read_size, 0, &len );
2320 if(res == ERROR_SUCCESS)
2321 req->read_size += len;
2326 /* remove some amount of data from the read buffer (the read section must be held) */
2327 static void remove_data( http_request_t *req, int count )
2329 if (!(req->read_size -= count)) req->read_pos = 0;
2330 else req->read_pos += count;
2333 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2335 int count, bytes_read, pos = 0;
2338 EnterCriticalSection( &req->read_section );
2341 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2345 count = eol - (req->read_buf + req->read_pos);
2346 bytes_read = count + 1;
2348 else count = bytes_read = req->read_size;
2350 count = min( count, *len - pos );
2351 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2353 remove_data( req, bytes_read );
2356 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2359 TRACE( "returning empty string %u\n", res);
2360 LeaveCriticalSection( &req->read_section );
2361 INTERNET_SetLastError(res);
2365 LeaveCriticalSection( &req->read_section );
2369 if (pos && buffer[pos - 1] == '\r') pos--;
2372 buffer[*len - 1] = 0;
2373 TRACE( "returning %s\n", debugstr_a(buffer));
2377 /* check if we have reached the end of the data to read (the read section must be held) */
2378 static BOOL end_of_read_data( http_request_t *req )
2380 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2383 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2387 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2388 assert(*read <= size);
2390 if(req->hCacheFile) {
2395 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2397 FIXME("WriteFile failed: %u\n", GetLastError());
2400 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2401 commit_cache_entry(req);
2407 /* fetch some more data into the read buffer (the read section must be held) */
2408 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2412 if(req->read_size == sizeof(req->read_buf))
2413 return ERROR_SUCCESS;
2417 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2421 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2423 req->read_size += read;
2425 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2431 /* return the size of data available to be read immediately (the read section must be held) */
2432 static DWORD get_avail_data( http_request_t *req )
2434 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2437 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2439 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2443 NETCON_query_data_available(req->netconn, &avail);
2444 return netconn_stream->content_length == ~0u
2446 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2449 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2451 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2452 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2455 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2456 DWORD *read, read_mode_t read_mode)
2458 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2461 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2463 if(read_mode == READMODE_NOBLOCK) {
2464 DWORD avail = netconn_get_avail_data(stream, req);
2469 if(size && req->netconn) {
2470 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2473 netconn_stream->content_length = netconn_stream->content_read;
2476 netconn_stream->content_read += *read = len;
2477 TRACE("read %u bytes\n", len);
2478 return ERROR_SUCCESS;
2481 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2483 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2488 if(netconn_end_of_data(stream, req))
2492 avail = netconn_get_avail_data(stream, req);
2496 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2499 netconn_stream->content_read += len;
2500 }while(netconn_stream->content_read < netconn_stream->content_length);
2505 static void netconn_destroy(data_stream_t *stream)
2509 static const data_stream_vtbl_t netconn_stream_vtbl = {
2510 netconn_get_avail_data,
2511 netconn_end_of_data,
2513 netconn_drain_content,
2517 /* read some more data into the read buffer (the read section must be held) */
2518 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2523 if (stream->buf_pos)
2525 /* move existing data to the start of the buffer */
2526 if(stream->buf_size)
2527 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2528 stream->buf_pos = 0;
2531 if (maxlen == -1) maxlen = sizeof(stream->buf);
2533 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2534 maxlen - stream->buf_size, 0, &len );
2535 if(res == ERROR_SUCCESS)
2536 stream->buf_size += len;
2541 /* remove some amount of data from the read buffer (the read section must be held) */
2542 static void remove_chunked_data(chunked_stream_t *stream, int count)
2544 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2545 else stream->buf_pos += count;
2548 /* discard data contents until we reach end of line (the read section must be held) */
2549 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2555 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2558 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2561 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2562 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2563 } while (stream->buf_size);
2564 return ERROR_SUCCESS;
2567 /* read the size of the next chunk (the read section must be held) */
2568 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2571 DWORD chunk_size = 0, res;
2573 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2578 while (stream->buf_size)
2580 char ch = stream->buf[stream->buf_pos];
2581 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2582 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2583 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2584 else if (ch == ';' || ch == '\r' || ch == '\n')
2586 TRACE( "reading %u byte chunk\n", chunk_size );
2587 stream->chunk_size = chunk_size;
2588 req->contentLength += chunk_size;
2589 return discard_chunked_eol(stream, req);
2591 remove_chunked_data(stream, 1);
2593 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2594 if (!stream->buf_size)
2596 stream->chunk_size = 0;
2597 return ERROR_SUCCESS;
2602 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2604 /* Allow reading only from read buffer */
2608 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2610 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2611 return !chunked_stream->chunk_size;
2614 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2615 DWORD *read, read_mode_t read_mode)
2617 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2618 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2620 if(chunked_stream->chunk_size == ~0u) {
2621 res = start_next_chunk(chunked_stream, req);
2622 if(res != ERROR_SUCCESS)
2626 while(size && chunked_stream->chunk_size) {
2627 if(chunked_stream->buf_size) {
2628 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2630 /* this could block */
2631 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2634 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2635 remove_chunked_data(chunked_stream, read_bytes);
2637 read_bytes = min(size, chunked_stream->chunk_size);
2639 if(read_mode == READMODE_NOBLOCK) {
2642 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2644 if(read_bytes > avail)
2647 /* this could block */
2648 if(read_bytes == chunked_stream->chunk_size)
2652 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2653 if(res != ERROR_SUCCESS)
2657 chunked_stream->chunk_size -= read_bytes;
2659 ret_read += read_bytes;
2660 if(!chunked_stream->chunk_size) {
2661 assert(read_mode != READMODE_NOBLOCK);
2662 res = start_next_chunk(chunked_stream, req);
2663 if(res != ERROR_SUCCESS)
2667 if(read_mode == READMODE_ASYNC)
2668 read_mode = READMODE_NOBLOCK;
2671 TRACE("read %u bytes\n", ret_read);
2676 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2678 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2680 /* FIXME: we can do better */
2681 return !chunked_stream->chunk_size;
2684 static void chunked_destroy(data_stream_t *stream)
2686 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2687 heap_free(chunked_stream);
2690 static const data_stream_vtbl_t chunked_stream_vtbl = {
2691 chunked_get_avail_data,
2692 chunked_end_of_data,
2694 chunked_drain_content,
2698 /* set the request content length based on the headers */
2699 static DWORD set_content_length(http_request_t *request)
2701 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2705 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2706 request->contentLength = request->netconn_stream.content_length = 0;
2707 return ERROR_SUCCESS;
2710 size = sizeof(request->contentLength);
2711 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2712 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2713 request->contentLength = ~0u;
2714 request->netconn_stream.content_length = request->contentLength;
2715 request->netconn_stream.content_read = request->read_size;
2717 size = sizeof(encoding);
2718 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2719 !strcmpiW(encoding, szChunked))
2721 chunked_stream_t *chunked_stream;
2723 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2725 return ERROR_OUTOFMEMORY;
2727 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2728 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2729 chunked_stream->chunk_size = ~0u;
2731 if(request->read_size) {
2732 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2733 chunked_stream->buf_size = request->read_size;
2734 request->read_size = request->read_pos = 0;
2737 request->data_stream = &chunked_stream->data_stream;
2738 request->contentLength = ~0u;
2739 request->read_chunked = TRUE;
2742 if(request->decoding) {
2745 static const WCHAR gzipW[] = {'g','z','i','p',0};
2747 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2748 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2749 return init_gzip_stream(request);
2752 return ERROR_SUCCESS;
2755 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2757 INTERNET_ASYNC_RESULT iar;
2759 iar.dwResult = result;
2760 iar.dwError = error;
2762 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2763 sizeof(INTERNET_ASYNC_RESULT));
2766 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2768 DWORD res, read = 0, avail = 0;
2773 EnterCriticalSection( &req->read_section );
2775 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2776 res = refill_read_buffer(req, mode, &read);
2777 if(res == ERROR_SUCCESS && !first_notif)
2778 avail = get_avail_data(req);
2780 *ret_size = get_avail_data(req);
2782 LeaveCriticalSection( &req->read_section );
2784 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2785 WARN("res %u read %u, closing connection\n", res, read);
2786 http_release_netconn(req, FALSE);
2789 if(res == ERROR_SUCCESS)
2790 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2792 send_request_complete(req, 0, res);
2795 /* read data from the http connection (the read section must be held) */
2796 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2798 DWORD current_read = 0, ret_read = 0;
2799 read_mode_t read_mode;
2800 DWORD res = ERROR_SUCCESS;
2802 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2804 EnterCriticalSection( &req->read_section );
2806 if(req->read_size) {
2807 ret_read = min(size, req->read_size);
2808 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2809 req->read_size -= ret_read;
2810 req->read_pos += ret_read;
2811 if(read_mode == READMODE_ASYNC)
2812 read_mode = READMODE_NOBLOCK;
2815 if(ret_read < size) {
2816 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2817 ret_read += current_read;
2820 LeaveCriticalSection( &req->read_section );
2823 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2825 if(size && !ret_read)
2826 http_release_netconn(req, res == ERROR_SUCCESS);
2831 static BOOL drain_content(http_request_t *req, BOOL blocking)
2835 if(!req->netconn || req->contentLength == -1)
2838 if(!strcmpW(req->verb, szHEAD))
2842 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2844 EnterCriticalSection( &req->read_section );
2847 DWORD bytes_read, res;
2850 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2851 if(res != ERROR_SUCCESS) {
2861 LeaveCriticalSection( &req->read_section );
2865 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2867 http_request_t *req = (http_request_t*)hdr;
2870 EnterCriticalSection( &req->read_section );
2871 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2872 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2874 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2875 if(res == ERROR_SUCCESS)
2877 LeaveCriticalSection( &req->read_section );
2887 } read_file_ex_task_t;
2889 static void AsyncReadFileExProc(task_header_t *hdr)
2891 read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2892 http_request_t *req = (http_request_t*)task->hdr.hdr;
2895 TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2897 res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2898 send_request_complete(req, res == ERROR_SUCCESS, res);
2901 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2902 DWORD flags, DWORD_PTR context)
2905 http_request_t *req = (http_request_t*)hdr;
2906 DWORD res, read, cread, error = ERROR_SUCCESS;
2908 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2909 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2911 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2913 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2915 read_file_ex_task_t *task;
2917 if (TryEnterCriticalSection( &req->read_section ))
2919 if (get_avail_data(req))
2921 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
2922 LeaveCriticalSection( &req->read_section );
2925 LeaveCriticalSection( &req->read_section );
2928 task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
2931 task->ret_read = ret_read;
2933 INTERNET_AsyncCall(&task->hdr);
2935 return ERROR_IO_PENDING;
2940 EnterCriticalSection( &req->read_section );
2941 if(hdr->dwError == ERROR_SUCCESS)
2942 hdr->dwError = INTERNET_HANDLE_IN_USE;
2943 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2944 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2947 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
2948 if(res != ERROR_SUCCESS)
2952 if(read == size || end_of_read_data(req))
2955 LeaveCriticalSection( &req->read_section );
2957 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2958 &cread, sizeof(cread));
2959 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2960 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2962 EnterCriticalSection( &req->read_section );
2965 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2966 hdr->dwError = ERROR_SUCCESS;
2968 error = hdr->dwError;
2970 LeaveCriticalSection( &req->read_section );
2974 if (res == ERROR_SUCCESS) {
2975 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2976 &read, sizeof(read));
2979 return res==ERROR_SUCCESS ? error : res;
2982 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2985 http_request_t *request = (http_request_t*)hdr;
2987 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2990 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2991 if (res == ERROR_SUCCESS)
2992 request->bytesWritten += *written;
2994 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3001 } http_data_available_task_t;
3003 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3005 http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3007 HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3010 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3012 http_request_t *req = (http_request_t*)hdr;
3014 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3016 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3018 http_data_available_task_t *task;
3020 /* never wait, if we can't enter the section we queue an async request right away */
3021 if (TryEnterCriticalSection( &req->read_section ))
3023 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3024 if ((*available = get_avail_data( req ))) goto done;
3025 if (end_of_read_data( req )) goto done;
3026 LeaveCriticalSection( &req->read_section );
3029 task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3030 task->ret_size = available;
3031 INTERNET_AsyncCall(&task->hdr);
3032 return ERROR_IO_PENDING;
3035 EnterCriticalSection( &req->read_section );
3037 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3039 refill_read_buffer( req, READMODE_ASYNC, NULL );
3040 *available = get_avail_data( req );
3044 LeaveCriticalSection( &req->read_section );
3046 TRACE( "returning %u\n", *available );
3047 return ERROR_SUCCESS;
3050 static const object_vtbl_t HTTPREQVtbl = {
3052 HTTPREQ_CloseConnection,
3053 HTTPREQ_QueryOption,
3058 HTTPREQ_QueryDataAvailable,
3062 /***********************************************************************
3063 * HTTP_HttpOpenRequestW (internal)
3065 * Open a HTTP request handle
3068 * HINTERNET a HTTP request handle on success
3072 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3073 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3074 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3075 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3077 appinfo_t *hIC = session->appInfo;
3078 http_request_t *request;
3079 DWORD len, res = ERROR_SUCCESS;
3083 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3085 return ERROR_OUTOFMEMORY;
3087 request->hdr.htype = WH_HHTTPREQ;
3088 request->hdr.dwFlags = dwFlags;
3089 request->hdr.dwContext = dwContext;
3090 request->contentLength = ~0u;
3092 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3093 request->data_stream = &request->netconn_stream.data_stream;
3094 request->connect_timeout = session->connect_timeout;
3095 request->send_timeout = session->send_timeout;
3096 request->receive_timeout = session->receive_timeout;
3098 InitializeCriticalSection( &request->read_section );
3099 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3101 WININET_AddRef( &session->hdr );
3102 request->session = session;
3103 list_add_head( &session->hdr.children, &request->hdr.entry );
3105 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3106 if(!request->server) {
3107 WININET_Release(&request->hdr);
3108 return ERROR_OUTOFMEMORY;
3111 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3112 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3113 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3114 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3116 if (lpszObjectName && *lpszObjectName) {
3120 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3121 if (rc != E_POINTER)
3122 len = strlenW(lpszObjectName)+1;
3123 request->path = heap_alloc(len*sizeof(WCHAR));
3124 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3125 URL_ESCAPE_SPACES_ONLY);
3128 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3129 strcpyW(request->path,lpszObjectName);
3132 static const WCHAR slashW[] = {'/',0};
3134 request->path = heap_strdupW(slashW);
3137 if (lpszReferrer && *lpszReferrer)
3138 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3140 if (lpszAcceptTypes)
3143 for (i = 0; lpszAcceptTypes[i]; i++)
3145 if (!*lpszAcceptTypes[i]) continue;
3146 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3147 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3148 HTTP_ADDHDR_FLAG_REQ |
3149 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3153 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3154 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3156 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3158 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3159 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3160 INTERNET_DEFAULT_HTTPS_PORT :
3161 INTERNET_DEFAULT_HTTP_PORT);
3163 if (hIC->proxy && hIC->proxy[0])
3164 HTTP_DealWithProxy( hIC, session, request );
3166 INTERNET_SendCallback(&session->hdr, dwContext,
3167 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3170 TRACE("<-- %u (%p)\n", res, request);
3172 if(res != ERROR_SUCCESS) {
3173 WININET_Release( &request->hdr );
3178 *ret = request->hdr.hInternet;
3179 return ERROR_SUCCESS;
3182 /***********************************************************************
3183 * HttpOpenRequestW (WININET.@)
3185 * Open a HTTP request handle
3188 * HINTERNET a HTTP request handle on success
3192 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3193 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3194 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3195 DWORD dwFlags, DWORD_PTR dwContext)
3197 http_session_t *session;
3198 HINTERNET handle = NULL;
3201 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3202 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3203 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3204 dwFlags, dwContext);
3205 if(lpszAcceptTypes!=NULL)
3208 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3209 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3212 session = (http_session_t*) get_handle_object( hHttpSession );
3213 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3215 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3220 * My tests seem to show that the windows version does not
3221 * become asynchronous until after this point. And anyhow
3222 * if this call was asynchronous then how would you get the
3223 * necessary HINTERNET pointer returned by this function.
3226 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3227 lpszVersion, lpszReferrer, lpszAcceptTypes,
3228 dwFlags, dwContext, &handle);
3231 WININET_Release( &session->hdr );
3232 TRACE("returning %p\n", handle);
3233 if(res != ERROR_SUCCESS)
3238 static const LPCWSTR header_lookup[] = {
3239 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3240 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3241 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3242 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3243 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3244 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3245 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3246 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3247 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3248 szDate, /* HTTP_QUERY_DATE = 9 */
3249 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3250 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3251 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3252 szURI, /* HTTP_QUERY_URI = 13 */
3253 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3254 NULL, /* HTTP_QUERY_COST = 15 */
3255 NULL, /* HTTP_QUERY_LINK = 16 */
3256 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3257 NULL, /* HTTP_QUERY_VERSION = 18 */
3258 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3259 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3260 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3261 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3262 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3263 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3264 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3265 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3266 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3267 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3268 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3269 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3270 NULL, /* HTTP_QUERY_FROM = 31 */
3271 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3272 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3273 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3274 szReferer, /* HTTP_QUERY_REFERER = 35 */
3275 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3276 szServer, /* HTTP_QUERY_SERVER = 37 */
3277 NULL, /* HTTP_TITLE = 38 */
3278 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3279 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3280 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3281 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3282 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3283 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3284 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3285 NULL, /* HTTP_QUERY_REFRESH = 46 */
3286 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3287 szAge, /* HTTP_QUERY_AGE = 48 */
3288 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3289 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3290 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3291 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3292 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3293 szETag, /* HTTP_QUERY_ETAG = 54 */
3294 hostW, /* HTTP_QUERY_HOST = 55 */
3295 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3296 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3297 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3298 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3299 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3300 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3301 szRange, /* HTTP_QUERY_RANGE = 62 */
3302 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3303 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3304 szVary, /* HTTP_QUERY_VARY = 65 */
3305 szVia, /* HTTP_QUERY_VIA = 66 */
3306 szWarning, /* HTTP_QUERY_WARNING = 67 */
3307 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3308 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3309 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3312 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3314 /***********************************************************************
3315 * HTTP_HttpQueryInfoW (internal)
3317 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3318 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3320 LPHTTPHEADERW lphttpHdr = NULL;
3321 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3322 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3323 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3326 /* Find requested header structure */
3329 case HTTP_QUERY_CUSTOM:
3330 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3331 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3333 case HTTP_QUERY_RAW_HEADERS_CRLF:
3337 DWORD res = ERROR_INVALID_PARAMETER;
3340 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3342 headers = request->rawHeaders;
3345 len = strlenW(headers) * sizeof(WCHAR);
3347 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3349 len += sizeof(WCHAR);
3350 res = ERROR_INSUFFICIENT_BUFFER;
3355 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3358 len = strlenW(szCrLf) * sizeof(WCHAR);
3359 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3361 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3362 res = ERROR_SUCCESS;
3364 *lpdwBufferLength = len;
3366 if (request_only) heap_free(headers);
3369 case HTTP_QUERY_RAW_HEADERS:
3371 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3373 LPWSTR pszString = lpBuffer;
3375 for (i = 0; ppszRawHeaderLines[i]; i++)
3376 size += strlenW(ppszRawHeaderLines[i]) + 1;
3378 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3380 HTTP_FreeTokens(ppszRawHeaderLines);
3381 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3382 return ERROR_INSUFFICIENT_BUFFER;
3386 for (i = 0; ppszRawHeaderLines[i]; i++)
3388 DWORD len = strlenW(ppszRawHeaderLines[i]);
3389 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3393 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3395 *lpdwBufferLength = size * sizeof(WCHAR);
3396 HTTP_FreeTokens(ppszRawHeaderLines);
3398 return ERROR_SUCCESS;
3400 case HTTP_QUERY_STATUS_TEXT:
3401 if (request->statusText)
3403 DWORD len = strlenW(request->statusText);
3404 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3406 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3407 return ERROR_INSUFFICIENT_BUFFER;
3411 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3412 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3414 *lpdwBufferLength = len * sizeof(WCHAR);
3415 return ERROR_SUCCESS;
3418 case HTTP_QUERY_VERSION:
3419 if (request->version)
3421 DWORD len = strlenW(request->version);
3422 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3424 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3425 return ERROR_INSUFFICIENT_BUFFER;
3429 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3430 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3432 *lpdwBufferLength = len * sizeof(WCHAR);
3433 return ERROR_SUCCESS;
3436 case HTTP_QUERY_CONTENT_ENCODING:
3437 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3438 requested_index,request_only);
3440 case HTTP_QUERY_STATUS_CODE: {
3441 DWORD res = ERROR_SUCCESS;
3444 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3449 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3450 if(*lpdwBufferLength >= sizeof(DWORD))
3451 *(DWORD*)lpBuffer = request->status_code;
3453 res = ERROR_INSUFFICIENT_BUFFER;
3454 *lpdwBufferLength = sizeof(DWORD);
3458 static const WCHAR formatW[] = {'%','u',0};
3460 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3462 if(size <= *lpdwBufferLength) {
3463 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3465 size += sizeof(WCHAR);
3466 res = ERROR_INSUFFICIENT_BUFFER;
3469 *lpdwBufferLength = size;
3474 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3476 if (level < LAST_TABLE_HEADER && header_lookup[level])
3477 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3478 requested_index,request_only);
3482 lphttpHdr = &request->custHeaders[index];
3484 /* Ensure header satisfies requested attributes */
3486 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3487 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3489 return ERROR_HTTP_HEADER_NOT_FOUND;
3492 if (lpdwIndex) (*lpdwIndex)++;
3494 /* coalesce value to requested type */
3495 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3497 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3498 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3500 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3506 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3508 tmpTM = *gmtime(&tmpTime);
3509 STHook = (SYSTEMTIME *)lpBuffer;
3510 STHook->wDay = tmpTM.tm_mday;
3511 STHook->wHour = tmpTM.tm_hour;
3512 STHook->wMilliseconds = 0;
3513 STHook->wMinute = tmpTM.tm_min;
3514 STHook->wDayOfWeek = tmpTM.tm_wday;
3515 STHook->wMonth = tmpTM.tm_mon + 1;
3516 STHook->wSecond = tmpTM.tm_sec;
3517 STHook->wYear = tmpTM.tm_year;
3519 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3520 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3521 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3523 else if (lphttpHdr->lpszValue)
3525 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3527 if (len > *lpdwBufferLength)
3529 *lpdwBufferLength = len;
3530 return ERROR_INSUFFICIENT_BUFFER;
3534 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3535 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3537 *lpdwBufferLength = len - sizeof(WCHAR);
3539 return ERROR_SUCCESS;
3542 /***********************************************************************
3543 * HttpQueryInfoW (WININET.@)
3545 * Queries for information about an HTTP request
3552 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3553 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3555 http_request_t *request;
3558 if (TRACE_ON(wininet)) {
3559 #define FE(x) { x, #x }
3560 static const wininet_flag_info query_flags[] = {
3561 FE(HTTP_QUERY_MIME_VERSION),
3562 FE(HTTP_QUERY_CONTENT_TYPE),
3563 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3564 FE(HTTP_QUERY_CONTENT_ID),
3565 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3566 FE(HTTP_QUERY_CONTENT_LENGTH),
3567 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3568 FE(HTTP_QUERY_ALLOW),
3569 FE(HTTP_QUERY_PUBLIC),
3570 FE(HTTP_QUERY_DATE),
3571 FE(HTTP_QUERY_EXPIRES),
3572 FE(HTTP_QUERY_LAST_MODIFIED),
3573 FE(HTTP_QUERY_MESSAGE_ID),
3575 FE(HTTP_QUERY_DERIVED_FROM),
3576 FE(HTTP_QUERY_COST),
3577 FE(HTTP_QUERY_LINK),
3578 FE(HTTP_QUERY_PRAGMA),
3579 FE(HTTP_QUERY_VERSION),
3580 FE(HTTP_QUERY_STATUS_CODE),
3581 FE(HTTP_QUERY_STATUS_TEXT),
3582 FE(HTTP_QUERY_RAW_HEADERS),
3583 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3584 FE(HTTP_QUERY_CONNECTION),
3585 FE(HTTP_QUERY_ACCEPT),
3586 FE(HTTP_QUERY_ACCEPT_CHARSET),
3587 FE(HTTP_QUERY_ACCEPT_ENCODING),
3588 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3589 FE(HTTP_QUERY_AUTHORIZATION),
3590 FE(HTTP_QUERY_CONTENT_ENCODING),
3591 FE(HTTP_QUERY_FORWARDED),
3592 FE(HTTP_QUERY_FROM),
3593 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3594 FE(HTTP_QUERY_LOCATION),
3595 FE(HTTP_QUERY_ORIG_URI),
3596 FE(HTTP_QUERY_REFERER),
3597 FE(HTTP_QUERY_RETRY_AFTER),
3598 FE(HTTP_QUERY_SERVER),
3599 FE(HTTP_QUERY_TITLE),
3600 FE(HTTP_QUERY_USER_AGENT),
3601 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3602 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3603 FE(HTTP_QUERY_ACCEPT_RANGES),
3604 FE(HTTP_QUERY_SET_COOKIE),
3605 FE(HTTP_QUERY_COOKIE),
3606 FE(HTTP_QUERY_REQUEST_METHOD),
3607 FE(HTTP_QUERY_REFRESH),
3608 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3610 FE(HTTP_QUERY_CACHE_CONTROL),
3611 FE(HTTP_QUERY_CONTENT_BASE),
3612 FE(HTTP_QUERY_CONTENT_LOCATION),
3613 FE(HTTP_QUERY_CONTENT_MD5),
3614 FE(HTTP_QUERY_CONTENT_RANGE),
3615 FE(HTTP_QUERY_ETAG),
3616 FE(HTTP_QUERY_HOST),
3617 FE(HTTP_QUERY_IF_MATCH),
3618 FE(HTTP_QUERY_IF_NONE_MATCH),
3619 FE(HTTP_QUERY_IF_RANGE),
3620 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3621 FE(HTTP_QUERY_MAX_FORWARDS),
3622 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3623 FE(HTTP_QUERY_RANGE),
3624 FE(HTTP_QUERY_TRANSFER_ENCODING),
3625 FE(HTTP_QUERY_UPGRADE),
3626 FE(HTTP_QUERY_VARY),
3628 FE(HTTP_QUERY_WARNING),
3629 FE(HTTP_QUERY_CUSTOM)
3631 static const wininet_flag_info modifier_flags[] = {
3632 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3633 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3634 FE(HTTP_QUERY_FLAG_NUMBER),
3635 FE(HTTP_QUERY_FLAG_COALESCE)
3638 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3639 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3642 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3643 TRACE(" Attribute:");
3644 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3645 if (query_flags[i].val == info) {
3646 TRACE(" %s", query_flags[i].name);
3650 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3651 TRACE(" Unknown (%08x)", info);
3654 TRACE(" Modifier:");
3655 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3656 if (modifier_flags[i].val & info_mod) {
3657 TRACE(" %s", modifier_flags[i].name);
3658 info_mod &= ~ modifier_flags[i].val;
3663 TRACE(" Unknown (%08x)", info_mod);
3668 request = (http_request_t*) get_handle_object( hHttpRequest );
3669 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3671 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3675 if (lpBuffer == NULL)
3676 *lpdwBufferLength = 0;
3677 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3678 lpBuffer, lpdwBufferLength, lpdwIndex);
3682 WININET_Release( &request->hdr );
3684 TRACE("%u <--\n", res);
3685 if(res != ERROR_SUCCESS)
3687 return res == ERROR_SUCCESS;
3690 /***********************************************************************
3691 * HttpQueryInfoA (WININET.@)
3693 * Queries for information about an HTTP request
3700 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3701 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3707 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3708 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3710 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3711 lpdwBufferLength, lpdwIndex );
3717 len = (*lpdwBufferLength)*sizeof(WCHAR);
3718 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3720 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3726 bufferW = heap_alloc(alloclen);
3727 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3728 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3729 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3736 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3740 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3741 lpBuffer, *lpdwBufferLength, NULL, NULL );
3742 *lpdwBufferLength = len - 1;
3744 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3747 /* since the strings being returned from HttpQueryInfoW should be
3748 * only ASCII characters, it is reasonable to assume that all of
3749 * the Unicode characters can be reduced to a single byte */
3750 *lpdwBufferLength = len / sizeof(WCHAR);
3752 heap_free( bufferW );
3756 /***********************************************************************
3757 * HTTP_GetRedirectURL (internal)
3759 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3761 static WCHAR szHttp[] = {'h','t','t','p',0};
3762 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3763 http_session_t *session = request->session;
3764 URL_COMPONENTSW urlComponents;
3765 DWORD url_length = 0;
3767 LPWSTR combined_url;
3769 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3770 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3771 urlComponents.dwSchemeLength = 0;
3772 urlComponents.lpszHostName = session->hostName;
3773 urlComponents.dwHostNameLength = 0;
3774 urlComponents.nPort = session->hostPort;
3775 urlComponents.lpszUserName = session->userName;
3776 urlComponents.dwUserNameLength = 0;
3777 urlComponents.lpszPassword = NULL;
3778 urlComponents.dwPasswordLength = 0;
3779 urlComponents.lpszUrlPath = request->path;
3780 urlComponents.dwUrlPathLength = 0;
3781 urlComponents.lpszExtraInfo = NULL;
3782 urlComponents.dwExtraInfoLength = 0;
3784 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3785 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3788 orig_url = heap_alloc(url_length);
3790 /* convert from bytes to characters */
3791 url_length = url_length / sizeof(WCHAR) - 1;
3792 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3794 heap_free(orig_url);
3799 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3800 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3802 heap_free(orig_url);
3805 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3807 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3809 heap_free(orig_url);
3810 heap_free(combined_url);
3813 heap_free(orig_url);
3814 return combined_url;
3818 /***********************************************************************
3819 * HTTP_HandleRedirect (internal)
3821 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3823 http_session_t *session = request->session;
3824 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3829 /* if it's an absolute path, keep the same session info */
3830 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3834 URL_COMPONENTSW urlComponents;
3835 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3836 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3837 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3838 BOOL custom_port = FALSE;
3840 static WCHAR httpW[] = {'h','t','t','p',0};
3841 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3847 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3848 urlComponents.lpszScheme = protocol;
3849 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3850 urlComponents.lpszHostName = hostName;
3851 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3852 urlComponents.lpszUserName = userName;
3853 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3854 urlComponents.lpszPassword = NULL;
3855 urlComponents.dwPasswordLength = 0;
3856 urlComponents.lpszUrlPath = path;
3857 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3858 urlComponents.lpszExtraInfo = NULL;
3859 urlComponents.dwExtraInfoLength = 0;
3860 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3861 return INTERNET_GetLastError();
3863 if(!strcmpiW(protocol, httpW)) {
3864 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3865 TRACE("redirect from secure page to non-secure page\n");
3866 /* FIXME: warn about from secure redirect to non-secure page */
3867 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3870 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3871 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3872 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3874 }else if(!strcmpiW(protocol, httpsW)) {
3875 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3876 TRACE("redirect from non-secure page to secure page\n");
3877 /* FIXME: notify about redirect to secure page */
3878 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3881 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3882 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3883 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3887 heap_free(session->hostName);
3891 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3892 len = lstrlenW(hostName);
3893 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3894 session->hostName = heap_alloc(len*sizeof(WCHAR));
3895 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3898 session->hostName = heap_strdupW(hostName);
3900 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3902 heap_free(session->userName);
3903 session->userName = NULL;
3905 session->userName = heap_strdupW(userName);
3907 reset_data_stream(request);
3909 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3910 server_t *new_server;
3912 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3913 server_release(request->server);
3914 request->server = new_server;
3917 heap_free(request->path);
3924 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3925 if (rc != E_POINTER)
3926 needed = strlenW(path)+1;
3927 request->path = heap_alloc(needed*sizeof(WCHAR));
3928 rc = UrlEscapeW(path, request->path, &needed,
3929 URL_ESCAPE_SPACES_ONLY);
3932 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3933 strcpyW(request->path,path);
3937 /* Remove custom content-type/length headers on redirects. */
3938 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3940 HTTP_DeleteCustomHeader(request, index);
3941 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3943 HTTP_DeleteCustomHeader(request, index);
3945 return ERROR_SUCCESS;
3948 /***********************************************************************
3949 * HTTP_build_req (internal)
3951 * concatenate all the strings in the request together
3953 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3958 for( t = list; *t ; t++ )
3959 len += strlenW( *t );
3962 str = heap_alloc(len*sizeof(WCHAR));
3965 for( t = list; *t ; t++ )
3971 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3973 server_t *server = request->server;
3974 LPWSTR requestString;
3981 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
3985 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
3987 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3988 NULL, 0, NULL, NULL );
3989 len--; /* the nul terminator isn't needed */
3990 ascii_req = heap_alloc(len);
3991 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3992 heap_free( requestString );
3994 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3996 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3997 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3998 heap_free( ascii_req );
3999 if (res != ERROR_SUCCESS)
4002 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4004 return ERROR_HTTP_INVALID_HEADER;
4006 return ERROR_SUCCESS;
4009 static void HTTP_InsertCookies(http_request_t *request)
4011 DWORD cookie_size, size, cnt = 0;
4015 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4017 host = HTTP_GetHeader(request, hostW);
4021 if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4024 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4025 if(!(cookies = heap_alloc(size)))
4028 cnt += sprintfW(cookies, cookieW);
4029 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4030 strcatW(cookies, szCrLf);
4032 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4037 static WORD HTTP_ParseWkday(LPCWSTR day)
4039 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4047 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4048 if (!strcmpiW(day, days[i]))
4055 static WORD HTTP_ParseMonth(LPCWSTR month)
4057 static const WCHAR jan[] = { 'j','a','n',0 };
4058 static const WCHAR feb[] = { 'f','e','b',0 };
4059 static const WCHAR mar[] = { 'm','a','r',0 };
4060 static const WCHAR apr[] = { 'a','p','r',0 };
4061 static const WCHAR may[] = { 'm','a','y',0 };
4062 static const WCHAR jun[] = { 'j','u','n',0 };
4063 static const WCHAR jul[] = { 'j','u','l',0 };
4064 static const WCHAR aug[] = { 'a','u','g',0 };
4065 static const WCHAR sep[] = { 's','e','p',0 };
4066 static const WCHAR oct[] = { 'o','c','t',0 };
4067 static const WCHAR nov[] = { 'n','o','v',0 };
4068 static const WCHAR dec[] = { 'd','e','c',0 };
4070 if (!strcmpiW(month, jan)) return 1;
4071 if (!strcmpiW(month, feb)) return 2;
4072 if (!strcmpiW(month, mar)) return 3;
4073 if (!strcmpiW(month, apr)) return 4;
4074 if (!strcmpiW(month, may)) return 5;
4075 if (!strcmpiW(month, jun)) return 6;
4076 if (!strcmpiW(month, jul)) return 7;
4077 if (!strcmpiW(month, aug)) return 8;
4078 if (!strcmpiW(month, sep)) return 9;
4079 if (!strcmpiW(month, oct)) return 10;
4080 if (!strcmpiW(month, nov)) return 11;
4081 if (!strcmpiW(month, dec)) return 12;
4086 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4087 * optionally preceded by whitespace.
4088 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4089 * st, and sets *str to the first character after the time format.
4091 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4097 while (isspaceW(*ptr))
4100 num = strtoulW(ptr, &nextPtr, 10);
4101 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4103 ERR("unexpected time format %s\n", debugstr_w(ptr));
4108 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4112 st->wHour = (WORD)num;
4113 num = strtoulW(ptr, &nextPtr, 10);
4114 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4116 ERR("unexpected time format %s\n", debugstr_w(ptr));
4121 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4125 st->wMinute = (WORD)num;
4126 num = strtoulW(ptr, &nextPtr, 10);
4127 if (!nextPtr || nextPtr <= ptr)
4129 ERR("unexpected time format %s\n", debugstr_w(ptr));
4134 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4139 st->wSecond = (WORD)num;
4143 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4145 static const WCHAR gmt[]= { 'G','M','T',0 };
4146 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4148 SYSTEMTIME st = { 0 };
4151 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4152 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4155 st.wDayOfWeek = HTTP_ParseWkday(day);
4156 if (st.wDayOfWeek >= 7)
4158 ERR("unexpected weekday %s\n", debugstr_w(day));
4162 while (isspaceW(*ptr))
4165 for (monthPtr = month; !isspace(*ptr) &&
4166 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4170 st.wMonth = HTTP_ParseMonth(month);
4171 if (!st.wMonth || st.wMonth > 12)
4173 ERR("unexpected month %s\n", debugstr_w(month));
4177 while (isspaceW(*ptr))
4180 num = strtoulW(ptr, &nextPtr, 10);
4181 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4183 ERR("unexpected day %s\n", debugstr_w(ptr));
4187 st.wDay = (WORD)num;
4189 while (isspaceW(*ptr))
4192 if (!HTTP_ParseTime(&st, &ptr))
4195 while (isspaceW(*ptr))
4198 num = strtoulW(ptr, &nextPtr, 10);
4199 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4201 ERR("unexpected year %s\n", debugstr_w(ptr));
4205 st.wYear = (WORD)num;
4207 while (isspaceW(*ptr))
4210 /* asctime() doesn't report a timezone, but some web servers do, so accept
4211 * with or without GMT.
4213 if (*ptr && strcmpW(ptr, gmt))
4215 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4218 return SystemTimeToFileTime(&st, ft);
4221 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4223 static const WCHAR gmt[]= { 'G','M','T',0 };
4224 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4227 SYSTEMTIME st = { 0 };
4229 ptr = strchrW(value, ',');
4232 if (ptr - value != 3)
4234 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4237 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4239 st.wDayOfWeek = HTTP_ParseWkday(day);
4240 if (st.wDayOfWeek > 6)
4242 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4247 while (isspaceW(*ptr))
4250 num = strtoulW(ptr, &nextPtr, 10);
4251 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4253 WARN("unexpected day %s\n", debugstr_w(value));
4257 st.wDay = (WORD)num;
4259 while (isspaceW(*ptr))
4262 for (monthPtr = month; !isspace(*ptr) &&
4263 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4267 st.wMonth = HTTP_ParseMonth(month);
4268 if (!st.wMonth || st.wMonth > 12)
4270 WARN("unexpected month %s\n", debugstr_w(month));
4274 while (isspaceW(*ptr))
4277 num = strtoulW(ptr, &nextPtr, 10);
4278 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4280 ERR("unexpected year %s\n", debugstr_w(value));
4284 st.wYear = (WORD)num;
4286 if (!HTTP_ParseTime(&st, &ptr))
4289 while (isspaceW(*ptr))
4292 if (strcmpW(ptr, gmt))
4294 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4297 return SystemTimeToFileTime(&st, ft);
4300 static WORD HTTP_ParseWeekday(LPCWSTR day)
4302 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4303 { 'm','o','n','d','a','y',0 },
4304 { 't','u','e','s','d','a','y',0 },
4305 { 'w','e','d','n','e','s','d','a','y',0 },
4306 { 't','h','u','r','s','d','a','y',0 },
4307 { 'f','r','i','d','a','y',0 },
4308 { 's','a','t','u','r','d','a','y',0 }};
4310 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4311 if (!strcmpiW(day, days[i]))
4318 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4320 static const WCHAR gmt[]= { 'G','M','T',0 };
4321 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4324 SYSTEMTIME st = { 0 };
4326 ptr = strchrW(value, ',');
4329 if (ptr - value == 3)
4331 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4333 st.wDayOfWeek = HTTP_ParseWkday(day);
4334 if (st.wDayOfWeek > 6)
4336 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4340 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4342 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4343 day[ptr - value + 1] = 0;
4344 st.wDayOfWeek = HTTP_ParseWeekday(day);
4345 if (st.wDayOfWeek > 6)
4347 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4353 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4358 while (isspaceW(*ptr))
4361 num = strtoulW(ptr, &nextPtr, 10);
4362 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4364 ERR("unexpected day %s\n", debugstr_w(value));
4368 st.wDay = (WORD)num;
4372 ERR("unexpected month format %s\n", debugstr_w(ptr));
4377 for (monthPtr = month; *ptr != '-' &&
4378 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4382 st.wMonth = HTTP_ParseMonth(month);
4383 if (!st.wMonth || st.wMonth > 12)
4385 ERR("unexpected month %s\n", debugstr_w(month));
4391 ERR("unexpected year format %s\n", debugstr_w(ptr));
4396 num = strtoulW(ptr, &nextPtr, 10);
4397 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4399 ERR("unexpected year %s\n", debugstr_w(value));
4403 st.wYear = (WORD)num;
4405 if (!HTTP_ParseTime(&st, &ptr))
4408 while (isspaceW(*ptr))
4411 if (strcmpW(ptr, gmt))
4413 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4416 return SystemTimeToFileTime(&st, ft);
4419 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4421 static const WCHAR zero[] = { '0',0 };
4424 if (!strcmpW(value, zero))
4426 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4429 else if (strchrW(value, ','))
4431 ret = HTTP_ParseRfc1123Date(value, ft);
4434 ret = HTTP_ParseRfc850Date(value, ft);
4436 ERR("unexpected date format %s\n", debugstr_w(value));
4441 ret = HTTP_ParseDateAsAsctime(value, ft);
4443 ERR("unexpected date format %s\n", debugstr_w(value));
4448 static void HTTP_ProcessExpires(http_request_t *request)
4450 BOOL expirationFound = FALSE;
4453 /* Look for a Cache-Control header with a max-age directive, as it takes
4454 * precedence over the Expires header.
4456 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4457 if (headerIndex != -1)
4459 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4462 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4464 LPWSTR comma = strchrW(ptr, ','), end, equal;
4469 end = ptr + strlenW(ptr);
4470 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4474 static const WCHAR max_age[] = {
4475 'm','a','x','-','a','g','e',0 };
4477 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4482 age = strtoulW(equal + 1, &nextPtr, 10);
4483 if (nextPtr > equal + 1)
4487 NtQuerySystemTime( &ft );
4488 /* Age is in seconds, FILETIME resolution is in
4489 * 100 nanosecond intervals.
4491 ft.QuadPart += age * (ULONGLONG)1000000;
4492 request->expires.dwLowDateTime = ft.u.LowPart;
4493 request->expires.dwHighDateTime = ft.u.HighPart;
4494 expirationFound = TRUE;
4501 while (isspaceW(*ptr))
4508 if (!expirationFound)
4510 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4511 if (headerIndex != -1)
4513 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4516 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4518 expirationFound = TRUE;
4519 request->expires = ft;
4523 if (!expirationFound)
4527 /* With no known age, default to 10 minutes until expiration. */
4528 NtQuerySystemTime( &t );
4529 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4530 request->expires.dwLowDateTime = t.u.LowPart;
4531 request->expires.dwHighDateTime = t.u.HighPart;
4535 static void HTTP_ProcessLastModified(http_request_t *request)
4539 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4540 if (headerIndex != -1)
4542 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4545 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4546 request->last_modified = ft;
4550 static void http_process_keep_alive(http_request_t *req)
4554 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4556 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4558 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4561 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4563 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4564 netconn_t *netconn = NULL;
4567 assert(!request->netconn);
4568 reset_data_stream(request);
4570 res = HTTP_ResolveName(request);
4571 if(res != ERROR_SUCCESS)
4574 EnterCriticalSection(&connection_pool_cs);
4576 while(!list_empty(&request->server->conn_pool)) {
4577 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4578 list_remove(&netconn->pool_entry);
4580 if(NETCON_is_alive(netconn))
4583 TRACE("connection %p closed during idle\n", netconn);
4584 free_netconn(netconn);
4588 LeaveCriticalSection(&connection_pool_cs);
4591 TRACE("<-- reusing %p netconn\n", netconn);
4592 request->netconn = netconn;
4594 return ERROR_SUCCESS;
4597 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4598 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4600 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4601 INTERNET_STATUS_CONNECTING_TO_SERVER,
4602 request->server->addr_str,
4603 strlen(request->server->addr_str)+1);
4605 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4606 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4607 request->connect_timeout, &netconn);
4608 if(res != ERROR_SUCCESS) {
4609 ERR("create_netconn failed: %u\n", res);
4613 request->netconn = netconn;
4615 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4616 INTERNET_STATUS_CONNECTED_TO_SERVER,
4617 request->server->addr_str, strlen(request->server->addr_str)+1);
4620 /* Note: we differ from Microsoft's WinINet here. they seem to have
4621 * a bug that causes no status callbacks to be sent when starting
4622 * a tunnel to a proxy server using the CONNECT verb. i believe our
4623 * behaviour to be more correct and to not cause any incompatibilities
4624 * because using a secure connection through a proxy server is a rare
4625 * case that would be hard for anyone to depend on */
4627 res = HTTP_SecureProxyConnect(request);
4628 if(res == ERROR_SUCCESS)
4629 res = NETCON_secure_connect(request->netconn, request->server);
4632 if(res != ERROR_SUCCESS) {
4633 http_release_netconn(request, FALSE);
4638 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4639 return ERROR_SUCCESS;
4642 /***********************************************************************
4643 * HTTP_HttpSendRequestW (internal)
4645 * Sends the specified request to the HTTP server
4648 * ERROR_SUCCESS on success
4649 * win32 error code on failure
4652 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4653 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4654 DWORD dwContentLength, BOOL bEndRequest)
4657 BOOL redirected = FALSE;
4658 LPWSTR requestString = NULL;
4661 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4662 static const WCHAR szContentLength[] =
4663 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4664 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4667 TRACE("--> %p\n", request);
4669 assert(request->hdr.htype == WH_HHTTPREQ);
4671 /* if the verb is NULL default to GET */
4673 request->verb = heap_strdupW(szGET);
4675 if (dwContentLength || strcmpW(request->verb, szGET))
4677 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4678 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4679 request->bytesToWrite = dwContentLength;
4681 if (request->session->appInfo->agent)
4683 WCHAR *agent_header;
4684 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4687 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4688 agent_header = heap_alloc(len * sizeof(WCHAR));
4689 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4691 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4692 heap_free(agent_header);
4694 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4696 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4697 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4699 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4701 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4702 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4703 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4706 /* add the headers the caller supplied */
4707 if( lpszHeaders && dwHeaderLength )
4708 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4713 BOOL reusing_connection;
4717 reusing_connection = request->netconn != NULL;
4720 request->contentLength = ~0u;
4721 request->bytesToWrite = 0;
4724 if (TRACE_ON(wininet))
4726 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4727 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4730 HTTP_FixURL(request);
4731 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4733 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4735 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4736 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4738 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4739 HTTP_InsertCookies(request);
4743 WCHAR *url = build_proxy_path_url(request);
4744 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4748 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4751 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4753 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4756 /* send the request as ASCII, tack on the optional data */
4757 if (!lpOptional || redirected)
4758 dwOptionalLength = 0;
4759 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4760 NULL, 0, NULL, NULL );
4761 ascii_req = heap_alloc(len + dwOptionalLength);
4762 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4763 ascii_req, len, NULL, NULL );
4765 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4766 len = (len + dwOptionalLength - 1);
4768 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4770 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4771 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4773 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4774 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4775 heap_free( ascii_req );
4776 if(res != ERROR_SUCCESS) {
4777 TRACE("send failed: %u\n", res);
4778 if(!reusing_connection)
4780 http_release_netconn(request, FALSE);
4785 request->bytesWritten = dwOptionalLength;
4787 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4788 INTERNET_STATUS_REQUEST_SENT,
4789 &len, sizeof(DWORD));
4795 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4796 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4798 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4799 /* FIXME: We should know that connection is closed before sending
4800 * headers. Otherwise wrong callbacks are executed */
4801 if(!responseLen && reusing_connection) {
4802 TRACE("Connection closed by server, reconnecting\n");
4803 http_release_netconn(request, FALSE);
4808 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4809 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4812 http_process_keep_alive(request);
4813 HTTP_ProcessCookies(request);
4814 HTTP_ProcessExpires(request);
4815 HTTP_ProcessLastModified(request);
4817 res = set_content_length(request);
4818 if(res != ERROR_SUCCESS)
4820 if(!request->contentLength)
4821 http_release_netconn(request, TRUE);
4823 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4825 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4826 dwBufferSize=sizeof(szNewLocation);
4827 switch(request->status_code) {
4828 case HTTP_STATUS_REDIRECT:
4829 case HTTP_STATUS_MOVED:
4830 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4831 case HTTP_STATUS_REDIRECT_METHOD:
4832 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4835 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4836 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4838 heap_free(request->verb);
4839 request->verb = heap_strdupW(szGET);
4841 http_release_netconn(request, drain_content(request, FALSE));
4842 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4844 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4845 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4846 res = HTTP_HandleRedirect(request, new_url);
4847 if (res == ERROR_SUCCESS)
4849 heap_free(requestString);
4852 heap_free( new_url );
4857 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4859 WCHAR szAuthValue[2048];
4861 if (request->status_code == HTTP_STATUS_DENIED)
4863 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4865 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4867 if (HTTP_DoAuthorization(request, szAuthValue,
4869 request->session->userName,
4870 request->session->password,
4873 heap_free(requestString);
4874 if(!drain_content(request, TRUE)) {
4875 FIXME("Could not drain content\n");
4876 http_release_netconn(request, FALSE);
4884 TRACE("Cleaning wrong authorization data\n");
4885 destroy_authinfo(request->authInfo);
4886 request->authInfo = NULL;
4889 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4892 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4894 if (HTTP_DoAuthorization(request, szAuthValue,
4895 &request->proxyAuthInfo,
4896 request->session->appInfo->proxyUsername,
4897 request->session->appInfo->proxyPassword,
4900 if(!drain_content(request, TRUE)) {
4901 FIXME("Could not drain content\n");
4902 http_release_netconn(request, FALSE);
4910 TRACE("Cleaning wrong proxy authorization data\n");
4911 destroy_authinfo(request->proxyAuthInfo);
4912 request->proxyAuthInfo = NULL;
4918 res = ERROR_SUCCESS;
4923 heap_free(requestString);
4925 /* TODO: send notification for P3P header */
4927 if(res == ERROR_SUCCESS)
4928 create_cache_entry(request);
4930 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4932 if (res == ERROR_SUCCESS) {
4933 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4934 HTTP_ReceiveRequestData(request, TRUE, NULL);
4936 send_request_complete(request,
4937 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4939 send_request_complete(request, 0, res);
4955 } send_request_task_t;
4957 /***********************************************************************
4959 * Helper functions for the HttpSendRequest(Ex) functions
4962 static void AsyncHttpSendRequestProc(task_header_t *hdr)
4964 send_request_task_t *task = (send_request_task_t*)hdr;
4965 http_request_t *request = (http_request_t*)task->hdr.hdr;
4967 TRACE("%p\n", request);
4969 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
4970 task->optional_len, task->content_len, task->end_request);
4972 heap_free(task->headers);
4976 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4980 DWORD res = ERROR_SUCCESS;
4982 if(!request->netconn) {
4983 WARN("Not connected\n");
4984 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
4985 return ERROR_INTERNET_OPERATION_CANCELLED;
4988 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4989 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4991 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4993 res = ERROR_HTTP_HEADER_NOT_FOUND;
4995 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4996 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4998 /* process cookies here. Is this right? */
4999 http_process_keep_alive(request);
5000 HTTP_ProcessCookies(request);
5001 HTTP_ProcessExpires(request);
5002 HTTP_ProcessLastModified(request);
5004 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5005 if(!request->contentLength)
5006 http_release_netconn(request, TRUE);
5009 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5011 switch(request->status_code) {
5012 case HTTP_STATUS_REDIRECT:
5013 case HTTP_STATUS_MOVED:
5014 case HTTP_STATUS_REDIRECT_METHOD:
5015 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5016 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5017 dwBufferSize=sizeof(szNewLocation);
5018 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5021 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5022 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5024 heap_free(request->verb);
5025 request->verb = heap_strdupW(szGET);
5027 http_release_netconn(request, drain_content(request, FALSE));
5028 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5030 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5031 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5032 res = HTTP_HandleRedirect(request, new_url);
5033 if (res == ERROR_SUCCESS)
5034 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5035 heap_free( new_url );
5041 if(res == ERROR_SUCCESS)
5042 create_cache_entry(request);
5044 if (res == ERROR_SUCCESS && request->contentLength)
5045 HTTP_ReceiveRequestData(request, TRUE, NULL);
5047 send_request_complete(request, res == ERROR_SUCCESS, res);
5052 /***********************************************************************
5053 * HttpEndRequestA (WININET.@)
5055 * Ends an HTTP request that was started by HttpSendRequestEx
5058 * TRUE if successful
5062 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5063 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5065 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5069 SetLastError(ERROR_INVALID_PARAMETER);
5073 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5080 } end_request_task_t;
5082 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5084 end_request_task_t *task = (end_request_task_t*)hdr;
5085 http_request_t *req = (http_request_t*)task->hdr.hdr;
5089 HTTP_HttpEndRequestW(req, task->flags, task->context);
5092 /***********************************************************************
5093 * HttpEndRequestW (WININET.@)
5095 * Ends an HTTP request that was started by HttpSendRequestEx
5098 * TRUE if successful
5102 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5103 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5105 http_request_t *request;
5108 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5112 SetLastError(ERROR_INVALID_PARAMETER);
5116 request = (http_request_t*) get_handle_object( hRequest );
5118 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5120 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5122 WININET_Release( &request->hdr );
5125 request->hdr.dwFlags |= dwFlags;
5127 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5129 end_request_task_t *task;
5131 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5132 task->flags = dwFlags;
5133 task->context = dwContext;
5135 INTERNET_AsyncCall(&task->hdr);
5136 res = ERROR_IO_PENDING;
5139 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5141 WININET_Release( &request->hdr );
5142 TRACE("%u <--\n", res);
5143 if(res != ERROR_SUCCESS)
5145 return res == ERROR_SUCCESS;
5148 /***********************************************************************
5149 * HttpSendRequestExA (WININET.@)
5151 * Sends the specified request to the HTTP server and allows chunked
5156 * Failure: FALSE, call GetLastError() for more information.
5158 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5159 LPINTERNET_BUFFERSA lpBuffersIn,
5160 LPINTERNET_BUFFERSA lpBuffersOut,
5161 DWORD dwFlags, DWORD_PTR dwContext)
5163 INTERNET_BUFFERSW BuffersInW;
5166 LPWSTR header = NULL;
5168 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5169 lpBuffersOut, dwFlags, dwContext);
5173 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5174 if (lpBuffersIn->lpcszHeader)
5176 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5177 lpBuffersIn->dwHeadersLength,0,0);
5178 header = heap_alloc(headerlen*sizeof(WCHAR));
5179 if (!(BuffersInW.lpcszHeader = header))
5181 SetLastError(ERROR_OUTOFMEMORY);
5184 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5185 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5189 BuffersInW.lpcszHeader = NULL;
5190 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5191 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5192 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5193 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5194 BuffersInW.Next = NULL;
5197 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5203 /***********************************************************************
5204 * HttpSendRequestExW (WININET.@)
5206 * Sends the specified request to the HTTP server and allows chunked
5211 * Failure: FALSE, call GetLastError() for more information.
5213 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5214 LPINTERNET_BUFFERSW lpBuffersIn,
5215 LPINTERNET_BUFFERSW lpBuffersOut,
5216 DWORD dwFlags, DWORD_PTR dwContext)
5218 http_request_t *request;
5219 http_session_t *session;
5223 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5224 lpBuffersOut, dwFlags, dwContext);
5226 request = (http_request_t*) get_handle_object( hRequest );
5228 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5230 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5234 session = request->session;
5235 assert(session->hdr.htype == WH_HHTTPSESSION);
5236 hIC = session->appInfo;
5237 assert(hIC->hdr.htype == WH_HINIT);
5239 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5241 send_request_task_t *task;
5243 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5248 if (lpBuffersIn->lpcszHeader)
5250 if (lpBuffersIn->dwHeadersLength == ~0u)
5251 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5253 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5255 task->headers = heap_alloc(size);
5256 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5258 else task->headers = NULL;
5260 task->headers_len = size / sizeof(WCHAR);
5261 task->optional = lpBuffersIn->lpvBuffer;
5262 task->optional_len = lpBuffersIn->dwBufferLength;
5263 task->content_len = lpBuffersIn->dwBufferTotal;
5267 task->headers = NULL;
5268 task->headers_len = 0;
5269 task->optional = NULL;
5270 task->optional_len = 0;
5271 task->content_len = 0;
5274 task->end_request = FALSE;
5276 INTERNET_AsyncCall(&task->hdr);
5277 res = ERROR_IO_PENDING;
5282 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5283 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5284 lpBuffersIn->dwBufferTotal, FALSE);
5286 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5291 WININET_Release( &request->hdr );
5295 return res == ERROR_SUCCESS;
5298 /***********************************************************************
5299 * HttpSendRequestW (WININET.@)
5301 * Sends the specified request to the HTTP server
5308 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5309 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5311 http_request_t *request;
5312 http_session_t *session = NULL;
5313 appinfo_t *hIC = NULL;
5314 DWORD res = ERROR_SUCCESS;
5316 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5317 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5319 request = (http_request_t*) get_handle_object( hHttpRequest );
5320 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5322 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5326 session = request->session;
5327 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5329 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5333 hIC = session->appInfo;
5334 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5336 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5340 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5342 send_request_task_t *task;
5344 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5349 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5350 else size = dwHeaderLength * sizeof(WCHAR);
5352 task->headers = heap_alloc(size);
5353 memcpy(task->headers, lpszHeaders, size);
5356 task->headers = NULL;
5357 task->headers_len = dwHeaderLength;
5358 task->optional = lpOptional;
5359 task->optional_len = dwOptionalLength;
5360 task->content_len = dwOptionalLength;
5361 task->end_request = TRUE;
5363 INTERNET_AsyncCall(&task->hdr);
5364 res = ERROR_IO_PENDING;
5368 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5369 dwHeaderLength, lpOptional, dwOptionalLength,
5370 dwOptionalLength, TRUE);
5374 WININET_Release( &request->hdr );
5377 return res == ERROR_SUCCESS;
5380 /***********************************************************************
5381 * HttpSendRequestA (WININET.@)
5383 * Sends the specified request to the HTTP server
5390 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5391 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5394 LPWSTR szHeaders=NULL;
5395 DWORD nLen=dwHeaderLength;
5396 if(lpszHeaders!=NULL)
5398 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5399 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5400 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5402 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5403 heap_free(szHeaders);
5407 /***********************************************************************
5408 * HTTPSESSION_Destroy (internal)
5410 * Deallocate session handle
5413 static void HTTPSESSION_Destroy(object_header_t *hdr)
5415 http_session_t *session = (http_session_t*) hdr;
5417 TRACE("%p\n", session);
5419 WININET_Release(&session->appInfo->hdr);
5421 heap_free(session->hostName);
5422 heap_free(session->password);
5423 heap_free(session->userName);
5426 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5428 http_session_t *ses = (http_session_t *)hdr;
5431 case INTERNET_OPTION_HANDLE_TYPE:
5432 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5434 if (*size < sizeof(ULONG))
5435 return ERROR_INSUFFICIENT_BUFFER;
5437 *size = sizeof(DWORD);
5438 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5439 return ERROR_SUCCESS;
5440 case INTERNET_OPTION_CONNECT_TIMEOUT:
5441 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5443 if (*size < sizeof(DWORD))
5444 return ERROR_INSUFFICIENT_BUFFER;
5446 *size = sizeof(DWORD);
5447 *(DWORD *)buffer = ses->connect_timeout;
5448 return ERROR_SUCCESS;
5450 case INTERNET_OPTION_SEND_TIMEOUT:
5451 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5453 if (*size < sizeof(DWORD))
5454 return ERROR_INSUFFICIENT_BUFFER;
5456 *size = sizeof(DWORD);
5457 *(DWORD *)buffer = ses->send_timeout;
5458 return ERROR_SUCCESS;
5460 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5461 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5463 if (*size < sizeof(DWORD))
5464 return ERROR_INSUFFICIENT_BUFFER;
5466 *size = sizeof(DWORD);
5467 *(DWORD *)buffer = ses->receive_timeout;
5468 return ERROR_SUCCESS;
5471 return INET_QueryOption(hdr, option, buffer, size, unicode);
5474 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5476 http_session_t *ses = (http_session_t*)hdr;
5479 case INTERNET_OPTION_USERNAME:
5481 heap_free(ses->userName);
5482 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5483 return ERROR_SUCCESS;
5485 case INTERNET_OPTION_PASSWORD:
5487 heap_free(ses->password);
5488 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5489 return ERROR_SUCCESS;
5491 case INTERNET_OPTION_CONNECT_TIMEOUT:
5493 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5494 ses->connect_timeout = *(DWORD *)buffer;
5495 return ERROR_SUCCESS;
5497 case INTERNET_OPTION_SEND_TIMEOUT:
5499 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5500 ses->send_timeout = *(DWORD *)buffer;
5501 return ERROR_SUCCESS;
5503 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5505 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5506 ses->receive_timeout = *(DWORD *)buffer;
5507 return ERROR_SUCCESS;
5512 return INET_SetOption(hdr, option, buffer, size);
5515 static const object_vtbl_t HTTPSESSIONVtbl = {
5516 HTTPSESSION_Destroy,
5518 HTTPSESSION_QueryOption,
5519 HTTPSESSION_SetOption,
5528 /***********************************************************************
5529 * HTTP_Connect (internal)
5531 * Create http session handle
5534 * HINTERNET a session handle on success
5538 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5539 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5540 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5541 DWORD dwInternalFlags, HINTERNET *ret)
5543 http_session_t *session = NULL;
5547 if (!lpszServerName || !lpszServerName[0])
5548 return ERROR_INVALID_PARAMETER;
5550 assert( hIC->hdr.htype == WH_HINIT );
5552 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5554 return ERROR_OUTOFMEMORY;
5557 * According to my tests. The name is not resolved until a request is sent
5560 session->hdr.htype = WH_HHTTPSESSION;
5561 session->hdr.dwFlags = dwFlags;
5562 session->hdr.dwContext = dwContext;
5563 session->hdr.dwInternalFlags |= dwInternalFlags;
5565 WININET_AddRef( &hIC->hdr );
5566 session->appInfo = hIC;
5567 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5569 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5570 if(hIC->proxyBypass)
5571 FIXME("Proxy bypass is ignored.\n");
5573 session->hostName = heap_strdupW(lpszServerName);
5574 if (lpszUserName && lpszUserName[0])
5575 session->userName = heap_strdupW(lpszUserName);
5576 if (lpszPassword && lpszPassword[0])
5577 session->password = heap_strdupW(lpszPassword);
5578 session->hostPort = serverPort;
5579 session->connect_timeout = hIC->connect_timeout;
5580 session->send_timeout = INFINITE;
5581 session->receive_timeout = INFINITE;
5583 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5584 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5586 INTERNET_SendCallback(&hIC->hdr, dwContext,
5587 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5592 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5596 TRACE("%p --> %p\n", hIC, session);
5598 *ret = session->hdr.hInternet;
5599 return ERROR_SUCCESS;
5602 /***********************************************************************
5603 * HTTP_clear_response_headers (internal)
5605 * clear out any old response headers
5607 static void HTTP_clear_response_headers( http_request_t *request )
5611 for( i=0; i<request->nCustHeaders; i++)
5613 if( !request->custHeaders[i].lpszField )
5615 if( !request->custHeaders[i].lpszValue )
5617 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5619 HTTP_DeleteCustomHeader( request, i );
5624 /***********************************************************************
5625 * HTTP_GetResponseHeaders (internal)
5627 * Read server response
5634 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5637 WCHAR buffer[MAX_REPLY_LEN];
5638 DWORD buflen = MAX_REPLY_LEN;
5639 BOOL bSuccess = FALSE;
5641 char bufferA[MAX_REPLY_LEN];
5642 LPWSTR status_code = NULL, status_text = NULL;
5643 DWORD cchMaxRawHeaders = 1024;
5644 LPWSTR lpszRawHeaders = NULL;
5646 DWORD cchRawHeaders = 0;
5647 BOOL codeHundred = FALSE;
5651 if(!request->netconn)
5654 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5656 static const WCHAR szHundred[] = {'1','0','0',0};
5658 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5660 buflen = MAX_REPLY_LEN;
5661 if (!read_line(request, bufferA, &buflen))
5664 /* clear old response headers (eg. from a redirect response) */
5666 HTTP_clear_response_headers( request );
5671 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5672 /* check is this a status code line? */
5673 if (!strncmpW(buffer, g_szHttp1_0, 4))
5675 /* split the version from the status code */
5676 status_code = strchrW( buffer, ' ' );
5681 /* split the status code from the status text */
5682 status_text = strchrW( status_code, ' ' );
5687 request->status_code = atoiW(status_code);
5689 TRACE("version [%s] status code [%s] status text [%s]\n",
5690 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5692 codeHundred = (!strcmpW(status_code, szHundred));
5694 else if (!codeHundred)
5696 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5698 heap_free(request->version);
5699 heap_free(request->statusText);
5701 request->status_code = HTTP_STATUS_OK;
5702 request->version = heap_strdupW(g_szHttp1_0);
5703 request->statusText = heap_strdupW(szOK);
5705 heap_free(request->rawHeaders);
5706 request->rawHeaders = heap_strdupW(szDefaultHeader);
5711 } while (codeHundred);
5713 /* Add status code */
5714 HTTP_ProcessHeader(request, szStatus, status_code,
5715 HTTP_ADDHDR_FLAG_REPLACE);
5717 heap_free(request->version);
5718 heap_free(request->statusText);
5720 request->version = heap_strdupW(buffer);
5721 request->statusText = heap_strdupW(status_text);
5723 /* Restore the spaces */
5724 *(status_code-1) = ' ';
5725 *(status_text-1) = ' ';
5727 /* regenerate raw headers */
5728 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5729 if (!lpszRawHeaders) goto lend;
5731 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5732 cchMaxRawHeaders *= 2;
5733 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5734 if (temp == NULL) goto lend;
5735 lpszRawHeaders = temp;
5736 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5737 cchRawHeaders += (buflen-1);
5738 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5739 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5740 lpszRawHeaders[cchRawHeaders] = '\0';
5742 /* Parse each response line */
5745 buflen = MAX_REPLY_LEN;
5746 if (read_line(request, bufferA, &buflen))
5748 LPWSTR * pFieldAndValue;
5750 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5752 if (!bufferA[0]) break;
5753 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5755 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5758 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5759 cchMaxRawHeaders *= 2;
5760 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5761 if (temp == NULL) goto lend;
5762 lpszRawHeaders = temp;
5763 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5764 cchRawHeaders += (buflen-1);
5765 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5766 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5767 lpszRawHeaders[cchRawHeaders] = '\0';
5769 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5770 HTTP_ADDREQ_FLAG_ADD );
5772 HTTP_FreeTokens(pFieldAndValue);
5783 /* make sure the response header is terminated with an empty line. Some apps really
5784 truly care about that empty line being there for some reason. Just add it to the
5786 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5788 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5789 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5790 if (temp == NULL) goto lend;
5791 lpszRawHeaders = temp;
5794 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5796 heap_free(request->rawHeaders);
5797 request->rawHeaders = lpszRawHeaders;
5798 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5808 heap_free(lpszRawHeaders);
5813 /***********************************************************************
5814 * HTTP_InterpretHttpHeader (internal)
5816 * Parse server response
5820 * Pointer to array of field, value, NULL on success.
5823 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5825 LPWSTR * pTokenPair;
5829 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5831 pszColon = strchrW(buffer, ':');
5832 /* must have two tokens */
5835 HTTP_FreeTokens(pTokenPair);
5837 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5841 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5844 HTTP_FreeTokens(pTokenPair);
5847 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5848 pTokenPair[0][pszColon - buffer] = '\0';
5852 len = strlenW(pszColon);
5853 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5856 HTTP_FreeTokens(pTokenPair);
5859 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5861 strip_spaces(pTokenPair[0]);
5862 strip_spaces(pTokenPair[1]);
5864 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5868 /***********************************************************************
5869 * HTTP_ProcessHeader (internal)
5871 * Stuff header into header tables according to <dwModifier>
5875 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5877 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5879 LPHTTPHEADERW lphttpHdr = NULL;
5881 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5882 DWORD res = ERROR_HTTP_INVALID_HEADER;
5884 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5886 /* REPLACE wins out over ADD */
5887 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5888 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5890 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5893 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5897 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5898 return ERROR_HTTP_INVALID_HEADER;
5899 lphttpHdr = &request->custHeaders[index];
5905 hdr.lpszField = (LPWSTR)field;
5906 hdr.lpszValue = (LPWSTR)value;
5907 hdr.wFlags = hdr.wCount = 0;
5909 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5910 hdr.wFlags |= HDR_ISREQUEST;
5912 return HTTP_InsertCustomHeader(request, &hdr);
5914 /* no value to delete */
5915 else return ERROR_SUCCESS;
5917 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5918 lphttpHdr->wFlags |= HDR_ISREQUEST;
5920 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5922 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5924 HTTP_DeleteCustomHeader( request, index );
5930 hdr.lpszField = (LPWSTR)field;
5931 hdr.lpszValue = (LPWSTR)value;
5932 hdr.wFlags = hdr.wCount = 0;
5934 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5935 hdr.wFlags |= HDR_ISREQUEST;
5937 return HTTP_InsertCustomHeader(request, &hdr);
5940 return ERROR_SUCCESS;
5942 else if (dwModifier & COALESCEFLAGS)
5947 INT origlen = strlenW(lphttpHdr->lpszValue);
5948 INT valuelen = strlenW(value);
5950 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5953 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5955 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5958 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5961 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5963 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5966 lphttpHdr->lpszValue = lpsztmp;
5967 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5970 lphttpHdr->lpszValue[origlen] = ch;
5972 lphttpHdr->lpszValue[origlen] = ' ';
5976 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5977 lphttpHdr->lpszValue[len] = '\0';
5978 res = ERROR_SUCCESS;
5982 WARN("heap_realloc (%d bytes) failed\n",len+1);
5983 res = ERROR_OUTOFMEMORY;
5986 TRACE("<-- %d\n", res);
5990 /***********************************************************************
5991 * HTTP_GetCustomHeaderIndex (internal)
5993 * Return index of custom header from header array
5996 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5997 int requested_index, BOOL request_only)
6001 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6003 for (index = 0; index < request->nCustHeaders; index++)
6005 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6008 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6011 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6014 if (requested_index == 0)
6019 if (index >= request->nCustHeaders)
6022 TRACE("Return: %d\n", index);
6027 /***********************************************************************
6028 * HTTP_InsertCustomHeader (internal)
6030 * Insert header into array
6033 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6036 LPHTTPHEADERW lph = NULL;
6038 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6039 count = request->nCustHeaders + 1;
6041 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6043 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6046 return ERROR_OUTOFMEMORY;
6048 request->custHeaders = lph;
6049 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6050 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6051 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6052 request->custHeaders[count-1].wCount= lpHdr->wCount;
6053 request->nCustHeaders++;
6055 return ERROR_SUCCESS;
6059 /***********************************************************************
6060 * HTTP_DeleteCustomHeader (internal)
6062 * Delete header from array
6063 * If this function is called, the indexs may change.
6065 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6067 if( request->nCustHeaders <= 0 )
6069 if( index >= request->nCustHeaders )
6071 request->nCustHeaders--;
6073 heap_free(request->custHeaders[index].lpszField);
6074 heap_free(request->custHeaders[index].lpszValue);
6076 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6077 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6078 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6084 /***********************************************************************
6085 * HTTP_VerifyValidHeader (internal)
6087 * Verify the given header is not invalid for the given http request
6090 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6092 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6093 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6094 return ERROR_HTTP_INVALID_HEADER;
6096 return ERROR_SUCCESS;
6099 /***********************************************************************
6100 * IsHostInProxyBypassList (@)
6105 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6107 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6111 /***********************************************************************
6112 * InternetShowSecurityInfoByURLA (@)
6114 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6116 FIXME("stub: %s %p\n", url, window);
6120 /***********************************************************************
6121 * InternetShowSecurityInfoByURLW (@)
6123 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6125 FIXME("stub: %s %p\n", debugstr_w(url), window);
6129 /***********************************************************************
6130 * ShowX509EncodedCertificate (@)
6132 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6134 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6140 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6142 memset(&view, 0, sizeof(view));
6143 view.hwndParent = parent;
6144 view.pCertContext = certContext;
6145 if (CryptUIDlgViewCertificateW(&view, NULL))
6146 ret = ERROR_SUCCESS;
6148 ret = GetLastError();
6149 CertFreeCertificateContext(certContext);
6152 ret = GetLastError();