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_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2882 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2883 http_request_t *req = (http_request_t*)workRequest->hdr;
2886 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2888 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2889 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2891 send_request_complete(req, res == ERROR_SUCCESS, res);
2894 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2895 DWORD flags, DWORD_PTR context)
2897 http_request_t *req = (http_request_t*)hdr;
2898 DWORD res, size, read, 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 if (buffers->dwStructSize != sizeof(*buffers))
2904 return ERROR_INVALID_PARAMETER;
2906 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2908 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2910 WORKREQUEST workRequest;
2912 if (TryEnterCriticalSection( &req->read_section ))
2914 if (get_avail_data(req))
2916 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2917 &buffers->dwBufferLength, FALSE);
2918 size = buffers->dwBufferLength;
2919 LeaveCriticalSection( &req->read_section );
2922 LeaveCriticalSection( &req->read_section );
2925 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2926 workRequest.hdr = WININET_AddRef(&req->hdr);
2927 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2929 INTERNET_AsyncCall(&workRequest);
2931 return ERROR_IO_PENDING;
2935 size = buffers->dwBufferLength;
2937 EnterCriticalSection( &req->read_section );
2938 if(hdr->dwError == ERROR_SUCCESS)
2939 hdr->dwError = INTERNET_HANDLE_IN_USE;
2940 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2941 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2944 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2945 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2946 if(res != ERROR_SUCCESS)
2949 read += buffers->dwBufferLength;
2950 if(read == size || end_of_read_data(req))
2953 LeaveCriticalSection( &req->read_section );
2955 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2956 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2957 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2958 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2960 EnterCriticalSection( &req->read_section );
2963 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2964 hdr->dwError = ERROR_SUCCESS;
2966 error = hdr->dwError;
2968 LeaveCriticalSection( &req->read_section );
2969 size = buffers->dwBufferLength;
2970 buffers->dwBufferLength = read;
2973 if (res == ERROR_SUCCESS) {
2974 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2975 &size, sizeof(size));
2978 return res==ERROR_SUCCESS ? error : res;
2981 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2983 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2984 http_request_t *req = (http_request_t*)workRequest->hdr;
2987 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2989 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2990 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2992 send_request_complete(req, res == ERROR_SUCCESS, res);
2995 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2996 DWORD flags, DWORD_PTR context)
2999 http_request_t *req = (http_request_t*)hdr;
3000 DWORD res, size, read, error = ERROR_SUCCESS;
3002 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3003 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3005 if (buffers->dwStructSize != sizeof(*buffers))
3006 return ERROR_INVALID_PARAMETER;
3008 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3010 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
3012 WORKREQUEST workRequest;
3014 if (TryEnterCriticalSection( &req->read_section ))
3016 if (get_avail_data(req))
3018 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
3019 &buffers->dwBufferLength, FALSE);
3020 size = buffers->dwBufferLength;
3021 LeaveCriticalSection( &req->read_section );
3024 LeaveCriticalSection( &req->read_section );
3027 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
3028 workRequest.hdr = WININET_AddRef(&req->hdr);
3029 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
3031 INTERNET_AsyncCall(&workRequest);
3033 return ERROR_IO_PENDING;
3037 size = buffers->dwBufferLength;
3039 EnterCriticalSection( &req->read_section );
3040 if(hdr->dwError == ERROR_SUCCESS)
3041 hdr->dwError = INTERNET_HANDLE_IN_USE;
3042 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3043 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3046 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
3047 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
3048 if(res != ERROR_SUCCESS)
3051 read += buffers->dwBufferLength;
3052 if(read == size || end_of_read_data(req))
3055 LeaveCriticalSection( &req->read_section );
3057 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3058 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
3059 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3060 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3062 EnterCriticalSection( &req->read_section );
3065 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3066 hdr->dwError = ERROR_SUCCESS;
3068 error = hdr->dwError;
3070 LeaveCriticalSection( &req->read_section );
3071 size = buffers->dwBufferLength;
3072 buffers->dwBufferLength = read;
3075 if (res == ERROR_SUCCESS) {
3076 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3077 &size, sizeof(size));
3080 return res==ERROR_SUCCESS ? error : res;
3083 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3086 http_request_t *request = (http_request_t*)hdr;
3088 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3091 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3092 if (res == ERROR_SUCCESS)
3093 request->bytesWritten += *written;
3095 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3099 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
3101 http_request_t *req = (http_request_t*)workRequest->hdr;
3103 HTTP_ReceiveRequestData(req, FALSE);
3106 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3108 http_request_t *req = (http_request_t*)hdr;
3110 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3112 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3114 WORKREQUEST workRequest;
3116 /* never wait, if we can't enter the section we queue an async request right away */
3117 if (TryEnterCriticalSection( &req->read_section ))
3119 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3120 if ((*available = get_avail_data( req ))) goto done;
3121 if (end_of_read_data( req )) goto done;
3122 LeaveCriticalSection( &req->read_section );
3125 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3126 workRequest.hdr = WININET_AddRef( &req->hdr );
3128 INTERNET_AsyncCall(&workRequest);
3130 return ERROR_IO_PENDING;
3133 EnterCriticalSection( &req->read_section );
3135 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3137 refill_read_buffer( req, READMODE_ASYNC, NULL );
3138 *available = get_avail_data( req );
3142 LeaveCriticalSection( &req->read_section );
3144 TRACE( "returning %u\n", *available );
3145 return ERROR_SUCCESS;
3148 static const object_vtbl_t HTTPREQVtbl = {
3150 HTTPREQ_CloseConnection,
3151 HTTPREQ_QueryOption,
3154 HTTPREQ_ReadFileExA,
3155 HTTPREQ_ReadFileExW,
3157 HTTPREQ_QueryDataAvailable,
3161 /***********************************************************************
3162 * HTTP_HttpOpenRequestW (internal)
3164 * Open a HTTP request handle
3167 * HINTERNET a HTTP request handle on success
3171 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3172 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3173 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3174 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3176 appinfo_t *hIC = session->appInfo;
3177 http_request_t *request;
3178 DWORD len, res = ERROR_SUCCESS;
3182 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3184 return ERROR_OUTOFMEMORY;
3186 request->hdr.htype = WH_HHTTPREQ;
3187 request->hdr.dwFlags = dwFlags;
3188 request->hdr.dwContext = dwContext;
3189 request->contentLength = ~0u;
3191 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3192 request->data_stream = &request->netconn_stream.data_stream;
3193 request->connect_timeout = session->connect_timeout;
3194 request->send_timeout = session->send_timeout;
3195 request->receive_timeout = session->receive_timeout;
3197 InitializeCriticalSection( &request->read_section );
3198 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3200 WININET_AddRef( &session->hdr );
3201 request->session = session;
3202 list_add_head( &session->hdr.children, &request->hdr.entry );
3204 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3205 if(!request->server) {
3206 WININET_Release(&request->hdr);
3207 return ERROR_OUTOFMEMORY;
3210 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3211 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3212 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3213 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3215 if (lpszObjectName && *lpszObjectName) {
3219 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3220 if (rc != E_POINTER)
3221 len = strlenW(lpszObjectName)+1;
3222 request->path = heap_alloc(len*sizeof(WCHAR));
3223 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3224 URL_ESCAPE_SPACES_ONLY);
3227 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3228 strcpyW(request->path,lpszObjectName);
3231 static const WCHAR slashW[] = {'/',0};
3233 request->path = heap_strdupW(slashW);
3236 if (lpszReferrer && *lpszReferrer)
3237 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3239 if (lpszAcceptTypes)
3242 for (i = 0; lpszAcceptTypes[i]; i++)
3244 if (!*lpszAcceptTypes[i]) continue;
3245 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3246 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3247 HTTP_ADDHDR_FLAG_REQ |
3248 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3252 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3253 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3255 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3257 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3258 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3259 INTERNET_DEFAULT_HTTPS_PORT :
3260 INTERNET_DEFAULT_HTTP_PORT);
3262 if (hIC->proxy && hIC->proxy[0])
3263 HTTP_DealWithProxy( hIC, session, request );
3265 INTERNET_SendCallback(&session->hdr, dwContext,
3266 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3269 TRACE("<-- %u (%p)\n", res, request);
3271 if(res != ERROR_SUCCESS) {
3272 WININET_Release( &request->hdr );
3277 *ret = request->hdr.hInternet;
3278 return ERROR_SUCCESS;
3281 /***********************************************************************
3282 * HttpOpenRequestW (WININET.@)
3284 * Open a HTTP request handle
3287 * HINTERNET a HTTP request handle on success
3291 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3292 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3293 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3294 DWORD dwFlags, DWORD_PTR dwContext)
3296 http_session_t *session;
3297 HINTERNET handle = NULL;
3300 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3301 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3302 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3303 dwFlags, dwContext);
3304 if(lpszAcceptTypes!=NULL)
3307 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3308 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3311 session = (http_session_t*) get_handle_object( hHttpSession );
3312 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3314 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3319 * My tests seem to show that the windows version does not
3320 * become asynchronous until after this point. And anyhow
3321 * if this call was asynchronous then how would you get the
3322 * necessary HINTERNET pointer returned by this function.
3325 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3326 lpszVersion, lpszReferrer, lpszAcceptTypes,
3327 dwFlags, dwContext, &handle);
3330 WININET_Release( &session->hdr );
3331 TRACE("returning %p\n", handle);
3332 if(res != ERROR_SUCCESS)
3337 static const LPCWSTR header_lookup[] = {
3338 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3339 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3340 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3341 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3342 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3343 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3344 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3345 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3346 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3347 szDate, /* HTTP_QUERY_DATE = 9 */
3348 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3349 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3350 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3351 szURI, /* HTTP_QUERY_URI = 13 */
3352 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3353 NULL, /* HTTP_QUERY_COST = 15 */
3354 NULL, /* HTTP_QUERY_LINK = 16 */
3355 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3356 NULL, /* HTTP_QUERY_VERSION = 18 */
3357 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3358 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3359 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3360 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3361 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3362 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3363 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3364 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3365 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3366 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3367 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3368 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3369 NULL, /* HTTP_QUERY_FROM = 31 */
3370 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3371 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3372 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3373 szReferer, /* HTTP_QUERY_REFERER = 35 */
3374 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3375 szServer, /* HTTP_QUERY_SERVER = 37 */
3376 NULL, /* HTTP_TITLE = 38 */
3377 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3378 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3379 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3380 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3381 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3382 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3383 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3384 NULL, /* HTTP_QUERY_REFRESH = 46 */
3385 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3386 szAge, /* HTTP_QUERY_AGE = 48 */
3387 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3388 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3389 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3390 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3391 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3392 szETag, /* HTTP_QUERY_ETAG = 54 */
3393 hostW, /* HTTP_QUERY_HOST = 55 */
3394 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3395 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3396 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3397 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3398 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3399 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3400 szRange, /* HTTP_QUERY_RANGE = 62 */
3401 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3402 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3403 szVary, /* HTTP_QUERY_VARY = 65 */
3404 szVia, /* HTTP_QUERY_VIA = 66 */
3405 szWarning, /* HTTP_QUERY_WARNING = 67 */
3406 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3407 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3408 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3411 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3413 /***********************************************************************
3414 * HTTP_HttpQueryInfoW (internal)
3416 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3417 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3419 LPHTTPHEADERW lphttpHdr = NULL;
3420 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3421 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3422 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3425 /* Find requested header structure */
3428 case HTTP_QUERY_CUSTOM:
3429 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3430 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3432 case HTTP_QUERY_RAW_HEADERS_CRLF:
3436 DWORD res = ERROR_INVALID_PARAMETER;
3439 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3441 headers = request->rawHeaders;
3444 len = strlenW(headers) * sizeof(WCHAR);
3446 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3448 len += sizeof(WCHAR);
3449 res = ERROR_INSUFFICIENT_BUFFER;
3454 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3457 len = strlenW(szCrLf) * sizeof(WCHAR);
3458 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3460 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3461 res = ERROR_SUCCESS;
3463 *lpdwBufferLength = len;
3465 if (request_only) heap_free(headers);
3468 case HTTP_QUERY_RAW_HEADERS:
3470 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3472 LPWSTR pszString = lpBuffer;
3474 for (i = 0; ppszRawHeaderLines[i]; i++)
3475 size += strlenW(ppszRawHeaderLines[i]) + 1;
3477 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3479 HTTP_FreeTokens(ppszRawHeaderLines);
3480 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3481 return ERROR_INSUFFICIENT_BUFFER;
3485 for (i = 0; ppszRawHeaderLines[i]; i++)
3487 DWORD len = strlenW(ppszRawHeaderLines[i]);
3488 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3492 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3494 *lpdwBufferLength = size * sizeof(WCHAR);
3495 HTTP_FreeTokens(ppszRawHeaderLines);
3497 return ERROR_SUCCESS;
3499 case HTTP_QUERY_STATUS_TEXT:
3500 if (request->statusText)
3502 DWORD len = strlenW(request->statusText);
3503 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3505 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3506 return ERROR_INSUFFICIENT_BUFFER;
3510 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3511 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3513 *lpdwBufferLength = len * sizeof(WCHAR);
3514 return ERROR_SUCCESS;
3517 case HTTP_QUERY_VERSION:
3518 if (request->version)
3520 DWORD len = strlenW(request->version);
3521 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3523 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3524 return ERROR_INSUFFICIENT_BUFFER;
3528 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3529 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3531 *lpdwBufferLength = len * sizeof(WCHAR);
3532 return ERROR_SUCCESS;
3535 case HTTP_QUERY_CONTENT_ENCODING:
3536 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3537 requested_index,request_only);
3539 case HTTP_QUERY_STATUS_CODE: {
3540 DWORD res = ERROR_SUCCESS;
3543 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3548 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3549 if(*lpdwBufferLength >= sizeof(DWORD))
3550 *(DWORD*)lpBuffer = request->status_code;
3552 res = ERROR_INSUFFICIENT_BUFFER;
3553 *lpdwBufferLength = sizeof(DWORD);
3557 static const WCHAR formatW[] = {'%','u',0};
3559 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3561 if(size <= *lpdwBufferLength) {
3562 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3564 size += sizeof(WCHAR);
3565 res = ERROR_INSUFFICIENT_BUFFER;
3568 *lpdwBufferLength = size;
3573 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3575 if (level < LAST_TABLE_HEADER && header_lookup[level])
3576 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3577 requested_index,request_only);
3581 lphttpHdr = &request->custHeaders[index];
3583 /* Ensure header satisfies requested attributes */
3585 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3586 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3588 return ERROR_HTTP_HEADER_NOT_FOUND;
3591 if (lpdwIndex) (*lpdwIndex)++;
3593 /* coalesce value to requested type */
3594 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3596 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3597 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3599 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3605 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3607 tmpTM = *gmtime(&tmpTime);
3608 STHook = (SYSTEMTIME *)lpBuffer;
3609 STHook->wDay = tmpTM.tm_mday;
3610 STHook->wHour = tmpTM.tm_hour;
3611 STHook->wMilliseconds = 0;
3612 STHook->wMinute = tmpTM.tm_min;
3613 STHook->wDayOfWeek = tmpTM.tm_wday;
3614 STHook->wMonth = tmpTM.tm_mon + 1;
3615 STHook->wSecond = tmpTM.tm_sec;
3616 STHook->wYear = tmpTM.tm_year;
3618 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3619 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3620 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3622 else if (lphttpHdr->lpszValue)
3624 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3626 if (len > *lpdwBufferLength)
3628 *lpdwBufferLength = len;
3629 return ERROR_INSUFFICIENT_BUFFER;
3633 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3634 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3636 *lpdwBufferLength = len - sizeof(WCHAR);
3638 return ERROR_SUCCESS;
3641 /***********************************************************************
3642 * HttpQueryInfoW (WININET.@)
3644 * Queries for information about an HTTP request
3651 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3652 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3654 http_request_t *request;
3657 if (TRACE_ON(wininet)) {
3658 #define FE(x) { x, #x }
3659 static const wininet_flag_info query_flags[] = {
3660 FE(HTTP_QUERY_MIME_VERSION),
3661 FE(HTTP_QUERY_CONTENT_TYPE),
3662 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3663 FE(HTTP_QUERY_CONTENT_ID),
3664 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3665 FE(HTTP_QUERY_CONTENT_LENGTH),
3666 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3667 FE(HTTP_QUERY_ALLOW),
3668 FE(HTTP_QUERY_PUBLIC),
3669 FE(HTTP_QUERY_DATE),
3670 FE(HTTP_QUERY_EXPIRES),
3671 FE(HTTP_QUERY_LAST_MODIFIED),
3672 FE(HTTP_QUERY_MESSAGE_ID),
3674 FE(HTTP_QUERY_DERIVED_FROM),
3675 FE(HTTP_QUERY_COST),
3676 FE(HTTP_QUERY_LINK),
3677 FE(HTTP_QUERY_PRAGMA),
3678 FE(HTTP_QUERY_VERSION),
3679 FE(HTTP_QUERY_STATUS_CODE),
3680 FE(HTTP_QUERY_STATUS_TEXT),
3681 FE(HTTP_QUERY_RAW_HEADERS),
3682 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3683 FE(HTTP_QUERY_CONNECTION),
3684 FE(HTTP_QUERY_ACCEPT),
3685 FE(HTTP_QUERY_ACCEPT_CHARSET),
3686 FE(HTTP_QUERY_ACCEPT_ENCODING),
3687 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3688 FE(HTTP_QUERY_AUTHORIZATION),
3689 FE(HTTP_QUERY_CONTENT_ENCODING),
3690 FE(HTTP_QUERY_FORWARDED),
3691 FE(HTTP_QUERY_FROM),
3692 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3693 FE(HTTP_QUERY_LOCATION),
3694 FE(HTTP_QUERY_ORIG_URI),
3695 FE(HTTP_QUERY_REFERER),
3696 FE(HTTP_QUERY_RETRY_AFTER),
3697 FE(HTTP_QUERY_SERVER),
3698 FE(HTTP_QUERY_TITLE),
3699 FE(HTTP_QUERY_USER_AGENT),
3700 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3701 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3702 FE(HTTP_QUERY_ACCEPT_RANGES),
3703 FE(HTTP_QUERY_SET_COOKIE),
3704 FE(HTTP_QUERY_COOKIE),
3705 FE(HTTP_QUERY_REQUEST_METHOD),
3706 FE(HTTP_QUERY_REFRESH),
3707 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3709 FE(HTTP_QUERY_CACHE_CONTROL),
3710 FE(HTTP_QUERY_CONTENT_BASE),
3711 FE(HTTP_QUERY_CONTENT_LOCATION),
3712 FE(HTTP_QUERY_CONTENT_MD5),
3713 FE(HTTP_QUERY_CONTENT_RANGE),
3714 FE(HTTP_QUERY_ETAG),
3715 FE(HTTP_QUERY_HOST),
3716 FE(HTTP_QUERY_IF_MATCH),
3717 FE(HTTP_QUERY_IF_NONE_MATCH),
3718 FE(HTTP_QUERY_IF_RANGE),
3719 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3720 FE(HTTP_QUERY_MAX_FORWARDS),
3721 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3722 FE(HTTP_QUERY_RANGE),
3723 FE(HTTP_QUERY_TRANSFER_ENCODING),
3724 FE(HTTP_QUERY_UPGRADE),
3725 FE(HTTP_QUERY_VARY),
3727 FE(HTTP_QUERY_WARNING),
3728 FE(HTTP_QUERY_CUSTOM)
3730 static const wininet_flag_info modifier_flags[] = {
3731 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3732 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3733 FE(HTTP_QUERY_FLAG_NUMBER),
3734 FE(HTTP_QUERY_FLAG_COALESCE)
3737 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3738 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3741 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3742 TRACE(" Attribute:");
3743 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3744 if (query_flags[i].val == info) {
3745 TRACE(" %s", query_flags[i].name);
3749 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3750 TRACE(" Unknown (%08x)", info);
3753 TRACE(" Modifier:");
3754 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3755 if (modifier_flags[i].val & info_mod) {
3756 TRACE(" %s", modifier_flags[i].name);
3757 info_mod &= ~ modifier_flags[i].val;
3762 TRACE(" Unknown (%08x)", info_mod);
3767 request = (http_request_t*) get_handle_object( hHttpRequest );
3768 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3770 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3774 if (lpBuffer == NULL)
3775 *lpdwBufferLength = 0;
3776 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3777 lpBuffer, lpdwBufferLength, lpdwIndex);
3781 WININET_Release( &request->hdr );
3783 TRACE("%u <--\n", res);
3784 if(res != ERROR_SUCCESS)
3786 return res == ERROR_SUCCESS;
3789 /***********************************************************************
3790 * HttpQueryInfoA (WININET.@)
3792 * Queries for information about an HTTP request
3799 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3800 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3806 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3807 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3809 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3810 lpdwBufferLength, lpdwIndex );
3816 len = (*lpdwBufferLength)*sizeof(WCHAR);
3817 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3819 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3825 bufferW = heap_alloc(alloclen);
3826 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3827 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3828 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3835 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3839 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3840 lpBuffer, *lpdwBufferLength, NULL, NULL );
3841 *lpdwBufferLength = len - 1;
3843 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3846 /* since the strings being returned from HttpQueryInfoW should be
3847 * only ASCII characters, it is reasonable to assume that all of
3848 * the Unicode characters can be reduced to a single byte */
3849 *lpdwBufferLength = len / sizeof(WCHAR);
3851 heap_free( bufferW );
3855 /***********************************************************************
3856 * HTTP_GetRedirectURL (internal)
3858 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3860 static WCHAR szHttp[] = {'h','t','t','p',0};
3861 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3862 http_session_t *session = request->session;
3863 URL_COMPONENTSW urlComponents;
3864 DWORD url_length = 0;
3866 LPWSTR combined_url;
3868 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3869 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3870 urlComponents.dwSchemeLength = 0;
3871 urlComponents.lpszHostName = session->hostName;
3872 urlComponents.dwHostNameLength = 0;
3873 urlComponents.nPort = session->hostPort;
3874 urlComponents.lpszUserName = session->userName;
3875 urlComponents.dwUserNameLength = 0;
3876 urlComponents.lpszPassword = NULL;
3877 urlComponents.dwPasswordLength = 0;
3878 urlComponents.lpszUrlPath = request->path;
3879 urlComponents.dwUrlPathLength = 0;
3880 urlComponents.lpszExtraInfo = NULL;
3881 urlComponents.dwExtraInfoLength = 0;
3883 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3884 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3887 orig_url = heap_alloc(url_length);
3889 /* convert from bytes to characters */
3890 url_length = url_length / sizeof(WCHAR) - 1;
3891 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3893 heap_free(orig_url);
3898 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3899 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3901 heap_free(orig_url);
3904 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3906 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3908 heap_free(orig_url);
3909 heap_free(combined_url);
3912 heap_free(orig_url);
3913 return combined_url;
3917 /***********************************************************************
3918 * HTTP_HandleRedirect (internal)
3920 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3922 http_session_t *session = request->session;
3923 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3928 /* if it's an absolute path, keep the same session info */
3929 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3933 URL_COMPONENTSW urlComponents;
3934 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3935 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3936 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3937 BOOL custom_port = FALSE;
3939 static WCHAR httpW[] = {'h','t','t','p',0};
3940 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3946 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3947 urlComponents.lpszScheme = protocol;
3948 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3949 urlComponents.lpszHostName = hostName;
3950 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3951 urlComponents.lpszUserName = userName;
3952 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3953 urlComponents.lpszPassword = NULL;
3954 urlComponents.dwPasswordLength = 0;
3955 urlComponents.lpszUrlPath = path;
3956 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3957 urlComponents.lpszExtraInfo = NULL;
3958 urlComponents.dwExtraInfoLength = 0;
3959 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3960 return INTERNET_GetLastError();
3962 if(!strcmpiW(protocol, httpW)) {
3963 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3964 TRACE("redirect from secure page to non-secure page\n");
3965 /* FIXME: warn about from secure redirect to non-secure page */
3966 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3969 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3970 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3971 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3973 }else if(!strcmpiW(protocol, httpsW)) {
3974 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3975 TRACE("redirect from non-secure page to secure page\n");
3976 /* FIXME: notify about redirect to secure page */
3977 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3980 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3981 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3982 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3986 heap_free(session->hostName);
3990 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3991 len = lstrlenW(hostName);
3992 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3993 session->hostName = heap_alloc(len*sizeof(WCHAR));
3994 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3997 session->hostName = heap_strdupW(hostName);
3999 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4001 heap_free(session->userName);
4002 session->userName = NULL;
4004 session->userName = heap_strdupW(userName);
4006 reset_data_stream(request);
4008 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
4009 server_t *new_server;
4011 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
4012 server_release(request->server);
4013 request->server = new_server;
4016 heap_free(request->path);
4023 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
4024 if (rc != E_POINTER)
4025 needed = strlenW(path)+1;
4026 request->path = heap_alloc(needed*sizeof(WCHAR));
4027 rc = UrlEscapeW(path, request->path, &needed,
4028 URL_ESCAPE_SPACES_ONLY);
4031 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4032 strcpyW(request->path,path);
4036 /* Remove custom content-type/length headers on redirects. */
4037 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
4039 HTTP_DeleteCustomHeader(request, index);
4040 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
4042 HTTP_DeleteCustomHeader(request, index);
4044 return ERROR_SUCCESS;
4047 /***********************************************************************
4048 * HTTP_build_req (internal)
4050 * concatenate all the strings in the request together
4052 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4057 for( t = list; *t ; t++ )
4058 len += strlenW( *t );
4061 str = heap_alloc(len*sizeof(WCHAR));
4064 for( t = list; *t ; t++ )
4070 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4072 server_t *server = request->server;
4073 LPWSTR requestString;
4080 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4084 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4086 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4087 NULL, 0, NULL, NULL );
4088 len--; /* the nul terminator isn't needed */
4089 ascii_req = heap_alloc(len);
4090 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4091 heap_free( requestString );
4093 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4095 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4096 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4097 heap_free( ascii_req );
4098 if (res != ERROR_SUCCESS)
4101 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4103 return ERROR_HTTP_INVALID_HEADER;
4105 return ERROR_SUCCESS;
4108 static void HTTP_InsertCookies(http_request_t *request)
4110 DWORD cookie_size, size, cnt = 0;
4114 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4116 host = HTTP_GetHeader(request, hostW);
4120 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4123 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4124 if(!(cookies = heap_alloc(size)))
4127 cnt += sprintfW(cookies, cookieW);
4128 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4129 strcatW(cookies, szCrLf);
4131 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4136 static WORD HTTP_ParseWkday(LPCWSTR day)
4138 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4146 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4147 if (!strcmpiW(day, days[i]))
4154 static WORD HTTP_ParseMonth(LPCWSTR month)
4156 static const WCHAR jan[] = { 'j','a','n',0 };
4157 static const WCHAR feb[] = { 'f','e','b',0 };
4158 static const WCHAR mar[] = { 'm','a','r',0 };
4159 static const WCHAR apr[] = { 'a','p','r',0 };
4160 static const WCHAR may[] = { 'm','a','y',0 };
4161 static const WCHAR jun[] = { 'j','u','n',0 };
4162 static const WCHAR jul[] = { 'j','u','l',0 };
4163 static const WCHAR aug[] = { 'a','u','g',0 };
4164 static const WCHAR sep[] = { 's','e','p',0 };
4165 static const WCHAR oct[] = { 'o','c','t',0 };
4166 static const WCHAR nov[] = { 'n','o','v',0 };
4167 static const WCHAR dec[] = { 'd','e','c',0 };
4169 if (!strcmpiW(month, jan)) return 1;
4170 if (!strcmpiW(month, feb)) return 2;
4171 if (!strcmpiW(month, mar)) return 3;
4172 if (!strcmpiW(month, apr)) return 4;
4173 if (!strcmpiW(month, may)) return 5;
4174 if (!strcmpiW(month, jun)) return 6;
4175 if (!strcmpiW(month, jul)) return 7;
4176 if (!strcmpiW(month, aug)) return 8;
4177 if (!strcmpiW(month, sep)) return 9;
4178 if (!strcmpiW(month, oct)) return 10;
4179 if (!strcmpiW(month, nov)) return 11;
4180 if (!strcmpiW(month, dec)) return 12;
4185 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4186 * optionally preceded by whitespace.
4187 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4188 * st, and sets *str to the first character after the time format.
4190 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4196 while (isspaceW(*ptr))
4199 num = strtoulW(ptr, &nextPtr, 10);
4200 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4202 ERR("unexpected time format %s\n", debugstr_w(ptr));
4207 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4211 st->wHour = (WORD)num;
4212 num = strtoulW(ptr, &nextPtr, 10);
4213 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4215 ERR("unexpected time format %s\n", debugstr_w(ptr));
4220 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4224 st->wMinute = (WORD)num;
4225 num = strtoulW(ptr, &nextPtr, 10);
4226 if (!nextPtr || nextPtr <= ptr)
4228 ERR("unexpected time format %s\n", debugstr_w(ptr));
4233 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4238 st->wSecond = (WORD)num;
4242 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4244 static const WCHAR gmt[]= { 'G','M','T',0 };
4245 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4247 SYSTEMTIME st = { 0 };
4250 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4251 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4254 st.wDayOfWeek = HTTP_ParseWkday(day);
4255 if (st.wDayOfWeek >= 7)
4257 ERR("unexpected weekday %s\n", debugstr_w(day));
4261 while (isspaceW(*ptr))
4264 for (monthPtr = month; !isspace(*ptr) &&
4265 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4269 st.wMonth = HTTP_ParseMonth(month);
4270 if (!st.wMonth || st.wMonth > 12)
4272 ERR("unexpected month %s\n", debugstr_w(month));
4276 while (isspaceW(*ptr))
4279 num = strtoulW(ptr, &nextPtr, 10);
4280 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4282 ERR("unexpected day %s\n", debugstr_w(ptr));
4286 st.wDay = (WORD)num;
4288 while (isspaceW(*ptr))
4291 if (!HTTP_ParseTime(&st, &ptr))
4294 while (isspaceW(*ptr))
4297 num = strtoulW(ptr, &nextPtr, 10);
4298 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4300 ERR("unexpected year %s\n", debugstr_w(ptr));
4304 st.wYear = (WORD)num;
4306 while (isspaceW(*ptr))
4309 /* asctime() doesn't report a timezone, but some web servers do, so accept
4310 * with or without GMT.
4312 if (*ptr && strcmpW(ptr, gmt))
4314 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4317 return SystemTimeToFileTime(&st, ft);
4320 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4322 static const WCHAR gmt[]= { 'G','M','T',0 };
4323 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4326 SYSTEMTIME st = { 0 };
4328 ptr = strchrW(value, ',');
4331 if (ptr - value != 3)
4333 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4336 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4338 st.wDayOfWeek = HTTP_ParseWkday(day);
4339 if (st.wDayOfWeek > 6)
4341 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4346 while (isspaceW(*ptr))
4349 num = strtoulW(ptr, &nextPtr, 10);
4350 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4352 WARN("unexpected day %s\n", debugstr_w(value));
4356 st.wDay = (WORD)num;
4358 while (isspaceW(*ptr))
4361 for (monthPtr = month; !isspace(*ptr) &&
4362 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4366 st.wMonth = HTTP_ParseMonth(month);
4367 if (!st.wMonth || st.wMonth > 12)
4369 WARN("unexpected month %s\n", debugstr_w(month));
4373 while (isspaceW(*ptr))
4376 num = strtoulW(ptr, &nextPtr, 10);
4377 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4379 ERR("unexpected year %s\n", debugstr_w(value));
4383 st.wYear = (WORD)num;
4385 if (!HTTP_ParseTime(&st, &ptr))
4388 while (isspaceW(*ptr))
4391 if (strcmpW(ptr, gmt))
4393 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4396 return SystemTimeToFileTime(&st, ft);
4399 static WORD HTTP_ParseWeekday(LPCWSTR day)
4401 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4402 { 'm','o','n','d','a','y',0 },
4403 { 't','u','e','s','d','a','y',0 },
4404 { 'w','e','d','n','e','s','d','a','y',0 },
4405 { 't','h','u','r','s','d','a','y',0 },
4406 { 'f','r','i','d','a','y',0 },
4407 { 's','a','t','u','r','d','a','y',0 }};
4409 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4410 if (!strcmpiW(day, days[i]))
4417 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4419 static const WCHAR gmt[]= { 'G','M','T',0 };
4420 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4423 SYSTEMTIME st = { 0 };
4425 ptr = strchrW(value, ',');
4428 if (ptr - value == 3)
4430 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4432 st.wDayOfWeek = HTTP_ParseWkday(day);
4433 if (st.wDayOfWeek > 6)
4435 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4439 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4441 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4442 day[ptr - value + 1] = 0;
4443 st.wDayOfWeek = HTTP_ParseWeekday(day);
4444 if (st.wDayOfWeek > 6)
4446 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4452 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4457 while (isspaceW(*ptr))
4460 num = strtoulW(ptr, &nextPtr, 10);
4461 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4463 ERR("unexpected day %s\n", debugstr_w(value));
4467 st.wDay = (WORD)num;
4471 ERR("unexpected month format %s\n", debugstr_w(ptr));
4476 for (monthPtr = month; *ptr != '-' &&
4477 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4481 st.wMonth = HTTP_ParseMonth(month);
4482 if (!st.wMonth || st.wMonth > 12)
4484 ERR("unexpected month %s\n", debugstr_w(month));
4490 ERR("unexpected year format %s\n", debugstr_w(ptr));
4495 num = strtoulW(ptr, &nextPtr, 10);
4496 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4498 ERR("unexpected year %s\n", debugstr_w(value));
4502 st.wYear = (WORD)num;
4504 if (!HTTP_ParseTime(&st, &ptr))
4507 while (isspaceW(*ptr))
4510 if (strcmpW(ptr, gmt))
4512 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4515 return SystemTimeToFileTime(&st, ft);
4518 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4520 static const WCHAR zero[] = { '0',0 };
4523 if (!strcmpW(value, zero))
4525 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4528 else if (strchrW(value, ','))
4530 ret = HTTP_ParseRfc1123Date(value, ft);
4533 ret = HTTP_ParseRfc850Date(value, ft);
4535 ERR("unexpected date format %s\n", debugstr_w(value));
4540 ret = HTTP_ParseDateAsAsctime(value, ft);
4542 ERR("unexpected date format %s\n", debugstr_w(value));
4547 static void HTTP_ProcessExpires(http_request_t *request)
4549 BOOL expirationFound = FALSE;
4552 /* Look for a Cache-Control header with a max-age directive, as it takes
4553 * precedence over the Expires header.
4555 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4556 if (headerIndex != -1)
4558 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4561 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4563 LPWSTR comma = strchrW(ptr, ','), end, equal;
4568 end = ptr + strlenW(ptr);
4569 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4573 static const WCHAR max_age[] = {
4574 'm','a','x','-','a','g','e',0 };
4576 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4581 age = strtoulW(equal + 1, &nextPtr, 10);
4582 if (nextPtr > equal + 1)
4586 NtQuerySystemTime( &ft );
4587 /* Age is in seconds, FILETIME resolution is in
4588 * 100 nanosecond intervals.
4590 ft.QuadPart += age * (ULONGLONG)1000000;
4591 request->expires.dwLowDateTime = ft.u.LowPart;
4592 request->expires.dwHighDateTime = ft.u.HighPart;
4593 expirationFound = TRUE;
4600 while (isspaceW(*ptr))
4607 if (!expirationFound)
4609 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4610 if (headerIndex != -1)
4612 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4615 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4617 expirationFound = TRUE;
4618 request->expires = ft;
4622 if (!expirationFound)
4626 /* With no known age, default to 10 minutes until expiration. */
4627 NtQuerySystemTime( &t );
4628 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4629 request->expires.dwLowDateTime = t.u.LowPart;
4630 request->expires.dwHighDateTime = t.u.HighPart;
4634 static void HTTP_ProcessLastModified(http_request_t *request)
4638 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4639 if (headerIndex != -1)
4641 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4644 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4645 request->last_modified = ft;
4649 static void http_process_keep_alive(http_request_t *req)
4653 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4655 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4657 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4660 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4662 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4663 netconn_t *netconn = NULL;
4666 assert(!request->netconn);
4667 reset_data_stream(request);
4669 res = HTTP_ResolveName(request);
4670 if(res != ERROR_SUCCESS)
4673 EnterCriticalSection(&connection_pool_cs);
4675 while(!list_empty(&request->server->conn_pool)) {
4676 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4677 list_remove(&netconn->pool_entry);
4679 if(NETCON_is_alive(netconn))
4682 TRACE("connection %p closed during idle\n", netconn);
4683 free_netconn(netconn);
4687 LeaveCriticalSection(&connection_pool_cs);
4690 TRACE("<-- reusing %p netconn\n", netconn);
4691 request->netconn = netconn;
4693 return ERROR_SUCCESS;
4696 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4697 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4699 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4700 INTERNET_STATUS_CONNECTING_TO_SERVER,
4701 request->server->addr_str,
4702 strlen(request->server->addr_str)+1);
4704 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4705 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4706 request->connect_timeout, &netconn);
4707 if(res != ERROR_SUCCESS) {
4708 ERR("create_netconn failed: %u\n", res);
4712 request->netconn = netconn;
4714 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4715 INTERNET_STATUS_CONNECTED_TO_SERVER,
4716 request->server->addr_str, strlen(request->server->addr_str)+1);
4719 /* Note: we differ from Microsoft's WinINet here. they seem to have
4720 * a bug that causes no status callbacks to be sent when starting
4721 * a tunnel to a proxy server using the CONNECT verb. i believe our
4722 * behaviour to be more correct and to not cause any incompatibilities
4723 * because using a secure connection through a proxy server is a rare
4724 * case that would be hard for anyone to depend on */
4726 res = HTTP_SecureProxyConnect(request);
4727 if(res == ERROR_SUCCESS)
4728 res = NETCON_secure_connect(request->netconn, request->server);
4731 if(res != ERROR_SUCCESS) {
4732 http_release_netconn(request, FALSE);
4737 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4738 return ERROR_SUCCESS;
4741 /***********************************************************************
4742 * HTTP_HttpSendRequestW (internal)
4744 * Sends the specified request to the HTTP server
4747 * ERROR_SUCCESS on success
4748 * win32 error code on failure
4751 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4752 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4753 DWORD dwContentLength, BOOL bEndRequest)
4756 BOOL redirected = FALSE;
4757 LPWSTR requestString = NULL;
4760 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4761 static const WCHAR szContentLength[] =
4762 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4763 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4766 TRACE("--> %p\n", request);
4768 assert(request->hdr.htype == WH_HHTTPREQ);
4770 /* if the verb is NULL default to GET */
4772 request->verb = heap_strdupW(szGET);
4774 if (dwContentLength || strcmpW(request->verb, szGET))
4776 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4777 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4778 request->bytesToWrite = dwContentLength;
4780 if (request->session->appInfo->agent)
4782 WCHAR *agent_header;
4783 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4786 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4787 agent_header = heap_alloc(len * sizeof(WCHAR));
4788 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4790 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4791 heap_free(agent_header);
4793 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4795 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4796 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4798 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4800 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4801 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4802 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4805 /* add the headers the caller supplied */
4806 if( lpszHeaders && dwHeaderLength )
4807 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4812 BOOL reusing_connection;
4816 reusing_connection = request->netconn != NULL;
4819 request->contentLength = ~0u;
4820 request->bytesToWrite = 0;
4823 if (TRACE_ON(wininet))
4825 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4826 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4829 HTTP_FixURL(request);
4830 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4832 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4834 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4835 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4837 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4838 HTTP_InsertCookies(request);
4842 WCHAR *url = build_proxy_path_url(request);
4843 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4847 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4850 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4852 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4855 /* send the request as ASCII, tack on the optional data */
4856 if (!lpOptional || redirected)
4857 dwOptionalLength = 0;
4858 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4859 NULL, 0, NULL, NULL );
4860 ascii_req = heap_alloc(len + dwOptionalLength);
4861 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4862 ascii_req, len, NULL, NULL );
4864 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4865 len = (len + dwOptionalLength - 1);
4867 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4869 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4870 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4872 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4873 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4874 heap_free( ascii_req );
4875 if(res != ERROR_SUCCESS) {
4876 TRACE("send failed: %u\n", res);
4877 if(!reusing_connection)
4879 http_release_netconn(request, FALSE);
4884 request->bytesWritten = dwOptionalLength;
4886 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4887 INTERNET_STATUS_REQUEST_SENT,
4888 &len, sizeof(DWORD));
4894 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4895 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4897 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4898 /* FIXME: We should know that connection is closed before sending
4899 * headers. Otherwise wrong callbacks are executed */
4900 if(!responseLen && reusing_connection) {
4901 TRACE("Connection closed by server, reconnecting\n");
4902 http_release_netconn(request, FALSE);
4907 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4908 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4911 http_process_keep_alive(request);
4912 HTTP_ProcessCookies(request);
4913 HTTP_ProcessExpires(request);
4914 HTTP_ProcessLastModified(request);
4916 res = set_content_length(request);
4917 if(res != ERROR_SUCCESS)
4919 if(!request->contentLength)
4920 http_release_netconn(request, TRUE);
4922 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4924 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4925 dwBufferSize=sizeof(szNewLocation);
4926 switch(request->status_code) {
4927 case HTTP_STATUS_REDIRECT:
4928 case HTTP_STATUS_MOVED:
4929 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4930 case HTTP_STATUS_REDIRECT_METHOD:
4931 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4934 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4935 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4937 heap_free(request->verb);
4938 request->verb = heap_strdupW(szGET);
4940 http_release_netconn(request, drain_content(request, FALSE));
4941 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4943 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4944 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4945 res = HTTP_HandleRedirect(request, new_url);
4946 if (res == ERROR_SUCCESS)
4948 heap_free(requestString);
4951 heap_free( new_url );
4956 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4958 WCHAR szAuthValue[2048];
4960 if (request->status_code == HTTP_STATUS_DENIED)
4962 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4964 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4966 if (HTTP_DoAuthorization(request, szAuthValue,
4968 request->session->userName,
4969 request->session->password,
4972 heap_free(requestString);
4973 if(!drain_content(request, TRUE)) {
4974 FIXME("Could not drain content\n");
4975 http_release_netconn(request, FALSE);
4983 TRACE("Cleaning wrong authorization data\n");
4984 destroy_authinfo(request->authInfo);
4985 request->authInfo = NULL;
4988 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4991 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4993 if (HTTP_DoAuthorization(request, szAuthValue,
4994 &request->proxyAuthInfo,
4995 request->session->appInfo->proxyUsername,
4996 request->session->appInfo->proxyPassword,
4999 if(!drain_content(request, TRUE)) {
5000 FIXME("Could not drain content\n");
5001 http_release_netconn(request, FALSE);
5009 TRACE("Cleaning wrong proxy authorization data\n");
5010 destroy_authinfo(request->proxyAuthInfo);
5011 request->proxyAuthInfo = NULL;
5017 res = ERROR_SUCCESS;
5022 heap_free(requestString);
5024 /* TODO: send notification for P3P header */
5026 if(res == ERROR_SUCCESS)
5027 create_cache_entry(request);
5029 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5031 if (res == ERROR_SUCCESS) {
5032 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5033 HTTP_ReceiveRequestData(request, TRUE);
5035 send_request_complete(request,
5036 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5038 send_request_complete(request, 0, res);
5046 /***********************************************************************
5048 * Helper functions for the HttpSendRequest(Ex) functions
5051 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5053 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5054 http_request_t *request = (http_request_t*) workRequest->hdr;
5056 TRACE("%p\n", request);
5058 HTTP_HttpSendRequestW(request, req->lpszHeader,
5059 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5060 req->dwContentLength, req->bEndRequest);
5062 heap_free(req->lpszHeader);
5066 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5070 DWORD res = ERROR_SUCCESS;
5072 if(!request->netconn) {
5073 WARN("Not connected\n");
5074 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5075 return ERROR_INTERNET_OPERATION_CANCELLED;
5078 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5079 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5081 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5083 res = ERROR_HTTP_HEADER_NOT_FOUND;
5085 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5086 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5088 /* process cookies here. Is this right? */
5089 http_process_keep_alive(request);
5090 HTTP_ProcessCookies(request);
5091 HTTP_ProcessExpires(request);
5092 HTTP_ProcessLastModified(request);
5094 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5095 if(!request->contentLength)
5096 http_release_netconn(request, TRUE);
5099 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5101 switch(request->status_code) {
5102 case HTTP_STATUS_REDIRECT:
5103 case HTTP_STATUS_MOVED:
5104 case HTTP_STATUS_REDIRECT_METHOD:
5105 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5106 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5107 dwBufferSize=sizeof(szNewLocation);
5108 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5111 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5112 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5114 heap_free(request->verb);
5115 request->verb = heap_strdupW(szGET);
5117 http_release_netconn(request, drain_content(request, FALSE));
5118 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5120 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5121 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5122 res = HTTP_HandleRedirect(request, new_url);
5123 if (res == ERROR_SUCCESS)
5124 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5125 heap_free( new_url );
5131 if(res == ERROR_SUCCESS)
5132 create_cache_entry(request);
5134 if (res == ERROR_SUCCESS && request->contentLength)
5135 HTTP_ReceiveRequestData(request, TRUE);
5137 send_request_complete(request, res == ERROR_SUCCESS, res);
5142 /***********************************************************************
5143 * HttpEndRequestA (WININET.@)
5145 * Ends an HTTP request that was started by HttpSendRequestEx
5148 * TRUE if successful
5152 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5153 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5155 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5159 SetLastError(ERROR_INVALID_PARAMETER);
5163 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5166 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5168 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5169 http_request_t *request = (http_request_t*)work->hdr;
5171 TRACE("%p\n", request);
5173 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5176 /***********************************************************************
5177 * HttpEndRequestW (WININET.@)
5179 * Ends an HTTP request that was started by HttpSendRequestEx
5182 * TRUE if successful
5186 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5187 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5189 http_request_t *request;
5192 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5196 SetLastError(ERROR_INVALID_PARAMETER);
5200 request = (http_request_t*) get_handle_object( hRequest );
5202 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5204 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5206 WININET_Release( &request->hdr );
5209 request->hdr.dwFlags |= dwFlags;
5211 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5214 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5216 work.asyncproc = AsyncHttpEndRequestProc;
5217 work.hdr = WININET_AddRef( &request->hdr );
5219 work_endrequest = &work.u.HttpEndRequestW;
5220 work_endrequest->dwFlags = dwFlags;
5221 work_endrequest->dwContext = dwContext;
5223 INTERNET_AsyncCall(&work);
5224 res = ERROR_IO_PENDING;
5227 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5229 WININET_Release( &request->hdr );
5230 TRACE("%u <--\n", res);
5231 if(res != ERROR_SUCCESS)
5233 return res == ERROR_SUCCESS;
5236 /***********************************************************************
5237 * HttpSendRequestExA (WININET.@)
5239 * Sends the specified request to the HTTP server and allows chunked
5244 * Failure: FALSE, call GetLastError() for more information.
5246 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5247 LPINTERNET_BUFFERSA lpBuffersIn,
5248 LPINTERNET_BUFFERSA lpBuffersOut,
5249 DWORD dwFlags, DWORD_PTR dwContext)
5251 INTERNET_BUFFERSW BuffersInW;
5254 LPWSTR header = NULL;
5256 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5257 lpBuffersOut, dwFlags, dwContext);
5261 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5262 if (lpBuffersIn->lpcszHeader)
5264 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5265 lpBuffersIn->dwHeadersLength,0,0);
5266 header = heap_alloc(headerlen*sizeof(WCHAR));
5267 if (!(BuffersInW.lpcszHeader = header))
5269 SetLastError(ERROR_OUTOFMEMORY);
5272 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5273 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5277 BuffersInW.lpcszHeader = NULL;
5278 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5279 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5280 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5281 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5282 BuffersInW.Next = NULL;
5285 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5291 /***********************************************************************
5292 * HttpSendRequestExW (WININET.@)
5294 * Sends the specified request to the HTTP server and allows chunked
5299 * Failure: FALSE, call GetLastError() for more information.
5301 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5302 LPINTERNET_BUFFERSW lpBuffersIn,
5303 LPINTERNET_BUFFERSW lpBuffersOut,
5304 DWORD dwFlags, DWORD_PTR dwContext)
5306 http_request_t *request;
5307 http_session_t *session;
5311 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5312 lpBuffersOut, dwFlags, dwContext);
5314 request = (http_request_t*) get_handle_object( hRequest );
5316 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5318 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5322 session = request->session;
5323 assert(session->hdr.htype == WH_HHTTPSESSION);
5324 hIC = session->appInfo;
5325 assert(hIC->hdr.htype == WH_HINIT);
5327 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5329 WORKREQUEST workRequest;
5330 struct WORKREQ_HTTPSENDREQUESTW *req;
5332 workRequest.asyncproc = AsyncHttpSendRequestProc;
5333 workRequest.hdr = WININET_AddRef( &request->hdr );
5334 req = &workRequest.u.HttpSendRequestW;
5339 if (lpBuffersIn->lpcszHeader)
5341 if (lpBuffersIn->dwHeadersLength == ~0u)
5342 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5344 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5346 req->lpszHeader = heap_alloc(size);
5347 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5349 else req->lpszHeader = NULL;
5351 req->dwHeaderLength = size / sizeof(WCHAR);
5352 req->lpOptional = lpBuffersIn->lpvBuffer;
5353 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5354 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5358 req->lpszHeader = NULL;
5359 req->dwHeaderLength = 0;
5360 req->lpOptional = NULL;
5361 req->dwOptionalLength = 0;
5362 req->dwContentLength = 0;
5365 req->bEndRequest = FALSE;
5367 INTERNET_AsyncCall(&workRequest);
5369 * This is from windows.
5371 res = ERROR_IO_PENDING;
5376 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5377 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5378 lpBuffersIn->dwBufferTotal, FALSE);
5380 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5385 WININET_Release( &request->hdr );
5389 return res == ERROR_SUCCESS;
5392 /***********************************************************************
5393 * HttpSendRequestW (WININET.@)
5395 * Sends the specified request to the HTTP server
5402 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5403 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5405 http_request_t *request;
5406 http_session_t *session = NULL;
5407 appinfo_t *hIC = NULL;
5408 DWORD res = ERROR_SUCCESS;
5410 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5411 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5413 request = (http_request_t*) get_handle_object( hHttpRequest );
5414 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5416 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5420 session = request->session;
5421 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5423 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5427 hIC = session->appInfo;
5428 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5430 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5434 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5436 WORKREQUEST workRequest;
5437 struct WORKREQ_HTTPSENDREQUESTW *req;
5439 workRequest.asyncproc = AsyncHttpSendRequestProc;
5440 workRequest.hdr = WININET_AddRef( &request->hdr );
5441 req = &workRequest.u.HttpSendRequestW;
5446 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5447 else size = dwHeaderLength * sizeof(WCHAR);
5449 req->lpszHeader = heap_alloc(size);
5450 memcpy(req->lpszHeader, lpszHeaders, size);
5453 req->lpszHeader = 0;
5454 req->dwHeaderLength = dwHeaderLength;
5455 req->lpOptional = lpOptional;
5456 req->dwOptionalLength = dwOptionalLength;
5457 req->dwContentLength = dwOptionalLength;
5458 req->bEndRequest = TRUE;
5460 INTERNET_AsyncCall(&workRequest);
5462 * This is from windows.
5464 res = ERROR_IO_PENDING;
5468 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5469 dwHeaderLength, lpOptional, dwOptionalLength,
5470 dwOptionalLength, TRUE);
5474 WININET_Release( &request->hdr );
5477 return res == ERROR_SUCCESS;
5480 /***********************************************************************
5481 * HttpSendRequestA (WININET.@)
5483 * Sends the specified request to the HTTP server
5490 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5491 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5494 LPWSTR szHeaders=NULL;
5495 DWORD nLen=dwHeaderLength;
5496 if(lpszHeaders!=NULL)
5498 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5499 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5500 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5502 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5503 heap_free(szHeaders);
5507 /***********************************************************************
5508 * HTTPSESSION_Destroy (internal)
5510 * Deallocate session handle
5513 static void HTTPSESSION_Destroy(object_header_t *hdr)
5515 http_session_t *session = (http_session_t*) hdr;
5517 TRACE("%p\n", session);
5519 WININET_Release(&session->appInfo->hdr);
5521 heap_free(session->hostName);
5522 heap_free(session->password);
5523 heap_free(session->userName);
5526 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5528 http_session_t *ses = (http_session_t *)hdr;
5531 case INTERNET_OPTION_HANDLE_TYPE:
5532 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5534 if (*size < sizeof(ULONG))
5535 return ERROR_INSUFFICIENT_BUFFER;
5537 *size = sizeof(DWORD);
5538 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5539 return ERROR_SUCCESS;
5540 case INTERNET_OPTION_CONNECT_TIMEOUT:
5541 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5543 if (*size < sizeof(DWORD))
5544 return ERROR_INSUFFICIENT_BUFFER;
5546 *size = sizeof(DWORD);
5547 *(DWORD *)buffer = ses->connect_timeout;
5548 return ERROR_SUCCESS;
5550 case INTERNET_OPTION_SEND_TIMEOUT:
5551 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5553 if (*size < sizeof(DWORD))
5554 return ERROR_INSUFFICIENT_BUFFER;
5556 *size = sizeof(DWORD);
5557 *(DWORD *)buffer = ses->send_timeout;
5558 return ERROR_SUCCESS;
5560 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5561 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5563 if (*size < sizeof(DWORD))
5564 return ERROR_INSUFFICIENT_BUFFER;
5566 *size = sizeof(DWORD);
5567 *(DWORD *)buffer = ses->receive_timeout;
5568 return ERROR_SUCCESS;
5571 return INET_QueryOption(hdr, option, buffer, size, unicode);
5574 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5576 http_session_t *ses = (http_session_t*)hdr;
5579 case INTERNET_OPTION_USERNAME:
5581 heap_free(ses->userName);
5582 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5583 return ERROR_SUCCESS;
5585 case INTERNET_OPTION_PASSWORD:
5587 heap_free(ses->password);
5588 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5589 return ERROR_SUCCESS;
5591 case INTERNET_OPTION_CONNECT_TIMEOUT:
5593 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5594 ses->connect_timeout = *(DWORD *)buffer;
5595 return ERROR_SUCCESS;
5597 case INTERNET_OPTION_SEND_TIMEOUT:
5599 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5600 ses->send_timeout = *(DWORD *)buffer;
5601 return ERROR_SUCCESS;
5603 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5605 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5606 ses->receive_timeout = *(DWORD *)buffer;
5607 return ERROR_SUCCESS;
5612 return INET_SetOption(hdr, option, buffer, size);
5615 static const object_vtbl_t HTTPSESSIONVtbl = {
5616 HTTPSESSION_Destroy,
5618 HTTPSESSION_QueryOption,
5619 HTTPSESSION_SetOption,
5628 /***********************************************************************
5629 * HTTP_Connect (internal)
5631 * Create http session handle
5634 * HINTERNET a session handle on success
5638 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5639 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5640 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5641 DWORD dwInternalFlags, HINTERNET *ret)
5643 http_session_t *session = NULL;
5647 if (!lpszServerName || !lpszServerName[0])
5648 return ERROR_INVALID_PARAMETER;
5650 assert( hIC->hdr.htype == WH_HINIT );
5652 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5654 return ERROR_OUTOFMEMORY;
5657 * According to my tests. The name is not resolved until a request is sent
5660 session->hdr.htype = WH_HHTTPSESSION;
5661 session->hdr.dwFlags = dwFlags;
5662 session->hdr.dwContext = dwContext;
5663 session->hdr.dwInternalFlags |= dwInternalFlags;
5665 WININET_AddRef( &hIC->hdr );
5666 session->appInfo = hIC;
5667 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5669 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5670 if(hIC->proxyBypass)
5671 FIXME("Proxy bypass is ignored.\n");
5673 session->hostName = heap_strdupW(lpszServerName);
5674 if (lpszUserName && lpszUserName[0])
5675 session->userName = heap_strdupW(lpszUserName);
5676 if (lpszPassword && lpszPassword[0])
5677 session->password = heap_strdupW(lpszPassword);
5678 session->hostPort = serverPort;
5679 session->connect_timeout = hIC->connect_timeout;
5680 session->send_timeout = INFINITE;
5681 session->receive_timeout = INFINITE;
5683 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5684 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5686 INTERNET_SendCallback(&hIC->hdr, dwContext,
5687 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5692 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5696 TRACE("%p --> %p\n", hIC, session);
5698 *ret = session->hdr.hInternet;
5699 return ERROR_SUCCESS;
5702 /***********************************************************************
5703 * HTTP_clear_response_headers (internal)
5705 * clear out any old response headers
5707 static void HTTP_clear_response_headers( http_request_t *request )
5711 for( i=0; i<request->nCustHeaders; i++)
5713 if( !request->custHeaders[i].lpszField )
5715 if( !request->custHeaders[i].lpszValue )
5717 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5719 HTTP_DeleteCustomHeader( request, i );
5724 /***********************************************************************
5725 * HTTP_GetResponseHeaders (internal)
5727 * Read server response
5734 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5737 WCHAR buffer[MAX_REPLY_LEN];
5738 DWORD buflen = MAX_REPLY_LEN;
5739 BOOL bSuccess = FALSE;
5741 char bufferA[MAX_REPLY_LEN];
5742 LPWSTR status_code = NULL, status_text = NULL;
5743 DWORD cchMaxRawHeaders = 1024;
5744 LPWSTR lpszRawHeaders = NULL;
5746 DWORD cchRawHeaders = 0;
5747 BOOL codeHundred = FALSE;
5751 if(!request->netconn)
5754 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5756 static const WCHAR szHundred[] = {'1','0','0',0};
5758 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5760 buflen = MAX_REPLY_LEN;
5761 if (!read_line(request, bufferA, &buflen))
5764 /* clear old response headers (eg. from a redirect response) */
5766 HTTP_clear_response_headers( request );
5771 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5772 /* check is this a status code line? */
5773 if (!strncmpW(buffer, g_szHttp1_0, 4))
5775 /* split the version from the status code */
5776 status_code = strchrW( buffer, ' ' );
5781 /* split the status code from the status text */
5782 status_text = strchrW( status_code, ' ' );
5787 request->status_code = atoiW(status_code);
5789 TRACE("version [%s] status code [%s] status text [%s]\n",
5790 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5792 codeHundred = (!strcmpW(status_code, szHundred));
5794 else if (!codeHundred)
5796 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5798 heap_free(request->version);
5799 heap_free(request->statusText);
5801 request->status_code = HTTP_STATUS_OK;
5802 request->version = heap_strdupW(g_szHttp1_0);
5803 request->statusText = heap_strdupW(szOK);
5805 heap_free(request->rawHeaders);
5806 request->rawHeaders = heap_strdupW(szDefaultHeader);
5811 } while (codeHundred);
5813 /* Add status code */
5814 HTTP_ProcessHeader(request, szStatus, status_code,
5815 HTTP_ADDHDR_FLAG_REPLACE);
5817 heap_free(request->version);
5818 heap_free(request->statusText);
5820 request->version = heap_strdupW(buffer);
5821 request->statusText = heap_strdupW(status_text);
5823 /* Restore the spaces */
5824 *(status_code-1) = ' ';
5825 *(status_text-1) = ' ';
5827 /* regenerate raw headers */
5828 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5829 if (!lpszRawHeaders) goto lend;
5831 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5832 cchMaxRawHeaders *= 2;
5833 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5834 if (temp == NULL) goto lend;
5835 lpszRawHeaders = temp;
5836 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5837 cchRawHeaders += (buflen-1);
5838 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5839 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5840 lpszRawHeaders[cchRawHeaders] = '\0';
5842 /* Parse each response line */
5845 buflen = MAX_REPLY_LEN;
5846 if (read_line(request, bufferA, &buflen))
5848 LPWSTR * pFieldAndValue;
5850 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5852 if (!bufferA[0]) break;
5853 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5855 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5858 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5859 cchMaxRawHeaders *= 2;
5860 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5861 if (temp == NULL) goto lend;
5862 lpszRawHeaders = temp;
5863 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5864 cchRawHeaders += (buflen-1);
5865 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5866 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5867 lpszRawHeaders[cchRawHeaders] = '\0';
5869 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5870 HTTP_ADDREQ_FLAG_ADD );
5872 HTTP_FreeTokens(pFieldAndValue);
5883 /* make sure the response header is terminated with an empty line. Some apps really
5884 truly care about that empty line being there for some reason. Just add it to the
5886 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5888 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5889 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5890 if (temp == NULL) goto lend;
5891 lpszRawHeaders = temp;
5894 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5896 heap_free(request->rawHeaders);
5897 request->rawHeaders = lpszRawHeaders;
5898 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5908 heap_free(lpszRawHeaders);
5913 /***********************************************************************
5914 * HTTP_InterpretHttpHeader (internal)
5916 * Parse server response
5920 * Pointer to array of field, value, NULL on success.
5923 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5925 LPWSTR * pTokenPair;
5929 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5931 pszColon = strchrW(buffer, ':');
5932 /* must have two tokens */
5935 HTTP_FreeTokens(pTokenPair);
5937 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5941 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5944 HTTP_FreeTokens(pTokenPair);
5947 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5948 pTokenPair[0][pszColon - buffer] = '\0';
5952 len = strlenW(pszColon);
5953 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5956 HTTP_FreeTokens(pTokenPair);
5959 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5961 strip_spaces(pTokenPair[0]);
5962 strip_spaces(pTokenPair[1]);
5964 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5968 /***********************************************************************
5969 * HTTP_ProcessHeader (internal)
5971 * Stuff header into header tables according to <dwModifier>
5975 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5977 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5979 LPHTTPHEADERW lphttpHdr = NULL;
5981 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5982 DWORD res = ERROR_HTTP_INVALID_HEADER;
5984 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5986 /* REPLACE wins out over ADD */
5987 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5988 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5990 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5993 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5997 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5998 return ERROR_HTTP_INVALID_HEADER;
5999 lphttpHdr = &request->custHeaders[index];
6005 hdr.lpszField = (LPWSTR)field;
6006 hdr.lpszValue = (LPWSTR)value;
6007 hdr.wFlags = hdr.wCount = 0;
6009 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6010 hdr.wFlags |= HDR_ISREQUEST;
6012 return HTTP_InsertCustomHeader(request, &hdr);
6014 /* no value to delete */
6015 else return ERROR_SUCCESS;
6017 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6018 lphttpHdr->wFlags |= HDR_ISREQUEST;
6020 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6022 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6024 HTTP_DeleteCustomHeader( request, index );
6030 hdr.lpszField = (LPWSTR)field;
6031 hdr.lpszValue = (LPWSTR)value;
6032 hdr.wFlags = hdr.wCount = 0;
6034 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6035 hdr.wFlags |= HDR_ISREQUEST;
6037 return HTTP_InsertCustomHeader(request, &hdr);
6040 return ERROR_SUCCESS;
6042 else if (dwModifier & COALESCEFLAGS)
6047 INT origlen = strlenW(lphttpHdr->lpszValue);
6048 INT valuelen = strlenW(value);
6050 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6053 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6055 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6058 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6061 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6063 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6066 lphttpHdr->lpszValue = lpsztmp;
6067 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6070 lphttpHdr->lpszValue[origlen] = ch;
6072 lphttpHdr->lpszValue[origlen] = ' ';
6076 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6077 lphttpHdr->lpszValue[len] = '\0';
6078 res = ERROR_SUCCESS;
6082 WARN("heap_realloc (%d bytes) failed\n",len+1);
6083 res = ERROR_OUTOFMEMORY;
6086 TRACE("<-- %d\n", res);
6090 /***********************************************************************
6091 * HTTP_GetCustomHeaderIndex (internal)
6093 * Return index of custom header from header array
6096 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6097 int requested_index, BOOL request_only)
6101 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6103 for (index = 0; index < request->nCustHeaders; index++)
6105 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6108 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6111 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6114 if (requested_index == 0)
6119 if (index >= request->nCustHeaders)
6122 TRACE("Return: %d\n", index);
6127 /***********************************************************************
6128 * HTTP_InsertCustomHeader (internal)
6130 * Insert header into array
6133 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6136 LPHTTPHEADERW lph = NULL;
6138 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6139 count = request->nCustHeaders + 1;
6141 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6143 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6146 return ERROR_OUTOFMEMORY;
6148 request->custHeaders = lph;
6149 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6150 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6151 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6152 request->custHeaders[count-1].wCount= lpHdr->wCount;
6153 request->nCustHeaders++;
6155 return ERROR_SUCCESS;
6159 /***********************************************************************
6160 * HTTP_DeleteCustomHeader (internal)
6162 * Delete header from array
6163 * If this function is called, the indexs may change.
6165 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6167 if( request->nCustHeaders <= 0 )
6169 if( index >= request->nCustHeaders )
6171 request->nCustHeaders--;
6173 heap_free(request->custHeaders[index].lpszField);
6174 heap_free(request->custHeaders[index].lpszValue);
6176 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6177 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6178 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6184 /***********************************************************************
6185 * HTTP_VerifyValidHeader (internal)
6187 * Verify the given header is not invalid for the given http request
6190 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6192 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6193 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6194 return ERROR_HTTP_INVALID_HEADER;
6196 return ERROR_SUCCESS;
6199 /***********************************************************************
6200 * IsHostInProxyBypassList (@)
6205 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6207 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6211 /***********************************************************************
6212 * InternetShowSecurityInfoByURLA (@)
6214 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6216 FIXME("stub: %s %p\n", url, window);
6220 /***********************************************************************
6221 * InternetShowSecurityInfoByURLW (@)
6223 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6225 FIXME("stub: %s %p\n", debugstr_w(url), window);
6229 /***********************************************************************
6230 * ShowX509EncodedCertificate (@)
6232 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6234 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6240 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6242 memset(&view, 0, sizeof(view));
6243 view.hwndParent = parent;
6244 view.pCertContext = certContext;
6245 if (CryptUIDlgViewCertificateW(&view, NULL))
6246 ret = ERROR_SUCCESS;
6248 ret = GetLastError();
6249 CertFreeCertificateContext(certContext);
6252 ret = GetLastError();