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
71 #include "wine/debug.h"
72 #include "wine/exception.h"
73 #include "wine/unicode.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
77 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
78 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
79 static const WCHAR szOK[] = {'O','K',0};
80 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
81 static const WCHAR hostW[] = { 'H','o','s','t',0 };
82 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
85 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
86 static const WCHAR szGET[] = { 'G','E','T', 0 };
87 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
88 static const WCHAR szCrLf[] = {'\r','\n', 0};
90 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
91 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
92 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
93 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
94 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
95 static const WCHAR szAge[] = { 'A','g','e',0 };
96 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
97 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
98 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
99 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
100 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
101 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
102 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
103 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
104 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
105 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
106 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
107 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
108 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 };
109 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
110 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
111 static const WCHAR szDate[] = { 'D','a','t','e',0 };
112 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
113 static const WCHAR szETag[] = { 'E','T','a','g',0 };
114 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
115 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
116 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
119 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
120 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
121 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
122 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
123 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
124 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
125 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
126 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
127 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
128 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
129 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
130 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
131 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
132 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
133 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
134 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
135 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 };
136 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
137 static const WCHAR szURI[] = { 'U','R','I',0 };
138 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
139 static const WCHAR szVary[] = { 'V','a','r','y',0 };
140 static const WCHAR szVia[] = { 'V','i','a',0 };
141 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
142 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define COLLECT_TIME 60000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
174 typedef struct _basicAuthorizationData
181 UINT authorizationLen;
182 } basicAuthorizationData;
184 typedef struct _authorizationData
198 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
199 static struct list authorizationCache = LIST_INIT(authorizationCache);
201 static CRITICAL_SECTION authcache_cs;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
210 static DWORD HTTP_GetResponseHeaders(http_request_t *req, INT *len);
211 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
212 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
213 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
214 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
215 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
216 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
217 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
218 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
219 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
220 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
221 static BOOL drain_content(http_request_t*,BOOL);
223 static CRITICAL_SECTION connection_pool_cs;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug =
226 0, 0, &connection_pool_cs,
227 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
228 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
232 static struct list connection_pool = LIST_INIT(connection_pool);
233 static BOOL collector_running;
235 void server_addref(server_t *server)
237 InterlockedIncrement(&server->ref);
240 void server_release(server_t *server)
242 if(InterlockedDecrement(&server->ref))
245 list_remove(&server->entry);
247 if(server->cert_chain)
248 CertFreeCertificateChain(server->cert_chain);
249 heap_free(server->name);
250 heap_free(server->scheme_host_port);
254 static BOOL process_host_port(server_t *server)
260 static const WCHAR httpW[] = {'h','t','t','p',0};
261 static const WCHAR httpsW[] = {'h','t','t','p','s',0};
262 static const WCHAR formatW[] = {'%','s',':','/','/','%','s',':','%','u',0};
264 name_len = strlenW(server->name);
265 buf = heap_alloc((name_len + 10 /* strlen("://:<port>") */)*sizeof(WCHAR) + sizeof(httpsW));
269 sprintfW(buf, formatW, server->is_https ? httpsW : httpW, server->name, server->port);
270 server->scheme_host_port = buf;
272 server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
276 default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
277 server->canon_host_port = default_port ? server->name : server->host_port;
281 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
283 server_t *iter, *server = NULL;
285 if(port == INTERNET_INVALID_PORT_NUMBER)
286 port = INTERNET_DEFAULT_HTTP_PORT;
288 EnterCriticalSection(&connection_pool_cs);
290 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
291 if(iter->port == port && !strcmpW(iter->name, name) && iter->is_https == is_https) {
293 server_addref(server);
298 if(!server && do_create) {
299 server = heap_alloc_zero(sizeof(*server));
301 server->ref = 2; /* list reference and return */
303 server->is_https = is_https;
304 list_init(&server->conn_pool);
305 server->name = heap_strdupW(name);
306 if(server->name && process_host_port(server)) {
307 list_add_head(&connection_pool, &server->entry);
315 LeaveCriticalSection(&connection_pool_cs);
320 BOOL collect_connections(collect_type_t collect_type)
322 netconn_t *netconn, *netconn_safe;
323 server_t *server, *server_safe;
324 BOOL remaining = FALSE;
327 now = GetTickCount64();
329 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
330 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
331 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
332 TRACE("freeing %p\n", netconn);
333 list_remove(&netconn->pool_entry);
334 free_netconn(netconn);
340 if(collect_type == COLLECT_CLEANUP) {
341 list_remove(&server->entry);
342 list_init(&server->entry);
343 server_release(server);
350 static DWORD WINAPI collect_connections_proc(void *arg)
352 BOOL remaining_conns;
355 /* FIXME: Use more sophisticated method */
358 EnterCriticalSection(&connection_pool_cs);
360 remaining_conns = collect_connections(COLLECT_TIMEOUT);
362 collector_running = FALSE;
364 LeaveCriticalSection(&connection_pool_cs);
365 }while(remaining_conns);
367 FreeLibraryAndExitThread(WININET_hModule, 0);
370 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
373 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
374 if (HeaderIndex == -1)
377 return &req->custHeaders[HeaderIndex];
386 struct data_stream_vtbl_t {
387 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
388 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
389 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
390 BOOL (*drain_content)(data_stream_t*,http_request_t*);
391 void (*destroy)(data_stream_t*);
395 data_stream_t data_stream;
397 BYTE buf[READ_BUFFER_SIZE];
403 static inline void destroy_data_stream(data_stream_t *stream)
405 stream->vtbl->destroy(stream);
408 static void reset_data_stream(http_request_t *req)
410 destroy_data_stream(req->data_stream);
411 req->data_stream = &req->netconn_stream.data_stream;
412 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
413 req->read_chunked = req->read_gzip = FALSE;
419 data_stream_t stream;
420 data_stream_t *parent_stream;
422 BYTE buf[READ_BUFFER_SIZE];
428 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
430 /* Allow reading only from read buffer */
434 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
436 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
437 return gzip_stream->end_of_data;
440 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
441 DWORD *read, read_mode_t read_mode)
443 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
444 z_stream *zstream = &gzip_stream->zstream;
445 DWORD current_read, ret_read = 0;
448 DWORD res = ERROR_SUCCESS;
450 while(size && !gzip_stream->end_of_data) {
451 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
453 if(gzip_stream->buf_size <= 64 && !end) {
454 if(gzip_stream->buf_pos) {
455 if(gzip_stream->buf_size)
456 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
457 gzip_stream->buf_pos = 0;
459 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
460 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, read_mode);
461 gzip_stream->buf_size += current_read;
462 if(res != ERROR_SUCCESS)
464 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
465 if(!current_read && !end) {
466 if(read_mode != READMODE_NOBLOCK) {
467 WARN("unexpected end of data\n");
468 gzip_stream->end_of_data = TRUE;
472 if(gzip_stream->buf_size <= 64 && !end)
476 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
477 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
478 zstream->next_out = buf+ret_read;
479 zstream->avail_out = size;
480 zres = inflate(&gzip_stream->zstream, 0);
481 current_read = size - zstream->avail_out;
482 size -= current_read;
483 ret_read += current_read;
484 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
485 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
486 if(zres == Z_STREAM_END) {
487 TRACE("end of data\n");
488 gzip_stream->end_of_data = TRUE;
490 }else if(zres != Z_OK) {
491 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
493 res = ERROR_INTERNET_DECODING_FAILED;
497 if(ret_read && read_mode == READMODE_ASYNC)
498 read_mode = READMODE_NOBLOCK;
501 TRACE("read %u bytes\n", ret_read);
506 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
508 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
509 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
512 static void gzip_destroy(data_stream_t *stream)
514 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
516 destroy_data_stream(gzip_stream->parent_stream);
518 if(!gzip_stream->end_of_data)
519 inflateEnd(&gzip_stream->zstream);
520 heap_free(gzip_stream);
523 static const data_stream_vtbl_t gzip_stream_vtbl = {
531 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
533 return heap_alloc(items*size);
536 static void wininet_zfree(voidpf opaque, voidpf address)
541 static DWORD init_gzip_stream(http_request_t *req)
543 gzip_stream_t *gzip_stream;
546 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
548 return ERROR_OUTOFMEMORY;
550 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
551 gzip_stream->zstream.zalloc = wininet_zalloc;
552 gzip_stream->zstream.zfree = wininet_zfree;
554 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
556 ERR("inflateInit failed: %d\n", zres);
557 heap_free(gzip_stream);
558 return ERROR_OUTOFMEMORY;
561 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
563 HTTP_DeleteCustomHeader(req, index);
566 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
567 gzip_stream->buf_size = req->read_size;
568 req->read_pos = req->read_size = 0;
571 req->read_gzip = TRUE;
572 gzip_stream->parent_stream = req->data_stream;
573 req->data_stream = &gzip_stream->stream;
574 return ERROR_SUCCESS;
579 static DWORD init_gzip_stream(http_request_t *req)
581 ERR("gzip stream not supported, missing zlib.\n");
582 return ERROR_SUCCESS;
587 /***********************************************************************
588 * HTTP_Tokenize (internal)
590 * Tokenize a string, allocating memory for the tokens.
592 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
594 LPWSTR * token_array;
601 /* empty string has no tokens */
605 for (i = 0; string[i]; i++)
607 if (!strncmpW(string+i, token_string, strlenW(token_string)))
611 /* we want to skip over separators, but not the null terminator */
612 for (j = 0; j < strlenW(token_string) - 1; j++)
620 /* add 1 for terminating NULL */
621 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
622 token_array[tokens] = NULL;
625 for (i = 0; i < tokens; i++)
628 next_token = strstrW(string, token_string);
629 if (!next_token) next_token = string+strlenW(string);
630 len = next_token - string;
631 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
632 memcpy(token_array[i], string, len*sizeof(WCHAR));
633 token_array[i][len] = '\0';
634 string = next_token+strlenW(token_string);
639 /***********************************************************************
640 * HTTP_FreeTokens (internal)
642 * Frees memory returned from HTTP_Tokenize.
644 static void HTTP_FreeTokens(LPWSTR * token_array)
647 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
648 heap_free(token_array);
651 static void HTTP_FixURL(http_request_t *request)
653 static const WCHAR szSlash[] = { '/',0 };
654 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
656 /* If we don't have a path we set it to root */
657 if (NULL == request->path)
658 request->path = heap_strdupW(szSlash);
659 else /* remove \r and \n*/
661 int nLen = strlenW(request->path);
662 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
665 request->path[nLen]='\0';
667 /* Replace '\' with '/' */
670 if (request->path[nLen] == '\\') request->path[nLen]='/';
674 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
675 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
676 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
678 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
680 strcpyW(fixurl + 1, request->path);
681 heap_free( request->path );
682 request->path = fixurl;
686 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
688 LPWSTR requestString;
694 static const WCHAR szSpace[] = { ' ',0 };
695 static const WCHAR szColon[] = { ':',' ',0 };
696 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
698 /* allocate space for an array of all the string pointers to be added */
699 len = (request->nCustHeaders)*4 + 10;
700 req = heap_alloc(len*sizeof(LPCWSTR));
702 /* add the verb, path and HTTP version string */
710 /* Append custom request headers */
711 for (i = 0; i < request->nCustHeaders; i++)
713 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
716 req[n++] = request->custHeaders[i].lpszField;
718 req[n++] = request->custHeaders[i].lpszValue;
720 TRACE("Adding custom header %s (%s)\n",
721 debugstr_w(request->custHeaders[i].lpszField),
722 debugstr_w(request->custHeaders[i].lpszValue));
727 ERR("oops. buffer overrun\n");
730 requestString = HTTP_build_req( req, 4 );
734 * Set (header) termination string for request
735 * Make sure there's exactly two new lines at the end of the request
737 p = &requestString[strlenW(requestString)-1];
738 while ( (*p == '\n') || (*p == '\r') )
740 strcpyW( p+1, sztwocrlf );
742 return requestString;
745 static void HTTP_ProcessCookies( http_request_t *request )
749 LPHTTPHEADERW setCookieHeader;
751 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
754 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
760 setCookieHeader = &request->custHeaders[HeaderIndex];
762 if (!setCookieHeader->lpszValue)
765 host = HTTP_GetHeader(request, hostW);
769 data = strchrW(setCookieHeader->lpszValue, '=');
773 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
778 set_cookie(host->lpszValue, request->path, name, data);
783 static void strip_spaces(LPWSTR start)
788 while (*str == ' ' && *str != '\0')
792 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
794 end = start + strlenW(start) - 1;
795 while (end >= start && *end == ' ')
802 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
804 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
805 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
807 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
808 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
809 if (is_basic && pszRealm)
812 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
816 token = strchrW(ptr,'=');
820 while (*realm == ' ' && *realm != '\0')
822 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
823 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
826 while (*token == ' ' && *token != '\0')
830 *pszRealm = heap_strdupW(token);
831 strip_spaces(*pszRealm);
838 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
840 if (!authinfo) return;
842 if (SecIsValidHandle(&authinfo->ctx))
843 DeleteSecurityContext(&authinfo->ctx);
844 if (SecIsValidHandle(&authinfo->cred))
845 FreeCredentialsHandle(&authinfo->cred);
847 heap_free(authinfo->auth_data);
848 heap_free(authinfo->scheme);
852 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
854 basicAuthorizationData *ad;
857 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
859 EnterCriticalSection(&authcache_cs);
860 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
862 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
864 TRACE("Authorization found in cache\n");
865 *auth_data = heap_alloc(ad->authorizationLen);
866 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
867 rc = ad->authorizationLen;
871 LeaveCriticalSection(&authcache_cs);
875 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
878 basicAuthorizationData* ad = NULL;
880 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
882 EnterCriticalSection(&authcache_cs);
883 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
885 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
886 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
895 TRACE("Found match in cache, replacing\n");
896 heap_free(ad->authorization);
897 ad->authorization = heap_alloc(auth_data_len);
898 memcpy(ad->authorization, auth_data, auth_data_len);
899 ad->authorizationLen = auth_data_len;
903 ad = heap_alloc(sizeof(basicAuthorizationData));
904 ad->host = heap_strdupW(host);
905 ad->realm = heap_strdupW(realm);
906 ad->authorization = heap_alloc(auth_data_len);
907 memcpy(ad->authorization, auth_data, auth_data_len);
908 ad->authorizationLen = auth_data_len;
909 list_add_head(&basicAuthorizationCache,&ad->entry);
910 TRACE("authorization cached\n");
912 LeaveCriticalSection(&authcache_cs);
915 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
916 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
918 authorizationData *ad;
920 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
922 EnterCriticalSection(&authcache_cs);
923 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
924 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
925 TRACE("Authorization found in cache\n");
927 nt_auth_identity->User = heap_strdupW(ad->user);
928 nt_auth_identity->Password = heap_strdupW(ad->password);
929 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
930 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
931 (!nt_auth_identity->Domain && ad->domain_len)) {
932 heap_free(nt_auth_identity->User);
933 heap_free(nt_auth_identity->Password);
934 heap_free(nt_auth_identity->Domain);
938 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
939 nt_auth_identity->UserLength = ad->user_len;
940 nt_auth_identity->PasswordLength = ad->password_len;
941 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
942 nt_auth_identity->DomainLength = ad->domain_len;
943 LeaveCriticalSection(&authcache_cs);
947 LeaveCriticalSection(&authcache_cs);
952 static void cache_authorization(LPWSTR host, LPWSTR scheme,
953 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
955 authorizationData *ad;
958 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
960 EnterCriticalSection(&authcache_cs);
961 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
962 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
969 heap_free(ad->password);
970 heap_free(ad->domain);
972 ad = heap_alloc(sizeof(authorizationData));
974 LeaveCriticalSection(&authcache_cs);
978 ad->host = heap_strdupW(host);
979 ad->scheme = heap_strdupW(scheme);
980 list_add_head(&authorizationCache, &ad->entry);
983 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
984 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
985 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
986 ad->user_len = nt_auth_identity->UserLength;
987 ad->password_len = nt_auth_identity->PasswordLength;
988 ad->domain_len = nt_auth_identity->DomainLength;
990 if(!ad->host || !ad->scheme || !ad->user || !ad->password
991 || (nt_auth_identity->Domain && !ad->domain)) {
993 heap_free(ad->scheme);
995 heap_free(ad->password);
996 heap_free(ad->domain);
997 list_remove(&ad->entry);
1001 LeaveCriticalSection(&authcache_cs);
1004 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
1005 struct HttpAuthInfo **ppAuthInfo,
1006 LPWSTR domain_and_username, LPWSTR password,
1009 SECURITY_STATUS sec_status;
1010 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
1012 LPWSTR szRealm = NULL;
1014 TRACE("%s\n", debugstr_w(pszAuthValue));
1021 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1025 SecInvalidateHandle(&pAuthInfo->cred);
1026 SecInvalidateHandle(&pAuthInfo->ctx);
1027 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1028 pAuthInfo->attr = 0;
1029 pAuthInfo->auth_data = NULL;
1030 pAuthInfo->auth_data_len = 0;
1031 pAuthInfo->finished = FALSE;
1033 if (is_basic_auth_value(pszAuthValue,NULL))
1035 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1036 pAuthInfo->scheme = heap_strdupW(szBasic);
1037 if (!pAuthInfo->scheme)
1039 heap_free(pAuthInfo);
1046 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1048 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1049 if (!pAuthInfo->scheme)
1051 heap_free(pAuthInfo);
1055 if (domain_and_username)
1057 WCHAR *user = strchrW(domain_and_username, '\\');
1058 WCHAR *domain = domain_and_username;
1060 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1062 pAuthData = &nt_auth_identity;
1067 user = domain_and_username;
1071 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1072 nt_auth_identity.User = user;
1073 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1074 nt_auth_identity.Domain = domain;
1075 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1076 nt_auth_identity.Password = password;
1077 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1079 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1081 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1082 pAuthData = &nt_auth_identity;
1084 /* use default credentials */
1087 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1088 SECPKG_CRED_OUTBOUND, NULL,
1090 NULL, &pAuthInfo->cred,
1093 if(pAuthData && !domain_and_username) {
1094 heap_free(nt_auth_identity.User);
1095 heap_free(nt_auth_identity.Domain);
1096 heap_free(nt_auth_identity.Password);
1099 if (sec_status == SEC_E_OK)
1101 PSecPkgInfoW sec_pkg_info;
1102 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1103 if (sec_status == SEC_E_OK)
1105 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1106 FreeContextBuffer(sec_pkg_info);
1109 if (sec_status != SEC_E_OK)
1111 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1112 debugstr_w(pAuthInfo->scheme), sec_status);
1113 heap_free(pAuthInfo->scheme);
1114 heap_free(pAuthInfo);
1118 *ppAuthInfo = pAuthInfo;
1120 else if (pAuthInfo->finished)
1123 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1124 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1126 ERR("authentication scheme changed from %s to %s\n",
1127 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1131 if (is_basic_auth_value(pszAuthValue,&szRealm))
1135 char *auth_data = NULL;
1136 UINT auth_data_len = 0;
1138 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1140 if (!domain_and_username)
1142 if (host && szRealm)
1143 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1144 if (auth_data_len == 0)
1152 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1153 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1155 /* length includes a nul terminator, which will be re-used for the ':' */
1156 auth_data = heap_alloc(userlen + 1 + passlen);
1163 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1164 auth_data[userlen] = ':';
1165 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1166 auth_data_len = userlen + 1 + passlen;
1167 if (host && szRealm)
1168 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1171 pAuthInfo->auth_data = auth_data;
1172 pAuthInfo->auth_data_len = auth_data_len;
1173 pAuthInfo->finished = TRUE;
1179 LPCWSTR pszAuthData;
1180 SecBufferDesc out_desc, in_desc;
1182 unsigned char *buffer;
1183 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1184 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1186 in.BufferType = SECBUFFER_TOKEN;
1190 in_desc.ulVersion = 0;
1191 in_desc.cBuffers = 1;
1192 in_desc.pBuffers = ∈
1194 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1195 if (*pszAuthData == ' ')
1198 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1199 in.pvBuffer = heap_alloc(in.cbBuffer);
1200 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1203 buffer = heap_alloc(pAuthInfo->max_token);
1205 out.BufferType = SECBUFFER_TOKEN;
1206 out.cbBuffer = pAuthInfo->max_token;
1207 out.pvBuffer = buffer;
1209 out_desc.ulVersion = 0;
1210 out_desc.cBuffers = 1;
1211 out_desc.pBuffers = &out;
1213 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1214 first ? NULL : &pAuthInfo->ctx,
1215 first ? request->server->name : NULL,
1216 context_req, 0, SECURITY_NETWORK_DREP,
1217 in.pvBuffer ? &in_desc : NULL,
1218 0, &pAuthInfo->ctx, &out_desc,
1219 &pAuthInfo->attr, &pAuthInfo->exp);
1220 if (sec_status == SEC_E_OK)
1222 pAuthInfo->finished = TRUE;
1223 pAuthInfo->auth_data = out.pvBuffer;
1224 pAuthInfo->auth_data_len = out.cbBuffer;
1225 TRACE("sending last auth packet\n");
1227 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1229 pAuthInfo->auth_data = out.pvBuffer;
1230 pAuthInfo->auth_data_len = out.cbBuffer;
1231 TRACE("sending next auth packet\n");
1235 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1236 heap_free(out.pvBuffer);
1237 destroy_authinfo(pAuthInfo);
1246 /***********************************************************************
1247 * HTTP_HttpAddRequestHeadersW (internal)
1249 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1250 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1255 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1257 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1259 if( dwHeaderLength == ~0U )
1260 len = strlenW(lpszHeader);
1262 len = dwHeaderLength;
1263 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1264 lstrcpynW( buffer, lpszHeader, len + 1);
1270 LPWSTR * pFieldAndValue;
1272 lpszEnd = lpszStart;
1274 while (*lpszEnd != '\0')
1276 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1281 if (*lpszStart == '\0')
1284 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1287 lpszEnd++; /* Jump over newline */
1289 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1290 if (*lpszStart == '\0')
1292 /* Skip 0-length headers */
1293 lpszStart = lpszEnd;
1294 res = ERROR_SUCCESS;
1297 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1300 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1301 if (res == ERROR_SUCCESS)
1302 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1303 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1304 HTTP_FreeTokens(pFieldAndValue);
1307 lpszStart = lpszEnd;
1308 } while (res == ERROR_SUCCESS);
1314 /***********************************************************************
1315 * HttpAddRequestHeadersW (WININET.@)
1317 * Adds one or more HTTP header to the request handler
1320 * On Windows if dwHeaderLength includes the trailing '\0', then
1321 * HttpAddRequestHeadersW() adds it too. However this results in an
1322 * invalid HTTP header which is rejected by some servers so we probably
1323 * don't need to match Windows on that point.
1330 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1331 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1333 http_request_t *request;
1334 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1336 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1341 request = (http_request_t*) get_handle_object( hHttpRequest );
1342 if (request && request->hdr.htype == WH_HHTTPREQ)
1343 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1345 WININET_Release( &request->hdr );
1347 if(res != ERROR_SUCCESS)
1349 return res == ERROR_SUCCESS;
1352 /***********************************************************************
1353 * HttpAddRequestHeadersA (WININET.@)
1355 * Adds one or more HTTP header to the request handler
1362 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1363 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1369 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1371 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1372 hdr = heap_alloc(len*sizeof(WCHAR));
1373 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1374 if( dwHeaderLength != ~0U )
1375 dwHeaderLength = len;
1377 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1383 static void free_accept_types( WCHAR **accept_types )
1385 WCHAR *ptr, **types = accept_types;
1388 while ((ptr = *types))
1393 heap_free( accept_types );
1396 static WCHAR **convert_accept_types( const char **accept_types )
1399 const char **types = accept_types;
1401 BOOL invalid_pointer = FALSE;
1403 if (!types) return NULL;
1409 /* find out how many there are */
1410 if (*types && **types)
1412 TRACE("accept type: %s\n", debugstr_a(*types));
1418 WARN("invalid accept type pointer\n");
1419 invalid_pointer = TRUE;
1424 if (invalid_pointer) return NULL;
1425 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1427 types = accept_types;
1430 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1433 typesW[count] = NULL;
1437 /***********************************************************************
1438 * HttpOpenRequestA (WININET.@)
1440 * Open a HTTP request handle
1443 * HINTERNET a HTTP request handle on success
1447 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1448 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1449 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1450 DWORD dwFlags, DWORD_PTR dwContext)
1452 LPWSTR szVerb = NULL, szObjectName = NULL;
1453 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1454 HINTERNET rc = FALSE;
1456 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1457 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1458 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1459 dwFlags, dwContext);
1463 szVerb = heap_strdupAtoW(lpszVerb);
1470 szObjectName = heap_strdupAtoW(lpszObjectName);
1471 if ( !szObjectName )
1477 szVersion = heap_strdupAtoW(lpszVersion);
1484 szReferrer = heap_strdupAtoW(lpszReferrer);
1489 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1490 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1491 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1494 free_accept_types(szAcceptTypes);
1495 heap_free(szReferrer);
1496 heap_free(szVersion);
1497 heap_free(szObjectName);
1502 /***********************************************************************
1505 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1508 static const CHAR HTTP_Base64Enc[] =
1509 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1513 /* first 6 bits, all from bin[0] */
1514 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1515 x = (bin[0] & 3) << 4;
1517 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1520 base64[n++] = HTTP_Base64Enc[x];
1525 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1526 x = ( bin[1] & 0x0f ) << 2;
1528 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1531 base64[n++] = HTTP_Base64Enc[x];
1535 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1537 /* last 6 bits, all from bin [2] */
1538 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1546 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1547 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1548 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1549 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1550 static const signed char HTTP_Base64Dec[256] =
1552 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1553 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1554 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1555 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1556 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1557 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1558 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1559 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1560 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1561 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1562 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1563 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1564 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1565 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1566 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1567 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1568 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1569 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1570 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1571 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1572 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1573 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1574 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1575 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1576 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1577 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1581 /***********************************************************************
1584 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1592 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1593 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1594 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1595 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1597 WARN("invalid base64: %s\n", debugstr_w(base64));
1601 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1604 if ((base64[2] == '=') && (base64[3] == '='))
1606 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1607 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1609 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1613 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1616 if (base64[3] == '=')
1618 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1619 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1621 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1625 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1634 /***********************************************************************
1635 * HTTP_InsertAuthorization
1637 * Insert or delete the authorization field in the request header.
1639 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1643 static const WCHAR wszSpace[] = {' ',0};
1644 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1646 WCHAR *authorization = NULL;
1648 if (pAuthInfo->auth_data_len)
1650 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1651 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1652 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1656 strcpyW(authorization, pAuthInfo->scheme);
1657 strcatW(authorization, wszSpace);
1658 HTTP_EncodeBase64(pAuthInfo->auth_data,
1659 pAuthInfo->auth_data_len,
1660 authorization+strlenW(authorization));
1662 /* clear the data as it isn't valid now that it has been sent to the
1663 * server, unless it's Basic authentication which doesn't do
1664 * connection tracking */
1665 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1667 heap_free(pAuthInfo->auth_data);
1668 pAuthInfo->auth_data = NULL;
1669 pAuthInfo->auth_data_len = 0;
1673 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1675 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1676 heap_free(authorization);
1681 static WCHAR *build_proxy_path_url(http_request_t *req)
1686 len = strlenW(req->server->scheme_host_port);
1687 size = len + strlenW(req->path) + 1;
1688 if(*req->path != '/')
1690 url = heap_alloc(size * sizeof(WCHAR));
1694 memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
1695 if(*req->path != '/')
1698 strcpyW(url+len, req->path);
1700 TRACE("url=%s\n", debugstr_w(url));
1704 /***********************************************************************
1705 * HTTP_DealWithProxy
1707 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1709 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1710 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1711 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1712 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1713 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1714 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1715 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1716 static WCHAR szNul[] = { 0 };
1717 URL_COMPONENTSW UrlComponents;
1718 server_t *new_server;
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 is_https = (UrlComponents.nScheme == INTERNET_SCHEME_HTTPS);
1742 if (is_https && UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1743 UrlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
1745 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, is_https, TRUE);
1749 request->proxy = new_server;
1751 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1755 static DWORD HTTP_ResolveName(http_request_t *request)
1757 server_t *server = request->proxy ? request->proxy : request->server;
1761 if(server->addr_len)
1762 return ERROR_SUCCESS;
1764 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1765 INTERNET_STATUS_RESOLVING_NAME,
1767 (strlenW(server->name)+1) * sizeof(WCHAR));
1769 addr_len = sizeof(server->addr);
1770 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1771 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1773 switch(server->addr.ss_family) {
1775 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1778 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1781 WARN("unsupported family %d\n", server->addr.ss_family);
1782 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1785 server->addr_len = addr_len;
1786 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1787 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1788 INTERNET_STATUS_NAME_RESOLVED,
1789 server->addr_str, strlen(server->addr_str)+1);
1791 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1792 return ERROR_SUCCESS;
1795 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1797 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1798 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1799 static const WCHAR slash[] = { '/',0 };
1800 LPHTTPHEADERW host_header;
1803 host_header = HTTP_GetHeader(req, hostW);
1807 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1811 strcpyW(buf, scheme);
1812 strcatW(buf, host_header->lpszValue);
1813 if (req->path[0] != '/')
1814 strcatW(buf, slash);
1815 strcatW(buf, req->path);
1820 /***********************************************************************
1821 * HTTPREQ_Destroy (internal)
1823 * Deallocate request handle
1826 static void HTTPREQ_Destroy(object_header_t *hdr)
1828 http_request_t *request = (http_request_t*) hdr;
1833 if(request->hCacheFile) {
1834 CloseHandle(request->hCacheFile);
1835 DeleteFileW(request->cacheFile);
1837 heap_free(request->cacheFile);
1839 request->read_section.DebugInfo->Spare[0] = 0;
1840 DeleteCriticalSection( &request->read_section );
1841 WININET_Release(&request->session->hdr);
1843 destroy_authinfo(request->authInfo);
1844 destroy_authinfo(request->proxyAuthInfo);
1847 server_release(request->server);
1849 server_release(request->proxy);
1851 heap_free(request->path);
1852 heap_free(request->verb);
1853 heap_free(request->rawHeaders);
1854 heap_free(request->version);
1855 heap_free(request->statusText);
1857 for (i = 0; i < request->nCustHeaders; i++)
1859 heap_free(request->custHeaders[i].lpszField);
1860 heap_free(request->custHeaders[i].lpszValue);
1862 destroy_data_stream(request->data_stream);
1863 heap_free(request->custHeaders);
1866 static void http_release_netconn(http_request_t *req, BOOL reuse)
1868 TRACE("%p %p\n",req, req->netconn);
1873 if(reuse && req->netconn->keep_alive) {
1876 EnterCriticalSection(&connection_pool_cs);
1878 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1879 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1880 req->netconn = NULL;
1882 run_collector = !collector_running;
1883 collector_running = TRUE;
1885 LeaveCriticalSection(&connection_pool_cs);
1888 HANDLE thread = NULL;
1891 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1893 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1895 EnterCriticalSection(&connection_pool_cs);
1896 collector_running = FALSE;
1897 LeaveCriticalSection(&connection_pool_cs);
1900 FreeLibrary(module);
1903 CloseHandle(thread);
1908 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1909 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1911 free_netconn(req->netconn);
1912 req->netconn = NULL;
1914 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1915 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1918 static BOOL HTTP_KeepAlive(http_request_t *request)
1920 WCHAR szVersion[10];
1921 WCHAR szConnectionResponse[20];
1922 DWORD dwBufferSize = sizeof(szVersion);
1923 BOOL keepalive = FALSE;
1925 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1926 * the connection is keep-alive by default */
1927 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1928 && !strcmpiW(szVersion, g_szHttp1_1))
1933 dwBufferSize = sizeof(szConnectionResponse);
1934 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1935 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1937 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1943 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1945 http_request_t *req = (http_request_t*)hdr;
1947 http_release_netconn(req, drain_content(req, FALSE));
1950 static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL unicode)
1956 if (*size < (len + 1) * sizeof(WCHAR))
1958 *size = (len + 1) * sizeof(WCHAR);
1959 return ERROR_INSUFFICIENT_BUFFER;
1961 strcpyW(buffer, str);
1963 return ERROR_SUCCESS;
1967 len = WideCharToMultiByte(CP_ACP, 0, str, -1, buffer, *size, NULL, NULL);
1971 return ERROR_INSUFFICIENT_BUFFER;
1974 return ERROR_SUCCESS;
1978 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1980 http_request_t *req = (http_request_t*)hdr;
1983 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1985 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1987 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1989 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1990 return ERROR_INSUFFICIENT_BUFFER;
1991 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1992 /* FIXME: can't get a SOCKET from our connection since we don't use
1996 /* FIXME: get source port from req->netConnection */
1997 info->SourcePort = 0;
1998 info->DestPort = req->server->port;
2000 if (HTTP_KeepAlive(req))
2001 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
2003 info->Flags |= IDSI_FLAG_PROXY;
2004 if (req->netconn->secure)
2005 info->Flags |= IDSI_FLAG_SECURE;
2007 return ERROR_SUCCESS;
2011 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
2013 case INTERNET_OPTION_SECURITY_FLAGS:
2017 if (*size < sizeof(ULONG))
2018 return ERROR_INSUFFICIENT_BUFFER;
2020 *size = sizeof(DWORD);
2021 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2022 *(DWORD *)buffer = flags;
2024 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2025 return ERROR_SUCCESS;
2028 case INTERNET_OPTION_HANDLE_TYPE:
2029 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2031 if (*size < sizeof(ULONG))
2032 return ERROR_INSUFFICIENT_BUFFER;
2034 *size = sizeof(DWORD);
2035 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2036 return ERROR_SUCCESS;
2038 case INTERNET_OPTION_URL: {
2039 WCHAR url[INTERNET_MAX_URL_LENGTH];
2042 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2044 TRACE("INTERNET_OPTION_URL\n");
2046 host = HTTP_GetHeader(req, hostW);
2047 strcpyW(url, httpW);
2048 strcatW(url, host->lpszValue);
2049 strcatW(url, req->path);
2051 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2052 return str_to_buffer(url, buffer, size, unicode);
2054 case INTERNET_OPTION_USER_AGENT:
2055 return str_to_buffer(req->session->appInfo->agent, buffer, size, unicode);
2056 case INTERNET_OPTION_USERNAME:
2057 return str_to_buffer(req->session->userName, buffer, size, unicode);
2058 case INTERNET_OPTION_PASSWORD:
2059 return str_to_buffer(req->session->password, buffer, size, unicode);
2060 case INTERNET_OPTION_PROXY_USERNAME:
2061 return str_to_buffer(req->session->appInfo->proxyUsername, buffer, size, unicode);
2062 case INTERNET_OPTION_PROXY_PASSWORD:
2063 return str_to_buffer(req->session->appInfo->proxyPassword, buffer, size, unicode);
2065 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2066 INTERNET_CACHE_ENTRY_INFOW *info;
2067 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2068 WCHAR url[INTERNET_MAX_URL_LENGTH];
2069 DWORD nbytes, error;
2072 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2074 if (*size < sizeof(*ts))
2076 *size = sizeof(*ts);
2077 return ERROR_INSUFFICIENT_BUFFER;
2080 HTTP_GetRequestURL(req, url);
2081 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2082 error = GetLastError();
2083 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2085 if (!(info = heap_alloc(nbytes)))
2086 return ERROR_OUTOFMEMORY;
2088 GetUrlCacheEntryInfoW(url, info, &nbytes);
2090 ts->ftExpires = info->ExpireTime;
2091 ts->ftLastModified = info->LastModifiedTime;
2094 *size = sizeof(*ts);
2095 return ERROR_SUCCESS;
2100 case INTERNET_OPTION_DATAFILE_NAME: {
2103 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2105 if(!req->cacheFile) {
2107 return ERROR_INTERNET_ITEM_NOT_FOUND;
2111 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2112 if(*size < req_size)
2113 return ERROR_INSUFFICIENT_BUFFER;
2116 memcpy(buffer, req->cacheFile, *size);
2117 return ERROR_SUCCESS;
2119 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2120 if (req_size > *size)
2121 return ERROR_INSUFFICIENT_BUFFER;
2123 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2124 -1, buffer, *size, NULL, NULL);
2125 return ERROR_SUCCESS;
2129 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2130 PCCERT_CONTEXT context;
2132 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2133 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2134 return ERROR_INSUFFICIENT_BUFFER;
2137 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2139 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2142 memset(info, 0, sizeof(*info));
2143 info->ftExpiry = context->pCertInfo->NotAfter;
2144 info->ftStart = context->pCertInfo->NotBefore;
2145 len = CertNameToStrA(context->dwCertEncodingType,
2146 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2147 info->lpszSubjectInfo = LocalAlloc(0, len);
2148 if(info->lpszSubjectInfo)
2149 CertNameToStrA(context->dwCertEncodingType,
2150 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2151 info->lpszSubjectInfo, len);
2152 len = CertNameToStrA(context->dwCertEncodingType,
2153 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2154 info->lpszIssuerInfo = LocalAlloc(0, len);
2155 if(info->lpszIssuerInfo)
2156 CertNameToStrA(context->dwCertEncodingType,
2157 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2158 info->lpszIssuerInfo, len);
2159 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2160 CertFreeCertificateContext(context);
2161 return ERROR_SUCCESS;
2163 return ERROR_NOT_SUPPORTED;
2165 case INTERNET_OPTION_CONNECT_TIMEOUT:
2166 if (*size < sizeof(DWORD))
2167 return ERROR_INSUFFICIENT_BUFFER;
2169 *size = sizeof(DWORD);
2170 *(DWORD *)buffer = req->connect_timeout;
2171 return ERROR_SUCCESS;
2172 case INTERNET_OPTION_REQUEST_FLAGS: {
2175 if (*size < sizeof(DWORD))
2176 return ERROR_INSUFFICIENT_BUFFER;
2178 /* FIXME: Add support for:
2179 * INTERNET_REQFLAG_FROM_CACHE
2180 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2184 flags |= INTERNET_REQFLAG_VIA_PROXY;
2185 if(!req->rawHeaders)
2186 flags |= INTERNET_REQFLAG_NO_HEADERS;
2188 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2190 *size = sizeof(DWORD);
2191 *(DWORD*)buffer = flags;
2192 return ERROR_SUCCESS;
2196 return INET_QueryOption(hdr, option, buffer, size, unicode);
2199 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2201 http_request_t *req = (http_request_t*)hdr;
2204 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2205 TRACE("Undocumented option 99\n");
2207 if (!buffer || size != sizeof(DWORD))
2208 return ERROR_INVALID_PARAMETER;
2209 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2210 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2213 case INTERNET_OPTION_SECURITY_FLAGS:
2217 if (!buffer || size != sizeof(DWORD))
2218 return ERROR_INVALID_PARAMETER;
2219 flags = *(DWORD *)buffer;
2220 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2221 flags &= SECURITY_SET_MASK;
2222 req->security_flags |= flags;
2224 req->netconn->security_flags |= flags;
2225 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_CONNECT_TIMEOUT:
2228 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2229 req->connect_timeout = *(DWORD *)buffer;
2230 return ERROR_SUCCESS;
2232 case INTERNET_OPTION_SEND_TIMEOUT:
2233 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2234 req->send_timeout = *(DWORD *)buffer;
2235 return ERROR_SUCCESS;
2237 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2238 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2239 req->receive_timeout = *(DWORD *)buffer;
2240 return ERROR_SUCCESS;
2242 case INTERNET_OPTION_USERNAME:
2243 heap_free(req->session->userName);
2244 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2245 return ERROR_SUCCESS;
2247 case INTERNET_OPTION_PASSWORD:
2248 heap_free(req->session->password);
2249 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2250 return ERROR_SUCCESS;
2252 case INTERNET_OPTION_PROXY_USERNAME:
2253 heap_free(req->session->appInfo->proxyUsername);
2254 if (!(req->session->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2255 return ERROR_SUCCESS;
2257 case INTERNET_OPTION_PROXY_PASSWORD:
2258 heap_free(req->session->appInfo->proxyPassword);
2259 if (!(req->session->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2260 return ERROR_SUCCESS;
2262 case INTERNET_OPTION_HTTP_DECODING:
2263 if(size != sizeof(BOOL))
2264 return ERROR_INVALID_PARAMETER;
2265 req->decoding = *(BOOL*)buffer;
2266 return ERROR_SUCCESS;
2269 return INET_SetOption(hdr, option, buffer, size);
2272 static void commit_cache_entry(http_request_t *req)
2274 WCHAR url[INTERNET_MAX_URL_LENGTH];
2278 CloseHandle(req->hCacheFile);
2279 req->hCacheFile = NULL;
2281 if(HTTP_GetRequestURL(req, url)) {
2284 headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
2285 CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
2286 req->last_modified, NORMAL_CACHE_ENTRY,
2287 req->rawHeaders, headersLen, NULL, 0);
2291 static void create_cache_entry(http_request_t *req)
2293 static const WCHAR no_cacheW[] = {'n','o','-','c','a','c','h','e',0};
2294 static const WCHAR no_storeW[] = {'n','o','-','s','t','o','r','e',0};
2296 WCHAR url[INTERNET_MAX_URL_LENGTH];
2297 WCHAR file_name[MAX_PATH+1];
2300 /* FIXME: We should free previous cache file earlier */
2301 heap_free(req->cacheFile);
2302 CloseHandle(req->hCacheFile);
2303 req->hCacheFile = NULL;
2305 if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)
2309 int header_idx = HTTP_GetCustomHeaderIndex(req, szCache_Control, 0, FALSE);
2310 if(header_idx != -1) {
2313 for(ptr=req->custHeaders[header_idx].lpszValue; *ptr; ) {
2316 while(*ptr==' ' || *ptr=='\t')
2319 end = strchrW(ptr, ',');
2321 end = ptr + strlenW(ptr);
2323 if(!strncmpiW(ptr, no_cacheW, sizeof(no_cacheW)/sizeof(*no_cacheW)-1)
2324 || !strncmpiW(ptr, no_storeW, sizeof(no_storeW)/sizeof(*no_storeW)-1)) {
2337 if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2340 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2343 b = HTTP_GetRequestURL(req, url);
2345 WARN("Could not get URL\n");
2349 b = CreateUrlCacheEntryW(url, req->contentLength == ~0u ? 0 : req->contentLength, NULL, file_name, 0);
2351 WARN("Could not create cache entry: %08x\n", GetLastError());
2355 req->cacheFile = heap_strdupW(file_name);
2356 req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2357 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2358 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2359 WARN("Could not create file: %u\n", GetLastError());
2360 req->hCacheFile = NULL;
2364 if(req->read_size) {
2367 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2369 FIXME("WriteFile failed: %u\n", GetLastError());
2371 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2372 commit_cache_entry(req);
2376 /* read some more data into the read buffer (the read section must be held) */
2377 static DWORD read_more_data( http_request_t *req, int maxlen )
2384 /* move existing data to the start of the buffer */
2386 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2390 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2392 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2393 maxlen - req->read_size, 0, &len );
2394 if(res == ERROR_SUCCESS)
2395 req->read_size += len;
2400 /* remove some amount of data from the read buffer (the read section must be held) */
2401 static void remove_data( http_request_t *req, int count )
2403 if (!(req->read_size -= count)) req->read_pos = 0;
2404 else req->read_pos += count;
2407 static DWORD read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2409 int count, bytes_read, pos = 0;
2412 EnterCriticalSection( &req->read_section );
2415 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2419 count = eol - (req->read_buf + req->read_pos);
2420 bytes_read = count + 1;
2422 else count = bytes_read = req->read_size;
2424 count = min( count, *len - pos );
2425 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2427 remove_data( req, bytes_read );
2430 if ((res = read_more_data( req, -1 )))
2432 WARN( "read failed %u\n", res );
2433 LeaveCriticalSection( &req->read_section );
2436 if (!req->read_size)
2439 TRACE( "returning empty string\n" );
2440 LeaveCriticalSection( &req->read_section );
2441 return ERROR_SUCCESS;
2444 LeaveCriticalSection( &req->read_section );
2448 if (pos && buffer[pos - 1] == '\r') pos--;
2451 buffer[*len - 1] = 0;
2452 TRACE( "returning %s\n", debugstr_a(buffer));
2453 return ERROR_SUCCESS;
2456 /* check if we have reached the end of the data to read (the read section must be held) */
2457 static BOOL end_of_read_data( http_request_t *req )
2459 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2462 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2466 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2467 assert(*read <= size);
2469 if(req->hCacheFile) {
2474 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2476 FIXME("WriteFile failed: %u\n", GetLastError());
2479 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2480 commit_cache_entry(req);
2486 /* fetch some more data into the read buffer (the read section must be held) */
2487 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2491 if(req->read_size == sizeof(req->read_buf))
2492 return ERROR_SUCCESS;
2496 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2500 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2502 req->read_size += read;
2504 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2510 /* return the size of data available to be read immediately (the read section must be held) */
2511 static DWORD get_avail_data( http_request_t *req )
2513 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2516 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2518 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2522 NETCON_query_data_available(req->netconn, &avail);
2523 return netconn_stream->content_length == ~0u
2525 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2528 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2530 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2531 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2534 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2535 DWORD *read, read_mode_t read_mode)
2537 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2538 DWORD res = ERROR_SUCCESS;
2541 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2543 if(read_mode == READMODE_NOBLOCK) {
2544 DWORD avail = netconn_get_avail_data(stream, req);
2549 if(size && req->netconn) {
2550 if((res = NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len)))
2553 netconn_stream->content_length = netconn_stream->content_read;
2556 netconn_stream->content_read += *read = len;
2557 TRACE("read %u bytes\n", len);
2561 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2563 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2568 if(netconn_end_of_data(stream, req))
2572 avail = netconn_get_avail_data(stream, req);
2576 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2579 netconn_stream->content_read += len;
2580 }while(netconn_stream->content_read < netconn_stream->content_length);
2585 static void netconn_destroy(data_stream_t *stream)
2589 static const data_stream_vtbl_t netconn_stream_vtbl = {
2590 netconn_get_avail_data,
2591 netconn_end_of_data,
2593 netconn_drain_content,
2597 /* read some more data into the read buffer (the read section must be held) */
2598 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2603 if (stream->buf_pos)
2605 /* move existing data to the start of the buffer */
2606 if(stream->buf_size)
2607 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2608 stream->buf_pos = 0;
2611 if (maxlen == -1) maxlen = sizeof(stream->buf);
2613 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2614 maxlen - stream->buf_size, 0, &len );
2615 if(res == ERROR_SUCCESS)
2616 stream->buf_size += len;
2621 /* remove some amount of data from the read buffer (the read section must be held) */
2622 static void remove_chunked_data(chunked_stream_t *stream, int count)
2624 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2625 else stream->buf_pos += count;
2628 /* discard data contents until we reach end of line (the read section must be held) */
2629 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2635 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2638 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2641 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2642 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2643 } while (stream->buf_size);
2644 return ERROR_SUCCESS;
2647 /* read the size of the next chunk (the read section must be held) */
2648 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2651 DWORD chunk_size = 0, res;
2653 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2658 while (stream->buf_size)
2660 char ch = stream->buf[stream->buf_pos];
2661 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2662 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2663 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2664 else if (ch == ';' || ch == '\r' || ch == '\n')
2666 TRACE( "reading %u byte chunk\n", chunk_size );
2667 stream->chunk_size = chunk_size;
2668 req->contentLength += chunk_size;
2669 return discard_chunked_eol(stream, req);
2671 remove_chunked_data(stream, 1);
2673 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2674 if (!stream->buf_size)
2676 stream->chunk_size = 0;
2677 return ERROR_SUCCESS;
2682 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2684 /* Allow reading only from read buffer */
2688 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2690 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2691 return !chunked_stream->chunk_size;
2694 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2695 DWORD *read, read_mode_t read_mode)
2697 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2698 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2700 if(chunked_stream->chunk_size == ~0u) {
2701 res = start_next_chunk(chunked_stream, req);
2702 if(res != ERROR_SUCCESS)
2706 while(size && chunked_stream->chunk_size) {
2707 if(chunked_stream->buf_size) {
2708 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2710 /* this could block */
2711 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2714 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2715 remove_chunked_data(chunked_stream, read_bytes);
2717 read_bytes = min(size, chunked_stream->chunk_size);
2719 if(read_mode == READMODE_NOBLOCK) {
2722 if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
2724 if(read_bytes > avail)
2727 /* this could block */
2728 if(read_bytes == chunked_stream->chunk_size)
2732 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2733 if(res != ERROR_SUCCESS)
2737 chunked_stream->chunk_size -= read_bytes;
2739 ret_read += read_bytes;
2740 if(!chunked_stream->chunk_size) {
2741 assert(read_mode != READMODE_NOBLOCK);
2742 res = start_next_chunk(chunked_stream, req);
2743 if(res != ERROR_SUCCESS)
2747 if(read_mode == READMODE_ASYNC)
2748 read_mode = READMODE_NOBLOCK;
2751 TRACE("read %u bytes\n", ret_read);
2756 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2758 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2760 /* FIXME: we can do better */
2761 return !chunked_stream->chunk_size;
2764 static void chunked_destroy(data_stream_t *stream)
2766 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2767 heap_free(chunked_stream);
2770 static const data_stream_vtbl_t chunked_stream_vtbl = {
2771 chunked_get_avail_data,
2772 chunked_end_of_data,
2774 chunked_drain_content,
2778 /* set the request content length based on the headers */
2779 static DWORD set_content_length(http_request_t *request)
2781 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2785 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2786 request->contentLength = request->netconn_stream.content_length = 0;
2787 return ERROR_SUCCESS;
2790 size = sizeof(request->contentLength);
2791 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2792 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2793 request->contentLength = ~0u;
2794 request->netconn_stream.content_length = request->contentLength;
2795 request->netconn_stream.content_read = request->read_size;
2797 size = sizeof(encoding);
2798 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2799 !strcmpiW(encoding, szChunked))
2801 chunked_stream_t *chunked_stream;
2803 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2805 return ERROR_OUTOFMEMORY;
2807 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2808 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2809 chunked_stream->chunk_size = ~0u;
2811 if(request->read_size) {
2812 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2813 chunked_stream->buf_size = request->read_size;
2814 request->read_size = request->read_pos = 0;
2817 request->data_stream = &chunked_stream->data_stream;
2818 request->contentLength = ~0u;
2819 request->read_chunked = TRUE;
2822 if(request->decoding) {
2825 static const WCHAR gzipW[] = {'g','z','i','p',0};
2827 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2828 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2829 return init_gzip_stream(request);
2832 return ERROR_SUCCESS;
2835 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2837 INTERNET_ASYNC_RESULT iar;
2839 iar.dwResult = result;
2840 iar.dwError = error;
2842 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2843 sizeof(INTERNET_ASYNC_RESULT));
2846 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2848 DWORD res, read = 0, avail = 0;
2853 EnterCriticalSection( &req->read_section );
2855 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2856 res = refill_read_buffer(req, mode, &read);
2857 if(res == ERROR_SUCCESS)
2858 avail = get_avail_data(req);
2860 LeaveCriticalSection( &req->read_section );
2862 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2863 WARN("res %u read %u, closing connection\n", res, read);
2864 http_release_netconn(req, FALSE);
2867 if(res != ERROR_SUCCESS) {
2868 send_request_complete(req, 0, res);
2877 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2880 /* read data from the http connection (the read section must be held) */
2881 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2883 DWORD current_read = 0, ret_read = 0;
2884 read_mode_t read_mode;
2885 DWORD res = ERROR_SUCCESS;
2887 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2889 EnterCriticalSection( &req->read_section );
2891 if(req->read_size) {
2892 ret_read = min(size, req->read_size);
2893 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2894 req->read_size -= ret_read;
2895 req->read_pos += ret_read;
2896 if(read_mode == READMODE_ASYNC)
2897 read_mode = READMODE_NOBLOCK;
2900 if(ret_read < size) {
2901 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2902 ret_read += current_read;
2905 LeaveCriticalSection( &req->read_section );
2908 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2910 if(size && !ret_read)
2911 http_release_netconn(req, res == ERROR_SUCCESS);
2916 static BOOL drain_content(http_request_t *req, BOOL blocking)
2920 if(!req->netconn || req->contentLength == -1)
2923 if(!strcmpW(req->verb, szHEAD))
2927 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2929 EnterCriticalSection( &req->read_section );
2932 DWORD bytes_read, res;
2935 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2936 if(res != ERROR_SUCCESS) {
2946 LeaveCriticalSection( &req->read_section );
2950 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2952 http_request_t *req = (http_request_t*)hdr;
2955 EnterCriticalSection( &req->read_section );
2956 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2957 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2959 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2960 if(res == ERROR_SUCCESS)
2962 LeaveCriticalSection( &req->read_section );
2972 } read_file_ex_task_t;
2974 static void AsyncReadFileExProc(task_header_t *hdr)
2976 read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2977 http_request_t *req = (http_request_t*)task->hdr.hdr;
2980 TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2982 res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2983 send_request_complete(req, res == ERROR_SUCCESS, res);
2986 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2987 DWORD flags, DWORD_PTR context)
2990 http_request_t *req = (http_request_t*)hdr;
2991 DWORD res, read, cread, error = ERROR_SUCCESS;
2993 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2994 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2996 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2998 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
3000 read_file_ex_task_t *task;
3002 if (TryEnterCriticalSection( &req->read_section ))
3004 if (get_avail_data(req))
3006 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
3007 LeaveCriticalSection( &req->read_section );
3010 LeaveCriticalSection( &req->read_section );
3013 task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
3016 task->ret_read = ret_read;
3018 INTERNET_AsyncCall(&task->hdr);
3020 return ERROR_IO_PENDING;
3025 EnterCriticalSection( &req->read_section );
3026 if(hdr->dwError == ERROR_SUCCESS)
3027 hdr->dwError = INTERNET_HANDLE_IN_USE;
3028 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3029 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3032 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
3033 if(res != ERROR_SUCCESS)
3037 if(read == size || end_of_read_data(req))
3040 LeaveCriticalSection( &req->read_section );
3042 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3043 &cread, sizeof(cread));
3044 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3045 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3047 EnterCriticalSection( &req->read_section );
3050 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3051 hdr->dwError = ERROR_SUCCESS;
3053 error = hdr->dwError;
3055 LeaveCriticalSection( &req->read_section );
3059 if (res == ERROR_SUCCESS) {
3060 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3061 &read, sizeof(read));
3064 return res==ERROR_SUCCESS ? error : res;
3067 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3070 http_request_t *request = (http_request_t*)hdr;
3072 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3075 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3076 if (res == ERROR_SUCCESS)
3077 request->bytesWritten += *written;
3079 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3086 } http_data_available_task_t;
3088 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3090 http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3092 HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3095 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3097 http_request_t *req = (http_request_t*)hdr;
3099 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3101 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3103 http_data_available_task_t *task;
3105 /* never wait, if we can't enter the section we queue an async request right away */
3106 if (TryEnterCriticalSection( &req->read_section ))
3108 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3109 if ((*available = get_avail_data( req ))) goto done;
3110 if (end_of_read_data( req )) goto done;
3111 LeaveCriticalSection( &req->read_section );
3114 task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3115 task->ret_size = available;
3116 INTERNET_AsyncCall(&task->hdr);
3117 return ERROR_IO_PENDING;
3120 EnterCriticalSection( &req->read_section );
3122 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3124 refill_read_buffer( req, READMODE_ASYNC, NULL );
3125 *available = get_avail_data( req );
3129 LeaveCriticalSection( &req->read_section );
3131 TRACE( "returning %u\n", *available );
3132 return ERROR_SUCCESS;
3135 static const object_vtbl_t HTTPREQVtbl = {
3137 HTTPREQ_CloseConnection,
3138 HTTPREQ_QueryOption,
3143 HTTPREQ_QueryDataAvailable,
3147 /***********************************************************************
3148 * HTTP_HttpOpenRequestW (internal)
3150 * Open a HTTP request handle
3153 * HINTERNET a HTTP request handle on success
3157 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3158 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3159 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3160 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3162 appinfo_t *hIC = session->appInfo;
3163 http_request_t *request;
3164 DWORD len, res = ERROR_SUCCESS;
3168 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3170 return ERROR_OUTOFMEMORY;
3172 request->hdr.htype = WH_HHTTPREQ;
3173 request->hdr.dwFlags = dwFlags;
3174 request->hdr.dwContext = dwContext;
3175 request->contentLength = ~0u;
3177 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3178 request->data_stream = &request->netconn_stream.data_stream;
3179 request->connect_timeout = session->connect_timeout;
3180 request->send_timeout = session->send_timeout;
3181 request->receive_timeout = session->receive_timeout;
3183 InitializeCriticalSection( &request->read_section );
3184 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3186 WININET_AddRef( &session->hdr );
3187 request->session = session;
3188 list_add_head( &session->hdr.children, &request->hdr.entry );
3190 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3191 if(!request->server) {
3192 WININET_Release(&request->hdr);
3193 return ERROR_OUTOFMEMORY;
3196 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3197 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3198 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3199 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3201 if (lpszObjectName && *lpszObjectName) {
3205 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3206 if (rc != E_POINTER)
3207 len = strlenW(lpszObjectName)+1;
3208 request->path = heap_alloc(len*sizeof(WCHAR));
3209 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3210 URL_ESCAPE_SPACES_ONLY);
3213 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3214 strcpyW(request->path,lpszObjectName);
3217 static const WCHAR slashW[] = {'/',0};
3219 request->path = heap_strdupW(slashW);
3222 if (lpszReferrer && *lpszReferrer)
3223 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3225 if (lpszAcceptTypes)
3228 for (i = 0; lpszAcceptTypes[i]; i++)
3230 if (!*lpszAcceptTypes[i]) continue;
3231 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3232 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3233 HTTP_ADDHDR_FLAG_REQ |
3234 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3238 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3239 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3241 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3243 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3244 session->hostPort = INTERNET_DEFAULT_HTTP_PORT;
3246 if (hIC->proxy && hIC->proxy[0])
3247 HTTP_DealWithProxy( hIC, session, request );
3249 INTERNET_SendCallback(&session->hdr, dwContext,
3250 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3253 TRACE("<-- %u (%p)\n", res, request);
3255 if(res != ERROR_SUCCESS) {
3256 WININET_Release( &request->hdr );
3261 *ret = request->hdr.hInternet;
3262 return ERROR_SUCCESS;
3265 /***********************************************************************
3266 * HttpOpenRequestW (WININET.@)
3268 * Open a HTTP request handle
3271 * HINTERNET a HTTP request handle on success
3275 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3276 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3277 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3278 DWORD dwFlags, DWORD_PTR dwContext)
3280 http_session_t *session;
3281 HINTERNET handle = NULL;
3284 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3285 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3286 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3287 dwFlags, dwContext);
3288 if(lpszAcceptTypes!=NULL)
3291 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3292 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3295 session = (http_session_t*) get_handle_object( hHttpSession );
3296 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3298 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3303 * My tests seem to show that the windows version does not
3304 * become asynchronous until after this point. And anyhow
3305 * if this call was asynchronous then how would you get the
3306 * necessary HINTERNET pointer returned by this function.
3309 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3310 lpszVersion, lpszReferrer, lpszAcceptTypes,
3311 dwFlags, dwContext, &handle);
3314 WININET_Release( &session->hdr );
3315 TRACE("returning %p\n", handle);
3316 if(res != ERROR_SUCCESS)
3321 static const LPCWSTR header_lookup[] = {
3322 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3323 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3324 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3325 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3326 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3327 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3328 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3329 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3330 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3331 szDate, /* HTTP_QUERY_DATE = 9 */
3332 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3333 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3334 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3335 szURI, /* HTTP_QUERY_URI = 13 */
3336 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3337 NULL, /* HTTP_QUERY_COST = 15 */
3338 NULL, /* HTTP_QUERY_LINK = 16 */
3339 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3340 NULL, /* HTTP_QUERY_VERSION = 18 */
3341 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3342 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3343 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3344 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3345 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3346 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3347 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3348 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3349 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3350 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3351 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3352 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3353 NULL, /* HTTP_QUERY_FROM = 31 */
3354 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3355 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3356 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3357 szReferer, /* HTTP_QUERY_REFERER = 35 */
3358 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3359 szServer, /* HTTP_QUERY_SERVER = 37 */
3360 NULL, /* HTTP_TITLE = 38 */
3361 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3362 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3363 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3364 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3365 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3366 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3367 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3368 NULL, /* HTTP_QUERY_REFRESH = 46 */
3369 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3370 szAge, /* HTTP_QUERY_AGE = 48 */
3371 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3372 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3373 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3374 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3375 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3376 szETag, /* HTTP_QUERY_ETAG = 54 */
3377 hostW, /* HTTP_QUERY_HOST = 55 */
3378 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3379 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3380 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3381 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3382 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3383 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3384 szRange, /* HTTP_QUERY_RANGE = 62 */
3385 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3386 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3387 szVary, /* HTTP_QUERY_VARY = 65 */
3388 szVia, /* HTTP_QUERY_VIA = 66 */
3389 szWarning, /* HTTP_QUERY_WARNING = 67 */
3390 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3391 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3392 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3395 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3397 /***********************************************************************
3398 * HTTP_HttpQueryInfoW (internal)
3400 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3401 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3403 LPHTTPHEADERW lphttpHdr = NULL;
3404 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3405 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3406 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3409 /* Find requested header structure */
3412 case HTTP_QUERY_CUSTOM:
3413 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3414 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3416 case HTTP_QUERY_RAW_HEADERS_CRLF:
3420 DWORD res = ERROR_INVALID_PARAMETER;
3423 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3425 headers = request->rawHeaders;
3428 len = strlenW(headers) * sizeof(WCHAR);
3430 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3432 len += sizeof(WCHAR);
3433 res = ERROR_INSUFFICIENT_BUFFER;
3438 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3441 len = strlenW(szCrLf) * sizeof(WCHAR);
3442 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3444 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3445 res = ERROR_SUCCESS;
3447 *lpdwBufferLength = len;
3449 if (request_only) heap_free(headers);
3452 case HTTP_QUERY_RAW_HEADERS:
3454 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3456 LPWSTR pszString = lpBuffer;
3458 for (i = 0; ppszRawHeaderLines[i]; i++)
3459 size += strlenW(ppszRawHeaderLines[i]) + 1;
3461 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3463 HTTP_FreeTokens(ppszRawHeaderLines);
3464 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3465 return ERROR_INSUFFICIENT_BUFFER;
3469 for (i = 0; ppszRawHeaderLines[i]; i++)
3471 DWORD len = strlenW(ppszRawHeaderLines[i]);
3472 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3476 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3478 *lpdwBufferLength = size * sizeof(WCHAR);
3479 HTTP_FreeTokens(ppszRawHeaderLines);
3481 return ERROR_SUCCESS;
3483 case HTTP_QUERY_STATUS_TEXT:
3484 if (request->statusText)
3486 DWORD len = strlenW(request->statusText);
3487 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3489 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3490 return ERROR_INSUFFICIENT_BUFFER;
3494 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3495 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3497 *lpdwBufferLength = len * sizeof(WCHAR);
3498 return ERROR_SUCCESS;
3501 case HTTP_QUERY_VERSION:
3502 if (request->version)
3504 DWORD len = strlenW(request->version);
3505 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3507 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3508 return ERROR_INSUFFICIENT_BUFFER;
3512 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3513 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3515 *lpdwBufferLength = len * sizeof(WCHAR);
3516 return ERROR_SUCCESS;
3519 case HTTP_QUERY_CONTENT_ENCODING:
3520 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3521 requested_index,request_only);
3523 case HTTP_QUERY_STATUS_CODE: {
3524 DWORD res = ERROR_SUCCESS;
3527 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3532 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3533 if(*lpdwBufferLength >= sizeof(DWORD))
3534 *(DWORD*)lpBuffer = request->status_code;
3536 res = ERROR_INSUFFICIENT_BUFFER;
3537 *lpdwBufferLength = sizeof(DWORD);
3541 static const WCHAR formatW[] = {'%','u',0};
3543 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3545 if(size <= *lpdwBufferLength) {
3546 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3548 size += sizeof(WCHAR);
3549 res = ERROR_INSUFFICIENT_BUFFER;
3552 *lpdwBufferLength = size;
3557 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3559 if (level < LAST_TABLE_HEADER && header_lookup[level])
3560 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3561 requested_index,request_only);
3565 lphttpHdr = &request->custHeaders[index];
3567 /* Ensure header satisfies requested attributes */
3569 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3570 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3572 return ERROR_HTTP_HEADER_NOT_FOUND;
3575 if (lpdwIndex) (*lpdwIndex)++;
3577 /* coalesce value to requested type */
3578 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3580 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3581 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3583 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3589 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3591 tmpTM = *gmtime(&tmpTime);
3592 STHook = (SYSTEMTIME *)lpBuffer;
3593 STHook->wDay = tmpTM.tm_mday;
3594 STHook->wHour = tmpTM.tm_hour;
3595 STHook->wMilliseconds = 0;
3596 STHook->wMinute = tmpTM.tm_min;
3597 STHook->wDayOfWeek = tmpTM.tm_wday;
3598 STHook->wMonth = tmpTM.tm_mon + 1;
3599 STHook->wSecond = tmpTM.tm_sec;
3600 STHook->wYear = 1900+tmpTM.tm_year;
3602 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3603 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3604 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3606 else if (lphttpHdr->lpszValue)
3608 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3610 if (len > *lpdwBufferLength)
3612 *lpdwBufferLength = len;
3613 return ERROR_INSUFFICIENT_BUFFER;
3617 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3618 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3620 *lpdwBufferLength = len - sizeof(WCHAR);
3622 return ERROR_SUCCESS;
3625 /***********************************************************************
3626 * HttpQueryInfoW (WININET.@)
3628 * Queries for information about an HTTP request
3635 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3636 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3638 http_request_t *request;
3641 if (TRACE_ON(wininet)) {
3642 #define FE(x) { x, #x }
3643 static const wininet_flag_info query_flags[] = {
3644 FE(HTTP_QUERY_MIME_VERSION),
3645 FE(HTTP_QUERY_CONTENT_TYPE),
3646 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3647 FE(HTTP_QUERY_CONTENT_ID),
3648 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3649 FE(HTTP_QUERY_CONTENT_LENGTH),
3650 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3651 FE(HTTP_QUERY_ALLOW),
3652 FE(HTTP_QUERY_PUBLIC),
3653 FE(HTTP_QUERY_DATE),
3654 FE(HTTP_QUERY_EXPIRES),
3655 FE(HTTP_QUERY_LAST_MODIFIED),
3656 FE(HTTP_QUERY_MESSAGE_ID),
3658 FE(HTTP_QUERY_DERIVED_FROM),
3659 FE(HTTP_QUERY_COST),
3660 FE(HTTP_QUERY_LINK),
3661 FE(HTTP_QUERY_PRAGMA),
3662 FE(HTTP_QUERY_VERSION),
3663 FE(HTTP_QUERY_STATUS_CODE),
3664 FE(HTTP_QUERY_STATUS_TEXT),
3665 FE(HTTP_QUERY_RAW_HEADERS),
3666 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3667 FE(HTTP_QUERY_CONNECTION),
3668 FE(HTTP_QUERY_ACCEPT),
3669 FE(HTTP_QUERY_ACCEPT_CHARSET),
3670 FE(HTTP_QUERY_ACCEPT_ENCODING),
3671 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3672 FE(HTTP_QUERY_AUTHORIZATION),
3673 FE(HTTP_QUERY_CONTENT_ENCODING),
3674 FE(HTTP_QUERY_FORWARDED),
3675 FE(HTTP_QUERY_FROM),
3676 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3677 FE(HTTP_QUERY_LOCATION),
3678 FE(HTTP_QUERY_ORIG_URI),
3679 FE(HTTP_QUERY_REFERER),
3680 FE(HTTP_QUERY_RETRY_AFTER),
3681 FE(HTTP_QUERY_SERVER),
3682 FE(HTTP_QUERY_TITLE),
3683 FE(HTTP_QUERY_USER_AGENT),
3684 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3685 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3686 FE(HTTP_QUERY_ACCEPT_RANGES),
3687 FE(HTTP_QUERY_SET_COOKIE),
3688 FE(HTTP_QUERY_COOKIE),
3689 FE(HTTP_QUERY_REQUEST_METHOD),
3690 FE(HTTP_QUERY_REFRESH),
3691 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3693 FE(HTTP_QUERY_CACHE_CONTROL),
3694 FE(HTTP_QUERY_CONTENT_BASE),
3695 FE(HTTP_QUERY_CONTENT_LOCATION),
3696 FE(HTTP_QUERY_CONTENT_MD5),
3697 FE(HTTP_QUERY_CONTENT_RANGE),
3698 FE(HTTP_QUERY_ETAG),
3699 FE(HTTP_QUERY_HOST),
3700 FE(HTTP_QUERY_IF_MATCH),
3701 FE(HTTP_QUERY_IF_NONE_MATCH),
3702 FE(HTTP_QUERY_IF_RANGE),
3703 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3704 FE(HTTP_QUERY_MAX_FORWARDS),
3705 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3706 FE(HTTP_QUERY_RANGE),
3707 FE(HTTP_QUERY_TRANSFER_ENCODING),
3708 FE(HTTP_QUERY_UPGRADE),
3709 FE(HTTP_QUERY_VARY),
3711 FE(HTTP_QUERY_WARNING),
3712 FE(HTTP_QUERY_CUSTOM)
3714 static const wininet_flag_info modifier_flags[] = {
3715 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3716 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3717 FE(HTTP_QUERY_FLAG_NUMBER),
3718 FE(HTTP_QUERY_FLAG_COALESCE)
3721 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3722 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3725 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3726 TRACE(" Attribute:");
3727 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3728 if (query_flags[i].val == info) {
3729 TRACE(" %s", query_flags[i].name);
3733 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3734 TRACE(" Unknown (%08x)", info);
3737 TRACE(" Modifier:");
3738 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3739 if (modifier_flags[i].val & info_mod) {
3740 TRACE(" %s", modifier_flags[i].name);
3741 info_mod &= ~ modifier_flags[i].val;
3746 TRACE(" Unknown (%08x)", info_mod);
3751 request = (http_request_t*) get_handle_object( hHttpRequest );
3752 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3754 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3758 if (lpBuffer == NULL)
3759 *lpdwBufferLength = 0;
3760 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3761 lpBuffer, lpdwBufferLength, lpdwIndex);
3765 WININET_Release( &request->hdr );
3767 TRACE("%u <--\n", res);
3768 if(res != ERROR_SUCCESS)
3770 return res == ERROR_SUCCESS;
3773 /***********************************************************************
3774 * HttpQueryInfoA (WININET.@)
3776 * Queries for information about an HTTP request
3783 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3784 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3790 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3791 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3793 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3794 lpdwBufferLength, lpdwIndex );
3800 len = (*lpdwBufferLength)*sizeof(WCHAR);
3801 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3803 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3809 bufferW = heap_alloc(alloclen);
3810 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3811 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3812 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3819 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3823 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3824 lpBuffer, *lpdwBufferLength, NULL, NULL );
3825 *lpdwBufferLength = len - 1;
3827 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3830 /* since the strings being returned from HttpQueryInfoW should be
3831 * only ASCII characters, it is reasonable to assume that all of
3832 * the Unicode characters can be reduced to a single byte */
3833 *lpdwBufferLength = len / sizeof(WCHAR);
3835 heap_free( bufferW );
3839 /***********************************************************************
3840 * HTTP_GetRedirectURL (internal)
3842 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3844 static WCHAR szHttp[] = {'h','t','t','p',0};
3845 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3846 http_session_t *session = request->session;
3847 URL_COMPONENTSW urlComponents;
3848 DWORD url_length = 0;
3850 LPWSTR combined_url;
3852 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3853 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3854 urlComponents.dwSchemeLength = 0;
3855 urlComponents.lpszHostName = session->hostName;
3856 urlComponents.dwHostNameLength = 0;
3857 urlComponents.nPort = session->hostPort;
3858 urlComponents.lpszUserName = session->userName;
3859 urlComponents.dwUserNameLength = 0;
3860 urlComponents.lpszPassword = NULL;
3861 urlComponents.dwPasswordLength = 0;
3862 urlComponents.lpszUrlPath = request->path;
3863 urlComponents.dwUrlPathLength = 0;
3864 urlComponents.lpszExtraInfo = NULL;
3865 urlComponents.dwExtraInfoLength = 0;
3867 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3868 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3871 orig_url = heap_alloc(url_length);
3873 /* convert from bytes to characters */
3874 url_length = url_length / sizeof(WCHAR) - 1;
3875 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3877 heap_free(orig_url);
3882 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3883 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3885 heap_free(orig_url);
3888 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3890 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3892 heap_free(orig_url);
3893 heap_free(combined_url);
3896 heap_free(orig_url);
3897 return combined_url;
3901 /***********************************************************************
3902 * HTTP_HandleRedirect (internal)
3904 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3906 http_session_t *session = request->session;
3907 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3912 /* if it's an absolute path, keep the same session info */
3913 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3917 URL_COMPONENTSW urlComponents;
3918 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3919 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3920 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3921 BOOL custom_port = FALSE;
3923 static WCHAR httpW[] = {'h','t','t','p',0};
3924 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3930 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3931 urlComponents.lpszScheme = protocol;
3932 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3933 urlComponents.lpszHostName = hostName;
3934 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3935 urlComponents.lpszUserName = userName;
3936 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3937 urlComponents.lpszPassword = NULL;
3938 urlComponents.dwPasswordLength = 0;
3939 urlComponents.lpszUrlPath = path;
3940 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3941 urlComponents.lpszExtraInfo = NULL;
3942 urlComponents.dwExtraInfoLength = 0;
3943 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3944 return INTERNET_GetLastError();
3946 if(!strcmpiW(protocol, httpW)) {
3947 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3948 TRACE("redirect from secure page to non-secure page\n");
3949 /* FIXME: warn about from secure redirect to non-secure page */
3950 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3953 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3954 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3955 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3957 }else if(!strcmpiW(protocol, httpsW)) {
3958 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3959 TRACE("redirect from non-secure page to secure page\n");
3960 /* FIXME: notify about redirect to secure page */
3961 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3964 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3965 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3966 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3970 heap_free(session->hostName);
3974 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3975 len = lstrlenW(hostName);
3976 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3977 session->hostName = heap_alloc(len*sizeof(WCHAR));
3978 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3981 session->hostName = heap_strdupW(hostName);
3983 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3985 heap_free(session->userName);
3986 session->userName = NULL;
3988 session->userName = heap_strdupW(userName);
3990 reset_data_stream(request);
3992 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3993 server_t *new_server;
3995 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3996 server_release(request->server);
3997 request->server = new_server;
4000 heap_free(request->path);
4007 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
4008 if (rc != E_POINTER)
4009 needed = strlenW(path)+1;
4010 request->path = heap_alloc(needed*sizeof(WCHAR));
4011 rc = UrlEscapeW(path, request->path, &needed,
4012 URL_ESCAPE_SPACES_ONLY);
4015 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4016 strcpyW(request->path,path);
4020 /* Remove custom content-type/length headers on redirects. */
4021 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
4023 HTTP_DeleteCustomHeader(request, index);
4024 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
4026 HTTP_DeleteCustomHeader(request, index);
4028 return ERROR_SUCCESS;
4031 /***********************************************************************
4032 * HTTP_build_req (internal)
4034 * concatenate all the strings in the request together
4036 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4041 for( t = list; *t ; t++ )
4042 len += strlenW( *t );
4045 str = heap_alloc(len*sizeof(WCHAR));
4048 for( t = list; *t ; t++ )
4054 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4056 server_t *server = request->server;
4057 LPWSTR requestString;
4064 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4068 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4070 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4071 NULL, 0, NULL, NULL );
4072 len--; /* the nul terminator isn't needed */
4073 ascii_req = heap_alloc(len);
4074 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4075 heap_free( requestString );
4077 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4079 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4080 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4081 heap_free( ascii_req );
4082 if (res != ERROR_SUCCESS)
4085 if (HTTP_GetResponseHeaders( request, &responseLen ) || !responseLen)
4086 return ERROR_HTTP_INVALID_HEADER;
4088 return ERROR_SUCCESS;
4091 static void HTTP_InsertCookies(http_request_t *request)
4093 DWORD cookie_size, size, cnt = 0;
4097 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4099 host = HTTP_GetHeader(request, hostW);
4103 if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4106 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4107 if(!(cookies = heap_alloc(size)))
4110 cnt += sprintfW(cookies, cookieW);
4111 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4112 strcatW(cookies, szCrLf);
4114 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4119 static WORD HTTP_ParseWkday(LPCWSTR day)
4121 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4129 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4130 if (!strcmpiW(day, days[i]))
4137 static WORD HTTP_ParseMonth(LPCWSTR month)
4139 static const WCHAR jan[] = { 'j','a','n',0 };
4140 static const WCHAR feb[] = { 'f','e','b',0 };
4141 static const WCHAR mar[] = { 'm','a','r',0 };
4142 static const WCHAR apr[] = { 'a','p','r',0 };
4143 static const WCHAR may[] = { 'm','a','y',0 };
4144 static const WCHAR jun[] = { 'j','u','n',0 };
4145 static const WCHAR jul[] = { 'j','u','l',0 };
4146 static const WCHAR aug[] = { 'a','u','g',0 };
4147 static const WCHAR sep[] = { 's','e','p',0 };
4148 static const WCHAR oct[] = { 'o','c','t',0 };
4149 static const WCHAR nov[] = { 'n','o','v',0 };
4150 static const WCHAR dec[] = { 'd','e','c',0 };
4152 if (!strcmpiW(month, jan)) return 1;
4153 if (!strcmpiW(month, feb)) return 2;
4154 if (!strcmpiW(month, mar)) return 3;
4155 if (!strcmpiW(month, apr)) return 4;
4156 if (!strcmpiW(month, may)) return 5;
4157 if (!strcmpiW(month, jun)) return 6;
4158 if (!strcmpiW(month, jul)) return 7;
4159 if (!strcmpiW(month, aug)) return 8;
4160 if (!strcmpiW(month, sep)) return 9;
4161 if (!strcmpiW(month, oct)) return 10;
4162 if (!strcmpiW(month, nov)) return 11;
4163 if (!strcmpiW(month, dec)) return 12;
4168 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4169 * optionally preceded by whitespace.
4170 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4171 * st, and sets *str to the first character after the time format.
4173 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4179 while (isspaceW(*ptr))
4182 num = strtoulW(ptr, &nextPtr, 10);
4183 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4185 ERR("unexpected time format %s\n", debugstr_w(ptr));
4190 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4194 st->wHour = (WORD)num;
4195 num = strtoulW(ptr, &nextPtr, 10);
4196 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4198 ERR("unexpected time format %s\n", debugstr_w(ptr));
4203 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4207 st->wMinute = (WORD)num;
4208 num = strtoulW(ptr, &nextPtr, 10);
4209 if (!nextPtr || nextPtr <= ptr)
4211 ERR("unexpected time format %s\n", debugstr_w(ptr));
4216 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4221 st->wSecond = (WORD)num;
4225 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4227 static const WCHAR gmt[]= { 'G','M','T',0 };
4228 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4230 SYSTEMTIME st = { 0 };
4233 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4234 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4237 st.wDayOfWeek = HTTP_ParseWkday(day);
4238 if (st.wDayOfWeek >= 7)
4240 ERR("unexpected weekday %s\n", debugstr_w(day));
4244 while (isspaceW(*ptr))
4247 for (monthPtr = month; !isspace(*ptr) &&
4248 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4252 st.wMonth = HTTP_ParseMonth(month);
4253 if (!st.wMonth || st.wMonth > 12)
4255 ERR("unexpected month %s\n", debugstr_w(month));
4259 while (isspaceW(*ptr))
4262 num = strtoulW(ptr, &nextPtr, 10);
4263 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4265 ERR("unexpected day %s\n", debugstr_w(ptr));
4269 st.wDay = (WORD)num;
4271 while (isspaceW(*ptr))
4274 if (!HTTP_ParseTime(&st, &ptr))
4277 while (isspaceW(*ptr))
4280 num = strtoulW(ptr, &nextPtr, 10);
4281 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4283 ERR("unexpected year %s\n", debugstr_w(ptr));
4287 st.wYear = (WORD)num;
4289 while (isspaceW(*ptr))
4292 /* asctime() doesn't report a timezone, but some web servers do, so accept
4293 * with or without GMT.
4295 if (*ptr && strcmpW(ptr, gmt))
4297 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4300 return SystemTimeToFileTime(&st, ft);
4303 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4305 static const WCHAR gmt[]= { 'G','M','T',0 };
4306 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4309 SYSTEMTIME st = { 0 };
4311 ptr = strchrW(value, ',');
4314 if (ptr - value != 3)
4316 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4319 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4321 st.wDayOfWeek = HTTP_ParseWkday(day);
4322 if (st.wDayOfWeek > 6)
4324 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4329 while (isspaceW(*ptr))
4332 num = strtoulW(ptr, &nextPtr, 10);
4333 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4335 WARN("unexpected day %s\n", debugstr_w(value));
4339 st.wDay = (WORD)num;
4341 while (isspaceW(*ptr))
4344 for (monthPtr = month; !isspace(*ptr) &&
4345 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4349 st.wMonth = HTTP_ParseMonth(month);
4350 if (!st.wMonth || st.wMonth > 12)
4352 WARN("unexpected month %s\n", debugstr_w(month));
4356 while (isspaceW(*ptr))
4359 num = strtoulW(ptr, &nextPtr, 10);
4360 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4362 ERR("unexpected year %s\n", debugstr_w(value));
4366 st.wYear = (WORD)num;
4368 if (!HTTP_ParseTime(&st, &ptr))
4371 while (isspaceW(*ptr))
4374 if (strcmpW(ptr, gmt))
4376 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4379 return SystemTimeToFileTime(&st, ft);
4382 static WORD HTTP_ParseWeekday(LPCWSTR day)
4384 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4385 { 'm','o','n','d','a','y',0 },
4386 { 't','u','e','s','d','a','y',0 },
4387 { 'w','e','d','n','e','s','d','a','y',0 },
4388 { 't','h','u','r','s','d','a','y',0 },
4389 { 'f','r','i','d','a','y',0 },
4390 { 's','a','t','u','r','d','a','y',0 }};
4392 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4393 if (!strcmpiW(day, days[i]))
4400 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4402 static const WCHAR gmt[]= { 'G','M','T',0 };
4403 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4406 SYSTEMTIME st = { 0 };
4408 ptr = strchrW(value, ',');
4411 if (ptr - value == 3)
4413 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4415 st.wDayOfWeek = HTTP_ParseWkday(day);
4416 if (st.wDayOfWeek > 6)
4418 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4422 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4424 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4425 day[ptr - value + 1] = 0;
4426 st.wDayOfWeek = HTTP_ParseWeekday(day);
4427 if (st.wDayOfWeek > 6)
4429 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4435 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4440 while (isspaceW(*ptr))
4443 num = strtoulW(ptr, &nextPtr, 10);
4444 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4446 ERR("unexpected day %s\n", debugstr_w(value));
4450 st.wDay = (WORD)num;
4454 ERR("unexpected month format %s\n", debugstr_w(ptr));
4459 for (monthPtr = month; *ptr != '-' &&
4460 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4464 st.wMonth = HTTP_ParseMonth(month);
4465 if (!st.wMonth || st.wMonth > 12)
4467 ERR("unexpected month %s\n", debugstr_w(month));
4473 ERR("unexpected year format %s\n", debugstr_w(ptr));
4478 num = strtoulW(ptr, &nextPtr, 10);
4479 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4481 ERR("unexpected year %s\n", debugstr_w(value));
4485 st.wYear = (WORD)num;
4487 if (!HTTP_ParseTime(&st, &ptr))
4490 while (isspaceW(*ptr))
4493 if (strcmpW(ptr, gmt))
4495 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4498 return SystemTimeToFileTime(&st, ft);
4501 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4503 static const WCHAR zero[] = { '0',0 };
4506 if (!strcmpW(value, zero))
4508 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4511 else if (strchrW(value, ','))
4513 ret = HTTP_ParseRfc1123Date(value, ft);
4516 ret = HTTP_ParseRfc850Date(value, ft);
4518 ERR("unexpected date format %s\n", debugstr_w(value));
4523 ret = HTTP_ParseDateAsAsctime(value, ft);
4525 ERR("unexpected date format %s\n", debugstr_w(value));
4530 static void HTTP_ProcessExpires(http_request_t *request)
4532 BOOL expirationFound = FALSE;
4535 /* Look for a Cache-Control header with a max-age directive, as it takes
4536 * precedence over the Expires header.
4538 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4539 if (headerIndex != -1)
4541 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4544 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4546 LPWSTR comma = strchrW(ptr, ','), end, equal;
4551 end = ptr + strlenW(ptr);
4552 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4556 static const WCHAR max_age[] = {
4557 'm','a','x','-','a','g','e',0 };
4559 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4564 age = strtoulW(equal + 1, &nextPtr, 10);
4565 if (nextPtr > equal + 1)
4569 NtQuerySystemTime( &ft );
4570 /* Age is in seconds, FILETIME resolution is in
4571 * 100 nanosecond intervals.
4573 ft.QuadPart += age * (ULONGLONG)1000000;
4574 request->expires.dwLowDateTime = ft.u.LowPart;
4575 request->expires.dwHighDateTime = ft.u.HighPart;
4576 expirationFound = TRUE;
4583 while (isspaceW(*ptr))
4590 if (!expirationFound)
4592 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4593 if (headerIndex != -1)
4595 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4598 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4600 expirationFound = TRUE;
4601 request->expires = ft;
4605 if (!expirationFound)
4609 /* With no known age, default to 10 minutes until expiration. */
4610 NtQuerySystemTime( &t );
4611 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4612 request->expires.dwLowDateTime = t.u.LowPart;
4613 request->expires.dwHighDateTime = t.u.HighPart;
4617 static void HTTP_ProcessLastModified(http_request_t *request)
4621 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4622 if (headerIndex != -1)
4624 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4627 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4628 request->last_modified = ft;
4632 static void http_process_keep_alive(http_request_t *req)
4636 if ((index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE)) != -1)
4637 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4638 else if ((index = HTTP_GetCustomHeaderIndex(req, szProxy_Connection, 0, FALSE)) != -1)
4639 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4641 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4644 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4646 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4647 netconn_t *netconn = NULL;
4650 assert(!request->netconn);
4651 reset_data_stream(request);
4653 res = HTTP_ResolveName(request);
4654 if(res != ERROR_SUCCESS)
4657 EnterCriticalSection(&connection_pool_cs);
4659 while(!list_empty(&request->server->conn_pool)) {
4660 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4661 list_remove(&netconn->pool_entry);
4663 if(NETCON_is_alive(netconn))
4666 TRACE("connection %p closed during idle\n", netconn);
4667 free_netconn(netconn);
4671 LeaveCriticalSection(&connection_pool_cs);
4674 TRACE("<-- reusing %p netconn\n", netconn);
4675 request->netconn = netconn;
4677 return ERROR_SUCCESS;
4680 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4681 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4683 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4684 INTERNET_STATUS_CONNECTING_TO_SERVER,
4685 request->server->addr_str,
4686 strlen(request->server->addr_str)+1);
4688 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4689 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4690 request->connect_timeout, &netconn);
4691 if(res != ERROR_SUCCESS) {
4692 ERR("create_netconn failed: %u\n", res);
4696 request->netconn = netconn;
4698 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4699 INTERNET_STATUS_CONNECTED_TO_SERVER,
4700 request->server->addr_str, strlen(request->server->addr_str)+1);
4703 /* Note: we differ from Microsoft's WinINet here. they seem to have
4704 * a bug that causes no status callbacks to be sent when starting
4705 * a tunnel to a proxy server using the CONNECT verb. i believe our
4706 * behaviour to be more correct and to not cause any incompatibilities
4707 * because using a secure connection through a proxy server is a rare
4708 * case that would be hard for anyone to depend on */
4710 res = HTTP_SecureProxyConnect(request);
4711 if(res == ERROR_SUCCESS)
4712 res = NETCON_secure_connect(request->netconn, request->server);
4715 if(res != ERROR_SUCCESS) {
4716 http_release_netconn(request, FALSE);
4721 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4722 return ERROR_SUCCESS;
4725 /***********************************************************************
4726 * HTTP_HttpSendRequestW (internal)
4728 * Sends the specified request to the HTTP server
4731 * ERROR_SUCCESS on success
4732 * win32 error code on failure
4735 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4736 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4737 DWORD dwContentLength, BOOL bEndRequest)
4740 BOOL redirected = FALSE;
4741 LPWSTR requestString = NULL;
4744 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4745 static const WCHAR szContentLength[] =
4746 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4747 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4750 TRACE("--> %p\n", request);
4752 assert(request->hdr.htype == WH_HHTTPREQ);
4754 /* if the verb is NULL default to GET */
4756 request->verb = heap_strdupW(szGET);
4758 if (dwContentLength || strcmpW(request->verb, szGET))
4760 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4761 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4762 request->bytesToWrite = dwContentLength;
4764 if (request->session->appInfo->agent)
4766 WCHAR *agent_header;
4767 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4770 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4771 agent_header = heap_alloc(len * sizeof(WCHAR));
4772 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4774 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4775 heap_free(agent_header);
4777 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4779 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4780 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4782 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4784 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4785 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4786 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4789 /* add the headers the caller supplied */
4790 if( lpszHeaders && dwHeaderLength )
4791 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4796 BOOL reusing_connection;
4800 reusing_connection = request->netconn != NULL;
4803 request->contentLength = ~0u;
4804 request->bytesToWrite = 0;
4807 if (TRACE_ON(wininet))
4809 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4810 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4813 HTTP_FixURL(request);
4814 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4816 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4818 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4819 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4821 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4822 HTTP_InsertCookies(request);
4826 WCHAR *url = build_proxy_path_url(request);
4827 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4831 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4834 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4836 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4839 /* send the request as ASCII, tack on the optional data */
4840 if (!lpOptional || redirected)
4841 dwOptionalLength = 0;
4842 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4843 NULL, 0, NULL, NULL );
4844 ascii_req = heap_alloc(len + dwOptionalLength);
4845 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4846 ascii_req, len, NULL, NULL );
4848 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4849 len = (len + dwOptionalLength - 1);
4851 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4853 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4854 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4856 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4857 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4858 heap_free( ascii_req );
4859 if(res != ERROR_SUCCESS) {
4860 TRACE("send failed: %u\n", res);
4861 if(!reusing_connection)
4863 http_release_netconn(request, FALSE);
4868 request->bytesWritten = dwOptionalLength;
4870 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4871 INTERNET_STATUS_REQUEST_SENT,
4872 &len, sizeof(DWORD));
4878 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4879 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4881 if (HTTP_GetResponseHeaders(request, &responseLen))
4883 http_release_netconn(request, FALSE);
4884 res = ERROR_INTERNET_CONNECTION_ABORTED;
4887 /* FIXME: We should know that connection is closed before sending
4888 * headers. Otherwise wrong callbacks are executed */
4889 if(!responseLen && reusing_connection) {
4890 TRACE("Connection closed by server, reconnecting\n");
4891 http_release_netconn(request, FALSE);
4896 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4897 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4900 http_process_keep_alive(request);
4901 HTTP_ProcessCookies(request);
4902 HTTP_ProcessExpires(request);
4903 HTTP_ProcessLastModified(request);
4905 res = set_content_length(request);
4906 if(res != ERROR_SUCCESS)
4908 if(!request->contentLength)
4909 http_release_netconn(request, TRUE);
4911 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4913 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4914 dwBufferSize=sizeof(szNewLocation);
4915 switch(request->status_code) {
4916 case HTTP_STATUS_REDIRECT:
4917 case HTTP_STATUS_MOVED:
4918 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4919 case HTTP_STATUS_REDIRECT_METHOD:
4920 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4923 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4924 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4926 heap_free(request->verb);
4927 request->verb = heap_strdupW(szGET);
4929 http_release_netconn(request, drain_content(request, FALSE));
4930 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4932 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4933 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4934 res = HTTP_HandleRedirect(request, new_url);
4935 if (res == ERROR_SUCCESS)
4937 heap_free(requestString);
4940 heap_free( new_url );
4945 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4947 WCHAR szAuthValue[2048];
4949 if (request->status_code == HTTP_STATUS_DENIED)
4951 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4953 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4955 if (HTTP_DoAuthorization(request, szAuthValue,
4957 request->session->userName,
4958 request->session->password,
4961 heap_free(requestString);
4962 if(!drain_content(request, TRUE)) {
4963 FIXME("Could not drain content\n");
4964 http_release_netconn(request, FALSE);
4972 TRACE("Cleaning wrong authorization data\n");
4973 destroy_authinfo(request->authInfo);
4974 request->authInfo = NULL;
4977 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4980 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4982 if (HTTP_DoAuthorization(request, szAuthValue,
4983 &request->proxyAuthInfo,
4984 request->session->appInfo->proxyUsername,
4985 request->session->appInfo->proxyPassword,
4988 heap_free(requestString);
4989 if(!drain_content(request, TRUE)) {
4990 FIXME("Could not drain content\n");
4991 http_release_netconn(request, FALSE);
4999 TRACE("Cleaning wrong proxy authorization data\n");
5000 destroy_authinfo(request->proxyAuthInfo);
5001 request->proxyAuthInfo = NULL;
5007 res = ERROR_SUCCESS;
5012 heap_free(requestString);
5014 /* TODO: send notification for P3P header */
5016 if(res == ERROR_SUCCESS)
5017 create_cache_entry(request);
5019 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5021 if (res == ERROR_SUCCESS) {
5022 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5023 HTTP_ReceiveRequestData(request, TRUE, NULL);
5025 send_request_complete(request,
5026 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5028 send_request_complete(request, 0, res);
5044 } send_request_task_t;
5046 /***********************************************************************
5048 * Helper functions for the HttpSendRequest(Ex) functions
5051 static void AsyncHttpSendRequestProc(task_header_t *hdr)
5053 send_request_task_t *task = (send_request_task_t*)hdr;
5054 http_request_t *request = (http_request_t*)task->hdr.hdr;
5056 TRACE("%p\n", request);
5058 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
5059 task->optional_len, task->content_len, task->end_request);
5061 heap_free(task->headers);
5065 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5069 DWORD res = ERROR_SUCCESS;
5071 if(!request->netconn) {
5072 WARN("Not connected\n");
5073 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5074 return ERROR_INTERNET_OPERATION_CANCELLED;
5077 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5078 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5080 if (HTTP_GetResponseHeaders(request, &responseLen) || !responseLen)
5081 res = ERROR_HTTP_HEADER_NOT_FOUND;
5083 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5084 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5086 /* process cookies here. Is this right? */
5087 http_process_keep_alive(request);
5088 HTTP_ProcessCookies(request);
5089 HTTP_ProcessExpires(request);
5090 HTTP_ProcessLastModified(request);
5092 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5093 if(!request->contentLength)
5094 http_release_netconn(request, TRUE);
5097 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5099 switch(request->status_code) {
5100 case HTTP_STATUS_REDIRECT:
5101 case HTTP_STATUS_MOVED:
5102 case HTTP_STATUS_REDIRECT_METHOD:
5103 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5104 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5105 dwBufferSize=sizeof(szNewLocation);
5106 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5109 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5110 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5112 heap_free(request->verb);
5113 request->verb = heap_strdupW(szGET);
5115 http_release_netconn(request, drain_content(request, FALSE));
5116 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5118 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5119 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5120 res = HTTP_HandleRedirect(request, new_url);
5121 if (res == ERROR_SUCCESS)
5122 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5123 heap_free( new_url );
5129 if(res == ERROR_SUCCESS)
5130 create_cache_entry(request);
5132 if (res == ERROR_SUCCESS && request->contentLength)
5133 HTTP_ReceiveRequestData(request, TRUE, NULL);
5135 send_request_complete(request, res == ERROR_SUCCESS, res);
5140 /***********************************************************************
5141 * HttpEndRequestA (WININET.@)
5143 * Ends an HTTP request that was started by HttpSendRequestEx
5146 * TRUE if successful
5150 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5151 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5153 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5157 SetLastError(ERROR_INVALID_PARAMETER);
5161 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5168 } end_request_task_t;
5170 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5172 end_request_task_t *task = (end_request_task_t*)hdr;
5173 http_request_t *req = (http_request_t*)task->hdr.hdr;
5177 HTTP_HttpEndRequestW(req, task->flags, task->context);
5180 /***********************************************************************
5181 * HttpEndRequestW (WININET.@)
5183 * Ends an HTTP request that was started by HttpSendRequestEx
5186 * TRUE if successful
5190 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5191 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5193 http_request_t *request;
5196 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5200 SetLastError(ERROR_INVALID_PARAMETER);
5204 request = (http_request_t*) get_handle_object( hRequest );
5206 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5208 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5210 WININET_Release( &request->hdr );
5213 request->hdr.dwFlags |= dwFlags;
5215 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5217 end_request_task_t *task;
5219 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5220 task->flags = dwFlags;
5221 task->context = dwContext;
5223 INTERNET_AsyncCall(&task->hdr);
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 send_request_task_t *task;
5331 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5336 if (lpBuffersIn->lpcszHeader)
5338 if (lpBuffersIn->dwHeadersLength == ~0u)
5339 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5341 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5343 task->headers = heap_alloc(size);
5344 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5346 else task->headers = NULL;
5348 task->headers_len = size / sizeof(WCHAR);
5349 task->optional = lpBuffersIn->lpvBuffer;
5350 task->optional_len = lpBuffersIn->dwBufferLength;
5351 task->content_len = lpBuffersIn->dwBufferTotal;
5355 task->headers = NULL;
5356 task->headers_len = 0;
5357 task->optional = NULL;
5358 task->optional_len = 0;
5359 task->content_len = 0;
5362 task->end_request = FALSE;
5364 INTERNET_AsyncCall(&task->hdr);
5365 res = ERROR_IO_PENDING;
5370 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5371 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5372 lpBuffersIn->dwBufferTotal, FALSE);
5374 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5379 WININET_Release( &request->hdr );
5383 return res == ERROR_SUCCESS;
5386 /***********************************************************************
5387 * HttpSendRequestW (WININET.@)
5389 * Sends the specified request to the HTTP server
5396 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5397 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5399 http_request_t *request;
5400 http_session_t *session = NULL;
5401 appinfo_t *hIC = NULL;
5402 DWORD res = ERROR_SUCCESS;
5404 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5405 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5407 request = (http_request_t*) get_handle_object( hHttpRequest );
5408 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5410 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5414 session = request->session;
5415 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5417 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5421 hIC = session->appInfo;
5422 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5424 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5428 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5430 send_request_task_t *task;
5432 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5437 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5438 else size = dwHeaderLength * sizeof(WCHAR);
5440 task->headers = heap_alloc(size);
5441 memcpy(task->headers, lpszHeaders, size);
5444 task->headers = NULL;
5445 task->headers_len = dwHeaderLength;
5446 task->optional = lpOptional;
5447 task->optional_len = dwOptionalLength;
5448 task->content_len = dwOptionalLength;
5449 task->end_request = TRUE;
5451 INTERNET_AsyncCall(&task->hdr);
5452 res = ERROR_IO_PENDING;
5456 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5457 dwHeaderLength, lpOptional, dwOptionalLength,
5458 dwOptionalLength, TRUE);
5462 WININET_Release( &request->hdr );
5465 return res == ERROR_SUCCESS;
5468 /***********************************************************************
5469 * HttpSendRequestA (WININET.@)
5471 * Sends the specified request to the HTTP server
5478 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5479 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5482 LPWSTR szHeaders=NULL;
5483 DWORD nLen=dwHeaderLength;
5484 if(lpszHeaders!=NULL)
5486 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5487 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5488 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5490 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5491 heap_free(szHeaders);
5495 /***********************************************************************
5496 * HTTPSESSION_Destroy (internal)
5498 * Deallocate session handle
5501 static void HTTPSESSION_Destroy(object_header_t *hdr)
5503 http_session_t *session = (http_session_t*) hdr;
5505 TRACE("%p\n", session);
5507 WININET_Release(&session->appInfo->hdr);
5509 heap_free(session->hostName);
5510 heap_free(session->password);
5511 heap_free(session->userName);
5514 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5516 http_session_t *ses = (http_session_t *)hdr;
5519 case INTERNET_OPTION_HANDLE_TYPE:
5520 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5522 if (*size < sizeof(ULONG))
5523 return ERROR_INSUFFICIENT_BUFFER;
5525 *size = sizeof(DWORD);
5526 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5527 return ERROR_SUCCESS;
5528 case INTERNET_OPTION_CONNECT_TIMEOUT:
5529 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5531 if (*size < sizeof(DWORD))
5532 return ERROR_INSUFFICIENT_BUFFER;
5534 *size = sizeof(DWORD);
5535 *(DWORD *)buffer = ses->connect_timeout;
5536 return ERROR_SUCCESS;
5538 case INTERNET_OPTION_SEND_TIMEOUT:
5539 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5541 if (*size < sizeof(DWORD))
5542 return ERROR_INSUFFICIENT_BUFFER;
5544 *size = sizeof(DWORD);
5545 *(DWORD *)buffer = ses->send_timeout;
5546 return ERROR_SUCCESS;
5548 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5549 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5551 if (*size < sizeof(DWORD))
5552 return ERROR_INSUFFICIENT_BUFFER;
5554 *size = sizeof(DWORD);
5555 *(DWORD *)buffer = ses->receive_timeout;
5556 return ERROR_SUCCESS;
5559 return INET_QueryOption(hdr, option, buffer, size, unicode);
5562 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5564 http_session_t *ses = (http_session_t*)hdr;
5567 case INTERNET_OPTION_USERNAME:
5569 heap_free(ses->userName);
5570 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5571 return ERROR_SUCCESS;
5573 case INTERNET_OPTION_PASSWORD:
5575 heap_free(ses->password);
5576 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5577 return ERROR_SUCCESS;
5579 case INTERNET_OPTION_PROXY_USERNAME:
5581 heap_free(ses->appInfo->proxyUsername);
5582 if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5583 return ERROR_SUCCESS;
5585 case INTERNET_OPTION_PROXY_PASSWORD:
5587 heap_free(ses->appInfo->proxyPassword);
5588 if (!(ses->appInfo->proxyPassword = 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 DWORD HTTP_GetResponseHeaders(http_request_t *request, INT *len)
5737 WCHAR buffer[MAX_REPLY_LEN];
5738 DWORD buflen = MAX_REPLY_LEN;
5740 char bufferA[MAX_REPLY_LEN];
5741 LPWSTR status_code = NULL, status_text = NULL;
5742 DWORD res = ERROR_HTTP_INVALID_SERVER_RESPONSE, cchMaxRawHeaders = 1024;
5743 LPWSTR lpszRawHeaders = NULL;
5745 DWORD cchRawHeaders = 0;
5746 BOOL codeHundred = FALSE;
5750 if(!request->netconn)
5753 /* clear old response headers (eg. from a redirect response) */
5754 HTTP_clear_response_headers( request );
5756 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5759 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5761 buflen = MAX_REPLY_LEN;
5762 if ((res = read_line(request, bufferA, &buflen)))
5765 if (!buflen) goto lend;
5768 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5769 /* check is this a status code line? */
5770 if (!strncmpW(buffer, g_szHttp1_0, 4))
5772 /* split the version from the status code */
5773 status_code = strchrW( buffer, ' ' );
5778 /* split the status code from the status text */
5779 status_text = strchrW( status_code, ' ' );
5784 request->status_code = atoiW(status_code);
5786 TRACE("version [%s] status code [%s] status text [%s]\n",
5787 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5789 codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5791 else if (!codeHundred)
5793 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5795 heap_free(request->version);
5796 heap_free(request->statusText);
5798 request->status_code = HTTP_STATUS_OK;
5799 request->version = heap_strdupW(g_szHttp1_0);
5800 request->statusText = heap_strdupW(szOK);
5802 heap_free(request->rawHeaders);
5803 request->rawHeaders = heap_strdupW(szDefaultHeader);
5807 } while (codeHundred);
5809 /* Add status code */
5810 HTTP_ProcessHeader(request, szStatus, status_code,
5811 HTTP_ADDHDR_FLAG_REPLACE);
5813 heap_free(request->version);
5814 heap_free(request->statusText);
5816 request->version = heap_strdupW(buffer);
5817 request->statusText = heap_strdupW(status_text);
5819 /* Restore the spaces */
5820 *(status_code-1) = ' ';
5821 *(status_text-1) = ' ';
5823 /* regenerate raw headers */
5824 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5825 if (!lpszRawHeaders) goto lend;
5827 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5828 cchMaxRawHeaders *= 2;
5829 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5830 if (temp == NULL) goto lend;
5831 lpszRawHeaders = temp;
5832 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5833 cchRawHeaders += (buflen-1);
5834 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5835 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5836 lpszRawHeaders[cchRawHeaders] = '\0';
5838 /* Parse each response line */
5841 buflen = MAX_REPLY_LEN;
5842 if (!read_line(request, bufferA, &buflen) && buflen)
5844 LPWSTR * pFieldAndValue;
5846 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5848 if (!bufferA[0]) break;
5849 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5851 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5854 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5855 cchMaxRawHeaders *= 2;
5856 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5857 if (temp == NULL) goto lend;
5858 lpszRawHeaders = temp;
5859 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5860 cchRawHeaders += (buflen-1);
5861 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5862 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5863 lpszRawHeaders[cchRawHeaders] = '\0';
5865 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5866 HTTP_ADDREQ_FLAG_ADD );
5868 HTTP_FreeTokens(pFieldAndValue);
5879 /* make sure the response header is terminated with an empty line. Some apps really
5880 truly care about that empty line being there for some reason. Just add it to the
5882 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5884 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5885 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5886 if (temp == NULL) goto lend;
5887 lpszRawHeaders = temp;
5890 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5892 heap_free(request->rawHeaders);
5893 request->rawHeaders = lpszRawHeaders;
5894 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5895 res = ERROR_SUCCESS;
5899 if (res) heap_free(lpszRawHeaders);
5905 /***********************************************************************
5906 * HTTP_InterpretHttpHeader (internal)
5908 * Parse server response
5912 * Pointer to array of field, value, NULL on success.
5915 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5917 LPWSTR * pTokenPair;
5921 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5923 pszColon = strchrW(buffer, ':');
5924 /* must have two tokens */
5927 HTTP_FreeTokens(pTokenPair);
5929 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5933 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5936 HTTP_FreeTokens(pTokenPair);
5939 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5940 pTokenPair[0][pszColon - buffer] = '\0';
5944 len = strlenW(pszColon);
5945 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5948 HTTP_FreeTokens(pTokenPair);
5951 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5953 strip_spaces(pTokenPair[0]);
5954 strip_spaces(pTokenPair[1]);
5956 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5960 /***********************************************************************
5961 * HTTP_ProcessHeader (internal)
5963 * Stuff header into header tables according to <dwModifier>
5967 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5969 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5971 LPHTTPHEADERW lphttpHdr = NULL;
5973 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5974 DWORD res = ERROR_HTTP_INVALID_HEADER;
5976 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5978 /* REPLACE wins out over ADD */
5979 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5980 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5982 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5985 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5989 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5990 return ERROR_HTTP_INVALID_HEADER;
5991 lphttpHdr = &request->custHeaders[index];
5997 hdr.lpszField = (LPWSTR)field;
5998 hdr.lpszValue = (LPWSTR)value;
5999 hdr.wFlags = hdr.wCount = 0;
6001 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6002 hdr.wFlags |= HDR_ISREQUEST;
6004 return HTTP_InsertCustomHeader(request, &hdr);
6006 /* no value to delete */
6007 else return ERROR_SUCCESS;
6009 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6010 lphttpHdr->wFlags |= HDR_ISREQUEST;
6012 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6014 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6016 HTTP_DeleteCustomHeader( request, index );
6022 hdr.lpszField = (LPWSTR)field;
6023 hdr.lpszValue = (LPWSTR)value;
6024 hdr.wFlags = hdr.wCount = 0;
6026 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6027 hdr.wFlags |= HDR_ISREQUEST;
6029 return HTTP_InsertCustomHeader(request, &hdr);
6032 return ERROR_SUCCESS;
6034 else if (dwModifier & COALESCEFLAGS)
6039 INT origlen = strlenW(lphttpHdr->lpszValue);
6040 INT valuelen = strlenW(value);
6042 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6045 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6047 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6050 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6053 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6055 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6058 lphttpHdr->lpszValue = lpsztmp;
6059 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6062 lphttpHdr->lpszValue[origlen] = ch;
6064 lphttpHdr->lpszValue[origlen] = ' ';
6068 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6069 lphttpHdr->lpszValue[len] = '\0';
6070 res = ERROR_SUCCESS;
6074 WARN("heap_realloc (%d bytes) failed\n",len+1);
6075 res = ERROR_OUTOFMEMORY;
6078 TRACE("<-- %d\n", res);
6082 /***********************************************************************
6083 * HTTP_GetCustomHeaderIndex (internal)
6085 * Return index of custom header from header array
6088 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6089 int requested_index, BOOL request_only)
6093 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6095 for (index = 0; index < request->nCustHeaders; index++)
6097 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6100 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6103 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6106 if (requested_index == 0)
6111 if (index >= request->nCustHeaders)
6114 TRACE("Return: %d\n", index);
6119 /***********************************************************************
6120 * HTTP_InsertCustomHeader (internal)
6122 * Insert header into array
6125 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6128 LPHTTPHEADERW lph = NULL;
6130 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6131 count = request->nCustHeaders + 1;
6133 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6135 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6138 return ERROR_OUTOFMEMORY;
6140 request->custHeaders = lph;
6141 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6142 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6143 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6144 request->custHeaders[count-1].wCount= lpHdr->wCount;
6145 request->nCustHeaders++;
6147 return ERROR_SUCCESS;
6151 /***********************************************************************
6152 * HTTP_DeleteCustomHeader (internal)
6154 * Delete header from array
6155 * If this function is called, the indexs may change.
6157 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6159 if( request->nCustHeaders <= 0 )
6161 if( index >= request->nCustHeaders )
6163 request->nCustHeaders--;
6165 heap_free(request->custHeaders[index].lpszField);
6166 heap_free(request->custHeaders[index].lpszValue);
6168 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6169 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6170 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6176 /***********************************************************************
6177 * HTTP_VerifyValidHeader (internal)
6179 * Verify the given header is not invalid for the given http request
6182 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6184 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6185 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6186 return ERROR_HTTP_INVALID_HEADER;
6188 return ERROR_SUCCESS;
6191 /***********************************************************************
6192 * IsHostInProxyBypassList (@)
6197 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6199 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);