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)
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 LeaveCriticalSection( &req->read_section );
2782 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2783 WARN("res %u read %u, closing connection\n", res, read);
2784 http_release_netconn(req, FALSE);
2787 if(res == ERROR_SUCCESS)
2788 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2790 send_request_complete(req, 0, res);
2793 /* read data from the http connection (the read section must be held) */
2794 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2796 DWORD current_read = 0, ret_read = 0;
2797 read_mode_t read_mode;
2798 DWORD res = ERROR_SUCCESS;
2800 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2802 EnterCriticalSection( &req->read_section );
2804 if(req->read_size) {
2805 ret_read = min(size, req->read_size);
2806 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2807 req->read_size -= ret_read;
2808 req->read_pos += ret_read;
2809 if(read_mode == READMODE_ASYNC)
2810 read_mode = READMODE_NOBLOCK;
2813 if(ret_read < size) {
2814 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2815 ret_read += current_read;
2818 LeaveCriticalSection( &req->read_section );
2821 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2823 if(size && !ret_read)
2824 http_release_netconn(req, res == ERROR_SUCCESS);
2829 static BOOL drain_content(http_request_t *req, BOOL blocking)
2833 if(!req->netconn || req->contentLength == -1)
2836 if(!strcmpW(req->verb, szHEAD))
2840 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2842 EnterCriticalSection( &req->read_section );
2845 DWORD bytes_read, res;
2848 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2849 if(res != ERROR_SUCCESS) {
2859 LeaveCriticalSection( &req->read_section );
2863 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2865 http_request_t *req = (http_request_t*)hdr;
2868 EnterCriticalSection( &req->read_section );
2869 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2870 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2872 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2873 if(res == ERROR_SUCCESS)
2875 LeaveCriticalSection( &req->read_section );
2880 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2882 struct WORKREQ_HTTPREADFILEEX const *data = &workRequest->u.HttpReadFileEx;
2883 http_request_t *req = (http_request_t*)workRequest->hdr;
2886 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2888 res = HTTPREQ_Read(req, data->buf, data->size, data->ret_read, TRUE);
2890 send_request_complete(req, res == ERROR_SUCCESS, res);
2893 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2894 DWORD flags, DWORD_PTR context)
2897 http_request_t *req = (http_request_t*)hdr;
2898 DWORD res, read, cread, error = ERROR_SUCCESS;
2900 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2901 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2903 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2905 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2907 WORKREQUEST workRequest;
2909 if (TryEnterCriticalSection( &req->read_section ))
2911 if (get_avail_data(req))
2913 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
2914 LeaveCriticalSection( &req->read_section );
2917 LeaveCriticalSection( &req->read_section );
2920 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2921 workRequest.hdr = WININET_AddRef(&req->hdr);
2922 workRequest.u.HttpReadFileEx.buf = buf;
2923 workRequest.u.HttpReadFileEx.size = size;
2924 workRequest.u.HttpReadFileEx.ret_read = ret_read;
2926 INTERNET_AsyncCall(&workRequest);
2928 return ERROR_IO_PENDING;
2933 EnterCriticalSection( &req->read_section );
2934 if(hdr->dwError == ERROR_SUCCESS)
2935 hdr->dwError = INTERNET_HANDLE_IN_USE;
2936 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2937 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2940 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
2941 if(res != ERROR_SUCCESS)
2945 if(read == size || end_of_read_data(req))
2948 LeaveCriticalSection( &req->read_section );
2950 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2951 &cread, sizeof(cread));
2952 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2953 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2955 EnterCriticalSection( &req->read_section );
2958 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2959 hdr->dwError = ERROR_SUCCESS;
2961 error = hdr->dwError;
2963 LeaveCriticalSection( &req->read_section );
2967 if (res == ERROR_SUCCESS) {
2968 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2969 &read, sizeof(read));
2972 return res==ERROR_SUCCESS ? error : res;
2975 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2978 http_request_t *request = (http_request_t*)hdr;
2980 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2983 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2984 if (res == ERROR_SUCCESS)
2985 request->bytesWritten += *written;
2987 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2991 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2993 http_request_t *req = (http_request_t*)workRequest->hdr;
2995 HTTP_ReceiveRequestData(req, FALSE);
2998 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3000 http_request_t *req = (http_request_t*)hdr;
3002 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3004 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3006 WORKREQUEST workRequest;
3008 /* never wait, if we can't enter the section we queue an async request right away */
3009 if (TryEnterCriticalSection( &req->read_section ))
3011 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3012 if ((*available = get_avail_data( req ))) goto done;
3013 if (end_of_read_data( req )) goto done;
3014 LeaveCriticalSection( &req->read_section );
3017 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3018 workRequest.hdr = WININET_AddRef( &req->hdr );
3020 INTERNET_AsyncCall(&workRequest);
3022 return ERROR_IO_PENDING;
3025 EnterCriticalSection( &req->read_section );
3027 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3029 refill_read_buffer( req, READMODE_ASYNC, NULL );
3030 *available = get_avail_data( req );
3034 LeaveCriticalSection( &req->read_section );
3036 TRACE( "returning %u\n", *available );
3037 return ERROR_SUCCESS;
3040 static const object_vtbl_t HTTPREQVtbl = {
3042 HTTPREQ_CloseConnection,
3043 HTTPREQ_QueryOption,
3048 HTTPREQ_QueryDataAvailable,
3052 /***********************************************************************
3053 * HTTP_HttpOpenRequestW (internal)
3055 * Open a HTTP request handle
3058 * HINTERNET a HTTP request handle on success
3062 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3063 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3064 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3065 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3067 appinfo_t *hIC = session->appInfo;
3068 http_request_t *request;
3069 DWORD len, res = ERROR_SUCCESS;
3073 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3075 return ERROR_OUTOFMEMORY;
3077 request->hdr.htype = WH_HHTTPREQ;
3078 request->hdr.dwFlags = dwFlags;
3079 request->hdr.dwContext = dwContext;
3080 request->contentLength = ~0u;
3082 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3083 request->data_stream = &request->netconn_stream.data_stream;
3084 request->connect_timeout = session->connect_timeout;
3085 request->send_timeout = session->send_timeout;
3086 request->receive_timeout = session->receive_timeout;
3088 InitializeCriticalSection( &request->read_section );
3089 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3091 WININET_AddRef( &session->hdr );
3092 request->session = session;
3093 list_add_head( &session->hdr.children, &request->hdr.entry );
3095 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3096 if(!request->server) {
3097 WININET_Release(&request->hdr);
3098 return ERROR_OUTOFMEMORY;
3101 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3102 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3103 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3104 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3106 if (lpszObjectName && *lpszObjectName) {
3110 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3111 if (rc != E_POINTER)
3112 len = strlenW(lpszObjectName)+1;
3113 request->path = heap_alloc(len*sizeof(WCHAR));
3114 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3115 URL_ESCAPE_SPACES_ONLY);
3118 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3119 strcpyW(request->path,lpszObjectName);
3122 static const WCHAR slashW[] = {'/',0};
3124 request->path = heap_strdupW(slashW);
3127 if (lpszReferrer && *lpszReferrer)
3128 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3130 if (lpszAcceptTypes)
3133 for (i = 0; lpszAcceptTypes[i]; i++)
3135 if (!*lpszAcceptTypes[i]) continue;
3136 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3137 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3138 HTTP_ADDHDR_FLAG_REQ |
3139 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3143 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3144 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3146 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3148 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3149 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3150 INTERNET_DEFAULT_HTTPS_PORT :
3151 INTERNET_DEFAULT_HTTP_PORT);
3153 if (hIC->proxy && hIC->proxy[0])
3154 HTTP_DealWithProxy( hIC, session, request );
3156 INTERNET_SendCallback(&session->hdr, dwContext,
3157 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3160 TRACE("<-- %u (%p)\n", res, request);
3162 if(res != ERROR_SUCCESS) {
3163 WININET_Release( &request->hdr );
3168 *ret = request->hdr.hInternet;
3169 return ERROR_SUCCESS;
3172 /***********************************************************************
3173 * HttpOpenRequestW (WININET.@)
3175 * Open a HTTP request handle
3178 * HINTERNET a HTTP request handle on success
3182 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3183 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3184 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3185 DWORD dwFlags, DWORD_PTR dwContext)
3187 http_session_t *session;
3188 HINTERNET handle = NULL;
3191 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3192 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3193 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3194 dwFlags, dwContext);
3195 if(lpszAcceptTypes!=NULL)
3198 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3199 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3202 session = (http_session_t*) get_handle_object( hHttpSession );
3203 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3205 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3210 * My tests seem to show that the windows version does not
3211 * become asynchronous until after this point. And anyhow
3212 * if this call was asynchronous then how would you get the
3213 * necessary HINTERNET pointer returned by this function.
3216 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3217 lpszVersion, lpszReferrer, lpszAcceptTypes,
3218 dwFlags, dwContext, &handle);
3221 WININET_Release( &session->hdr );
3222 TRACE("returning %p\n", handle);
3223 if(res != ERROR_SUCCESS)
3228 static const LPCWSTR header_lookup[] = {
3229 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3230 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3231 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3232 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3233 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3234 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3235 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3236 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3237 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3238 szDate, /* HTTP_QUERY_DATE = 9 */
3239 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3240 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3241 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3242 szURI, /* HTTP_QUERY_URI = 13 */
3243 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3244 NULL, /* HTTP_QUERY_COST = 15 */
3245 NULL, /* HTTP_QUERY_LINK = 16 */
3246 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3247 NULL, /* HTTP_QUERY_VERSION = 18 */
3248 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3249 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3250 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3251 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3252 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3253 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3254 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3255 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3256 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3257 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3258 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3259 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3260 NULL, /* HTTP_QUERY_FROM = 31 */
3261 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3262 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3263 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3264 szReferer, /* HTTP_QUERY_REFERER = 35 */
3265 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3266 szServer, /* HTTP_QUERY_SERVER = 37 */
3267 NULL, /* HTTP_TITLE = 38 */
3268 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3269 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3270 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3271 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3272 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3273 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3274 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3275 NULL, /* HTTP_QUERY_REFRESH = 46 */
3276 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3277 szAge, /* HTTP_QUERY_AGE = 48 */
3278 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3279 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3280 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3281 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3282 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3283 szETag, /* HTTP_QUERY_ETAG = 54 */
3284 hostW, /* HTTP_QUERY_HOST = 55 */
3285 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3286 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3287 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3288 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3289 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3290 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3291 szRange, /* HTTP_QUERY_RANGE = 62 */
3292 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3293 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3294 szVary, /* HTTP_QUERY_VARY = 65 */
3295 szVia, /* HTTP_QUERY_VIA = 66 */
3296 szWarning, /* HTTP_QUERY_WARNING = 67 */
3297 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3298 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3299 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3302 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3304 /***********************************************************************
3305 * HTTP_HttpQueryInfoW (internal)
3307 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3308 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3310 LPHTTPHEADERW lphttpHdr = NULL;
3311 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3312 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3313 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3316 /* Find requested header structure */
3319 case HTTP_QUERY_CUSTOM:
3320 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3321 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3323 case HTTP_QUERY_RAW_HEADERS_CRLF:
3327 DWORD res = ERROR_INVALID_PARAMETER;
3330 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3332 headers = request->rawHeaders;
3335 len = strlenW(headers) * sizeof(WCHAR);
3337 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3339 len += sizeof(WCHAR);
3340 res = ERROR_INSUFFICIENT_BUFFER;
3345 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3348 len = strlenW(szCrLf) * sizeof(WCHAR);
3349 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3351 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3352 res = ERROR_SUCCESS;
3354 *lpdwBufferLength = len;
3356 if (request_only) heap_free(headers);
3359 case HTTP_QUERY_RAW_HEADERS:
3361 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3363 LPWSTR pszString = lpBuffer;
3365 for (i = 0; ppszRawHeaderLines[i]; i++)
3366 size += strlenW(ppszRawHeaderLines[i]) + 1;
3368 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3370 HTTP_FreeTokens(ppszRawHeaderLines);
3371 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3372 return ERROR_INSUFFICIENT_BUFFER;
3376 for (i = 0; ppszRawHeaderLines[i]; i++)
3378 DWORD len = strlenW(ppszRawHeaderLines[i]);
3379 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3383 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3385 *lpdwBufferLength = size * sizeof(WCHAR);
3386 HTTP_FreeTokens(ppszRawHeaderLines);
3388 return ERROR_SUCCESS;
3390 case HTTP_QUERY_STATUS_TEXT:
3391 if (request->statusText)
3393 DWORD len = strlenW(request->statusText);
3394 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3396 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3397 return ERROR_INSUFFICIENT_BUFFER;
3401 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3402 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3404 *lpdwBufferLength = len * sizeof(WCHAR);
3405 return ERROR_SUCCESS;
3408 case HTTP_QUERY_VERSION:
3409 if (request->version)
3411 DWORD len = strlenW(request->version);
3412 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3414 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3415 return ERROR_INSUFFICIENT_BUFFER;
3419 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3420 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3422 *lpdwBufferLength = len * sizeof(WCHAR);
3423 return ERROR_SUCCESS;
3426 case HTTP_QUERY_CONTENT_ENCODING:
3427 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3428 requested_index,request_only);
3430 case HTTP_QUERY_STATUS_CODE: {
3431 DWORD res = ERROR_SUCCESS;
3434 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3439 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3440 if(*lpdwBufferLength >= sizeof(DWORD))
3441 *(DWORD*)lpBuffer = request->status_code;
3443 res = ERROR_INSUFFICIENT_BUFFER;
3444 *lpdwBufferLength = sizeof(DWORD);
3448 static const WCHAR formatW[] = {'%','u',0};
3450 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3452 if(size <= *lpdwBufferLength) {
3453 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3455 size += sizeof(WCHAR);
3456 res = ERROR_INSUFFICIENT_BUFFER;
3459 *lpdwBufferLength = size;
3464 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3466 if (level < LAST_TABLE_HEADER && header_lookup[level])
3467 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3468 requested_index,request_only);
3472 lphttpHdr = &request->custHeaders[index];
3474 /* Ensure header satisfies requested attributes */
3476 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3477 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3479 return ERROR_HTTP_HEADER_NOT_FOUND;
3482 if (lpdwIndex) (*lpdwIndex)++;
3484 /* coalesce value to requested type */
3485 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3487 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3488 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3490 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3496 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3498 tmpTM = *gmtime(&tmpTime);
3499 STHook = (SYSTEMTIME *)lpBuffer;
3500 STHook->wDay = tmpTM.tm_mday;
3501 STHook->wHour = tmpTM.tm_hour;
3502 STHook->wMilliseconds = 0;
3503 STHook->wMinute = tmpTM.tm_min;
3504 STHook->wDayOfWeek = tmpTM.tm_wday;
3505 STHook->wMonth = tmpTM.tm_mon + 1;
3506 STHook->wSecond = tmpTM.tm_sec;
3507 STHook->wYear = tmpTM.tm_year;
3509 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3510 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3511 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3513 else if (lphttpHdr->lpszValue)
3515 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3517 if (len > *lpdwBufferLength)
3519 *lpdwBufferLength = len;
3520 return ERROR_INSUFFICIENT_BUFFER;
3524 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3525 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3527 *lpdwBufferLength = len - sizeof(WCHAR);
3529 return ERROR_SUCCESS;
3532 /***********************************************************************
3533 * HttpQueryInfoW (WININET.@)
3535 * Queries for information about an HTTP request
3542 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3543 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3545 http_request_t *request;
3548 if (TRACE_ON(wininet)) {
3549 #define FE(x) { x, #x }
3550 static const wininet_flag_info query_flags[] = {
3551 FE(HTTP_QUERY_MIME_VERSION),
3552 FE(HTTP_QUERY_CONTENT_TYPE),
3553 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3554 FE(HTTP_QUERY_CONTENT_ID),
3555 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3556 FE(HTTP_QUERY_CONTENT_LENGTH),
3557 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3558 FE(HTTP_QUERY_ALLOW),
3559 FE(HTTP_QUERY_PUBLIC),
3560 FE(HTTP_QUERY_DATE),
3561 FE(HTTP_QUERY_EXPIRES),
3562 FE(HTTP_QUERY_LAST_MODIFIED),
3563 FE(HTTP_QUERY_MESSAGE_ID),
3565 FE(HTTP_QUERY_DERIVED_FROM),
3566 FE(HTTP_QUERY_COST),
3567 FE(HTTP_QUERY_LINK),
3568 FE(HTTP_QUERY_PRAGMA),
3569 FE(HTTP_QUERY_VERSION),
3570 FE(HTTP_QUERY_STATUS_CODE),
3571 FE(HTTP_QUERY_STATUS_TEXT),
3572 FE(HTTP_QUERY_RAW_HEADERS),
3573 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3574 FE(HTTP_QUERY_CONNECTION),
3575 FE(HTTP_QUERY_ACCEPT),
3576 FE(HTTP_QUERY_ACCEPT_CHARSET),
3577 FE(HTTP_QUERY_ACCEPT_ENCODING),
3578 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3579 FE(HTTP_QUERY_AUTHORIZATION),
3580 FE(HTTP_QUERY_CONTENT_ENCODING),
3581 FE(HTTP_QUERY_FORWARDED),
3582 FE(HTTP_QUERY_FROM),
3583 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3584 FE(HTTP_QUERY_LOCATION),
3585 FE(HTTP_QUERY_ORIG_URI),
3586 FE(HTTP_QUERY_REFERER),
3587 FE(HTTP_QUERY_RETRY_AFTER),
3588 FE(HTTP_QUERY_SERVER),
3589 FE(HTTP_QUERY_TITLE),
3590 FE(HTTP_QUERY_USER_AGENT),
3591 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3592 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3593 FE(HTTP_QUERY_ACCEPT_RANGES),
3594 FE(HTTP_QUERY_SET_COOKIE),
3595 FE(HTTP_QUERY_COOKIE),
3596 FE(HTTP_QUERY_REQUEST_METHOD),
3597 FE(HTTP_QUERY_REFRESH),
3598 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3600 FE(HTTP_QUERY_CACHE_CONTROL),
3601 FE(HTTP_QUERY_CONTENT_BASE),
3602 FE(HTTP_QUERY_CONTENT_LOCATION),
3603 FE(HTTP_QUERY_CONTENT_MD5),
3604 FE(HTTP_QUERY_CONTENT_RANGE),
3605 FE(HTTP_QUERY_ETAG),
3606 FE(HTTP_QUERY_HOST),
3607 FE(HTTP_QUERY_IF_MATCH),
3608 FE(HTTP_QUERY_IF_NONE_MATCH),
3609 FE(HTTP_QUERY_IF_RANGE),
3610 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3611 FE(HTTP_QUERY_MAX_FORWARDS),
3612 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3613 FE(HTTP_QUERY_RANGE),
3614 FE(HTTP_QUERY_TRANSFER_ENCODING),
3615 FE(HTTP_QUERY_UPGRADE),
3616 FE(HTTP_QUERY_VARY),
3618 FE(HTTP_QUERY_WARNING),
3619 FE(HTTP_QUERY_CUSTOM)
3621 static const wininet_flag_info modifier_flags[] = {
3622 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3623 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3624 FE(HTTP_QUERY_FLAG_NUMBER),
3625 FE(HTTP_QUERY_FLAG_COALESCE)
3628 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3629 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3632 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3633 TRACE(" Attribute:");
3634 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3635 if (query_flags[i].val == info) {
3636 TRACE(" %s", query_flags[i].name);
3640 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3641 TRACE(" Unknown (%08x)", info);
3644 TRACE(" Modifier:");
3645 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3646 if (modifier_flags[i].val & info_mod) {
3647 TRACE(" %s", modifier_flags[i].name);
3648 info_mod &= ~ modifier_flags[i].val;
3653 TRACE(" Unknown (%08x)", info_mod);
3658 request = (http_request_t*) get_handle_object( hHttpRequest );
3659 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3661 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3665 if (lpBuffer == NULL)
3666 *lpdwBufferLength = 0;
3667 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3668 lpBuffer, lpdwBufferLength, lpdwIndex);
3672 WININET_Release( &request->hdr );
3674 TRACE("%u <--\n", res);
3675 if(res != ERROR_SUCCESS)
3677 return res == ERROR_SUCCESS;
3680 /***********************************************************************
3681 * HttpQueryInfoA (WININET.@)
3683 * Queries for information about an HTTP request
3690 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3691 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3697 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3698 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3700 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3701 lpdwBufferLength, lpdwIndex );
3707 len = (*lpdwBufferLength)*sizeof(WCHAR);
3708 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3710 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3716 bufferW = heap_alloc(alloclen);
3717 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3718 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3719 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3726 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3730 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3731 lpBuffer, *lpdwBufferLength, NULL, NULL );
3732 *lpdwBufferLength = len - 1;
3734 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3737 /* since the strings being returned from HttpQueryInfoW should be
3738 * only ASCII characters, it is reasonable to assume that all of
3739 * the Unicode characters can be reduced to a single byte */
3740 *lpdwBufferLength = len / sizeof(WCHAR);
3742 heap_free( bufferW );
3746 /***********************************************************************
3747 * HTTP_GetRedirectURL (internal)
3749 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3751 static WCHAR szHttp[] = {'h','t','t','p',0};
3752 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3753 http_session_t *session = request->session;
3754 URL_COMPONENTSW urlComponents;
3755 DWORD url_length = 0;
3757 LPWSTR combined_url;
3759 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3760 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3761 urlComponents.dwSchemeLength = 0;
3762 urlComponents.lpszHostName = session->hostName;
3763 urlComponents.dwHostNameLength = 0;
3764 urlComponents.nPort = session->hostPort;
3765 urlComponents.lpszUserName = session->userName;
3766 urlComponents.dwUserNameLength = 0;
3767 urlComponents.lpszPassword = NULL;
3768 urlComponents.dwPasswordLength = 0;
3769 urlComponents.lpszUrlPath = request->path;
3770 urlComponents.dwUrlPathLength = 0;
3771 urlComponents.lpszExtraInfo = NULL;
3772 urlComponents.dwExtraInfoLength = 0;
3774 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3775 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3778 orig_url = heap_alloc(url_length);
3780 /* convert from bytes to characters */
3781 url_length = url_length / sizeof(WCHAR) - 1;
3782 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3784 heap_free(orig_url);
3789 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3790 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3792 heap_free(orig_url);
3795 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3797 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3799 heap_free(orig_url);
3800 heap_free(combined_url);
3803 heap_free(orig_url);
3804 return combined_url;
3808 /***********************************************************************
3809 * HTTP_HandleRedirect (internal)
3811 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3813 http_session_t *session = request->session;
3814 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3819 /* if it's an absolute path, keep the same session info */
3820 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3824 URL_COMPONENTSW urlComponents;
3825 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3826 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3827 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3828 BOOL custom_port = FALSE;
3830 static WCHAR httpW[] = {'h','t','t','p',0};
3831 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3837 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3838 urlComponents.lpszScheme = protocol;
3839 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3840 urlComponents.lpszHostName = hostName;
3841 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3842 urlComponents.lpszUserName = userName;
3843 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3844 urlComponents.lpszPassword = NULL;
3845 urlComponents.dwPasswordLength = 0;
3846 urlComponents.lpszUrlPath = path;
3847 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3848 urlComponents.lpszExtraInfo = NULL;
3849 urlComponents.dwExtraInfoLength = 0;
3850 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3851 return INTERNET_GetLastError();
3853 if(!strcmpiW(protocol, httpW)) {
3854 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3855 TRACE("redirect from secure page to non-secure page\n");
3856 /* FIXME: warn about from secure redirect to non-secure page */
3857 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3860 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3861 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3862 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3864 }else if(!strcmpiW(protocol, httpsW)) {
3865 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3866 TRACE("redirect from non-secure page to secure page\n");
3867 /* FIXME: notify about redirect to secure page */
3868 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3871 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3872 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3873 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3877 heap_free(session->hostName);
3881 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3882 len = lstrlenW(hostName);
3883 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3884 session->hostName = heap_alloc(len*sizeof(WCHAR));
3885 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3888 session->hostName = heap_strdupW(hostName);
3890 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3892 heap_free(session->userName);
3893 session->userName = NULL;
3895 session->userName = heap_strdupW(userName);
3897 reset_data_stream(request);
3899 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3900 server_t *new_server;
3902 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3903 server_release(request->server);
3904 request->server = new_server;
3907 heap_free(request->path);
3914 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3915 if (rc != E_POINTER)
3916 needed = strlenW(path)+1;
3917 request->path = heap_alloc(needed*sizeof(WCHAR));
3918 rc = UrlEscapeW(path, request->path, &needed,
3919 URL_ESCAPE_SPACES_ONLY);
3922 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3923 strcpyW(request->path,path);
3927 /* Remove custom content-type/length headers on redirects. */
3928 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3930 HTTP_DeleteCustomHeader(request, index);
3931 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3933 HTTP_DeleteCustomHeader(request, index);
3935 return ERROR_SUCCESS;
3938 /***********************************************************************
3939 * HTTP_build_req (internal)
3941 * concatenate all the strings in the request together
3943 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3948 for( t = list; *t ; t++ )
3949 len += strlenW( *t );
3952 str = heap_alloc(len*sizeof(WCHAR));
3955 for( t = list; *t ; t++ )
3961 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3963 server_t *server = request->server;
3964 LPWSTR requestString;
3971 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
3975 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
3977 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3978 NULL, 0, NULL, NULL );
3979 len--; /* the nul terminator isn't needed */
3980 ascii_req = heap_alloc(len);
3981 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3982 heap_free( requestString );
3984 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3986 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3987 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3988 heap_free( ascii_req );
3989 if (res != ERROR_SUCCESS)
3992 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3994 return ERROR_HTTP_INVALID_HEADER;
3996 return ERROR_SUCCESS;
3999 static void HTTP_InsertCookies(http_request_t *request)
4001 DWORD cookie_size, size, cnt = 0;
4005 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4007 host = HTTP_GetHeader(request, hostW);
4011 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4014 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4015 if(!(cookies = heap_alloc(size)))
4018 cnt += sprintfW(cookies, cookieW);
4019 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4020 strcatW(cookies, szCrLf);
4022 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4027 static WORD HTTP_ParseWkday(LPCWSTR day)
4029 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4037 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4038 if (!strcmpiW(day, days[i]))
4045 static WORD HTTP_ParseMonth(LPCWSTR month)
4047 static const WCHAR jan[] = { 'j','a','n',0 };
4048 static const WCHAR feb[] = { 'f','e','b',0 };
4049 static const WCHAR mar[] = { 'm','a','r',0 };
4050 static const WCHAR apr[] = { 'a','p','r',0 };
4051 static const WCHAR may[] = { 'm','a','y',0 };
4052 static const WCHAR jun[] = { 'j','u','n',0 };
4053 static const WCHAR jul[] = { 'j','u','l',0 };
4054 static const WCHAR aug[] = { 'a','u','g',0 };
4055 static const WCHAR sep[] = { 's','e','p',0 };
4056 static const WCHAR oct[] = { 'o','c','t',0 };
4057 static const WCHAR nov[] = { 'n','o','v',0 };
4058 static const WCHAR dec[] = { 'd','e','c',0 };
4060 if (!strcmpiW(month, jan)) return 1;
4061 if (!strcmpiW(month, feb)) return 2;
4062 if (!strcmpiW(month, mar)) return 3;
4063 if (!strcmpiW(month, apr)) return 4;
4064 if (!strcmpiW(month, may)) return 5;
4065 if (!strcmpiW(month, jun)) return 6;
4066 if (!strcmpiW(month, jul)) return 7;
4067 if (!strcmpiW(month, aug)) return 8;
4068 if (!strcmpiW(month, sep)) return 9;
4069 if (!strcmpiW(month, oct)) return 10;
4070 if (!strcmpiW(month, nov)) return 11;
4071 if (!strcmpiW(month, dec)) return 12;
4076 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4077 * optionally preceded by whitespace.
4078 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4079 * st, and sets *str to the first character after the time format.
4081 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4087 while (isspaceW(*ptr))
4090 num = strtoulW(ptr, &nextPtr, 10);
4091 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4093 ERR("unexpected time format %s\n", debugstr_w(ptr));
4098 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4102 st->wHour = (WORD)num;
4103 num = strtoulW(ptr, &nextPtr, 10);
4104 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4106 ERR("unexpected time format %s\n", debugstr_w(ptr));
4111 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4115 st->wMinute = (WORD)num;
4116 num = strtoulW(ptr, &nextPtr, 10);
4117 if (!nextPtr || nextPtr <= ptr)
4119 ERR("unexpected time format %s\n", debugstr_w(ptr));
4124 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4129 st->wSecond = (WORD)num;
4133 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4135 static const WCHAR gmt[]= { 'G','M','T',0 };
4136 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4138 SYSTEMTIME st = { 0 };
4141 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4142 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4145 st.wDayOfWeek = HTTP_ParseWkday(day);
4146 if (st.wDayOfWeek >= 7)
4148 ERR("unexpected weekday %s\n", debugstr_w(day));
4152 while (isspaceW(*ptr))
4155 for (monthPtr = month; !isspace(*ptr) &&
4156 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4160 st.wMonth = HTTP_ParseMonth(month);
4161 if (!st.wMonth || st.wMonth > 12)
4163 ERR("unexpected month %s\n", debugstr_w(month));
4167 while (isspaceW(*ptr))
4170 num = strtoulW(ptr, &nextPtr, 10);
4171 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4173 ERR("unexpected day %s\n", debugstr_w(ptr));
4177 st.wDay = (WORD)num;
4179 while (isspaceW(*ptr))
4182 if (!HTTP_ParseTime(&st, &ptr))
4185 while (isspaceW(*ptr))
4188 num = strtoulW(ptr, &nextPtr, 10);
4189 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4191 ERR("unexpected year %s\n", debugstr_w(ptr));
4195 st.wYear = (WORD)num;
4197 while (isspaceW(*ptr))
4200 /* asctime() doesn't report a timezone, but some web servers do, so accept
4201 * with or without GMT.
4203 if (*ptr && strcmpW(ptr, gmt))
4205 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4208 return SystemTimeToFileTime(&st, ft);
4211 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4213 static const WCHAR gmt[]= { 'G','M','T',0 };
4214 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4217 SYSTEMTIME st = { 0 };
4219 ptr = strchrW(value, ',');
4222 if (ptr - value != 3)
4224 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4227 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4229 st.wDayOfWeek = HTTP_ParseWkday(day);
4230 if (st.wDayOfWeek > 6)
4232 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4237 while (isspaceW(*ptr))
4240 num = strtoulW(ptr, &nextPtr, 10);
4241 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4243 WARN("unexpected day %s\n", debugstr_w(value));
4247 st.wDay = (WORD)num;
4249 while (isspaceW(*ptr))
4252 for (monthPtr = month; !isspace(*ptr) &&
4253 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4257 st.wMonth = HTTP_ParseMonth(month);
4258 if (!st.wMonth || st.wMonth > 12)
4260 WARN("unexpected month %s\n", debugstr_w(month));
4264 while (isspaceW(*ptr))
4267 num = strtoulW(ptr, &nextPtr, 10);
4268 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4270 ERR("unexpected year %s\n", debugstr_w(value));
4274 st.wYear = (WORD)num;
4276 if (!HTTP_ParseTime(&st, &ptr))
4279 while (isspaceW(*ptr))
4282 if (strcmpW(ptr, gmt))
4284 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4287 return SystemTimeToFileTime(&st, ft);
4290 static WORD HTTP_ParseWeekday(LPCWSTR day)
4292 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4293 { 'm','o','n','d','a','y',0 },
4294 { 't','u','e','s','d','a','y',0 },
4295 { 'w','e','d','n','e','s','d','a','y',0 },
4296 { 't','h','u','r','s','d','a','y',0 },
4297 { 'f','r','i','d','a','y',0 },
4298 { 's','a','t','u','r','d','a','y',0 }};
4300 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4301 if (!strcmpiW(day, days[i]))
4308 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4310 static const WCHAR gmt[]= { 'G','M','T',0 };
4311 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4314 SYSTEMTIME st = { 0 };
4316 ptr = strchrW(value, ',');
4319 if (ptr - value == 3)
4321 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4323 st.wDayOfWeek = HTTP_ParseWkday(day);
4324 if (st.wDayOfWeek > 6)
4326 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4330 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4332 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4333 day[ptr - value + 1] = 0;
4334 st.wDayOfWeek = HTTP_ParseWeekday(day);
4335 if (st.wDayOfWeek > 6)
4337 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4343 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4348 while (isspaceW(*ptr))
4351 num = strtoulW(ptr, &nextPtr, 10);
4352 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4354 ERR("unexpected day %s\n", debugstr_w(value));
4358 st.wDay = (WORD)num;
4362 ERR("unexpected month format %s\n", debugstr_w(ptr));
4367 for (monthPtr = month; *ptr != '-' &&
4368 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4372 st.wMonth = HTTP_ParseMonth(month);
4373 if (!st.wMonth || st.wMonth > 12)
4375 ERR("unexpected month %s\n", debugstr_w(month));
4381 ERR("unexpected year format %s\n", debugstr_w(ptr));
4386 num = strtoulW(ptr, &nextPtr, 10);
4387 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4389 ERR("unexpected year %s\n", debugstr_w(value));
4393 st.wYear = (WORD)num;
4395 if (!HTTP_ParseTime(&st, &ptr))
4398 while (isspaceW(*ptr))
4401 if (strcmpW(ptr, gmt))
4403 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4406 return SystemTimeToFileTime(&st, ft);
4409 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4411 static const WCHAR zero[] = { '0',0 };
4414 if (!strcmpW(value, zero))
4416 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4419 else if (strchrW(value, ','))
4421 ret = HTTP_ParseRfc1123Date(value, ft);
4424 ret = HTTP_ParseRfc850Date(value, ft);
4426 ERR("unexpected date format %s\n", debugstr_w(value));
4431 ret = HTTP_ParseDateAsAsctime(value, ft);
4433 ERR("unexpected date format %s\n", debugstr_w(value));
4438 static void HTTP_ProcessExpires(http_request_t *request)
4440 BOOL expirationFound = FALSE;
4443 /* Look for a Cache-Control header with a max-age directive, as it takes
4444 * precedence over the Expires header.
4446 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4447 if (headerIndex != -1)
4449 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4452 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4454 LPWSTR comma = strchrW(ptr, ','), end, equal;
4459 end = ptr + strlenW(ptr);
4460 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4464 static const WCHAR max_age[] = {
4465 'm','a','x','-','a','g','e',0 };
4467 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4472 age = strtoulW(equal + 1, &nextPtr, 10);
4473 if (nextPtr > equal + 1)
4477 NtQuerySystemTime( &ft );
4478 /* Age is in seconds, FILETIME resolution is in
4479 * 100 nanosecond intervals.
4481 ft.QuadPart += age * (ULONGLONG)1000000;
4482 request->expires.dwLowDateTime = ft.u.LowPart;
4483 request->expires.dwHighDateTime = ft.u.HighPart;
4484 expirationFound = TRUE;
4491 while (isspaceW(*ptr))
4498 if (!expirationFound)
4500 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4501 if (headerIndex != -1)
4503 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4506 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4508 expirationFound = TRUE;
4509 request->expires = ft;
4513 if (!expirationFound)
4517 /* With no known age, default to 10 minutes until expiration. */
4518 NtQuerySystemTime( &t );
4519 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4520 request->expires.dwLowDateTime = t.u.LowPart;
4521 request->expires.dwHighDateTime = t.u.HighPart;
4525 static void HTTP_ProcessLastModified(http_request_t *request)
4529 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4530 if (headerIndex != -1)
4532 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4535 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4536 request->last_modified = ft;
4540 static void http_process_keep_alive(http_request_t *req)
4544 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4546 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4548 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4551 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4553 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4554 netconn_t *netconn = NULL;
4557 assert(!request->netconn);
4558 reset_data_stream(request);
4560 res = HTTP_ResolveName(request);
4561 if(res != ERROR_SUCCESS)
4564 EnterCriticalSection(&connection_pool_cs);
4566 while(!list_empty(&request->server->conn_pool)) {
4567 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4568 list_remove(&netconn->pool_entry);
4570 if(NETCON_is_alive(netconn))
4573 TRACE("connection %p closed during idle\n", netconn);
4574 free_netconn(netconn);
4578 LeaveCriticalSection(&connection_pool_cs);
4581 TRACE("<-- reusing %p netconn\n", netconn);
4582 request->netconn = netconn;
4584 return ERROR_SUCCESS;
4587 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4588 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4590 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4591 INTERNET_STATUS_CONNECTING_TO_SERVER,
4592 request->server->addr_str,
4593 strlen(request->server->addr_str)+1);
4595 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4596 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4597 request->connect_timeout, &netconn);
4598 if(res != ERROR_SUCCESS) {
4599 ERR("create_netconn failed: %u\n", res);
4603 request->netconn = netconn;
4605 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4606 INTERNET_STATUS_CONNECTED_TO_SERVER,
4607 request->server->addr_str, strlen(request->server->addr_str)+1);
4610 /* Note: we differ from Microsoft's WinINet here. they seem to have
4611 * a bug that causes no status callbacks to be sent when starting
4612 * a tunnel to a proxy server using the CONNECT verb. i believe our
4613 * behaviour to be more correct and to not cause any incompatibilities
4614 * because using a secure connection through a proxy server is a rare
4615 * case that would be hard for anyone to depend on */
4617 res = HTTP_SecureProxyConnect(request);
4618 if(res == ERROR_SUCCESS)
4619 res = NETCON_secure_connect(request->netconn, request->server);
4622 if(res != ERROR_SUCCESS) {
4623 http_release_netconn(request, FALSE);
4628 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4629 return ERROR_SUCCESS;
4632 /***********************************************************************
4633 * HTTP_HttpSendRequestW (internal)
4635 * Sends the specified request to the HTTP server
4638 * ERROR_SUCCESS on success
4639 * win32 error code on failure
4642 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4643 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4644 DWORD dwContentLength, BOOL bEndRequest)
4647 BOOL redirected = FALSE;
4648 LPWSTR requestString = NULL;
4651 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4652 static const WCHAR szContentLength[] =
4653 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4654 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4657 TRACE("--> %p\n", request);
4659 assert(request->hdr.htype == WH_HHTTPREQ);
4661 /* if the verb is NULL default to GET */
4663 request->verb = heap_strdupW(szGET);
4665 if (dwContentLength || strcmpW(request->verb, szGET))
4667 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4668 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4669 request->bytesToWrite = dwContentLength;
4671 if (request->session->appInfo->agent)
4673 WCHAR *agent_header;
4674 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4677 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4678 agent_header = heap_alloc(len * sizeof(WCHAR));
4679 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4681 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4682 heap_free(agent_header);
4684 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4686 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4687 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4689 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4691 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4692 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4693 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4696 /* add the headers the caller supplied */
4697 if( lpszHeaders && dwHeaderLength )
4698 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4703 BOOL reusing_connection;
4707 reusing_connection = request->netconn != NULL;
4710 request->contentLength = ~0u;
4711 request->bytesToWrite = 0;
4714 if (TRACE_ON(wininet))
4716 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4717 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4720 HTTP_FixURL(request);
4721 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4723 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4725 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4726 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4728 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4729 HTTP_InsertCookies(request);
4733 WCHAR *url = build_proxy_path_url(request);
4734 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4738 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4741 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4743 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4746 /* send the request as ASCII, tack on the optional data */
4747 if (!lpOptional || redirected)
4748 dwOptionalLength = 0;
4749 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4750 NULL, 0, NULL, NULL );
4751 ascii_req = heap_alloc(len + dwOptionalLength);
4752 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4753 ascii_req, len, NULL, NULL );
4755 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4756 len = (len + dwOptionalLength - 1);
4758 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4760 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4761 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4763 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4764 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4765 heap_free( ascii_req );
4766 if(res != ERROR_SUCCESS) {
4767 TRACE("send failed: %u\n", res);
4768 if(!reusing_connection)
4770 http_release_netconn(request, FALSE);
4775 request->bytesWritten = dwOptionalLength;
4777 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4778 INTERNET_STATUS_REQUEST_SENT,
4779 &len, sizeof(DWORD));
4785 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4786 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4788 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4789 /* FIXME: We should know that connection is closed before sending
4790 * headers. Otherwise wrong callbacks are executed */
4791 if(!responseLen && reusing_connection) {
4792 TRACE("Connection closed by server, reconnecting\n");
4793 http_release_netconn(request, FALSE);
4798 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4799 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4802 http_process_keep_alive(request);
4803 HTTP_ProcessCookies(request);
4804 HTTP_ProcessExpires(request);
4805 HTTP_ProcessLastModified(request);
4807 res = set_content_length(request);
4808 if(res != ERROR_SUCCESS)
4810 if(!request->contentLength)
4811 http_release_netconn(request, TRUE);
4813 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4815 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4816 dwBufferSize=sizeof(szNewLocation);
4817 switch(request->status_code) {
4818 case HTTP_STATUS_REDIRECT:
4819 case HTTP_STATUS_MOVED:
4820 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4821 case HTTP_STATUS_REDIRECT_METHOD:
4822 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4825 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4826 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4828 heap_free(request->verb);
4829 request->verb = heap_strdupW(szGET);
4831 http_release_netconn(request, drain_content(request, FALSE));
4832 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4834 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4835 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4836 res = HTTP_HandleRedirect(request, new_url);
4837 if (res == ERROR_SUCCESS)
4839 heap_free(requestString);
4842 heap_free( new_url );
4847 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4849 WCHAR szAuthValue[2048];
4851 if (request->status_code == HTTP_STATUS_DENIED)
4853 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4855 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4857 if (HTTP_DoAuthorization(request, szAuthValue,
4859 request->session->userName,
4860 request->session->password,
4863 heap_free(requestString);
4864 if(!drain_content(request, TRUE)) {
4865 FIXME("Could not drain content\n");
4866 http_release_netconn(request, FALSE);
4874 TRACE("Cleaning wrong authorization data\n");
4875 destroy_authinfo(request->authInfo);
4876 request->authInfo = NULL;
4879 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4882 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4884 if (HTTP_DoAuthorization(request, szAuthValue,
4885 &request->proxyAuthInfo,
4886 request->session->appInfo->proxyUsername,
4887 request->session->appInfo->proxyPassword,
4890 if(!drain_content(request, TRUE)) {
4891 FIXME("Could not drain content\n");
4892 http_release_netconn(request, FALSE);
4900 TRACE("Cleaning wrong proxy authorization data\n");
4901 destroy_authinfo(request->proxyAuthInfo);
4902 request->proxyAuthInfo = NULL;
4908 res = ERROR_SUCCESS;
4913 heap_free(requestString);
4915 /* TODO: send notification for P3P header */
4917 if(res == ERROR_SUCCESS)
4918 create_cache_entry(request);
4920 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4922 if (res == ERROR_SUCCESS) {
4923 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4924 HTTP_ReceiveRequestData(request, TRUE);
4926 send_request_complete(request,
4927 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4929 send_request_complete(request, 0, res);
4937 /***********************************************************************
4939 * Helper functions for the HttpSendRequest(Ex) functions
4942 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4944 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4945 http_request_t *request = (http_request_t*) workRequest->hdr;
4947 TRACE("%p\n", request);
4949 HTTP_HttpSendRequestW(request, req->lpszHeader,
4950 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4951 req->dwContentLength, req->bEndRequest);
4953 heap_free(req->lpszHeader);
4957 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4961 DWORD res = ERROR_SUCCESS;
4963 if(!request->netconn) {
4964 WARN("Not connected\n");
4965 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
4966 return ERROR_INTERNET_OPERATION_CANCELLED;
4969 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4970 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4972 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4974 res = ERROR_HTTP_HEADER_NOT_FOUND;
4976 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4977 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4979 /* process cookies here. Is this right? */
4980 http_process_keep_alive(request);
4981 HTTP_ProcessCookies(request);
4982 HTTP_ProcessExpires(request);
4983 HTTP_ProcessLastModified(request);
4985 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
4986 if(!request->contentLength)
4987 http_release_netconn(request, TRUE);
4990 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4992 switch(request->status_code) {
4993 case HTTP_STATUS_REDIRECT:
4994 case HTTP_STATUS_MOVED:
4995 case HTTP_STATUS_REDIRECT_METHOD:
4996 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
4997 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4998 dwBufferSize=sizeof(szNewLocation);
4999 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5002 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5003 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5005 heap_free(request->verb);
5006 request->verb = heap_strdupW(szGET);
5008 http_release_netconn(request, drain_content(request, FALSE));
5009 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5011 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5012 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5013 res = HTTP_HandleRedirect(request, new_url);
5014 if (res == ERROR_SUCCESS)
5015 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5016 heap_free( new_url );
5022 if(res == ERROR_SUCCESS)
5023 create_cache_entry(request);
5025 if (res == ERROR_SUCCESS && request->contentLength)
5026 HTTP_ReceiveRequestData(request, TRUE);
5028 send_request_complete(request, res == ERROR_SUCCESS, res);
5033 /***********************************************************************
5034 * HttpEndRequestA (WININET.@)
5036 * Ends an HTTP request that was started by HttpSendRequestEx
5039 * TRUE if successful
5043 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5044 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5046 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5050 SetLastError(ERROR_INVALID_PARAMETER);
5054 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5057 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5059 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5060 http_request_t *request = (http_request_t*)work->hdr;
5062 TRACE("%p\n", request);
5064 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5067 /***********************************************************************
5068 * HttpEndRequestW (WININET.@)
5070 * Ends an HTTP request that was started by HttpSendRequestEx
5073 * TRUE if successful
5077 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5078 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5080 http_request_t *request;
5083 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5087 SetLastError(ERROR_INVALID_PARAMETER);
5091 request = (http_request_t*) get_handle_object( hRequest );
5093 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5095 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5097 WININET_Release( &request->hdr );
5100 request->hdr.dwFlags |= dwFlags;
5102 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5105 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5107 work.asyncproc = AsyncHttpEndRequestProc;
5108 work.hdr = WININET_AddRef( &request->hdr );
5110 work_endrequest = &work.u.HttpEndRequestW;
5111 work_endrequest->dwFlags = dwFlags;
5112 work_endrequest->dwContext = dwContext;
5114 INTERNET_AsyncCall(&work);
5115 res = ERROR_IO_PENDING;
5118 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5120 WININET_Release( &request->hdr );
5121 TRACE("%u <--\n", res);
5122 if(res != ERROR_SUCCESS)
5124 return res == ERROR_SUCCESS;
5127 /***********************************************************************
5128 * HttpSendRequestExA (WININET.@)
5130 * Sends the specified request to the HTTP server and allows chunked
5135 * Failure: FALSE, call GetLastError() for more information.
5137 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5138 LPINTERNET_BUFFERSA lpBuffersIn,
5139 LPINTERNET_BUFFERSA lpBuffersOut,
5140 DWORD dwFlags, DWORD_PTR dwContext)
5142 INTERNET_BUFFERSW BuffersInW;
5145 LPWSTR header = NULL;
5147 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5148 lpBuffersOut, dwFlags, dwContext);
5152 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5153 if (lpBuffersIn->lpcszHeader)
5155 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5156 lpBuffersIn->dwHeadersLength,0,0);
5157 header = heap_alloc(headerlen*sizeof(WCHAR));
5158 if (!(BuffersInW.lpcszHeader = header))
5160 SetLastError(ERROR_OUTOFMEMORY);
5163 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5164 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5168 BuffersInW.lpcszHeader = NULL;
5169 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5170 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5171 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5172 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5173 BuffersInW.Next = NULL;
5176 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5182 /***********************************************************************
5183 * HttpSendRequestExW (WININET.@)
5185 * Sends the specified request to the HTTP server and allows chunked
5190 * Failure: FALSE, call GetLastError() for more information.
5192 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5193 LPINTERNET_BUFFERSW lpBuffersIn,
5194 LPINTERNET_BUFFERSW lpBuffersOut,
5195 DWORD dwFlags, DWORD_PTR dwContext)
5197 http_request_t *request;
5198 http_session_t *session;
5202 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5203 lpBuffersOut, dwFlags, dwContext);
5205 request = (http_request_t*) get_handle_object( hRequest );
5207 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5209 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5213 session = request->session;
5214 assert(session->hdr.htype == WH_HHTTPSESSION);
5215 hIC = session->appInfo;
5216 assert(hIC->hdr.htype == WH_HINIT);
5218 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5220 WORKREQUEST workRequest;
5221 struct WORKREQ_HTTPSENDREQUESTW *req;
5223 workRequest.asyncproc = AsyncHttpSendRequestProc;
5224 workRequest.hdr = WININET_AddRef( &request->hdr );
5225 req = &workRequest.u.HttpSendRequestW;
5230 if (lpBuffersIn->lpcszHeader)
5232 if (lpBuffersIn->dwHeadersLength == ~0u)
5233 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5235 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5237 req->lpszHeader = heap_alloc(size);
5238 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5240 else req->lpszHeader = NULL;
5242 req->dwHeaderLength = size / sizeof(WCHAR);
5243 req->lpOptional = lpBuffersIn->lpvBuffer;
5244 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5245 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5249 req->lpszHeader = NULL;
5250 req->dwHeaderLength = 0;
5251 req->lpOptional = NULL;
5252 req->dwOptionalLength = 0;
5253 req->dwContentLength = 0;
5256 req->bEndRequest = FALSE;
5258 INTERNET_AsyncCall(&workRequest);
5260 * This is from windows.
5262 res = ERROR_IO_PENDING;
5267 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5268 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5269 lpBuffersIn->dwBufferTotal, FALSE);
5271 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5276 WININET_Release( &request->hdr );
5280 return res == ERROR_SUCCESS;
5283 /***********************************************************************
5284 * HttpSendRequestW (WININET.@)
5286 * Sends the specified request to the HTTP server
5293 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5294 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5296 http_request_t *request;
5297 http_session_t *session = NULL;
5298 appinfo_t *hIC = NULL;
5299 DWORD res = ERROR_SUCCESS;
5301 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5302 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5304 request = (http_request_t*) get_handle_object( hHttpRequest );
5305 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5307 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5311 session = request->session;
5312 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5314 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5318 hIC = session->appInfo;
5319 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5321 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5325 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5327 WORKREQUEST workRequest;
5328 struct WORKREQ_HTTPSENDREQUESTW *req;
5330 workRequest.asyncproc = AsyncHttpSendRequestProc;
5331 workRequest.hdr = WININET_AddRef( &request->hdr );
5332 req = &workRequest.u.HttpSendRequestW;
5337 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5338 else size = dwHeaderLength * sizeof(WCHAR);
5340 req->lpszHeader = heap_alloc(size);
5341 memcpy(req->lpszHeader, lpszHeaders, size);
5344 req->lpszHeader = 0;
5345 req->dwHeaderLength = dwHeaderLength;
5346 req->lpOptional = lpOptional;
5347 req->dwOptionalLength = dwOptionalLength;
5348 req->dwContentLength = dwOptionalLength;
5349 req->bEndRequest = TRUE;
5351 INTERNET_AsyncCall(&workRequest);
5353 * This is from windows.
5355 res = ERROR_IO_PENDING;
5359 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5360 dwHeaderLength, lpOptional, dwOptionalLength,
5361 dwOptionalLength, TRUE);
5365 WININET_Release( &request->hdr );
5368 return res == ERROR_SUCCESS;
5371 /***********************************************************************
5372 * HttpSendRequestA (WININET.@)
5374 * Sends the specified request to the HTTP server
5381 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5382 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5385 LPWSTR szHeaders=NULL;
5386 DWORD nLen=dwHeaderLength;
5387 if(lpszHeaders!=NULL)
5389 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5390 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5391 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5393 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5394 heap_free(szHeaders);
5398 /***********************************************************************
5399 * HTTPSESSION_Destroy (internal)
5401 * Deallocate session handle
5404 static void HTTPSESSION_Destroy(object_header_t *hdr)
5406 http_session_t *session = (http_session_t*) hdr;
5408 TRACE("%p\n", session);
5410 WININET_Release(&session->appInfo->hdr);
5412 heap_free(session->hostName);
5413 heap_free(session->password);
5414 heap_free(session->userName);
5417 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5419 http_session_t *ses = (http_session_t *)hdr;
5422 case INTERNET_OPTION_HANDLE_TYPE:
5423 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5425 if (*size < sizeof(ULONG))
5426 return ERROR_INSUFFICIENT_BUFFER;
5428 *size = sizeof(DWORD);
5429 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5430 return ERROR_SUCCESS;
5431 case INTERNET_OPTION_CONNECT_TIMEOUT:
5432 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5434 if (*size < sizeof(DWORD))
5435 return ERROR_INSUFFICIENT_BUFFER;
5437 *size = sizeof(DWORD);
5438 *(DWORD *)buffer = ses->connect_timeout;
5439 return ERROR_SUCCESS;
5441 case INTERNET_OPTION_SEND_TIMEOUT:
5442 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5444 if (*size < sizeof(DWORD))
5445 return ERROR_INSUFFICIENT_BUFFER;
5447 *size = sizeof(DWORD);
5448 *(DWORD *)buffer = ses->send_timeout;
5449 return ERROR_SUCCESS;
5451 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5452 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5454 if (*size < sizeof(DWORD))
5455 return ERROR_INSUFFICIENT_BUFFER;
5457 *size = sizeof(DWORD);
5458 *(DWORD *)buffer = ses->receive_timeout;
5459 return ERROR_SUCCESS;
5462 return INET_QueryOption(hdr, option, buffer, size, unicode);
5465 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5467 http_session_t *ses = (http_session_t*)hdr;
5470 case INTERNET_OPTION_USERNAME:
5472 heap_free(ses->userName);
5473 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5474 return ERROR_SUCCESS;
5476 case INTERNET_OPTION_PASSWORD:
5478 heap_free(ses->password);
5479 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5480 return ERROR_SUCCESS;
5482 case INTERNET_OPTION_CONNECT_TIMEOUT:
5484 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5485 ses->connect_timeout = *(DWORD *)buffer;
5486 return ERROR_SUCCESS;
5488 case INTERNET_OPTION_SEND_TIMEOUT:
5490 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5491 ses->send_timeout = *(DWORD *)buffer;
5492 return ERROR_SUCCESS;
5494 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5496 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5497 ses->receive_timeout = *(DWORD *)buffer;
5498 return ERROR_SUCCESS;
5503 return INET_SetOption(hdr, option, buffer, size);
5506 static const object_vtbl_t HTTPSESSIONVtbl = {
5507 HTTPSESSION_Destroy,
5509 HTTPSESSION_QueryOption,
5510 HTTPSESSION_SetOption,
5519 /***********************************************************************
5520 * HTTP_Connect (internal)
5522 * Create http session handle
5525 * HINTERNET a session handle on success
5529 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5530 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5531 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5532 DWORD dwInternalFlags, HINTERNET *ret)
5534 http_session_t *session = NULL;
5538 if (!lpszServerName || !lpszServerName[0])
5539 return ERROR_INVALID_PARAMETER;
5541 assert( hIC->hdr.htype == WH_HINIT );
5543 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5545 return ERROR_OUTOFMEMORY;
5548 * According to my tests. The name is not resolved until a request is sent
5551 session->hdr.htype = WH_HHTTPSESSION;
5552 session->hdr.dwFlags = dwFlags;
5553 session->hdr.dwContext = dwContext;
5554 session->hdr.dwInternalFlags |= dwInternalFlags;
5556 WININET_AddRef( &hIC->hdr );
5557 session->appInfo = hIC;
5558 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5560 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5561 if(hIC->proxyBypass)
5562 FIXME("Proxy bypass is ignored.\n");
5564 session->hostName = heap_strdupW(lpszServerName);
5565 if (lpszUserName && lpszUserName[0])
5566 session->userName = heap_strdupW(lpszUserName);
5567 if (lpszPassword && lpszPassword[0])
5568 session->password = heap_strdupW(lpszPassword);
5569 session->hostPort = serverPort;
5570 session->connect_timeout = hIC->connect_timeout;
5571 session->send_timeout = INFINITE;
5572 session->receive_timeout = INFINITE;
5574 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5575 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5577 INTERNET_SendCallback(&hIC->hdr, dwContext,
5578 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5583 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5587 TRACE("%p --> %p\n", hIC, session);
5589 *ret = session->hdr.hInternet;
5590 return ERROR_SUCCESS;
5593 /***********************************************************************
5594 * HTTP_clear_response_headers (internal)
5596 * clear out any old response headers
5598 static void HTTP_clear_response_headers( http_request_t *request )
5602 for( i=0; i<request->nCustHeaders; i++)
5604 if( !request->custHeaders[i].lpszField )
5606 if( !request->custHeaders[i].lpszValue )
5608 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5610 HTTP_DeleteCustomHeader( request, i );
5615 /***********************************************************************
5616 * HTTP_GetResponseHeaders (internal)
5618 * Read server response
5625 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5628 WCHAR buffer[MAX_REPLY_LEN];
5629 DWORD buflen = MAX_REPLY_LEN;
5630 BOOL bSuccess = FALSE;
5632 char bufferA[MAX_REPLY_LEN];
5633 LPWSTR status_code = NULL, status_text = NULL;
5634 DWORD cchMaxRawHeaders = 1024;
5635 LPWSTR lpszRawHeaders = NULL;
5637 DWORD cchRawHeaders = 0;
5638 BOOL codeHundred = FALSE;
5642 if(!request->netconn)
5645 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5647 static const WCHAR szHundred[] = {'1','0','0',0};
5649 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5651 buflen = MAX_REPLY_LEN;
5652 if (!read_line(request, bufferA, &buflen))
5655 /* clear old response headers (eg. from a redirect response) */
5657 HTTP_clear_response_headers( request );
5662 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5663 /* check is this a status code line? */
5664 if (!strncmpW(buffer, g_szHttp1_0, 4))
5666 /* split the version from the status code */
5667 status_code = strchrW( buffer, ' ' );
5672 /* split the status code from the status text */
5673 status_text = strchrW( status_code, ' ' );
5678 request->status_code = atoiW(status_code);
5680 TRACE("version [%s] status code [%s] status text [%s]\n",
5681 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5683 codeHundred = (!strcmpW(status_code, szHundred));
5685 else if (!codeHundred)
5687 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5689 heap_free(request->version);
5690 heap_free(request->statusText);
5692 request->status_code = HTTP_STATUS_OK;
5693 request->version = heap_strdupW(g_szHttp1_0);
5694 request->statusText = heap_strdupW(szOK);
5696 heap_free(request->rawHeaders);
5697 request->rawHeaders = heap_strdupW(szDefaultHeader);
5702 } while (codeHundred);
5704 /* Add status code */
5705 HTTP_ProcessHeader(request, szStatus, status_code,
5706 HTTP_ADDHDR_FLAG_REPLACE);
5708 heap_free(request->version);
5709 heap_free(request->statusText);
5711 request->version = heap_strdupW(buffer);
5712 request->statusText = heap_strdupW(status_text);
5714 /* Restore the spaces */
5715 *(status_code-1) = ' ';
5716 *(status_text-1) = ' ';
5718 /* regenerate raw headers */
5719 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5720 if (!lpszRawHeaders) goto lend;
5722 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5723 cchMaxRawHeaders *= 2;
5724 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5725 if (temp == NULL) goto lend;
5726 lpszRawHeaders = temp;
5727 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5728 cchRawHeaders += (buflen-1);
5729 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5730 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5731 lpszRawHeaders[cchRawHeaders] = '\0';
5733 /* Parse each response line */
5736 buflen = MAX_REPLY_LEN;
5737 if (read_line(request, bufferA, &buflen))
5739 LPWSTR * pFieldAndValue;
5741 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5743 if (!bufferA[0]) break;
5744 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5746 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5749 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5750 cchMaxRawHeaders *= 2;
5751 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5752 if (temp == NULL) goto lend;
5753 lpszRawHeaders = temp;
5754 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5755 cchRawHeaders += (buflen-1);
5756 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5757 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5758 lpszRawHeaders[cchRawHeaders] = '\0';
5760 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5761 HTTP_ADDREQ_FLAG_ADD );
5763 HTTP_FreeTokens(pFieldAndValue);
5774 /* make sure the response header is terminated with an empty line. Some apps really
5775 truly care about that empty line being there for some reason. Just add it to the
5777 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5779 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5780 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5781 if (temp == NULL) goto lend;
5782 lpszRawHeaders = temp;
5785 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5787 heap_free(request->rawHeaders);
5788 request->rawHeaders = lpszRawHeaders;
5789 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5799 heap_free(lpszRawHeaders);
5804 /***********************************************************************
5805 * HTTP_InterpretHttpHeader (internal)
5807 * Parse server response
5811 * Pointer to array of field, value, NULL on success.
5814 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5816 LPWSTR * pTokenPair;
5820 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5822 pszColon = strchrW(buffer, ':');
5823 /* must have two tokens */
5826 HTTP_FreeTokens(pTokenPair);
5828 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5832 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5835 HTTP_FreeTokens(pTokenPair);
5838 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5839 pTokenPair[0][pszColon - buffer] = '\0';
5843 len = strlenW(pszColon);
5844 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5847 HTTP_FreeTokens(pTokenPair);
5850 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5852 strip_spaces(pTokenPair[0]);
5853 strip_spaces(pTokenPair[1]);
5855 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5859 /***********************************************************************
5860 * HTTP_ProcessHeader (internal)
5862 * Stuff header into header tables according to <dwModifier>
5866 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5868 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5870 LPHTTPHEADERW lphttpHdr = NULL;
5872 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5873 DWORD res = ERROR_HTTP_INVALID_HEADER;
5875 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5877 /* REPLACE wins out over ADD */
5878 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5879 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5881 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5884 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5888 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5889 return ERROR_HTTP_INVALID_HEADER;
5890 lphttpHdr = &request->custHeaders[index];
5896 hdr.lpszField = (LPWSTR)field;
5897 hdr.lpszValue = (LPWSTR)value;
5898 hdr.wFlags = hdr.wCount = 0;
5900 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5901 hdr.wFlags |= HDR_ISREQUEST;
5903 return HTTP_InsertCustomHeader(request, &hdr);
5905 /* no value to delete */
5906 else return ERROR_SUCCESS;
5908 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5909 lphttpHdr->wFlags |= HDR_ISREQUEST;
5911 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5913 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5915 HTTP_DeleteCustomHeader( request, index );
5921 hdr.lpszField = (LPWSTR)field;
5922 hdr.lpszValue = (LPWSTR)value;
5923 hdr.wFlags = hdr.wCount = 0;
5925 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5926 hdr.wFlags |= HDR_ISREQUEST;
5928 return HTTP_InsertCustomHeader(request, &hdr);
5931 return ERROR_SUCCESS;
5933 else if (dwModifier & COALESCEFLAGS)
5938 INT origlen = strlenW(lphttpHdr->lpszValue);
5939 INT valuelen = strlenW(value);
5941 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5944 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5946 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5949 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5952 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5954 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5957 lphttpHdr->lpszValue = lpsztmp;
5958 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5961 lphttpHdr->lpszValue[origlen] = ch;
5963 lphttpHdr->lpszValue[origlen] = ' ';
5967 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5968 lphttpHdr->lpszValue[len] = '\0';
5969 res = ERROR_SUCCESS;
5973 WARN("heap_realloc (%d bytes) failed\n",len+1);
5974 res = ERROR_OUTOFMEMORY;
5977 TRACE("<-- %d\n", res);
5981 /***********************************************************************
5982 * HTTP_GetCustomHeaderIndex (internal)
5984 * Return index of custom header from header array
5987 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5988 int requested_index, BOOL request_only)
5992 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5994 for (index = 0; index < request->nCustHeaders; index++)
5996 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5999 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6002 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6005 if (requested_index == 0)
6010 if (index >= request->nCustHeaders)
6013 TRACE("Return: %d\n", index);
6018 /***********************************************************************
6019 * HTTP_InsertCustomHeader (internal)
6021 * Insert header into array
6024 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6027 LPHTTPHEADERW lph = NULL;
6029 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6030 count = request->nCustHeaders + 1;
6032 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6034 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6037 return ERROR_OUTOFMEMORY;
6039 request->custHeaders = lph;
6040 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6041 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6042 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6043 request->custHeaders[count-1].wCount= lpHdr->wCount;
6044 request->nCustHeaders++;
6046 return ERROR_SUCCESS;
6050 /***********************************************************************
6051 * HTTP_DeleteCustomHeader (internal)
6053 * Delete header from array
6054 * If this function is called, the indexs may change.
6056 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6058 if( request->nCustHeaders <= 0 )
6060 if( index >= request->nCustHeaders )
6062 request->nCustHeaders--;
6064 heap_free(request->custHeaders[index].lpszField);
6065 heap_free(request->custHeaders[index].lpszValue);
6067 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6068 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6069 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6075 /***********************************************************************
6076 * HTTP_VerifyValidHeader (internal)
6078 * Verify the given header is not invalid for the given http request
6081 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6083 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6084 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6085 return ERROR_HTTP_INVALID_HEADER;
6087 return ERROR_SUCCESS;
6090 /***********************************************************************
6091 * IsHostInProxyBypassList (@)
6096 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6098 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6102 /***********************************************************************
6103 * InternetShowSecurityInfoByURLA (@)
6105 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6107 FIXME("stub: %s %p\n", url, window);
6111 /***********************************************************************
6112 * InternetShowSecurityInfoByURLW (@)
6114 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6116 FIXME("stub: %s %p\n", debugstr_w(url), window);
6120 /***********************************************************************
6121 * ShowX509EncodedCertificate (@)
6123 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6125 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6131 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6133 memset(&view, 0, sizeof(view));
6134 view.hwndParent = parent;
6135 view.pCertContext = certContext;
6136 if (CryptUIDlgViewCertificateW(&view, NULL))
6137 ret = ERROR_SUCCESS;
6139 ret = GetLastError();
6140 CertFreeCertificateContext(certContext);
6143 ret = GetLastError();