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 BOOL HTTP_GetResponseHeaders(http_request_t *req);
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 BOOL 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 )) != ERROR_SUCCESS || !req->read_size)
2433 TRACE( "returning empty string %u\n", res);
2434 LeaveCriticalSection( &req->read_section );
2435 INTERNET_SetLastError(res);
2439 LeaveCriticalSection( &req->read_section );
2443 if (pos && buffer[pos - 1] == '\r') pos--;
2446 buffer[*len - 1] = 0;
2447 TRACE( "returning %s\n", debugstr_a(buffer));
2451 /* check if we have reached the end of the data to read (the read section must be held) */
2452 static BOOL end_of_read_data( http_request_t *req )
2454 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2457 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2461 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2462 assert(*read <= size);
2464 if(req->hCacheFile) {
2469 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2471 FIXME("WriteFile failed: %u\n", GetLastError());
2474 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2475 commit_cache_entry(req);
2481 /* fetch some more data into the read buffer (the read section must be held) */
2482 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2486 if(req->read_size == sizeof(req->read_buf))
2487 return ERROR_SUCCESS;
2491 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2495 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2497 req->read_size += read;
2499 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2505 /* return the size of data available to be read immediately (the read section must be held) */
2506 static DWORD get_avail_data( http_request_t *req )
2508 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2511 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2513 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2517 NETCON_query_data_available(req->netconn, &avail);
2518 return netconn_stream->content_length == ~0u
2520 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2523 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2525 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2526 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2529 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2530 DWORD *read, read_mode_t read_mode)
2532 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2535 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2537 if(read_mode == READMODE_NOBLOCK) {
2538 DWORD avail = netconn_get_avail_data(stream, req);
2543 if(size && req->netconn) {
2544 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2547 netconn_stream->content_length = netconn_stream->content_read;
2550 netconn_stream->content_read += *read = len;
2551 TRACE("read %u bytes\n", len);
2552 return ERROR_SUCCESS;
2555 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2557 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2562 if(netconn_end_of_data(stream, req))
2566 avail = netconn_get_avail_data(stream, req);
2570 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2573 netconn_stream->content_read += len;
2574 }while(netconn_stream->content_read < netconn_stream->content_length);
2579 static void netconn_destroy(data_stream_t *stream)
2583 static const data_stream_vtbl_t netconn_stream_vtbl = {
2584 netconn_get_avail_data,
2585 netconn_end_of_data,
2587 netconn_drain_content,
2591 /* read some more data into the read buffer (the read section must be held) */
2592 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2597 if (stream->buf_pos)
2599 /* move existing data to the start of the buffer */
2600 if(stream->buf_size)
2601 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2602 stream->buf_pos = 0;
2605 if (maxlen == -1) maxlen = sizeof(stream->buf);
2607 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2608 maxlen - stream->buf_size, 0, &len );
2609 if(res == ERROR_SUCCESS)
2610 stream->buf_size += len;
2615 /* remove some amount of data from the read buffer (the read section must be held) */
2616 static void remove_chunked_data(chunked_stream_t *stream, int count)
2618 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2619 else stream->buf_pos += count;
2622 /* discard data contents until we reach end of line (the read section must be held) */
2623 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2629 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2632 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2635 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2636 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2637 } while (stream->buf_size);
2638 return ERROR_SUCCESS;
2641 /* read the size of the next chunk (the read section must be held) */
2642 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2645 DWORD chunk_size = 0, res;
2647 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2652 while (stream->buf_size)
2654 char ch = stream->buf[stream->buf_pos];
2655 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2656 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2657 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2658 else if (ch == ';' || ch == '\r' || ch == '\n')
2660 TRACE( "reading %u byte chunk\n", chunk_size );
2661 stream->chunk_size = chunk_size;
2662 req->contentLength += chunk_size;
2663 return discard_chunked_eol(stream, req);
2665 remove_chunked_data(stream, 1);
2667 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2668 if (!stream->buf_size)
2670 stream->chunk_size = 0;
2671 return ERROR_SUCCESS;
2676 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2678 /* Allow reading only from read buffer */
2682 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2684 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2685 return !chunked_stream->chunk_size;
2688 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2689 DWORD *read, read_mode_t read_mode)
2691 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2692 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2694 if(chunked_stream->chunk_size == ~0u) {
2695 res = start_next_chunk(chunked_stream, req);
2696 if(res != ERROR_SUCCESS)
2700 while(size && chunked_stream->chunk_size) {
2701 if(chunked_stream->buf_size) {
2702 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2704 /* this could block */
2705 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2708 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2709 remove_chunked_data(chunked_stream, read_bytes);
2711 read_bytes = min(size, chunked_stream->chunk_size);
2713 if(read_mode == READMODE_NOBLOCK) {
2716 if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
2718 if(read_bytes > avail)
2721 /* this could block */
2722 if(read_bytes == chunked_stream->chunk_size)
2726 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2727 if(res != ERROR_SUCCESS)
2731 chunked_stream->chunk_size -= read_bytes;
2733 ret_read += read_bytes;
2734 if(!chunked_stream->chunk_size) {
2735 assert(read_mode != READMODE_NOBLOCK);
2736 res = start_next_chunk(chunked_stream, req);
2737 if(res != ERROR_SUCCESS)
2741 if(read_mode == READMODE_ASYNC)
2742 read_mode = READMODE_NOBLOCK;
2745 TRACE("read %u bytes\n", ret_read);
2750 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2752 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2754 /* FIXME: we can do better */
2755 return !chunked_stream->chunk_size;
2758 static void chunked_destroy(data_stream_t *stream)
2760 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2761 heap_free(chunked_stream);
2764 static const data_stream_vtbl_t chunked_stream_vtbl = {
2765 chunked_get_avail_data,
2766 chunked_end_of_data,
2768 chunked_drain_content,
2772 /* set the request content length based on the headers */
2773 static DWORD set_content_length(http_request_t *request)
2775 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2779 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2780 request->contentLength = request->netconn_stream.content_length = 0;
2781 return ERROR_SUCCESS;
2784 size = sizeof(request->contentLength);
2785 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2786 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2787 request->contentLength = ~0u;
2788 request->netconn_stream.content_length = request->contentLength;
2789 request->netconn_stream.content_read = request->read_size;
2791 size = sizeof(encoding);
2792 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2793 !strcmpiW(encoding, szChunked))
2795 chunked_stream_t *chunked_stream;
2797 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2799 return ERROR_OUTOFMEMORY;
2801 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2802 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2803 chunked_stream->chunk_size = ~0u;
2805 if(request->read_size) {
2806 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2807 chunked_stream->buf_size = request->read_size;
2808 request->read_size = request->read_pos = 0;
2811 request->data_stream = &chunked_stream->data_stream;
2812 request->contentLength = ~0u;
2813 request->read_chunked = TRUE;
2816 if(request->decoding) {
2819 static const WCHAR gzipW[] = {'g','z','i','p',0};
2821 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2822 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2823 return init_gzip_stream(request);
2826 return ERROR_SUCCESS;
2829 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2831 INTERNET_ASYNC_RESULT iar;
2833 iar.dwResult = result;
2834 iar.dwError = error;
2836 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2837 sizeof(INTERNET_ASYNC_RESULT));
2840 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2842 DWORD res, read = 0, avail = 0;
2847 EnterCriticalSection( &req->read_section );
2849 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2850 res = refill_read_buffer(req, mode, &read);
2851 if(res == ERROR_SUCCESS)
2852 avail = get_avail_data(req);
2854 LeaveCriticalSection( &req->read_section );
2856 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2857 WARN("res %u read %u, closing connection\n", res, read);
2858 http_release_netconn(req, FALSE);
2861 if(res != ERROR_SUCCESS) {
2862 send_request_complete(req, 0, res);
2871 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2874 /* read data from the http connection (the read section must be held) */
2875 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2877 DWORD current_read = 0, ret_read = 0;
2878 read_mode_t read_mode;
2879 DWORD res = ERROR_SUCCESS;
2881 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2883 EnterCriticalSection( &req->read_section );
2885 if(req->read_size) {
2886 ret_read = min(size, req->read_size);
2887 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2888 req->read_size -= ret_read;
2889 req->read_pos += ret_read;
2890 if(read_mode == READMODE_ASYNC)
2891 read_mode = READMODE_NOBLOCK;
2894 if(ret_read < size) {
2895 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, read_mode);
2896 ret_read += current_read;
2899 LeaveCriticalSection( &req->read_section );
2902 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2904 if(size && !ret_read)
2905 http_release_netconn(req, res == ERROR_SUCCESS);
2910 static BOOL drain_content(http_request_t *req, BOOL blocking)
2914 if(!req->netconn || req->contentLength == -1)
2917 if(!strcmpW(req->verb, szHEAD))
2921 return req->data_stream->vtbl->drain_content(req->data_stream, req);
2923 EnterCriticalSection( &req->read_section );
2926 DWORD bytes_read, res;
2929 res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2930 if(res != ERROR_SUCCESS) {
2940 LeaveCriticalSection( &req->read_section );
2944 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2946 http_request_t *req = (http_request_t*)hdr;
2949 EnterCriticalSection( &req->read_section );
2950 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2951 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2953 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2954 if(res == ERROR_SUCCESS)
2956 LeaveCriticalSection( &req->read_section );
2966 } read_file_ex_task_t;
2968 static void AsyncReadFileExProc(task_header_t *hdr)
2970 read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2971 http_request_t *req = (http_request_t*)task->hdr.hdr;
2974 TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2976 res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2977 send_request_complete(req, res == ERROR_SUCCESS, res);
2980 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2981 DWORD flags, DWORD_PTR context)
2984 http_request_t *req = (http_request_t*)hdr;
2985 DWORD res, read, cread, error = ERROR_SUCCESS;
2987 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2988 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2990 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2992 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2994 read_file_ex_task_t *task;
2996 if (TryEnterCriticalSection( &req->read_section ))
2998 if (get_avail_data(req))
3000 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
3001 LeaveCriticalSection( &req->read_section );
3004 LeaveCriticalSection( &req->read_section );
3007 task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
3010 task->ret_read = ret_read;
3012 INTERNET_AsyncCall(&task->hdr);
3014 return ERROR_IO_PENDING;
3019 EnterCriticalSection( &req->read_section );
3020 if(hdr->dwError == ERROR_SUCCESS)
3021 hdr->dwError = INTERNET_HANDLE_IN_USE;
3022 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3023 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3026 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
3027 if(res != ERROR_SUCCESS)
3031 if(read == size || end_of_read_data(req))
3034 LeaveCriticalSection( &req->read_section );
3036 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3037 &cread, sizeof(cread));
3038 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3039 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3041 EnterCriticalSection( &req->read_section );
3044 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3045 hdr->dwError = ERROR_SUCCESS;
3047 error = hdr->dwError;
3049 LeaveCriticalSection( &req->read_section );
3053 if (res == ERROR_SUCCESS) {
3054 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3055 &read, sizeof(read));
3058 return res==ERROR_SUCCESS ? error : res;
3061 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3064 http_request_t *request = (http_request_t*)hdr;
3066 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3069 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3070 if (res == ERROR_SUCCESS)
3071 request->bytesWritten += *written;
3073 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3080 } http_data_available_task_t;
3082 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3084 http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3086 HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3089 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3091 http_request_t *req = (http_request_t*)hdr;
3093 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3095 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3097 http_data_available_task_t *task;
3099 /* never wait, if we can't enter the section we queue an async request right away */
3100 if (TryEnterCriticalSection( &req->read_section ))
3102 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3103 if ((*available = get_avail_data( req ))) goto done;
3104 if (end_of_read_data( req )) goto done;
3105 LeaveCriticalSection( &req->read_section );
3108 task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3109 task->ret_size = available;
3110 INTERNET_AsyncCall(&task->hdr);
3111 return ERROR_IO_PENDING;
3114 EnterCriticalSection( &req->read_section );
3116 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3118 refill_read_buffer( req, READMODE_ASYNC, NULL );
3119 *available = get_avail_data( req );
3123 LeaveCriticalSection( &req->read_section );
3125 TRACE( "returning %u\n", *available );
3126 return ERROR_SUCCESS;
3129 static const object_vtbl_t HTTPREQVtbl = {
3131 HTTPREQ_CloseConnection,
3132 HTTPREQ_QueryOption,
3137 HTTPREQ_QueryDataAvailable,
3141 /***********************************************************************
3142 * HTTP_HttpOpenRequestW (internal)
3144 * Open a HTTP request handle
3147 * HINTERNET a HTTP request handle on success
3151 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3152 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3153 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3154 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3156 appinfo_t *hIC = session->appInfo;
3157 http_request_t *request;
3158 DWORD len, res = ERROR_SUCCESS;
3162 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3164 return ERROR_OUTOFMEMORY;
3166 request->hdr.htype = WH_HHTTPREQ;
3167 request->hdr.dwFlags = dwFlags;
3168 request->hdr.dwContext = dwContext;
3169 request->contentLength = ~0u;
3171 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3172 request->data_stream = &request->netconn_stream.data_stream;
3173 request->connect_timeout = session->connect_timeout;
3174 request->send_timeout = session->send_timeout;
3175 request->receive_timeout = session->receive_timeout;
3177 InitializeCriticalSection( &request->read_section );
3178 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3180 WININET_AddRef( &session->hdr );
3181 request->session = session;
3182 list_add_head( &session->hdr.children, &request->hdr.entry );
3184 request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3185 if(!request->server) {
3186 WININET_Release(&request->hdr);
3187 return ERROR_OUTOFMEMORY;
3190 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3191 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3192 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3193 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3195 if (lpszObjectName && *lpszObjectName) {
3199 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3200 if (rc != E_POINTER)
3201 len = strlenW(lpszObjectName)+1;
3202 request->path = heap_alloc(len*sizeof(WCHAR));
3203 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3204 URL_ESCAPE_SPACES_ONLY);
3207 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3208 strcpyW(request->path,lpszObjectName);
3211 static const WCHAR slashW[] = {'/',0};
3213 request->path = heap_strdupW(slashW);
3216 if (lpszReferrer && *lpszReferrer)
3217 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3219 if (lpszAcceptTypes)
3222 for (i = 0; lpszAcceptTypes[i]; i++)
3224 if (!*lpszAcceptTypes[i]) continue;
3225 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3226 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3227 HTTP_ADDHDR_FLAG_REQ |
3228 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3232 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3233 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3235 HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3237 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3238 session->hostPort = INTERNET_DEFAULT_HTTP_PORT;
3240 if (hIC->proxy && hIC->proxy[0])
3241 HTTP_DealWithProxy( hIC, session, request );
3243 INTERNET_SendCallback(&session->hdr, dwContext,
3244 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3247 TRACE("<-- %u (%p)\n", res, request);
3249 if(res != ERROR_SUCCESS) {
3250 WININET_Release( &request->hdr );
3255 *ret = request->hdr.hInternet;
3256 return ERROR_SUCCESS;
3259 /***********************************************************************
3260 * HttpOpenRequestW (WININET.@)
3262 * Open a HTTP request handle
3265 * HINTERNET a HTTP request handle on success
3269 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3270 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3271 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3272 DWORD dwFlags, DWORD_PTR dwContext)
3274 http_session_t *session;
3275 HINTERNET handle = NULL;
3278 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3279 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3280 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3281 dwFlags, dwContext);
3282 if(lpszAcceptTypes!=NULL)
3285 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3286 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3289 session = (http_session_t*) get_handle_object( hHttpSession );
3290 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3292 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3297 * My tests seem to show that the windows version does not
3298 * become asynchronous until after this point. And anyhow
3299 * if this call was asynchronous then how would you get the
3300 * necessary HINTERNET pointer returned by this function.
3303 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3304 lpszVersion, lpszReferrer, lpszAcceptTypes,
3305 dwFlags, dwContext, &handle);
3308 WININET_Release( &session->hdr );
3309 TRACE("returning %p\n", handle);
3310 if(res != ERROR_SUCCESS)
3315 static const LPCWSTR header_lookup[] = {
3316 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3317 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3318 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3319 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3320 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3321 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3322 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3323 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3324 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3325 szDate, /* HTTP_QUERY_DATE = 9 */
3326 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3327 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3328 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3329 szURI, /* HTTP_QUERY_URI = 13 */
3330 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3331 NULL, /* HTTP_QUERY_COST = 15 */
3332 NULL, /* HTTP_QUERY_LINK = 16 */
3333 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3334 NULL, /* HTTP_QUERY_VERSION = 18 */
3335 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3336 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3337 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3338 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3339 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3340 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3341 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3342 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3343 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3344 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3345 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3346 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3347 NULL, /* HTTP_QUERY_FROM = 31 */
3348 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3349 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3350 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3351 szReferer, /* HTTP_QUERY_REFERER = 35 */
3352 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3353 szServer, /* HTTP_QUERY_SERVER = 37 */
3354 NULL, /* HTTP_TITLE = 38 */
3355 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3356 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3357 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3358 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3359 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3360 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3361 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3362 NULL, /* HTTP_QUERY_REFRESH = 46 */
3363 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3364 szAge, /* HTTP_QUERY_AGE = 48 */
3365 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3366 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3367 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3368 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3369 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3370 szETag, /* HTTP_QUERY_ETAG = 54 */
3371 hostW, /* HTTP_QUERY_HOST = 55 */
3372 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3373 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3374 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3375 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3376 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3377 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3378 szRange, /* HTTP_QUERY_RANGE = 62 */
3379 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3380 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3381 szVary, /* HTTP_QUERY_VARY = 65 */
3382 szVia, /* HTTP_QUERY_VIA = 66 */
3383 szWarning, /* HTTP_QUERY_WARNING = 67 */
3384 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3385 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3386 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3389 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3391 /***********************************************************************
3392 * HTTP_HttpQueryInfoW (internal)
3394 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3395 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3397 LPHTTPHEADERW lphttpHdr = NULL;
3398 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3399 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3400 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3403 /* Find requested header structure */
3406 case HTTP_QUERY_CUSTOM:
3407 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3408 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3410 case HTTP_QUERY_RAW_HEADERS_CRLF:
3414 DWORD res = ERROR_INVALID_PARAMETER;
3417 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3419 headers = request->rawHeaders;
3422 len = strlenW(headers) * sizeof(WCHAR);
3424 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3426 len += sizeof(WCHAR);
3427 res = ERROR_INSUFFICIENT_BUFFER;
3432 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3435 len = strlenW(szCrLf) * sizeof(WCHAR);
3436 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3438 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3439 res = ERROR_SUCCESS;
3441 *lpdwBufferLength = len;
3443 if (request_only) heap_free(headers);
3446 case HTTP_QUERY_RAW_HEADERS:
3448 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3450 LPWSTR pszString = lpBuffer;
3452 for (i = 0; ppszRawHeaderLines[i]; i++)
3453 size += strlenW(ppszRawHeaderLines[i]) + 1;
3455 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3457 HTTP_FreeTokens(ppszRawHeaderLines);
3458 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3459 return ERROR_INSUFFICIENT_BUFFER;
3463 for (i = 0; ppszRawHeaderLines[i]; i++)
3465 DWORD len = strlenW(ppszRawHeaderLines[i]);
3466 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3470 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3472 *lpdwBufferLength = size * sizeof(WCHAR);
3473 HTTP_FreeTokens(ppszRawHeaderLines);
3475 return ERROR_SUCCESS;
3477 case HTTP_QUERY_STATUS_TEXT:
3478 if (request->statusText)
3480 DWORD len = strlenW(request->statusText);
3481 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3483 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3484 return ERROR_INSUFFICIENT_BUFFER;
3488 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3489 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3491 *lpdwBufferLength = len * sizeof(WCHAR);
3492 return ERROR_SUCCESS;
3495 case HTTP_QUERY_VERSION:
3496 if (request->version)
3498 DWORD len = strlenW(request->version);
3499 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3501 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3502 return ERROR_INSUFFICIENT_BUFFER;
3506 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3507 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3509 *lpdwBufferLength = len * sizeof(WCHAR);
3510 return ERROR_SUCCESS;
3513 case HTTP_QUERY_CONTENT_ENCODING:
3514 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3515 requested_index,request_only);
3517 case HTTP_QUERY_STATUS_CODE: {
3518 DWORD res = ERROR_SUCCESS;
3521 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3526 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3527 if(*lpdwBufferLength >= sizeof(DWORD))
3528 *(DWORD*)lpBuffer = request->status_code;
3530 res = ERROR_INSUFFICIENT_BUFFER;
3531 *lpdwBufferLength = sizeof(DWORD);
3535 static const WCHAR formatW[] = {'%','u',0};
3537 size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3539 if(size <= *lpdwBufferLength) {
3540 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3542 size += sizeof(WCHAR);
3543 res = ERROR_INSUFFICIENT_BUFFER;
3546 *lpdwBufferLength = size;
3551 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3553 if (level < LAST_TABLE_HEADER && header_lookup[level])
3554 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3555 requested_index,request_only);
3559 lphttpHdr = &request->custHeaders[index];
3561 /* Ensure header satisfies requested attributes */
3563 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3564 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3566 return ERROR_HTTP_HEADER_NOT_FOUND;
3569 if (lpdwIndex) (*lpdwIndex)++;
3571 /* coalesce value to requested type */
3572 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3574 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3575 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3577 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3583 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3585 tmpTM = *gmtime(&tmpTime);
3586 STHook = (SYSTEMTIME *)lpBuffer;
3587 STHook->wDay = tmpTM.tm_mday;
3588 STHook->wHour = tmpTM.tm_hour;
3589 STHook->wMilliseconds = 0;
3590 STHook->wMinute = tmpTM.tm_min;
3591 STHook->wDayOfWeek = tmpTM.tm_wday;
3592 STHook->wMonth = tmpTM.tm_mon + 1;
3593 STHook->wSecond = tmpTM.tm_sec;
3594 STHook->wYear = 1900+tmpTM.tm_year;
3596 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3597 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3598 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3600 else if (lphttpHdr->lpszValue)
3602 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3604 if (len > *lpdwBufferLength)
3606 *lpdwBufferLength = len;
3607 return ERROR_INSUFFICIENT_BUFFER;
3611 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3612 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3614 *lpdwBufferLength = len - sizeof(WCHAR);
3616 return ERROR_SUCCESS;
3619 /***********************************************************************
3620 * HttpQueryInfoW (WININET.@)
3622 * Queries for information about an HTTP request
3629 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3630 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3632 http_request_t *request;
3635 if (TRACE_ON(wininet)) {
3636 #define FE(x) { x, #x }
3637 static const wininet_flag_info query_flags[] = {
3638 FE(HTTP_QUERY_MIME_VERSION),
3639 FE(HTTP_QUERY_CONTENT_TYPE),
3640 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3641 FE(HTTP_QUERY_CONTENT_ID),
3642 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3643 FE(HTTP_QUERY_CONTENT_LENGTH),
3644 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3645 FE(HTTP_QUERY_ALLOW),
3646 FE(HTTP_QUERY_PUBLIC),
3647 FE(HTTP_QUERY_DATE),
3648 FE(HTTP_QUERY_EXPIRES),
3649 FE(HTTP_QUERY_LAST_MODIFIED),
3650 FE(HTTP_QUERY_MESSAGE_ID),
3652 FE(HTTP_QUERY_DERIVED_FROM),
3653 FE(HTTP_QUERY_COST),
3654 FE(HTTP_QUERY_LINK),
3655 FE(HTTP_QUERY_PRAGMA),
3656 FE(HTTP_QUERY_VERSION),
3657 FE(HTTP_QUERY_STATUS_CODE),
3658 FE(HTTP_QUERY_STATUS_TEXT),
3659 FE(HTTP_QUERY_RAW_HEADERS),
3660 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3661 FE(HTTP_QUERY_CONNECTION),
3662 FE(HTTP_QUERY_ACCEPT),
3663 FE(HTTP_QUERY_ACCEPT_CHARSET),
3664 FE(HTTP_QUERY_ACCEPT_ENCODING),
3665 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3666 FE(HTTP_QUERY_AUTHORIZATION),
3667 FE(HTTP_QUERY_CONTENT_ENCODING),
3668 FE(HTTP_QUERY_FORWARDED),
3669 FE(HTTP_QUERY_FROM),
3670 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3671 FE(HTTP_QUERY_LOCATION),
3672 FE(HTTP_QUERY_ORIG_URI),
3673 FE(HTTP_QUERY_REFERER),
3674 FE(HTTP_QUERY_RETRY_AFTER),
3675 FE(HTTP_QUERY_SERVER),
3676 FE(HTTP_QUERY_TITLE),
3677 FE(HTTP_QUERY_USER_AGENT),
3678 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3679 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3680 FE(HTTP_QUERY_ACCEPT_RANGES),
3681 FE(HTTP_QUERY_SET_COOKIE),
3682 FE(HTTP_QUERY_COOKIE),
3683 FE(HTTP_QUERY_REQUEST_METHOD),
3684 FE(HTTP_QUERY_REFRESH),
3685 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3687 FE(HTTP_QUERY_CACHE_CONTROL),
3688 FE(HTTP_QUERY_CONTENT_BASE),
3689 FE(HTTP_QUERY_CONTENT_LOCATION),
3690 FE(HTTP_QUERY_CONTENT_MD5),
3691 FE(HTTP_QUERY_CONTENT_RANGE),
3692 FE(HTTP_QUERY_ETAG),
3693 FE(HTTP_QUERY_HOST),
3694 FE(HTTP_QUERY_IF_MATCH),
3695 FE(HTTP_QUERY_IF_NONE_MATCH),
3696 FE(HTTP_QUERY_IF_RANGE),
3697 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3698 FE(HTTP_QUERY_MAX_FORWARDS),
3699 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3700 FE(HTTP_QUERY_RANGE),
3701 FE(HTTP_QUERY_TRANSFER_ENCODING),
3702 FE(HTTP_QUERY_UPGRADE),
3703 FE(HTTP_QUERY_VARY),
3705 FE(HTTP_QUERY_WARNING),
3706 FE(HTTP_QUERY_CUSTOM)
3708 static const wininet_flag_info modifier_flags[] = {
3709 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3710 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3711 FE(HTTP_QUERY_FLAG_NUMBER),
3712 FE(HTTP_QUERY_FLAG_COALESCE)
3715 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3716 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3719 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3720 TRACE(" Attribute:");
3721 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3722 if (query_flags[i].val == info) {
3723 TRACE(" %s", query_flags[i].name);
3727 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3728 TRACE(" Unknown (%08x)", info);
3731 TRACE(" Modifier:");
3732 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3733 if (modifier_flags[i].val & info_mod) {
3734 TRACE(" %s", modifier_flags[i].name);
3735 info_mod &= ~ modifier_flags[i].val;
3740 TRACE(" Unknown (%08x)", info_mod);
3745 request = (http_request_t*) get_handle_object( hHttpRequest );
3746 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3748 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3752 if (lpBuffer == NULL)
3753 *lpdwBufferLength = 0;
3754 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3755 lpBuffer, lpdwBufferLength, lpdwIndex);
3759 WININET_Release( &request->hdr );
3761 TRACE("%u <--\n", res);
3762 if(res != ERROR_SUCCESS)
3764 return res == ERROR_SUCCESS;
3767 /***********************************************************************
3768 * HttpQueryInfoA (WININET.@)
3770 * Queries for information about an HTTP request
3777 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3778 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3784 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3785 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3787 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3788 lpdwBufferLength, lpdwIndex );
3794 len = (*lpdwBufferLength)*sizeof(WCHAR);
3795 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3797 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3803 bufferW = heap_alloc(alloclen);
3804 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3805 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3806 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3813 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3817 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3818 lpBuffer, *lpdwBufferLength, NULL, NULL );
3819 *lpdwBufferLength = len - 1;
3821 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3824 /* since the strings being returned from HttpQueryInfoW should be
3825 * only ASCII characters, it is reasonable to assume that all of
3826 * the Unicode characters can be reduced to a single byte */
3827 *lpdwBufferLength = len / sizeof(WCHAR);
3829 heap_free( bufferW );
3833 /***********************************************************************
3834 * HTTP_GetRedirectURL (internal)
3836 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3838 static WCHAR szHttp[] = {'h','t','t','p',0};
3839 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3840 http_session_t *session = request->session;
3841 URL_COMPONENTSW urlComponents;
3842 DWORD url_length = 0;
3844 LPWSTR combined_url;
3846 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3847 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3848 urlComponents.dwSchemeLength = 0;
3849 urlComponents.lpszHostName = session->hostName;
3850 urlComponents.dwHostNameLength = 0;
3851 urlComponents.nPort = session->hostPort;
3852 urlComponents.lpszUserName = session->userName;
3853 urlComponents.dwUserNameLength = 0;
3854 urlComponents.lpszPassword = NULL;
3855 urlComponents.dwPasswordLength = 0;
3856 urlComponents.lpszUrlPath = request->path;
3857 urlComponents.dwUrlPathLength = 0;
3858 urlComponents.lpszExtraInfo = NULL;
3859 urlComponents.dwExtraInfoLength = 0;
3861 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3862 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3865 orig_url = heap_alloc(url_length);
3867 /* convert from bytes to characters */
3868 url_length = url_length / sizeof(WCHAR) - 1;
3869 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3871 heap_free(orig_url);
3876 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3877 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3879 heap_free(orig_url);
3882 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3884 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3886 heap_free(orig_url);
3887 heap_free(combined_url);
3890 heap_free(orig_url);
3891 return combined_url;
3895 /***********************************************************************
3896 * HTTP_HandleRedirect (internal)
3898 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3900 http_session_t *session = request->session;
3901 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3906 /* if it's an absolute path, keep the same session info */
3907 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3911 URL_COMPONENTSW urlComponents;
3912 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3913 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3914 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3915 BOOL custom_port = FALSE;
3917 static WCHAR httpW[] = {'h','t','t','p',0};
3918 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3924 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3925 urlComponents.lpszScheme = protocol;
3926 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3927 urlComponents.lpszHostName = hostName;
3928 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3929 urlComponents.lpszUserName = userName;
3930 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3931 urlComponents.lpszPassword = NULL;
3932 urlComponents.dwPasswordLength = 0;
3933 urlComponents.lpszUrlPath = path;
3934 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3935 urlComponents.lpszExtraInfo = NULL;
3936 urlComponents.dwExtraInfoLength = 0;
3937 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3938 return INTERNET_GetLastError();
3940 if(!strcmpiW(protocol, httpW)) {
3941 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3942 TRACE("redirect from secure page to non-secure page\n");
3943 /* FIXME: warn about from secure redirect to non-secure page */
3944 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3947 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3948 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3949 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3951 }else if(!strcmpiW(protocol, httpsW)) {
3952 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3953 TRACE("redirect from non-secure page to secure page\n");
3954 /* FIXME: notify about redirect to secure page */
3955 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3958 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3959 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3960 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3964 heap_free(session->hostName);
3968 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3969 len = lstrlenW(hostName);
3970 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3971 session->hostName = heap_alloc(len*sizeof(WCHAR));
3972 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3975 session->hostName = heap_strdupW(hostName);
3977 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3979 heap_free(session->userName);
3980 session->userName = NULL;
3982 session->userName = heap_strdupW(userName);
3984 reset_data_stream(request);
3986 if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3987 server_t *new_server;
3989 new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3990 server_release(request->server);
3991 request->server = new_server;
3994 heap_free(request->path);
4001 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
4002 if (rc != E_POINTER)
4003 needed = strlenW(path)+1;
4004 request->path = heap_alloc(needed*sizeof(WCHAR));
4005 rc = UrlEscapeW(path, request->path, &needed,
4006 URL_ESCAPE_SPACES_ONLY);
4009 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4010 strcpyW(request->path,path);
4014 /* Remove custom content-type/length headers on redirects. */
4015 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
4017 HTTP_DeleteCustomHeader(request, index);
4018 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
4020 HTTP_DeleteCustomHeader(request, index);
4022 return ERROR_SUCCESS;
4025 /***********************************************************************
4026 * HTTP_build_req (internal)
4028 * concatenate all the strings in the request together
4030 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4035 for( t = list; *t ; t++ )
4036 len += strlenW( *t );
4039 str = heap_alloc(len*sizeof(WCHAR));
4042 for( t = list; *t ; t++ )
4048 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4050 server_t *server = request->server;
4051 LPWSTR requestString;
4058 static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4062 requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4064 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4065 NULL, 0, NULL, NULL );
4066 len--; /* the nul terminator isn't needed */
4067 ascii_req = heap_alloc(len);
4068 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4069 heap_free( requestString );
4071 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4073 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4074 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4075 heap_free( ascii_req );
4076 if (res != ERROR_SUCCESS)
4079 responseLen = HTTP_GetResponseHeaders( request );
4081 return ERROR_HTTP_INVALID_HEADER;
4083 return ERROR_SUCCESS;
4086 static void HTTP_InsertCookies(http_request_t *request)
4088 DWORD cookie_size, size, cnt = 0;
4092 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4094 host = HTTP_GetHeader(request, hostW);
4098 if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4101 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4102 if(!(cookies = heap_alloc(size)))
4105 cnt += sprintfW(cookies, cookieW);
4106 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4107 strcatW(cookies, szCrLf);
4109 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4114 static WORD HTTP_ParseWkday(LPCWSTR day)
4116 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4124 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4125 if (!strcmpiW(day, days[i]))
4132 static WORD HTTP_ParseMonth(LPCWSTR month)
4134 static const WCHAR jan[] = { 'j','a','n',0 };
4135 static const WCHAR feb[] = { 'f','e','b',0 };
4136 static const WCHAR mar[] = { 'm','a','r',0 };
4137 static const WCHAR apr[] = { 'a','p','r',0 };
4138 static const WCHAR may[] = { 'm','a','y',0 };
4139 static const WCHAR jun[] = { 'j','u','n',0 };
4140 static const WCHAR jul[] = { 'j','u','l',0 };
4141 static const WCHAR aug[] = { 'a','u','g',0 };
4142 static const WCHAR sep[] = { 's','e','p',0 };
4143 static const WCHAR oct[] = { 'o','c','t',0 };
4144 static const WCHAR nov[] = { 'n','o','v',0 };
4145 static const WCHAR dec[] = { 'd','e','c',0 };
4147 if (!strcmpiW(month, jan)) return 1;
4148 if (!strcmpiW(month, feb)) return 2;
4149 if (!strcmpiW(month, mar)) return 3;
4150 if (!strcmpiW(month, apr)) return 4;
4151 if (!strcmpiW(month, may)) return 5;
4152 if (!strcmpiW(month, jun)) return 6;
4153 if (!strcmpiW(month, jul)) return 7;
4154 if (!strcmpiW(month, aug)) return 8;
4155 if (!strcmpiW(month, sep)) return 9;
4156 if (!strcmpiW(month, oct)) return 10;
4157 if (!strcmpiW(month, nov)) return 11;
4158 if (!strcmpiW(month, dec)) return 12;
4163 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4164 * optionally preceded by whitespace.
4165 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4166 * st, and sets *str to the first character after the time format.
4168 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4174 while (isspaceW(*ptr))
4177 num = strtoulW(ptr, &nextPtr, 10);
4178 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4180 ERR("unexpected time format %s\n", debugstr_w(ptr));
4185 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4189 st->wHour = (WORD)num;
4190 num = strtoulW(ptr, &nextPtr, 10);
4191 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4193 ERR("unexpected time format %s\n", debugstr_w(ptr));
4198 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4202 st->wMinute = (WORD)num;
4203 num = strtoulW(ptr, &nextPtr, 10);
4204 if (!nextPtr || nextPtr <= ptr)
4206 ERR("unexpected time format %s\n", debugstr_w(ptr));
4211 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4216 st->wSecond = (WORD)num;
4220 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4222 static const WCHAR gmt[]= { 'G','M','T',0 };
4223 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4225 SYSTEMTIME st = { 0 };
4228 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4229 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4232 st.wDayOfWeek = HTTP_ParseWkday(day);
4233 if (st.wDayOfWeek >= 7)
4235 ERR("unexpected weekday %s\n", debugstr_w(day));
4239 while (isspaceW(*ptr))
4242 for (monthPtr = month; !isspace(*ptr) &&
4243 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4247 st.wMonth = HTTP_ParseMonth(month);
4248 if (!st.wMonth || st.wMonth > 12)
4250 ERR("unexpected month %s\n", debugstr_w(month));
4254 while (isspaceW(*ptr))
4257 num = strtoulW(ptr, &nextPtr, 10);
4258 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4260 ERR("unexpected day %s\n", debugstr_w(ptr));
4264 st.wDay = (WORD)num;
4266 while (isspaceW(*ptr))
4269 if (!HTTP_ParseTime(&st, &ptr))
4272 while (isspaceW(*ptr))
4275 num = strtoulW(ptr, &nextPtr, 10);
4276 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4278 ERR("unexpected year %s\n", debugstr_w(ptr));
4282 st.wYear = (WORD)num;
4284 while (isspaceW(*ptr))
4287 /* asctime() doesn't report a timezone, but some web servers do, so accept
4288 * with or without GMT.
4290 if (*ptr && strcmpW(ptr, gmt))
4292 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4295 return SystemTimeToFileTime(&st, ft);
4298 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4300 static const WCHAR gmt[]= { 'G','M','T',0 };
4301 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4304 SYSTEMTIME st = { 0 };
4306 ptr = strchrW(value, ',');
4309 if (ptr - value != 3)
4311 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4314 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4316 st.wDayOfWeek = HTTP_ParseWkday(day);
4317 if (st.wDayOfWeek > 6)
4319 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4324 while (isspaceW(*ptr))
4327 num = strtoulW(ptr, &nextPtr, 10);
4328 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4330 WARN("unexpected day %s\n", debugstr_w(value));
4334 st.wDay = (WORD)num;
4336 while (isspaceW(*ptr))
4339 for (monthPtr = month; !isspace(*ptr) &&
4340 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4344 st.wMonth = HTTP_ParseMonth(month);
4345 if (!st.wMonth || st.wMonth > 12)
4347 WARN("unexpected month %s\n", debugstr_w(month));
4351 while (isspaceW(*ptr))
4354 num = strtoulW(ptr, &nextPtr, 10);
4355 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4357 ERR("unexpected year %s\n", debugstr_w(value));
4361 st.wYear = (WORD)num;
4363 if (!HTTP_ParseTime(&st, &ptr))
4366 while (isspaceW(*ptr))
4369 if (strcmpW(ptr, gmt))
4371 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4374 return SystemTimeToFileTime(&st, ft);
4377 static WORD HTTP_ParseWeekday(LPCWSTR day)
4379 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4380 { 'm','o','n','d','a','y',0 },
4381 { 't','u','e','s','d','a','y',0 },
4382 { 'w','e','d','n','e','s','d','a','y',0 },
4383 { 't','h','u','r','s','d','a','y',0 },
4384 { 'f','r','i','d','a','y',0 },
4385 { 's','a','t','u','r','d','a','y',0 }};
4387 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4388 if (!strcmpiW(day, days[i]))
4395 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4397 static const WCHAR gmt[]= { 'G','M','T',0 };
4398 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4401 SYSTEMTIME st = { 0 };
4403 ptr = strchrW(value, ',');
4406 if (ptr - value == 3)
4408 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4410 st.wDayOfWeek = HTTP_ParseWkday(day);
4411 if (st.wDayOfWeek > 6)
4413 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4417 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4419 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4420 day[ptr - value + 1] = 0;
4421 st.wDayOfWeek = HTTP_ParseWeekday(day);
4422 if (st.wDayOfWeek > 6)
4424 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4430 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4435 while (isspaceW(*ptr))
4438 num = strtoulW(ptr, &nextPtr, 10);
4439 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4441 ERR("unexpected day %s\n", debugstr_w(value));
4445 st.wDay = (WORD)num;
4449 ERR("unexpected month format %s\n", debugstr_w(ptr));
4454 for (monthPtr = month; *ptr != '-' &&
4455 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4459 st.wMonth = HTTP_ParseMonth(month);
4460 if (!st.wMonth || st.wMonth > 12)
4462 ERR("unexpected month %s\n", debugstr_w(month));
4468 ERR("unexpected year format %s\n", debugstr_w(ptr));
4473 num = strtoulW(ptr, &nextPtr, 10);
4474 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4476 ERR("unexpected year %s\n", debugstr_w(value));
4480 st.wYear = (WORD)num;
4482 if (!HTTP_ParseTime(&st, &ptr))
4485 while (isspaceW(*ptr))
4488 if (strcmpW(ptr, gmt))
4490 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4493 return SystemTimeToFileTime(&st, ft);
4496 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4498 static const WCHAR zero[] = { '0',0 };
4501 if (!strcmpW(value, zero))
4503 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4506 else if (strchrW(value, ','))
4508 ret = HTTP_ParseRfc1123Date(value, ft);
4511 ret = HTTP_ParseRfc850Date(value, ft);
4513 ERR("unexpected date format %s\n", debugstr_w(value));
4518 ret = HTTP_ParseDateAsAsctime(value, ft);
4520 ERR("unexpected date format %s\n", debugstr_w(value));
4525 static void HTTP_ProcessExpires(http_request_t *request)
4527 BOOL expirationFound = FALSE;
4530 /* Look for a Cache-Control header with a max-age directive, as it takes
4531 * precedence over the Expires header.
4533 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4534 if (headerIndex != -1)
4536 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4539 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4541 LPWSTR comma = strchrW(ptr, ','), end, equal;
4546 end = ptr + strlenW(ptr);
4547 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4551 static const WCHAR max_age[] = {
4552 'm','a','x','-','a','g','e',0 };
4554 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4559 age = strtoulW(equal + 1, &nextPtr, 10);
4560 if (nextPtr > equal + 1)
4564 NtQuerySystemTime( &ft );
4565 /* Age is in seconds, FILETIME resolution is in
4566 * 100 nanosecond intervals.
4568 ft.QuadPart += age * (ULONGLONG)1000000;
4569 request->expires.dwLowDateTime = ft.u.LowPart;
4570 request->expires.dwHighDateTime = ft.u.HighPart;
4571 expirationFound = TRUE;
4578 while (isspaceW(*ptr))
4585 if (!expirationFound)
4587 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4588 if (headerIndex != -1)
4590 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4593 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4595 expirationFound = TRUE;
4596 request->expires = ft;
4600 if (!expirationFound)
4604 /* With no known age, default to 10 minutes until expiration. */
4605 NtQuerySystemTime( &t );
4606 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4607 request->expires.dwLowDateTime = t.u.LowPart;
4608 request->expires.dwHighDateTime = t.u.HighPart;
4612 static void HTTP_ProcessLastModified(http_request_t *request)
4616 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4617 if (headerIndex != -1)
4619 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4622 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4623 request->last_modified = ft;
4627 static void http_process_keep_alive(http_request_t *req)
4631 if ((index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE)) != -1)
4632 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4633 else if ((index = HTTP_GetCustomHeaderIndex(req, szProxy_Connection, 0, FALSE)) != -1)
4634 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4636 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4639 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4641 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4642 netconn_t *netconn = NULL;
4645 assert(!request->netconn);
4646 reset_data_stream(request);
4648 res = HTTP_ResolveName(request);
4649 if(res != ERROR_SUCCESS)
4652 EnterCriticalSection(&connection_pool_cs);
4654 while(!list_empty(&request->server->conn_pool)) {
4655 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4656 list_remove(&netconn->pool_entry);
4658 if(NETCON_is_alive(netconn))
4661 TRACE("connection %p closed during idle\n", netconn);
4662 free_netconn(netconn);
4666 LeaveCriticalSection(&connection_pool_cs);
4669 TRACE("<-- reusing %p netconn\n", netconn);
4670 request->netconn = netconn;
4672 return ERROR_SUCCESS;
4675 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4676 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4678 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4679 INTERNET_STATUS_CONNECTING_TO_SERVER,
4680 request->server->addr_str,
4681 strlen(request->server->addr_str)+1);
4683 res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4684 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4685 request->connect_timeout, &netconn);
4686 if(res != ERROR_SUCCESS) {
4687 ERR("create_netconn failed: %u\n", res);
4691 request->netconn = netconn;
4693 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4694 INTERNET_STATUS_CONNECTED_TO_SERVER,
4695 request->server->addr_str, strlen(request->server->addr_str)+1);
4698 /* Note: we differ from Microsoft's WinINet here. they seem to have
4699 * a bug that causes no status callbacks to be sent when starting
4700 * a tunnel to a proxy server using the CONNECT verb. i believe our
4701 * behaviour to be more correct and to not cause any incompatibilities
4702 * because using a secure connection through a proxy server is a rare
4703 * case that would be hard for anyone to depend on */
4705 res = HTTP_SecureProxyConnect(request);
4706 if(res == ERROR_SUCCESS)
4707 res = NETCON_secure_connect(request->netconn, request->server);
4710 if(res != ERROR_SUCCESS) {
4711 http_release_netconn(request, FALSE);
4716 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4717 return ERROR_SUCCESS;
4720 /***********************************************************************
4721 * HTTP_HttpSendRequestW (internal)
4723 * Sends the specified request to the HTTP server
4726 * ERROR_SUCCESS on success
4727 * win32 error code on failure
4730 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4731 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4732 DWORD dwContentLength, BOOL bEndRequest)
4735 BOOL redirected = FALSE;
4736 LPWSTR requestString = NULL;
4739 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4740 static const WCHAR szContentLength[] =
4741 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4742 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4745 TRACE("--> %p\n", request);
4747 assert(request->hdr.htype == WH_HHTTPREQ);
4749 /* if the verb is NULL default to GET */
4751 request->verb = heap_strdupW(szGET);
4753 if (dwContentLength || strcmpW(request->verb, szGET))
4755 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4756 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4757 request->bytesToWrite = dwContentLength;
4759 if (request->session->appInfo->agent)
4761 WCHAR *agent_header;
4762 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4765 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4766 agent_header = heap_alloc(len * sizeof(WCHAR));
4767 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4769 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4770 heap_free(agent_header);
4772 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4774 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4775 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4777 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4779 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4780 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4781 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4784 /* add the headers the caller supplied */
4785 if( lpszHeaders && dwHeaderLength )
4786 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4791 BOOL reusing_connection;
4795 reusing_connection = request->netconn != NULL;
4798 request->contentLength = ~0u;
4799 request->bytesToWrite = 0;
4802 if (TRACE_ON(wininet))
4804 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4805 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4808 HTTP_FixURL(request);
4809 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4811 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4813 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4814 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4816 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4817 HTTP_InsertCookies(request);
4821 WCHAR *url = build_proxy_path_url(request);
4822 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4826 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4829 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4831 if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4834 /* send the request as ASCII, tack on the optional data */
4835 if (!lpOptional || redirected)
4836 dwOptionalLength = 0;
4837 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4838 NULL, 0, NULL, NULL );
4839 ascii_req = heap_alloc(len + dwOptionalLength);
4840 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4841 ascii_req, len, NULL, NULL );
4843 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4844 len = (len + dwOptionalLength - 1);
4846 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4848 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4849 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4851 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4852 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4853 heap_free( ascii_req );
4854 if(res != ERROR_SUCCESS) {
4855 TRACE("send failed: %u\n", res);
4856 if(!reusing_connection)
4858 http_release_netconn(request, FALSE);
4863 request->bytesWritten = dwOptionalLength;
4865 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4866 INTERNET_STATUS_REQUEST_SENT,
4867 &len, sizeof(DWORD));
4873 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4874 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4876 responseLen = HTTP_GetResponseHeaders(request);
4877 /* FIXME: We should know that connection is closed before sending
4878 * headers. Otherwise wrong callbacks are executed */
4879 if(!responseLen && reusing_connection) {
4880 TRACE("Connection closed by server, reconnecting\n");
4881 http_release_netconn(request, FALSE);
4886 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4887 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4890 http_process_keep_alive(request);
4891 HTTP_ProcessCookies(request);
4892 HTTP_ProcessExpires(request);
4893 HTTP_ProcessLastModified(request);
4895 res = set_content_length(request);
4896 if(res != ERROR_SUCCESS)
4898 if(!request->contentLength)
4899 http_release_netconn(request, TRUE);
4901 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4903 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4904 dwBufferSize=sizeof(szNewLocation);
4905 switch(request->status_code) {
4906 case HTTP_STATUS_REDIRECT:
4907 case HTTP_STATUS_MOVED:
4908 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4909 case HTTP_STATUS_REDIRECT_METHOD:
4910 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4913 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4914 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4916 heap_free(request->verb);
4917 request->verb = heap_strdupW(szGET);
4919 http_release_netconn(request, drain_content(request, FALSE));
4920 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4922 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4923 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4924 res = HTTP_HandleRedirect(request, new_url);
4925 if (res == ERROR_SUCCESS)
4927 heap_free(requestString);
4930 heap_free( new_url );
4935 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4937 WCHAR szAuthValue[2048];
4939 if (request->status_code == HTTP_STATUS_DENIED)
4941 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4943 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4945 if (HTTP_DoAuthorization(request, szAuthValue,
4947 request->session->userName,
4948 request->session->password,
4951 heap_free(requestString);
4952 if(!drain_content(request, TRUE)) {
4953 FIXME("Could not drain content\n");
4954 http_release_netconn(request, FALSE);
4962 TRACE("Cleaning wrong authorization data\n");
4963 destroy_authinfo(request->authInfo);
4964 request->authInfo = NULL;
4967 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4970 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4972 if (HTTP_DoAuthorization(request, szAuthValue,
4973 &request->proxyAuthInfo,
4974 request->session->appInfo->proxyUsername,
4975 request->session->appInfo->proxyPassword,
4978 heap_free(requestString);
4979 if(!drain_content(request, TRUE)) {
4980 FIXME("Could not drain content\n");
4981 http_release_netconn(request, FALSE);
4989 TRACE("Cleaning wrong proxy authorization data\n");
4990 destroy_authinfo(request->proxyAuthInfo);
4991 request->proxyAuthInfo = NULL;
4997 res = ERROR_SUCCESS;
5002 heap_free(requestString);
5004 /* TODO: send notification for P3P header */
5006 if(res == ERROR_SUCCESS)
5007 create_cache_entry(request);
5009 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5011 if (res == ERROR_SUCCESS) {
5012 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5013 HTTP_ReceiveRequestData(request, TRUE, NULL);
5015 send_request_complete(request,
5016 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5018 send_request_complete(request, 0, res);
5034 } send_request_task_t;
5036 /***********************************************************************
5038 * Helper functions for the HttpSendRequest(Ex) functions
5041 static void AsyncHttpSendRequestProc(task_header_t *hdr)
5043 send_request_task_t *task = (send_request_task_t*)hdr;
5044 http_request_t *request = (http_request_t*)task->hdr.hdr;
5046 TRACE("%p\n", request);
5048 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
5049 task->optional_len, task->content_len, task->end_request);
5051 heap_free(task->headers);
5055 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5059 DWORD res = ERROR_SUCCESS;
5061 if(!request->netconn) {
5062 WARN("Not connected\n");
5063 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5064 return ERROR_INTERNET_OPERATION_CANCELLED;
5067 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5068 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5070 responseLen = HTTP_GetResponseHeaders(request);
5072 res = ERROR_HTTP_HEADER_NOT_FOUND;
5074 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5075 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5077 /* process cookies here. Is this right? */
5078 http_process_keep_alive(request);
5079 HTTP_ProcessCookies(request);
5080 HTTP_ProcessExpires(request);
5081 HTTP_ProcessLastModified(request);
5083 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5084 if(!request->contentLength)
5085 http_release_netconn(request, TRUE);
5088 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5090 switch(request->status_code) {
5091 case HTTP_STATUS_REDIRECT:
5092 case HTTP_STATUS_MOVED:
5093 case HTTP_STATUS_REDIRECT_METHOD:
5094 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5095 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5096 dwBufferSize=sizeof(szNewLocation);
5097 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5100 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5101 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5103 heap_free(request->verb);
5104 request->verb = heap_strdupW(szGET);
5106 http_release_netconn(request, drain_content(request, FALSE));
5107 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5109 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5110 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5111 res = HTTP_HandleRedirect(request, new_url);
5112 if (res == ERROR_SUCCESS)
5113 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5114 heap_free( new_url );
5120 if(res == ERROR_SUCCESS)
5121 create_cache_entry(request);
5123 if (res == ERROR_SUCCESS && request->contentLength)
5124 HTTP_ReceiveRequestData(request, TRUE, NULL);
5126 send_request_complete(request, res == ERROR_SUCCESS, res);
5131 /***********************************************************************
5132 * HttpEndRequestA (WININET.@)
5134 * Ends an HTTP request that was started by HttpSendRequestEx
5137 * TRUE if successful
5141 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5142 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5144 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5148 SetLastError(ERROR_INVALID_PARAMETER);
5152 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5159 } end_request_task_t;
5161 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5163 end_request_task_t *task = (end_request_task_t*)hdr;
5164 http_request_t *req = (http_request_t*)task->hdr.hdr;
5168 HTTP_HttpEndRequestW(req, task->flags, task->context);
5171 /***********************************************************************
5172 * HttpEndRequestW (WININET.@)
5174 * Ends an HTTP request that was started by HttpSendRequestEx
5177 * TRUE if successful
5181 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5182 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5184 http_request_t *request;
5187 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5191 SetLastError(ERROR_INVALID_PARAMETER);
5195 request = (http_request_t*) get_handle_object( hRequest );
5197 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5199 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5201 WININET_Release( &request->hdr );
5204 request->hdr.dwFlags |= dwFlags;
5206 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5208 end_request_task_t *task;
5210 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5211 task->flags = dwFlags;
5212 task->context = dwContext;
5214 INTERNET_AsyncCall(&task->hdr);
5215 res = ERROR_IO_PENDING;
5218 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5220 WININET_Release( &request->hdr );
5221 TRACE("%u <--\n", res);
5222 if(res != ERROR_SUCCESS)
5224 return res == ERROR_SUCCESS;
5227 /***********************************************************************
5228 * HttpSendRequestExA (WININET.@)
5230 * Sends the specified request to the HTTP server and allows chunked
5235 * Failure: FALSE, call GetLastError() for more information.
5237 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5238 LPINTERNET_BUFFERSA lpBuffersIn,
5239 LPINTERNET_BUFFERSA lpBuffersOut,
5240 DWORD dwFlags, DWORD_PTR dwContext)
5242 INTERNET_BUFFERSW BuffersInW;
5245 LPWSTR header = NULL;
5247 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5248 lpBuffersOut, dwFlags, dwContext);
5252 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5253 if (lpBuffersIn->lpcszHeader)
5255 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5256 lpBuffersIn->dwHeadersLength,0,0);
5257 header = heap_alloc(headerlen*sizeof(WCHAR));
5258 if (!(BuffersInW.lpcszHeader = header))
5260 SetLastError(ERROR_OUTOFMEMORY);
5263 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5264 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5268 BuffersInW.lpcszHeader = NULL;
5269 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5270 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5271 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5272 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5273 BuffersInW.Next = NULL;
5276 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5282 /***********************************************************************
5283 * HttpSendRequestExW (WININET.@)
5285 * Sends the specified request to the HTTP server and allows chunked
5290 * Failure: FALSE, call GetLastError() for more information.
5292 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5293 LPINTERNET_BUFFERSW lpBuffersIn,
5294 LPINTERNET_BUFFERSW lpBuffersOut,
5295 DWORD dwFlags, DWORD_PTR dwContext)
5297 http_request_t *request;
5298 http_session_t *session;
5302 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5303 lpBuffersOut, dwFlags, dwContext);
5305 request = (http_request_t*) get_handle_object( hRequest );
5307 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5309 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5313 session = request->session;
5314 assert(session->hdr.htype == WH_HHTTPSESSION);
5315 hIC = session->appInfo;
5316 assert(hIC->hdr.htype == WH_HINIT);
5318 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5320 send_request_task_t *task;
5322 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5327 if (lpBuffersIn->lpcszHeader)
5329 if (lpBuffersIn->dwHeadersLength == ~0u)
5330 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5332 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5334 task->headers = heap_alloc(size);
5335 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5337 else task->headers = NULL;
5339 task->headers_len = size / sizeof(WCHAR);
5340 task->optional = lpBuffersIn->lpvBuffer;
5341 task->optional_len = lpBuffersIn->dwBufferLength;
5342 task->content_len = lpBuffersIn->dwBufferTotal;
5346 task->headers = NULL;
5347 task->headers_len = 0;
5348 task->optional = NULL;
5349 task->optional_len = 0;
5350 task->content_len = 0;
5353 task->end_request = FALSE;
5355 INTERNET_AsyncCall(&task->hdr);
5356 res = ERROR_IO_PENDING;
5361 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5362 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5363 lpBuffersIn->dwBufferTotal, FALSE);
5365 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5370 WININET_Release( &request->hdr );
5374 return res == ERROR_SUCCESS;
5377 /***********************************************************************
5378 * HttpSendRequestW (WININET.@)
5380 * Sends the specified request to the HTTP server
5387 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5388 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5390 http_request_t *request;
5391 http_session_t *session = NULL;
5392 appinfo_t *hIC = NULL;
5393 DWORD res = ERROR_SUCCESS;
5395 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5396 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5398 request = (http_request_t*) get_handle_object( hHttpRequest );
5399 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5401 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5405 session = request->session;
5406 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5408 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5412 hIC = session->appInfo;
5413 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5415 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5419 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5421 send_request_task_t *task;
5423 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5428 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5429 else size = dwHeaderLength * sizeof(WCHAR);
5431 task->headers = heap_alloc(size);
5432 memcpy(task->headers, lpszHeaders, size);
5435 task->headers = NULL;
5436 task->headers_len = dwHeaderLength;
5437 task->optional = lpOptional;
5438 task->optional_len = dwOptionalLength;
5439 task->content_len = dwOptionalLength;
5440 task->end_request = TRUE;
5442 INTERNET_AsyncCall(&task->hdr);
5443 res = ERROR_IO_PENDING;
5447 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5448 dwHeaderLength, lpOptional, dwOptionalLength,
5449 dwOptionalLength, TRUE);
5453 WININET_Release( &request->hdr );
5456 return res == ERROR_SUCCESS;
5459 /***********************************************************************
5460 * HttpSendRequestA (WININET.@)
5462 * Sends the specified request to the HTTP server
5469 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5470 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5473 LPWSTR szHeaders=NULL;
5474 DWORD nLen=dwHeaderLength;
5475 if(lpszHeaders!=NULL)
5477 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5478 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5479 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5481 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5482 heap_free(szHeaders);
5486 /***********************************************************************
5487 * HTTPSESSION_Destroy (internal)
5489 * Deallocate session handle
5492 static void HTTPSESSION_Destroy(object_header_t *hdr)
5494 http_session_t *session = (http_session_t*) hdr;
5496 TRACE("%p\n", session);
5498 WININET_Release(&session->appInfo->hdr);
5500 heap_free(session->hostName);
5501 heap_free(session->password);
5502 heap_free(session->userName);
5505 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5507 http_session_t *ses = (http_session_t *)hdr;
5510 case INTERNET_OPTION_HANDLE_TYPE:
5511 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5513 if (*size < sizeof(ULONG))
5514 return ERROR_INSUFFICIENT_BUFFER;
5516 *size = sizeof(DWORD);
5517 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5518 return ERROR_SUCCESS;
5519 case INTERNET_OPTION_CONNECT_TIMEOUT:
5520 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5522 if (*size < sizeof(DWORD))
5523 return ERROR_INSUFFICIENT_BUFFER;
5525 *size = sizeof(DWORD);
5526 *(DWORD *)buffer = ses->connect_timeout;
5527 return ERROR_SUCCESS;
5529 case INTERNET_OPTION_SEND_TIMEOUT:
5530 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5532 if (*size < sizeof(DWORD))
5533 return ERROR_INSUFFICIENT_BUFFER;
5535 *size = sizeof(DWORD);
5536 *(DWORD *)buffer = ses->send_timeout;
5537 return ERROR_SUCCESS;
5539 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5540 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5542 if (*size < sizeof(DWORD))
5543 return ERROR_INSUFFICIENT_BUFFER;
5545 *size = sizeof(DWORD);
5546 *(DWORD *)buffer = ses->receive_timeout;
5547 return ERROR_SUCCESS;
5550 return INET_QueryOption(hdr, option, buffer, size, unicode);
5553 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5555 http_session_t *ses = (http_session_t*)hdr;
5558 case INTERNET_OPTION_USERNAME:
5560 heap_free(ses->userName);
5561 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5562 return ERROR_SUCCESS;
5564 case INTERNET_OPTION_PASSWORD:
5566 heap_free(ses->password);
5567 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5568 return ERROR_SUCCESS;
5570 case INTERNET_OPTION_PROXY_USERNAME:
5572 heap_free(ses->appInfo->proxyUsername);
5573 if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5574 return ERROR_SUCCESS;
5576 case INTERNET_OPTION_PROXY_PASSWORD:
5578 heap_free(ses->appInfo->proxyPassword);
5579 if (!(ses->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5580 return ERROR_SUCCESS;
5582 case INTERNET_OPTION_CONNECT_TIMEOUT:
5584 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5585 ses->connect_timeout = *(DWORD *)buffer;
5586 return ERROR_SUCCESS;
5588 case INTERNET_OPTION_SEND_TIMEOUT:
5590 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5591 ses->send_timeout = *(DWORD *)buffer;
5592 return ERROR_SUCCESS;
5594 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5596 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5597 ses->receive_timeout = *(DWORD *)buffer;
5598 return ERROR_SUCCESS;
5603 return INET_SetOption(hdr, option, buffer, size);
5606 static const object_vtbl_t HTTPSESSIONVtbl = {
5607 HTTPSESSION_Destroy,
5609 HTTPSESSION_QueryOption,
5610 HTTPSESSION_SetOption,
5619 /***********************************************************************
5620 * HTTP_Connect (internal)
5622 * Create http session handle
5625 * HINTERNET a session handle on success
5629 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5630 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5631 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5632 DWORD dwInternalFlags, HINTERNET *ret)
5634 http_session_t *session = NULL;
5638 if (!lpszServerName || !lpszServerName[0])
5639 return ERROR_INVALID_PARAMETER;
5641 assert( hIC->hdr.htype == WH_HINIT );
5643 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5645 return ERROR_OUTOFMEMORY;
5648 * According to my tests. The name is not resolved until a request is sent
5651 session->hdr.htype = WH_HHTTPSESSION;
5652 session->hdr.dwFlags = dwFlags;
5653 session->hdr.dwContext = dwContext;
5654 session->hdr.dwInternalFlags |= dwInternalFlags;
5656 WININET_AddRef( &hIC->hdr );
5657 session->appInfo = hIC;
5658 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5660 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5661 if(hIC->proxyBypass)
5662 FIXME("Proxy bypass is ignored.\n");
5664 session->hostName = heap_strdupW(lpszServerName);
5665 if (lpszUserName && lpszUserName[0])
5666 session->userName = heap_strdupW(lpszUserName);
5667 if (lpszPassword && lpszPassword[0])
5668 session->password = heap_strdupW(lpszPassword);
5669 session->hostPort = serverPort;
5670 session->connect_timeout = hIC->connect_timeout;
5671 session->send_timeout = INFINITE;
5672 session->receive_timeout = INFINITE;
5674 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5675 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5677 INTERNET_SendCallback(&hIC->hdr, dwContext,
5678 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5683 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5687 TRACE("%p --> %p\n", hIC, session);
5689 *ret = session->hdr.hInternet;
5690 return ERROR_SUCCESS;
5693 /***********************************************************************
5694 * HTTP_clear_response_headers (internal)
5696 * clear out any old response headers
5698 static void HTTP_clear_response_headers( http_request_t *request )
5702 for( i=0; i<request->nCustHeaders; i++)
5704 if( !request->custHeaders[i].lpszField )
5706 if( !request->custHeaders[i].lpszValue )
5708 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5710 HTTP_DeleteCustomHeader( request, i );
5715 /***********************************************************************
5716 * HTTP_GetResponseHeaders (internal)
5718 * Read server response
5725 static INT HTTP_GetResponseHeaders(http_request_t *request)
5728 WCHAR buffer[MAX_REPLY_LEN];
5729 DWORD buflen = MAX_REPLY_LEN;
5730 BOOL bSuccess = FALSE;
5732 char bufferA[MAX_REPLY_LEN];
5733 LPWSTR status_code = NULL, status_text = NULL;
5734 DWORD cchMaxRawHeaders = 1024;
5735 LPWSTR lpszRawHeaders = NULL;
5737 DWORD cchRawHeaders = 0;
5738 BOOL codeHundred = FALSE;
5742 if(!request->netconn)
5745 /* clear old response headers (eg. from a redirect response) */
5746 HTTP_clear_response_headers( request );
5748 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5751 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5753 buflen = MAX_REPLY_LEN;
5754 if (!read_line(request, bufferA, &buflen))
5758 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5759 /* check is this a status code line? */
5760 if (!strncmpW(buffer, g_szHttp1_0, 4))
5762 /* split the version from the status code */
5763 status_code = strchrW( buffer, ' ' );
5768 /* split the status code from the status text */
5769 status_text = strchrW( status_code, ' ' );
5774 request->status_code = atoiW(status_code);
5776 TRACE("version [%s] status code [%s] status text [%s]\n",
5777 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5779 codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5781 else if (!codeHundred)
5783 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5785 heap_free(request->version);
5786 heap_free(request->statusText);
5788 request->status_code = HTTP_STATUS_OK;
5789 request->version = heap_strdupW(g_szHttp1_0);
5790 request->statusText = heap_strdupW(szOK);
5792 heap_free(request->rawHeaders);
5793 request->rawHeaders = heap_strdupW(szDefaultHeader);
5798 } while (codeHundred);
5800 /* Add status code */
5801 HTTP_ProcessHeader(request, szStatus, status_code,
5802 HTTP_ADDHDR_FLAG_REPLACE);
5804 heap_free(request->version);
5805 heap_free(request->statusText);
5807 request->version = heap_strdupW(buffer);
5808 request->statusText = heap_strdupW(status_text);
5810 /* Restore the spaces */
5811 *(status_code-1) = ' ';
5812 *(status_text-1) = ' ';
5814 /* regenerate raw headers */
5815 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5816 if (!lpszRawHeaders) goto lend;
5818 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5819 cchMaxRawHeaders *= 2;
5820 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5821 if (temp == NULL) goto lend;
5822 lpszRawHeaders = temp;
5823 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5824 cchRawHeaders += (buflen-1);
5825 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5826 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5827 lpszRawHeaders[cchRawHeaders] = '\0';
5829 /* Parse each response line */
5832 buflen = MAX_REPLY_LEN;
5833 if (read_line(request, bufferA, &buflen))
5835 LPWSTR * pFieldAndValue;
5837 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5839 if (!bufferA[0]) break;
5840 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5842 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5845 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5846 cchMaxRawHeaders *= 2;
5847 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5848 if (temp == NULL) goto lend;
5849 lpszRawHeaders = temp;
5850 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5851 cchRawHeaders += (buflen-1);
5852 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5853 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5854 lpszRawHeaders[cchRawHeaders] = '\0';
5856 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5857 HTTP_ADDREQ_FLAG_ADD );
5859 HTTP_FreeTokens(pFieldAndValue);
5870 /* make sure the response header is terminated with an empty line. Some apps really
5871 truly care about that empty line being there for some reason. Just add it to the
5873 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5875 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5876 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5877 if (temp == NULL) goto lend;
5878 lpszRawHeaders = temp;
5881 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5883 heap_free(request->rawHeaders);
5884 request->rawHeaders = lpszRawHeaders;
5885 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5895 heap_free(lpszRawHeaders);
5900 /***********************************************************************
5901 * HTTP_InterpretHttpHeader (internal)
5903 * Parse server response
5907 * Pointer to array of field, value, NULL on success.
5910 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5912 LPWSTR * pTokenPair;
5916 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5918 pszColon = strchrW(buffer, ':');
5919 /* must have two tokens */
5922 HTTP_FreeTokens(pTokenPair);
5924 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5928 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5931 HTTP_FreeTokens(pTokenPair);
5934 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5935 pTokenPair[0][pszColon - buffer] = '\0';
5939 len = strlenW(pszColon);
5940 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5943 HTTP_FreeTokens(pTokenPair);
5946 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5948 strip_spaces(pTokenPair[0]);
5949 strip_spaces(pTokenPair[1]);
5951 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5955 /***********************************************************************
5956 * HTTP_ProcessHeader (internal)
5958 * Stuff header into header tables according to <dwModifier>
5962 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5964 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5966 LPHTTPHEADERW lphttpHdr = NULL;
5968 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5969 DWORD res = ERROR_HTTP_INVALID_HEADER;
5971 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5973 /* REPLACE wins out over ADD */
5974 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5975 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5977 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5980 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5984 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5985 return ERROR_HTTP_INVALID_HEADER;
5986 lphttpHdr = &request->custHeaders[index];
5992 hdr.lpszField = (LPWSTR)field;
5993 hdr.lpszValue = (LPWSTR)value;
5994 hdr.wFlags = hdr.wCount = 0;
5996 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5997 hdr.wFlags |= HDR_ISREQUEST;
5999 return HTTP_InsertCustomHeader(request, &hdr);
6001 /* no value to delete */
6002 else return ERROR_SUCCESS;
6004 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6005 lphttpHdr->wFlags |= HDR_ISREQUEST;
6007 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6009 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6011 HTTP_DeleteCustomHeader( request, index );
6017 hdr.lpszField = (LPWSTR)field;
6018 hdr.lpszValue = (LPWSTR)value;
6019 hdr.wFlags = hdr.wCount = 0;
6021 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6022 hdr.wFlags |= HDR_ISREQUEST;
6024 return HTTP_InsertCustomHeader(request, &hdr);
6027 return ERROR_SUCCESS;
6029 else if (dwModifier & COALESCEFLAGS)
6034 INT origlen = strlenW(lphttpHdr->lpszValue);
6035 INT valuelen = strlenW(value);
6037 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6040 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6042 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6045 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6048 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6050 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6053 lphttpHdr->lpszValue = lpsztmp;
6054 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6057 lphttpHdr->lpszValue[origlen] = ch;
6059 lphttpHdr->lpszValue[origlen] = ' ';
6063 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6064 lphttpHdr->lpszValue[len] = '\0';
6065 res = ERROR_SUCCESS;
6069 WARN("heap_realloc (%d bytes) failed\n",len+1);
6070 res = ERROR_OUTOFMEMORY;
6073 TRACE("<-- %d\n", res);
6077 /***********************************************************************
6078 * HTTP_GetCustomHeaderIndex (internal)
6080 * Return index of custom header from header array
6083 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6084 int requested_index, BOOL request_only)
6088 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6090 for (index = 0; index < request->nCustHeaders; index++)
6092 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6095 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6098 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6101 if (requested_index == 0)
6106 if (index >= request->nCustHeaders)
6109 TRACE("Return: %d\n", index);
6114 /***********************************************************************
6115 * HTTP_InsertCustomHeader (internal)
6117 * Insert header into array
6120 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6123 LPHTTPHEADERW lph = NULL;
6125 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6126 count = request->nCustHeaders + 1;
6128 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6130 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6133 return ERROR_OUTOFMEMORY;
6135 request->custHeaders = lph;
6136 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6137 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6138 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6139 request->custHeaders[count-1].wCount= lpHdr->wCount;
6140 request->nCustHeaders++;
6142 return ERROR_SUCCESS;
6146 /***********************************************************************
6147 * HTTP_DeleteCustomHeader (internal)
6149 * Delete header from array
6150 * If this function is called, the indexs may change.
6152 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6154 if( request->nCustHeaders <= 0 )
6156 if( index >= request->nCustHeaders )
6158 request->nCustHeaders--;
6160 heap_free(request->custHeaders[index].lpszField);
6161 heap_free(request->custHeaders[index].lpszValue);
6163 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6164 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6165 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6171 /***********************************************************************
6172 * HTTP_VerifyValidHeader (internal)
6174 * Verify the given header is not invalid for the given http request
6177 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6179 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6180 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6181 return ERROR_HTTP_INVALID_HEADER;
6183 return ERROR_SUCCESS;
6186 /***********************************************************************
6187 * IsHostInProxyBypassList (@)
6192 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6194 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);