xref: /reactos/dll/win32/wininet/http.c (revision 139a3d66)
1 /*
2  * Wininet - HTTP Implementation
3  *
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
11  *
12  * Ulrich Czekalla
13  * David Hammerton
14  *
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.
19  *
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.
24  *
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
28  */
29 
30 #include "config.h"
31 
32 #include <stdlib.h>
33 
34 #ifdef HAVE_ZLIB
35 #  include <zlib.h>
36 #endif
37 
38 #include "winsock2.h"
39 #include "ws2ipdef.h"
40 
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <time.h>
44 #include <assert.h>
45 #include <errno.h>
46 #include <limits.h>
47 
48 #include "windef.h"
49 #include "winbase.h"
50 #include "wininet.h"
51 #include "winerror.h"
52 #include "winternl.h"
53 #define NO_SHLWAPI_STREAM
54 #define NO_SHLWAPI_REG
55 #define NO_SHLWAPI_GDI
56 #include "shlwapi.h"
57 #include "sspi.h"
58 #include "wincrypt.h"
59 #include "winuser.h"
60 
61 #include "internet.h"
62 #include "wine/debug.h"
63 #include "wine/exception.h"
64 #include "wine/unicode.h"
65 
66 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
67 
68 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
69 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
70 static const WCHAR szOK[] = {'O','K',0};
71 static const WCHAR hostW[] = { 'H','o','s','t',0 };
72 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
73 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
74 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
75 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
76 static const WCHAR szGET[] = { 'G','E','T', 0 };
77 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
78 
79 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
80 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
81 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
82 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
83 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
84 static const WCHAR szAge[] = { 'A','g','e',0 };
85 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
86 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
87 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
88 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
89 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
90 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
91 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
92 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
94 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
95 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
96 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
97 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 };
98 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
99 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
100 static const WCHAR szDate[] = { 'D','a','t','e',0 };
101 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
102 static const WCHAR szETag[] = { 'E','T','a','g',0 };
103 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
104 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
105 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
106 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
107 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
108 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
109 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
110 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
111 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
112 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
113 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
114 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
115 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
116 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
117 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
118 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
119 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
120 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
121 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
122 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
123 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
124 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 };
125 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
126 static const WCHAR szURI[] = { 'U','R','I',0 };
127 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
128 static const WCHAR szVary[] = { 'V','a','r','y',0 };
129 static const WCHAR szVia[] = { 'V','i','a',0 };
130 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
131 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
132 
133 static const WCHAR emptyW[] = {0};
134 
135 #define HTTP_REFERER    szReferer
136 #define HTTP_ACCEPT     szAccept
137 #define HTTP_USERAGENT  szUser_Agent
138 
139 #define HTTP_ADDHDR_FLAG_ADD				0x20000000
140 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW			0x10000000
141 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA		0x40000000
142 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON	0x01000000
143 #define HTTP_ADDHDR_FLAG_REPLACE			0x80000000
144 #define HTTP_ADDHDR_FLAG_REQ				0x02000000
145 
146 #define COLLECT_TIME 60000
147 
148 struct HttpAuthInfo
149 {
150     LPWSTR scheme;
151     CredHandle cred;
152     CtxtHandle ctx;
153     TimeStamp exp;
154     ULONG attr;
155     ULONG max_token;
156     void *auth_data;
157     unsigned int auth_data_len;
158     BOOL finished; /* finished authenticating */
159 };
160 
161 
162 typedef struct _basicAuthorizationData
163 {
164     struct list entry;
165 
166     LPWSTR host;
167     LPWSTR realm;
168     LPSTR  authorization;
169     UINT   authorizationLen;
170 } basicAuthorizationData;
171 
172 typedef struct _authorizationData
173 {
174     struct list entry;
175 
176     LPWSTR host;
177     LPWSTR scheme;
178     LPWSTR domain;
179     UINT   domain_len;
180     LPWSTR user;
181     UINT   user_len;
182     LPWSTR password;
183     UINT   password_len;
184 } authorizationData;
185 
186 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
187 static struct list authorizationCache = LIST_INIT(authorizationCache);
188 
189 static CRITICAL_SECTION authcache_cs;
190 static CRITICAL_SECTION_DEBUG critsect_debug =
191 {
192     0, 0, &authcache_cs,
193     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
194       0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
195 };
196 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
197 
198 static DWORD HTTP_GetResponseHeaders(http_request_t *req, INT *len);
199 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
200 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
201 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
202 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
203 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
204 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
205 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
206 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
207 static DWORD drain_content(http_request_t*,BOOL);
208 
209 static CRITICAL_SECTION connection_pool_cs;
210 static CRITICAL_SECTION_DEBUG connection_pool_debug =
211 {
212     0, 0, &connection_pool_cs,
213     { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
214       0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
215 };
216 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
217 
218 static struct list connection_pool = LIST_INIT(connection_pool);
219 static BOOL collector_running;
220 
221 void server_addref(server_t *server)
222 {
223     InterlockedIncrement(&server->ref);
224 }
225 
226 void server_release(server_t *server)
227 {
228     if(InterlockedDecrement(&server->ref))
229         return;
230 
231 #ifdef __REACTOS__
232     EnterCriticalSection(&connection_pool_cs);
233 #endif
234     list_remove(&server->entry);
235 #ifdef __REACTOS__
236     LeaveCriticalSection(&connection_pool_cs);
237 #endif
238 
239     if(server->cert_chain)
240         CertFreeCertificateChain(server->cert_chain);
241     heap_free(server->name);
242     heap_free(server->scheme_host_port);
243     heap_free(server);
244 }
245 
246 static BOOL process_host_port(server_t *server)
247 {
248     BOOL default_port;
249     size_t name_len;
250     WCHAR *buf;
251 
252     static const WCHAR httpW[] = {'h','t','t','p',0};
253     static const WCHAR httpsW[] = {'h','t','t','p','s',0};
254     static const WCHAR formatW[] = {'%','s',':','/','/','%','s',':','%','u',0};
255 
256     name_len = strlenW(server->name);
257     buf = heap_alloc((name_len + 10 /* strlen("://:<port>") */)*sizeof(WCHAR) + sizeof(httpsW));
258     if(!buf)
259         return FALSE;
260 
261     sprintfW(buf, formatW, server->is_https ? httpsW : httpW, server->name, server->port);
262     server->scheme_host_port = buf;
263 
264     server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
265     if(server->is_https)
266         server->host_port++;
267 
268     default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
269     server->canon_host_port = default_port ? server->name : server->host_port;
270     return TRUE;
271 }
272 
273 server_t *get_server(substr_t name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
274 {
275     server_t *iter, *server = NULL;
276 
277     EnterCriticalSection(&connection_pool_cs);
278 
279     LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
280         if(iter->port == port && name.len == strlenW(iter->name) && !strncmpiW(iter->name, name.str, name.len)
281                 && iter->is_https == is_https) {
282             server = iter;
283             server_addref(server);
284             break;
285         }
286     }
287 
288     if(!server && do_create) {
289         server = heap_alloc_zero(sizeof(*server));
290         if(server) {
291             server->ref = 2; /* list reference and return */
292             server->port = port;
293             server->is_https = is_https;
294             list_init(&server->conn_pool);
295             server->name = heap_strndupW(name.str, name.len);
296             if(server->name && process_host_port(server)) {
297                 list_add_head(&connection_pool, &server->entry);
298             }else {
299                 heap_free(server);
300                 server = NULL;
301             }
302         }
303     }
304 
305     LeaveCriticalSection(&connection_pool_cs);
306 
307     return server;
308 }
309 
310 BOOL collect_connections(collect_type_t collect_type)
311 {
312     netconn_t *netconn, *netconn_safe;
313     server_t *server, *server_safe;
314     BOOL remaining = FALSE;
315     DWORD64 now;
316 
317 #ifdef __REACTOS__
318     now = GetTickCount();
319 #else
320     now = GetTickCount64();
321 #endif
322 
323     LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
324         LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
325             if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
326                 TRACE("freeing %p\n", netconn);
327                 list_remove(&netconn->pool_entry);
328                 free_netconn(netconn);
329             }else {
330                 remaining = TRUE;
331             }
332         }
333 
334         if(collect_type == COLLECT_CLEANUP) {
335             list_remove(&server->entry);
336             list_init(&server->entry);
337             server_release(server);
338         }
339     }
340 
341     return remaining;
342 }
343 
344 static DWORD WINAPI collect_connections_proc(void *arg)
345 {
346     BOOL remaining_conns;
347 
348     do {
349         /* FIXME: Use more sophisticated method */
350         Sleep(5000);
351 
352         EnterCriticalSection(&connection_pool_cs);
353 
354         remaining_conns = collect_connections(COLLECT_TIMEOUT);
355         if(!remaining_conns)
356             collector_running = FALSE;
357 
358         LeaveCriticalSection(&connection_pool_cs);
359     }while(remaining_conns);
360 
361     FreeLibraryAndExitThread(WININET_hModule, 0);
362 }
363 
364 /***********************************************************************
365  *           HTTP_GetHeader (internal)
366  *
367  * Headers section must be held
368  */
369 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
370 {
371     int HeaderIndex = 0;
372     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
373     if (HeaderIndex == -1)
374         return NULL;
375     else
376         return &req->custHeaders[HeaderIndex];
377 }
378 
379 static WCHAR *get_host_header( http_request_t *req )
380 {
381     HTTPHEADERW *header;
382     WCHAR *ret = NULL;
383 
384     EnterCriticalSection( &req->headers_section );
385     if ((header = HTTP_GetHeader( req, hostW ))) ret = heap_strdupW( header->lpszValue );
386     else ret = heap_strdupW( req->server->canon_host_port );
387     LeaveCriticalSection( &req->headers_section );
388     return ret;
389 }
390 
391 struct data_stream_vtbl_t {
392     BOOL (*end_of_data)(data_stream_t*,http_request_t*);
393     DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,BOOL);
394     DWORD (*drain_content)(data_stream_t*,http_request_t*,BOOL);
395     void (*destroy)(data_stream_t*);
396 };
397 
398 typedef struct {
399     data_stream_t data_stream;
400 
401     BYTE buf[READ_BUFFER_SIZE];
402     DWORD buf_size;
403     DWORD buf_pos;
404     DWORD chunk_size;
405 
406     enum {
407         CHUNKED_STREAM_STATE_READING_CHUNK_SIZE,
408         CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE,
409         CHUNKED_STREAM_STATE_READING_CHUNK,
410         CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA,
411         CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END,
412         CHUNKED_STREAM_STATE_END_OF_STREAM,
413         CHUNKED_STREAM_STATE_ERROR
414     } state;
415 } chunked_stream_t;
416 
417 static inline void destroy_data_stream(data_stream_t *stream)
418 {
419     stream->vtbl->destroy(stream);
420 }
421 
422 static void reset_data_stream(http_request_t *req)
423 {
424     destroy_data_stream(req->data_stream);
425     req->data_stream = &req->netconn_stream.data_stream;
426     req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
427     req->read_gzip = FALSE;
428 }
429 
430 static void remove_header( http_request_t *request, const WCHAR *str, BOOL from_request )
431 {
432     int index;
433     EnterCriticalSection( &request->headers_section );
434     index = HTTP_GetCustomHeaderIndex( request, str, 0, from_request );
435     if (index != -1) HTTP_DeleteCustomHeader( request, index );
436     LeaveCriticalSection( &request->headers_section );
437 }
438 
439 #ifdef HAVE_ZLIB
440 
441 typedef struct {
442     data_stream_t stream;
443     data_stream_t *parent_stream;
444     z_stream zstream;
445     BYTE buf[READ_BUFFER_SIZE];
446     DWORD buf_size;
447     DWORD buf_pos;
448     BOOL end_of_data;
449 } gzip_stream_t;
450 
451 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
452 {
453     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
454     return gzip_stream->end_of_data
455         || (!gzip_stream->buf_size && gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req));
456 }
457 
458 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
459         DWORD *read, BOOL allow_blocking)
460 {
461     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
462     z_stream *zstream = &gzip_stream->zstream;
463     DWORD current_read, ret_read = 0;
464     int zres;
465     DWORD res = ERROR_SUCCESS;
466 
467     TRACE("(%d %x)\n", size, allow_blocking);
468 
469     while(size && !gzip_stream->end_of_data) {
470         if(!gzip_stream->buf_size) {
471             if(gzip_stream->buf_pos) {
472                 if(gzip_stream->buf_size)
473                     memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
474                 gzip_stream->buf_pos = 0;
475             }
476             res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
477                     sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, allow_blocking);
478             if(res != ERROR_SUCCESS)
479                 break;
480 
481             gzip_stream->buf_size += current_read;
482             if(!current_read) {
483                 WARN("unexpected end of data\n");
484                 gzip_stream->end_of_data = TRUE;
485                 break;
486             }
487         }
488 
489         zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
490         zstream->avail_in = gzip_stream->buf_size;
491         zstream->next_out = buf+ret_read;
492         zstream->avail_out = size;
493         zres = inflate(&gzip_stream->zstream, 0);
494         current_read = size - zstream->avail_out;
495         size -= current_read;
496         ret_read += current_read;
497         gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
498         gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
499         if(zres == Z_STREAM_END) {
500             TRACE("end of data\n");
501             gzip_stream->end_of_data = TRUE;
502             inflateEnd(zstream);
503         }else if(zres != Z_OK) {
504             WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
505             if(!ret_read)
506                 res = ERROR_INTERNET_DECODING_FAILED;
507             break;
508         }
509 
510         if(ret_read)
511             allow_blocking = FALSE;
512     }
513 
514     TRACE("read %u bytes\n", ret_read);
515     if(ret_read)
516         res = ERROR_SUCCESS;
517     *read = ret_read;
518     return res;
519 }
520 
521 static DWORD gzip_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
522 {
523     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
524     return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req, allow_blocking);
525 }
526 
527 static void gzip_destroy(data_stream_t *stream)
528 {
529     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
530 
531     destroy_data_stream(gzip_stream->parent_stream);
532 
533     if(!gzip_stream->end_of_data)
534         inflateEnd(&gzip_stream->zstream);
535     heap_free(gzip_stream);
536 }
537 
538 static const data_stream_vtbl_t gzip_stream_vtbl = {
539     gzip_end_of_data,
540     gzip_read,
541     gzip_drain_content,
542     gzip_destroy
543 };
544 
545 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
546 {
547     return heap_alloc(items*size);
548 }
549 
550 static void wininet_zfree(voidpf opaque, voidpf address)
551 {
552     heap_free(address);
553 }
554 
555 static DWORD init_gzip_stream(http_request_t *req, BOOL is_gzip)
556 {
557     gzip_stream_t *gzip_stream;
558     int zres;
559 
560     gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
561     if(!gzip_stream)
562         return ERROR_OUTOFMEMORY;
563 
564     gzip_stream->stream.vtbl = &gzip_stream_vtbl;
565     gzip_stream->zstream.zalloc = wininet_zalloc;
566     gzip_stream->zstream.zfree = wininet_zfree;
567 
568     zres = inflateInit2(&gzip_stream->zstream, is_gzip ? 0x1f : -15);
569     if(zres != Z_OK) {
570         ERR("inflateInit failed: %d\n", zres);
571         heap_free(gzip_stream);
572         return ERROR_OUTOFMEMORY;
573     }
574 
575     remove_header(req, szContent_Length, FALSE);
576 
577     if(req->read_size) {
578         memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
579         gzip_stream->buf_size = req->read_size;
580         req->read_pos = req->read_size = 0;
581     }
582 
583     req->read_gzip = TRUE;
584     gzip_stream->parent_stream = req->data_stream;
585     req->data_stream = &gzip_stream->stream;
586     return ERROR_SUCCESS;
587 }
588 
589 #else
590 
591 static DWORD init_gzip_stream(http_request_t *req, BOOL is_gzip)
592 {
593     ERR("gzip stream not supported, missing zlib.\n");
594     return ERROR_SUCCESS;
595 }
596 
597 #endif
598 
599 /***********************************************************************
600  *           HTTP_FreeTokens (internal)
601  *
602  *  Frees table of pointers.
603  */
604 static void HTTP_FreeTokens(LPWSTR * token_array)
605 {
606     int i;
607     for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
608     heap_free(token_array);
609 }
610 
611 static void HTTP_FixURL(http_request_t *request)
612 {
613     static const WCHAR szSlash[] = { '/',0 };
614     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
615 
616     /* If we don't have a path we set it to root */
617     if (NULL == request->path)
618         request->path = heap_strdupW(szSlash);
619     else /* remove \r and \n*/
620     {
621         int nLen = strlenW(request->path);
622         while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
623         {
624             nLen--;
625             request->path[nLen]='\0';
626         }
627         /* Replace '\' with '/' */
628         while (nLen>0) {
629             nLen--;
630             if (request->path[nLen] == '\\') request->path[nLen]='/';
631         }
632     }
633 
634     if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
635                        request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
636        && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
637     {
638         WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
639         *fixurl = '/';
640         strcpyW(fixurl + 1, request->path);
641         heap_free( request->path );
642         request->path = fixurl;
643     }
644 }
645 
646 static WCHAR* build_request_header(http_request_t *request, const WCHAR *verb,
647         const WCHAR *path, const WCHAR *version, BOOL use_cr)
648 {
649     static const WCHAR szSpace[] = {' ',0};
650     static const WCHAR szColon[] = {':',' ',0};
651     static const WCHAR szCr[] = {'\r',0};
652     static const WCHAR szLf[] = {'\n',0};
653     LPWSTR requestString;
654     DWORD len, n;
655     LPCWSTR *req;
656     UINT i;
657 
658     EnterCriticalSection( &request->headers_section );
659 
660     /* allocate space for an array of all the string pointers to be added */
661     len = request->nCustHeaders * 5 + 10;
662     if (!(req = heap_alloc( len * sizeof(const WCHAR *) )))
663     {
664         LeaveCriticalSection( &request->headers_section );
665         return NULL;
666     }
667 
668     /* add the verb, path and HTTP version string */
669     n = 0;
670     req[n++] = verb;
671     req[n++] = szSpace;
672     req[n++] = path;
673     req[n++] = szSpace;
674     req[n++] = version;
675     if (use_cr)
676         req[n++] = szCr;
677     req[n++] = szLf;
678 
679     /* Append custom request headers */
680     for (i = 0; i < request->nCustHeaders; i++)
681     {
682         if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
683         {
684             req[n++] = request->custHeaders[i].lpszField;
685             req[n++] = szColon;
686             req[n++] = request->custHeaders[i].lpszValue;
687             if (use_cr)
688                 req[n++] = szCr;
689             req[n++] = szLf;
690 
691             TRACE("Adding custom header %s (%s)\n",
692                    debugstr_w(request->custHeaders[i].lpszField),
693                    debugstr_w(request->custHeaders[i].lpszValue));
694         }
695     }
696     if (use_cr)
697         req[n++] = szCr;
698     req[n++] = szLf;
699     req[n] = NULL;
700 
701     requestString = HTTP_build_req( req, 4 );
702     heap_free( req );
703     LeaveCriticalSection( &request->headers_section );
704     return requestString;
705 }
706 
707 static WCHAR* build_response_header(http_request_t *request, BOOL use_cr)
708 {
709     static const WCHAR colonW[] = { ':',' ',0 };
710     static const WCHAR crW[] = { '\r',0 };
711     static const WCHAR lfW[] = { '\n',0 };
712     static const WCHAR status_fmt[] = { ' ','%','u',' ',0 };
713     const WCHAR **req;
714     WCHAR *ret, buf[14];
715     DWORD i, n = 0;
716 
717     EnterCriticalSection( &request->headers_section );
718 
719     if (!(req = heap_alloc( (request->nCustHeaders * 5 + 8) * sizeof(WCHAR *) )))
720     {
721         LeaveCriticalSection( &request->headers_section );
722         return NULL;
723     }
724 
725     if (request->status_code)
726     {
727         req[n++] = request->version;
728         sprintfW(buf, status_fmt, request->status_code);
729         req[n++] = buf;
730         req[n++] = request->statusText;
731         if (use_cr)
732             req[n++] = crW;
733         req[n++] = lfW;
734     }
735 
736     for(i = 0; i < request->nCustHeaders; i++)
737     {
738         if(!(request->custHeaders[i].wFlags & HDR_ISREQUEST)
739                 && strcmpW(request->custHeaders[i].lpszField, szStatus))
740         {
741             req[n++] = request->custHeaders[i].lpszField;
742             req[n++] = colonW;
743             req[n++] = request->custHeaders[i].lpszValue;
744             if(use_cr)
745                 req[n++] = crW;
746             req[n++] = lfW;
747 
748             TRACE("Adding custom header %s (%s)\n",
749                     debugstr_w(request->custHeaders[i].lpszField),
750                     debugstr_w(request->custHeaders[i].lpszValue));
751         }
752     }
753     if(use_cr)
754         req[n++] = crW;
755     req[n++] = lfW;
756     req[n] = NULL;
757 
758     ret = HTTP_build_req(req, 0);
759     heap_free(req);
760     LeaveCriticalSection( &request->headers_section );
761     return ret;
762 }
763 
764 static void HTTP_ProcessCookies( http_request_t *request )
765 {
766     int HeaderIndex;
767     int numCookies = 0;
768     LPHTTPHEADERW setCookieHeader;
769     WCHAR *path, *tmp;
770 
771     if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
772         return;
773 
774     path = heap_strdupW(request->path);
775     if (!path)
776         return;
777 
778     tmp = strrchrW(path, '/');
779     if (tmp && tmp[1]) tmp[1] = 0;
780 
781     EnterCriticalSection( &request->headers_section );
782 
783     while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
784     {
785         const WCHAR *data;
786         substr_t name;
787 
788         setCookieHeader = &request->custHeaders[HeaderIndex];
789 
790         if (!setCookieHeader->lpszValue)
791             continue;
792 
793         data = strchrW(setCookieHeader->lpszValue, '=');
794         if(!data)
795             continue;
796 
797         name = substr(setCookieHeader->lpszValue, data - setCookieHeader->lpszValue);
798         data++;
799         set_cookie(substrz(request->server->name), substrz(path), name, substrz(data), INTERNET_COOKIE_HTTPONLY);
800     }
801 
802     LeaveCriticalSection( &request->headers_section );
803     heap_free(path);
804 }
805 
806 static void strip_spaces(LPWSTR start)
807 {
808     LPWSTR str = start;
809     LPWSTR end;
810 
811     while (*str == ' ')
812         str++;
813 
814     if (str != start)
815         memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
816 
817     end = start + strlenW(start) - 1;
818     while (end >= start && *end == ' ')
819     {
820         *end = '\0';
821         end--;
822     }
823 }
824 
825 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
826 {
827     static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
828     static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
829     BOOL is_basic;
830     is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAY_SIZE(szBasic)) &&
831         ((pszAuthValue[ARRAY_SIZE(szBasic)] == ' ') || !pszAuthValue[ARRAY_SIZE(szBasic)]);
832     if (is_basic && pszRealm)
833     {
834         LPCWSTR token;
835         LPCWSTR ptr = &pszAuthValue[ARRAY_SIZE(szBasic)];
836         LPCWSTR realm;
837         ptr++;
838         *pszRealm=NULL;
839         token = strchrW(ptr,'=');
840         if (!token)
841             return TRUE;
842         realm = ptr;
843         while (*realm == ' ')
844             realm++;
845         if(!strncmpiW(realm, szRealm, ARRAY_SIZE(szRealm)) &&
846             (realm[ARRAY_SIZE(szRealm)] == ' ' || realm[ARRAY_SIZE(szRealm)] == '='))
847         {
848             token++;
849             while (*token == ' ')
850                 token++;
851             if (*token == '\0')
852                 return TRUE;
853             *pszRealm = heap_strdupW(token);
854             strip_spaces(*pszRealm);
855         }
856     }
857 
858     return is_basic;
859 }
860 
861 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
862 {
863     if (!authinfo) return;
864 
865     if (SecIsValidHandle(&authinfo->ctx))
866         DeleteSecurityContext(&authinfo->ctx);
867     if (SecIsValidHandle(&authinfo->cred))
868         FreeCredentialsHandle(&authinfo->cred);
869 
870     heap_free(authinfo->auth_data);
871     heap_free(authinfo->scheme);
872     heap_free(authinfo);
873 }
874 
875 static UINT retrieve_cached_basic_authorization(http_request_t *req, const WCHAR *host, const WCHAR *realm, char **auth_data)
876 {
877     basicAuthorizationData *ad;
878     UINT rc = 0;
879 
880     TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
881 
882     EnterCriticalSection(&authcache_cs);
883     LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
884     {
885         if (!strcmpiW(host, ad->host) && (!realm || !strcmpW(realm, ad->realm)))
886         {
887             char *colon;
888             DWORD length;
889 
890             TRACE("Authorization found in cache\n");
891             *auth_data = heap_alloc(ad->authorizationLen);
892             memcpy(*auth_data,ad->authorization,ad->authorizationLen);
893             rc = ad->authorizationLen;
894 
895             /* update session username and password to reflect current credentials */
896             colon = strchr(ad->authorization, ':');
897             length = colon - ad->authorization;
898 
899             heap_free(req->session->userName);
900             heap_free(req->session->password);
901 
902             req->session->userName = heap_strndupAtoW(ad->authorization, length, &length);
903             length++;
904             req->session->password = heap_strndupAtoW(&ad->authorization[length], ad->authorizationLen - length, &length);
905             break;
906         }
907     }
908     LeaveCriticalSection(&authcache_cs);
909     return rc;
910 }
911 
912 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
913 {
914     struct list *cursor;
915     basicAuthorizationData* ad = NULL;
916 
917     TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
918 
919     EnterCriticalSection(&authcache_cs);
920     LIST_FOR_EACH(cursor, &basicAuthorizationCache)
921     {
922         basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
923         if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
924         {
925             ad = check;
926             break;
927         }
928     }
929 
930     if (ad)
931     {
932         TRACE("Found match in cache, replacing\n");
933         heap_free(ad->authorization);
934         ad->authorization = heap_alloc(auth_data_len);
935         memcpy(ad->authorization, auth_data, auth_data_len);
936         ad->authorizationLen = auth_data_len;
937     }
938     else
939     {
940         ad = heap_alloc(sizeof(basicAuthorizationData));
941         ad->host = heap_strdupW(host);
942         ad->realm = heap_strdupW(realm);
943         ad->authorization = heap_alloc(auth_data_len);
944         memcpy(ad->authorization, auth_data, auth_data_len);
945         ad->authorizationLen = auth_data_len;
946         list_add_head(&basicAuthorizationCache,&ad->entry);
947         TRACE("authorization cached\n");
948     }
949     LeaveCriticalSection(&authcache_cs);
950 }
951 
952 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
953         SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
954 {
955     authorizationData *ad;
956 
957     TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
958 
959     EnterCriticalSection(&authcache_cs);
960     LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
961         if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
962             TRACE("Authorization found in cache\n");
963 
964             nt_auth_identity->User = heap_strdupW(ad->user);
965             nt_auth_identity->Password = heap_strdupW(ad->password);
966             nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
967             if(!nt_auth_identity->User || !nt_auth_identity->Password ||
968                     (!nt_auth_identity->Domain && ad->domain_len)) {
969                 heap_free(nt_auth_identity->User);
970                 heap_free(nt_auth_identity->Password);
971                 heap_free(nt_auth_identity->Domain);
972                 break;
973             }
974 
975             nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
976             nt_auth_identity->UserLength = ad->user_len;
977             nt_auth_identity->PasswordLength = ad->password_len;
978             memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
979             nt_auth_identity->DomainLength = ad->domain_len;
980             LeaveCriticalSection(&authcache_cs);
981             return TRUE;
982         }
983     }
984     LeaveCriticalSection(&authcache_cs);
985 
986     return FALSE;
987 }
988 
989 static void cache_authorization(LPWSTR host, LPWSTR scheme,
990         SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
991 {
992     authorizationData *ad;
993     BOOL found = FALSE;
994 
995     TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
996 
997     EnterCriticalSection(&authcache_cs);
998     LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
999         if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
1000             found = TRUE;
1001             break;
1002         }
1003 
1004     if(found) {
1005         heap_free(ad->user);
1006         heap_free(ad->password);
1007         heap_free(ad->domain);
1008     } else {
1009         ad = heap_alloc(sizeof(authorizationData));
1010         if(!ad) {
1011             LeaveCriticalSection(&authcache_cs);
1012             return;
1013         }
1014 
1015         ad->host = heap_strdupW(host);
1016         ad->scheme = heap_strdupW(scheme);
1017         list_add_head(&authorizationCache, &ad->entry);
1018     }
1019 
1020     ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
1021     ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
1022     ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
1023     ad->user_len = nt_auth_identity->UserLength;
1024     ad->password_len = nt_auth_identity->PasswordLength;
1025     ad->domain_len = nt_auth_identity->DomainLength;
1026 
1027     if(!ad->host || !ad->scheme || !ad->user || !ad->password
1028             || (nt_auth_identity->Domain && !ad->domain)) {
1029         heap_free(ad->host);
1030         heap_free(ad->scheme);
1031         heap_free(ad->user);
1032         heap_free(ad->password);
1033         heap_free(ad->domain);
1034         list_remove(&ad->entry);
1035         heap_free(ad);
1036     }
1037 
1038     LeaveCriticalSection(&authcache_cs);
1039 }
1040 
1041 void free_authorization_cache(void)
1042 {
1043     authorizationData *ad, *sa_safe;
1044     basicAuthorizationData *basic, *basic_safe;
1045 
1046     EnterCriticalSection(&authcache_cs);
1047 
1048     LIST_FOR_EACH_ENTRY_SAFE(basic, basic_safe, &basicAuthorizationCache, basicAuthorizationData, entry)
1049     {
1050         heap_free(basic->host);
1051         heap_free(basic->realm);
1052         heap_free(basic->authorization);
1053 
1054         list_remove(&basic->entry);
1055         heap_free(basic);
1056     }
1057 
1058     LIST_FOR_EACH_ENTRY_SAFE(ad, sa_safe, &authorizationCache, authorizationData, entry)
1059     {
1060         heap_free(ad->host);
1061         heap_free(ad->scheme);
1062         heap_free(ad->user);
1063         heap_free(ad->password);
1064         heap_free(ad->domain);
1065         list_remove(&ad->entry);
1066         heap_free(ad);
1067     }
1068 
1069     LeaveCriticalSection(&authcache_cs);
1070 }
1071 
1072 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
1073                                   struct HttpAuthInfo **ppAuthInfo,
1074                                   LPWSTR domain_and_username, LPWSTR password,
1075                                   LPWSTR host )
1076 {
1077     SECURITY_STATUS sec_status;
1078     struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
1079     BOOL first = FALSE;
1080     LPWSTR szRealm = NULL;
1081 
1082     TRACE("%s\n", debugstr_w(pszAuthValue));
1083 
1084     if (!pAuthInfo)
1085     {
1086         TimeStamp exp;
1087 
1088         first = TRUE;
1089         pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1090         if (!pAuthInfo)
1091             return FALSE;
1092 
1093         SecInvalidateHandle(&pAuthInfo->cred);
1094         SecInvalidateHandle(&pAuthInfo->ctx);
1095         memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1096         pAuthInfo->attr = 0;
1097         pAuthInfo->auth_data = NULL;
1098         pAuthInfo->auth_data_len = 0;
1099         pAuthInfo->finished = FALSE;
1100 
1101         if (is_basic_auth_value(pszAuthValue,NULL))
1102         {
1103             static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1104             pAuthInfo->scheme = heap_strdupW(szBasic);
1105             if (!pAuthInfo->scheme)
1106             {
1107                 heap_free(pAuthInfo);
1108                 return FALSE;
1109             }
1110         }
1111         else
1112         {
1113             PVOID pAuthData;
1114             SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1115 
1116             pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1117             if (!pAuthInfo->scheme)
1118             {
1119                 heap_free(pAuthInfo);
1120                 return FALSE;
1121             }
1122 
1123             if (domain_and_username)
1124             {
1125                 WCHAR *user = strchrW(domain_and_username, '\\');
1126                 WCHAR *domain = domain_and_username;
1127 
1128                 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1129 
1130                 pAuthData = &nt_auth_identity;
1131 
1132                 if (user) user++;
1133                 else
1134                 {
1135                     user = domain_and_username;
1136                     domain = NULL;
1137                 }
1138 
1139                 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1140                 nt_auth_identity.User = user;
1141                 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1142                 nt_auth_identity.Domain = domain;
1143                 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1144                 nt_auth_identity.Password = password;
1145                 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1146 
1147                 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1148             }
1149             else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1150                 pAuthData = &nt_auth_identity;
1151             else
1152                 /* use default credentials */
1153                 pAuthData = NULL;
1154 
1155             sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1156                                                    SECPKG_CRED_OUTBOUND, NULL,
1157                                                    pAuthData, NULL,
1158                                                    NULL, &pAuthInfo->cred,
1159                                                    &exp);
1160 
1161             if(pAuthData && !domain_and_username) {
1162                 heap_free(nt_auth_identity.User);
1163                 heap_free(nt_auth_identity.Domain);
1164                 heap_free(nt_auth_identity.Password);
1165             }
1166 
1167             if (sec_status == SEC_E_OK)
1168             {
1169                 PSecPkgInfoW sec_pkg_info;
1170                 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1171                 if (sec_status == SEC_E_OK)
1172                 {
1173                     pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1174                     FreeContextBuffer(sec_pkg_info);
1175                 }
1176             }
1177             if (sec_status != SEC_E_OK)
1178             {
1179                 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1180                      debugstr_w(pAuthInfo->scheme), sec_status);
1181                 heap_free(pAuthInfo->scheme);
1182                 heap_free(pAuthInfo);
1183                 return FALSE;
1184             }
1185         }
1186         *ppAuthInfo = pAuthInfo;
1187     }
1188     else if (pAuthInfo->finished)
1189         return FALSE;
1190 
1191     if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1192         strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1193     {
1194         ERR("authentication scheme changed from %s to %s\n",
1195             debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1196         return FALSE;
1197     }
1198 
1199     if (is_basic_auth_value(pszAuthValue,&szRealm))
1200     {
1201         int userlen;
1202         int passlen;
1203         char *auth_data = NULL;
1204         UINT auth_data_len = 0;
1205 
1206         TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1207 
1208         if (!domain_and_username)
1209         {
1210             if (host && szRealm)
1211                 auth_data_len = retrieve_cached_basic_authorization(request, host, szRealm,&auth_data);
1212             if (auth_data_len == 0)
1213             {
1214                 heap_free(szRealm);
1215                 return FALSE;
1216             }
1217         }
1218         else
1219         {
1220             userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1221             passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1222 
1223             /* length includes a nul terminator, which will be re-used for the ':' */
1224             auth_data = heap_alloc(userlen + 1 + passlen);
1225             if (!auth_data)
1226             {
1227                 heap_free(szRealm);
1228                 return FALSE;
1229             }
1230 
1231             WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1232             auth_data[userlen] = ':';
1233             WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1234             auth_data_len = userlen + 1 + passlen;
1235             if (host && szRealm)
1236                 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1237         }
1238 
1239         pAuthInfo->auth_data = auth_data;
1240         pAuthInfo->auth_data_len = auth_data_len;
1241         pAuthInfo->finished = TRUE;
1242         heap_free(szRealm);
1243         return TRUE;
1244     }
1245     else
1246     {
1247         LPCWSTR pszAuthData;
1248         SecBufferDesc out_desc, in_desc;
1249         SecBuffer out, in;
1250         unsigned char *buffer;
1251         ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1252             ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1253 
1254         in.BufferType = SECBUFFER_TOKEN;
1255         in.cbBuffer = 0;
1256         in.pvBuffer = NULL;
1257 
1258         in_desc.ulVersion = 0;
1259         in_desc.cBuffers = 1;
1260         in_desc.pBuffers = &in;
1261 
1262         pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1263         if (*pszAuthData == ' ')
1264         {
1265             pszAuthData++;
1266             in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1267             in.pvBuffer = heap_alloc(in.cbBuffer);
1268             HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1269         }
1270 
1271         buffer = heap_alloc(pAuthInfo->max_token);
1272 
1273         out.BufferType = SECBUFFER_TOKEN;
1274         out.cbBuffer = pAuthInfo->max_token;
1275         out.pvBuffer = buffer;
1276 
1277         out_desc.ulVersion = 0;
1278         out_desc.cBuffers = 1;
1279         out_desc.pBuffers = &out;
1280 
1281         sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1282                                                 first ? NULL : &pAuthInfo->ctx,
1283                                                 first ? request->server->name : NULL,
1284                                                 context_req, 0, SECURITY_NETWORK_DREP,
1285                                                 in.pvBuffer ? &in_desc : NULL,
1286                                                 0, &pAuthInfo->ctx, &out_desc,
1287                                                 &pAuthInfo->attr, &pAuthInfo->exp);
1288         if (sec_status == SEC_E_OK)
1289         {
1290             pAuthInfo->finished = TRUE;
1291             pAuthInfo->auth_data = out.pvBuffer;
1292             pAuthInfo->auth_data_len = out.cbBuffer;
1293             TRACE("sending last auth packet\n");
1294         }
1295         else if (sec_status == SEC_I_CONTINUE_NEEDED)
1296         {
1297             pAuthInfo->auth_data = out.pvBuffer;
1298             pAuthInfo->auth_data_len = out.cbBuffer;
1299             TRACE("sending next auth packet\n");
1300         }
1301         else
1302         {
1303             ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1304             heap_free(out.pvBuffer);
1305             destroy_authinfo(pAuthInfo);
1306             *ppAuthInfo = NULL;
1307             return FALSE;
1308         }
1309     }
1310 
1311     return TRUE;
1312 }
1313 
1314 /***********************************************************************
1315  *           HTTP_HttpAddRequestHeadersW (internal)
1316  */
1317 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1318 	LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1319 {
1320     LPWSTR lpszStart;
1321     LPWSTR lpszEnd;
1322     LPWSTR buffer;
1323     DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1324 
1325     TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1326 
1327     if( dwHeaderLength == ~0U )
1328         len = strlenW(lpszHeader);
1329     else
1330         len = dwHeaderLength;
1331     buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1332     lstrcpynW( buffer, lpszHeader, len + 1);
1333 
1334     lpszStart = buffer;
1335 
1336     do
1337     {
1338         LPWSTR * pFieldAndValue;
1339 
1340         lpszEnd = lpszStart;
1341 
1342         while (*lpszEnd != '\0')
1343         {
1344             if (*lpszEnd == '\r' || *lpszEnd == '\n')
1345                  break;
1346             lpszEnd++;
1347         }
1348 
1349         if (*lpszStart == '\0')
1350 	    break;
1351 
1352         if (*lpszEnd == '\r' || *lpszEnd == '\n')
1353         {
1354             *lpszEnd = '\0';
1355             lpszEnd++; /* Jump over newline */
1356         }
1357         TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1358         if (*lpszStart == '\0')
1359         {
1360             /* Skip 0-length headers */
1361             lpszStart = lpszEnd;
1362             res = ERROR_SUCCESS;
1363             continue;
1364         }
1365         pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1366         if (pFieldAndValue)
1367         {
1368             res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1369                 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1370             HTTP_FreeTokens(pFieldAndValue);
1371         }
1372 
1373         lpszStart = lpszEnd;
1374     } while (res == ERROR_SUCCESS);
1375 
1376     heap_free(buffer);
1377     return res;
1378 }
1379 
1380 /***********************************************************************
1381  *           HttpAddRequestHeadersW (WININET.@)
1382  *
1383  * Adds one or more HTTP header to the request handler
1384  *
1385  * NOTE
1386  * On Windows if dwHeaderLength includes the trailing '\0', then
1387  * HttpAddRequestHeadersW() adds it too. However this results in an
1388  * invalid HTTP header which is rejected by some servers so we probably
1389  * don't need to match Windows on that point.
1390  *
1391  * RETURNS
1392  *    TRUE  on success
1393  *    FALSE on failure
1394  *
1395  */
1396 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1397 	LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1398 {
1399     http_request_t *request;
1400     DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1401 
1402     TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1403 
1404     if (!lpszHeader)
1405       return TRUE;
1406 
1407     request = (http_request_t*) get_handle_object( hHttpRequest );
1408     if (request && request->hdr.htype == WH_HHTTPREQ)
1409         res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1410     if( request )
1411         WININET_Release( &request->hdr );
1412 
1413     if(res != ERROR_SUCCESS)
1414         SetLastError(res);
1415     return res == ERROR_SUCCESS;
1416 }
1417 
1418 /***********************************************************************
1419  *           HttpAddRequestHeadersA (WININET.@)
1420  *
1421  * Adds one or more HTTP header to the request handler
1422  *
1423  * RETURNS
1424  *    TRUE  on success
1425  *    FALSE on failure
1426  *
1427  */
1428 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1429 	LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1430 {
1431     WCHAR *headers = NULL;
1432     BOOL r;
1433 
1434     TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1435 
1436     if(lpszHeader)
1437         headers = heap_strndupAtoW(lpszHeader, dwHeaderLength, &dwHeaderLength);
1438 
1439     r = HttpAddRequestHeadersW(hHttpRequest, headers, dwHeaderLength, dwModifier);
1440 
1441     heap_free(headers);
1442     return r;
1443 }
1444 
1445 static void free_accept_types( WCHAR **accept_types )
1446 {
1447     WCHAR *ptr, **types = accept_types;
1448 
1449     if (!types) return;
1450     while ((ptr = *types))
1451     {
1452         heap_free( ptr );
1453         types++;
1454     }
1455     heap_free( accept_types );
1456 }
1457 
1458 static WCHAR **convert_accept_types( const char **accept_types )
1459 {
1460     unsigned int count;
1461     const char **types = accept_types;
1462     WCHAR **typesW;
1463     BOOL invalid_pointer = FALSE;
1464 
1465     if (!types) return NULL;
1466     count = 0;
1467     while (*types)
1468     {
1469         __TRY
1470         {
1471             /* find out how many there are */
1472             if (*types && **types)
1473             {
1474                 TRACE("accept type: %s\n", debugstr_a(*types));
1475                 count++;
1476             }
1477         }
1478         __EXCEPT_PAGE_FAULT
1479         {
1480             WARN("invalid accept type pointer\n");
1481             invalid_pointer = TRUE;
1482         }
1483         __ENDTRY;
1484         types++;
1485     }
1486     if (invalid_pointer) return NULL;
1487     if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1488     count = 0;
1489     types = accept_types;
1490     while (*types)
1491     {
1492         if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1493         types++;
1494     }
1495     typesW[count] = NULL;
1496     return typesW;
1497 }
1498 
1499 /***********************************************************************
1500  *           HttpOpenRequestA (WININET.@)
1501  *
1502  * Open a HTTP request handle
1503  *
1504  * RETURNS
1505  *    HINTERNET  a HTTP request handle on success
1506  *    NULL 	 on failure
1507  *
1508  */
1509 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1510 	LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1511 	LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1512 	DWORD dwFlags, DWORD_PTR dwContext)
1513 {
1514     LPWSTR szVerb = NULL, szObjectName = NULL;
1515     LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1516     HINTERNET rc = NULL;
1517 
1518     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1519           debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1520           debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1521           dwFlags, dwContext);
1522 
1523     if (lpszVerb)
1524     {
1525         szVerb = heap_strdupAtoW(lpszVerb);
1526         if ( !szVerb )
1527             goto end;
1528     }
1529 
1530     if (lpszObjectName)
1531     {
1532         szObjectName = heap_strdupAtoW(lpszObjectName);
1533         if ( !szObjectName )
1534             goto end;
1535     }
1536 
1537     if (lpszVersion)
1538     {
1539         szVersion = heap_strdupAtoW(lpszVersion);
1540         if ( !szVersion )
1541             goto end;
1542     }
1543 
1544     if (lpszReferrer)
1545     {
1546         szReferrer = heap_strdupAtoW(lpszReferrer);
1547         if ( !szReferrer )
1548             goto end;
1549     }
1550 
1551     szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1552     rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1553                           (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1554 
1555 end:
1556     free_accept_types(szAcceptTypes);
1557     heap_free(szReferrer);
1558     heap_free(szVersion);
1559     heap_free(szObjectName);
1560     heap_free(szVerb);
1561     return rc;
1562 }
1563 
1564 /***********************************************************************
1565  *  HTTP_EncodeBase64
1566  */
1567 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1568 {
1569     UINT n = 0, x;
1570     static const CHAR HTTP_Base64Enc[] =
1571         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1572 
1573     while( len > 0 )
1574     {
1575         /* first 6 bits, all from bin[0] */
1576         base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1577         x = (bin[0] & 3) << 4;
1578 
1579         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1580         if( len == 1 )
1581         {
1582             base64[n++] = HTTP_Base64Enc[x];
1583             base64[n++] = '=';
1584             base64[n++] = '=';
1585             break;
1586         }
1587         base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1588         x = ( bin[1] & 0x0f ) << 2;
1589 
1590         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1591         if( len == 2 )
1592         {
1593             base64[n++] = HTTP_Base64Enc[x];
1594             base64[n++] = '=';
1595             break;
1596         }
1597         base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1598 
1599         /* last 6 bits, all from bin [2] */
1600         base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1601         bin += 3;
1602         len -= 3;
1603     }
1604     base64[n] = 0;
1605     return n;
1606 }
1607 
1608 static const signed char HTTP_Base64Dec[] =
1609 {
1610     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
1611     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
1612     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
1613     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
1614     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40 */
1615     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
1616     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
1617     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70 */
1618 };
1619 
1620 /***********************************************************************
1621  *  HTTP_DecodeBase64
1622  */
1623 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1624 {
1625     unsigned int n = 0;
1626 
1627     while(*base64)
1628     {
1629         signed char in[4];
1630 
1631         if (base64[0] >= ARRAY_SIZE(HTTP_Base64Dec) ||
1632             ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1633             base64[1] >= ARRAY_SIZE(HTTP_Base64Dec) ||
1634             ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1635         {
1636             WARN("invalid base64: %s\n", debugstr_w(base64));
1637             return 0;
1638         }
1639         if (bin)
1640             bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1641         n++;
1642 
1643         if ((base64[2] == '=') && (base64[3] == '='))
1644             break;
1645         if (base64[2] > ARRAY_SIZE(HTTP_Base64Dec) ||
1646             ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1647         {
1648             WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1649             return 0;
1650         }
1651         if (bin)
1652             bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1653         n++;
1654 
1655         if (base64[3] == '=')
1656             break;
1657         if (base64[3] > ARRAY_SIZE(HTTP_Base64Dec) ||
1658             ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1659         {
1660             WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1661             return 0;
1662         }
1663         if (bin)
1664             bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1665         n++;
1666 
1667         base64 += 4;
1668     }
1669 
1670     return n;
1671 }
1672 
1673 static WCHAR *encode_auth_data( const WCHAR *scheme, const char *data, UINT data_len )
1674 {
1675     WCHAR *ret;
1676     UINT len, scheme_len = strlenW( scheme );
1677 
1678     /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1679     len = scheme_len + 1 + ((data_len + 2) * 4) / 3;
1680     if (!(ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
1681     memcpy( ret, scheme, scheme_len * sizeof(WCHAR) );
1682     ret[scheme_len] = ' ';
1683     HTTP_EncodeBase64( data, data_len, ret + scheme_len + 1 );
1684     return ret;
1685 }
1686 
1687 
1688 /***********************************************************************
1689  *  HTTP_InsertAuthorization
1690  *
1691  *   Insert or delete the authorization field in the request header.
1692  */
1693 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1694 {
1695     static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1696     WCHAR *host, *authorization = NULL;
1697 
1698     if (pAuthInfo)
1699     {
1700         if (pAuthInfo->auth_data_len)
1701         {
1702             if (!(authorization = encode_auth_data(pAuthInfo->scheme, pAuthInfo->auth_data, pAuthInfo->auth_data_len)))
1703                 return FALSE;
1704 
1705             /* clear the data as it isn't valid now that it has been sent to the
1706              * server, unless it's Basic authentication which doesn't do
1707              * connection tracking */
1708             if (strcmpiW(pAuthInfo->scheme, wszBasic))
1709             {
1710                 heap_free(pAuthInfo->auth_data);
1711                 pAuthInfo->auth_data = NULL;
1712                 pAuthInfo->auth_data_len = 0;
1713             }
1714         }
1715 
1716         TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1717 
1718         HTTP_ProcessHeader(request, header, authorization,
1719                            HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD);
1720         heap_free(authorization);
1721     }
1722     else
1723     {
1724         UINT data_len;
1725         char *data;
1726 
1727         /* Don't use cached credentials when a username or Authorization was specified */
1728         if ((request->session->userName && request->session->userName[0]) || strcmpW(header, szAuthorization))
1729             return TRUE;
1730 
1731         if (!(host = get_host_header(request)))
1732             return TRUE;
1733 
1734         if ((data_len = retrieve_cached_basic_authorization(request, host, NULL, &data)))
1735         {
1736             TRACE("Found cached basic authorization for %s\n", debugstr_w(host));
1737 
1738             if (!(authorization = encode_auth_data(wszBasic, data, data_len)))
1739             {
1740                 heap_free(data);
1741                 heap_free(host);
1742                 return FALSE;
1743             }
1744 
1745             TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1746 
1747             HTTP_ProcessHeader(request, header, authorization,
1748                                HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD);
1749             heap_free(data);
1750             heap_free(authorization);
1751         }
1752         heap_free(host);
1753     }
1754     return TRUE;
1755 }
1756 
1757 static WCHAR *build_proxy_path_url(http_request_t *req)
1758 {
1759     DWORD size, len;
1760     WCHAR *url;
1761 
1762     len = strlenW(req->server->scheme_host_port);
1763     size = len + strlenW(req->path) + 1;
1764     if(*req->path != '/')
1765         size++;
1766     url = heap_alloc(size * sizeof(WCHAR));
1767     if(!url)
1768         return NULL;
1769 
1770     memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
1771     if(*req->path != '/')
1772         url[len++] = '/';
1773 
1774     strcpyW(url+len, req->path);
1775 
1776     TRACE("url=%s\n", debugstr_w(url));
1777     return url;
1778 }
1779 
1780 static BOOL HTTP_DomainMatches(LPCWSTR server, substr_t domain)
1781 {
1782     static const WCHAR localW[] = { '<','l','o','c','a','l','>',0 };
1783     const WCHAR *dot, *ptr;
1784     int len;
1785 
1786     if(domain.len == ARRAY_SIZE(localW)-1 && !strncmpiW(domain.str, localW, domain.len) && !strchrW(server, '.' ))
1787         return TRUE;
1788 
1789     if(domain.len && *domain.str != '*')
1790         return domain.len == strlenW(server) && !strncmpiW(server, domain.str, domain.len);
1791 
1792     if(domain.len < 2 || domain.str[1] != '.')
1793         return FALSE;
1794 
1795     /* For a hostname to match a wildcard, the last domain must match
1796      * the wildcard exactly.  E.g. if the wildcard is *.a.b, and the
1797      * hostname is www.foo.a.b, it matches, but a.b does not.
1798      */
1799     dot = strchrW(server, '.');
1800     if(!dot)
1801         return FALSE;
1802 
1803     len = strlenW(dot + 1);
1804     if(len < domain.len - 2)
1805         return FALSE;
1806 
1807     /* The server's domain is longer than the wildcard, so it
1808      * could be a subdomain.  Compare the last portion of the
1809      * server's domain.
1810      */
1811     ptr = dot + 1 + len - domain.len + 2;
1812     if(!strncmpiW(ptr, domain.str+2, domain.len-2))
1813         /* This is only a match if the preceding character is
1814          * a '.', i.e. that it is a matching domain.  E.g.
1815          * if domain is '*.b.c' and server is 'www.ab.c' they
1816          * do not match.
1817          */
1818         return *(ptr - 1) == '.';
1819 
1820     return len == domain.len-2 && !strncmpiW(dot + 1, domain.str + 2, len);
1821 }
1822 
1823 static BOOL HTTP_ShouldBypassProxy(appinfo_t *lpwai, LPCWSTR server)
1824 {
1825     LPCWSTR ptr;
1826     BOOL ret = FALSE;
1827 
1828     if (!lpwai->proxyBypass) return FALSE;
1829     ptr = lpwai->proxyBypass;
1830     while(1) {
1831         LPCWSTR tmp = ptr;
1832 
1833         ptr = strchrW( ptr, ';' );
1834         if (!ptr)
1835             ptr = strchrW( tmp, ' ' );
1836         if (!ptr)
1837             ptr = tmp + strlenW(tmp);
1838         ret = HTTP_DomainMatches( server, substr(tmp, ptr-tmp) );
1839         if (ret || !*ptr)
1840             break;
1841         ptr++;
1842     }
1843     return ret;
1844 }
1845 
1846 /***********************************************************************
1847  *           HTTP_DealWithProxy
1848  */
1849 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1850 {
1851     static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1852     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1853     static WCHAR szNul[] = { 0 };
1854     URL_COMPONENTSW UrlComponents = { sizeof(UrlComponents) };
1855     server_t *new_server = NULL;
1856     WCHAR *proxy;
1857 
1858     proxy = INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp);
1859     if(!proxy)
1860         return FALSE;
1861     if(CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1862                                     proxy, strlenW(szHttp), szHttp, strlenW(szHttp))) {
1863         WCHAR *proxy_url = heap_alloc(strlenW(proxy)*sizeof(WCHAR) + sizeof(szHttp));
1864         if(!proxy_url) {
1865             heap_free(proxy);
1866             return FALSE;
1867         }
1868         strcpyW(proxy_url, szHttp);
1869         strcatW(proxy_url, proxy);
1870         heap_free(proxy);
1871         proxy = proxy_url;
1872     }
1873 
1874     UrlComponents.dwHostNameLength = 1;
1875     if(InternetCrackUrlW(proxy, 0, 0, &UrlComponents) && UrlComponents.dwHostNameLength) {
1876         if( !request->path )
1877             request->path = szNul;
1878 
1879         new_server = get_server(substr(UrlComponents.lpszHostName, UrlComponents.dwHostNameLength),
1880                                 UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
1881     }
1882     heap_free(proxy);
1883     if(!new_server)
1884         return FALSE;
1885 
1886     request->proxy = new_server;
1887 
1888     TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1889     return TRUE;
1890 }
1891 
1892 static DWORD HTTP_ResolveName(http_request_t *request)
1893 {
1894     server_t *server = request->proxy ? request->proxy : request->server;
1895     int addr_len;
1896 
1897     if(server->addr_len)
1898         return ERROR_SUCCESS;
1899 
1900     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1901                           INTERNET_STATUS_RESOLVING_NAME,
1902                           server->name,
1903                           (strlenW(server->name)+1) * sizeof(WCHAR));
1904 
1905     addr_len = sizeof(server->addr);
1906     if (!GetAddress(server->name, server->port, (SOCKADDR*)&server->addr, &addr_len, server->addr_str))
1907         return ERROR_INTERNET_NAME_NOT_RESOLVED;
1908 
1909     server->addr_len = addr_len;
1910     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1911                           INTERNET_STATUS_NAME_RESOLVED,
1912                           server->addr_str, strlen(server->addr_str)+1);
1913 
1914     TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1915     return ERROR_SUCCESS;
1916 }
1917 
1918 static WCHAR *compose_request_url(http_request_t *req)
1919 {
1920     static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1921     static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1922     const WCHAR *host, *scheme;
1923     WCHAR *buf, *ptr;
1924     size_t len;
1925 
1926     host = req->server->canon_host_port;
1927 
1928     if (req->server->is_https)
1929         scheme = https;
1930     else
1931         scheme = http;
1932 
1933     len = strlenW(scheme) + strlenW(host) + (req->path[0] != '/' ? 1 : 0) + strlenW(req->path);
1934     ptr = buf = heap_alloc((len+1) * sizeof(WCHAR));
1935     if(buf) {
1936         strcpyW(ptr, scheme);
1937         ptr += strlenW(ptr);
1938 
1939         strcpyW(ptr, host);
1940         ptr += strlenW(ptr);
1941 
1942         if(req->path[0] != '/')
1943             *ptr++ = '/';
1944 
1945         strcpyW(ptr, req->path);
1946         ptr += strlenW(ptr);
1947         *ptr = 0;
1948     }
1949 
1950     return buf;
1951 }
1952 
1953 
1954 /***********************************************************************
1955  *           HTTPREQ_Destroy (internal)
1956  *
1957  * Deallocate request handle
1958  *
1959  */
1960 static void HTTPREQ_Destroy(object_header_t *hdr)
1961 {
1962     http_request_t *request = (http_request_t*) hdr;
1963     DWORD i;
1964 
1965     TRACE("\n");
1966 
1967     if(request->hCacheFile)
1968         CloseHandle(request->hCacheFile);
1969     if(request->req_file)
1970         req_file_release(request->req_file);
1971 
1972     request->headers_section.DebugInfo->Spare[0] = 0;
1973     DeleteCriticalSection( &request->headers_section );
1974     request->read_section.DebugInfo->Spare[0] = 0;
1975     DeleteCriticalSection( &request->read_section );
1976     WININET_Release(&request->session->hdr);
1977 
1978     destroy_authinfo(request->authInfo);
1979     destroy_authinfo(request->proxyAuthInfo);
1980 
1981     if(request->server)
1982         server_release(request->server);
1983     if(request->proxy)
1984         server_release(request->proxy);
1985 
1986     heap_free(request->path);
1987     heap_free(request->verb);
1988     heap_free(request->version);
1989     heap_free(request->statusText);
1990 
1991     for (i = 0; i < request->nCustHeaders; i++)
1992     {
1993         heap_free(request->custHeaders[i].lpszField);
1994         heap_free(request->custHeaders[i].lpszValue);
1995     }
1996     destroy_data_stream(request->data_stream);
1997     heap_free(request->custHeaders);
1998 }
1999 
2000 static void http_release_netconn(http_request_t *req, BOOL reuse)
2001 {
2002     TRACE("%p %p %x\n",req, req->netconn, reuse);
2003 
2004     if(!is_valid_netconn(req->netconn))
2005         return;
2006 
2007 #ifndef __REACTOS__
2008     if(reuse && req->netconn->keep_alive) {
2009         BOOL run_collector;
2010 
2011         EnterCriticalSection(&connection_pool_cs);
2012 
2013         list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
2014         req->netconn->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME;
2015         req->netconn = NULL;
2016 
2017         run_collector = !collector_running;
2018         collector_running = TRUE;
2019 
2020         LeaveCriticalSection(&connection_pool_cs);
2021 
2022         if(run_collector) {
2023             HANDLE thread = NULL;
2024             HMODULE module;
2025 
2026             GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
2027             if(module)
2028                 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
2029             if(!thread) {
2030                 EnterCriticalSection(&connection_pool_cs);
2031                 collector_running = FALSE;
2032                 LeaveCriticalSection(&connection_pool_cs);
2033 
2034                 if(module)
2035                     FreeLibrary(module);
2036             }
2037             else
2038                 CloseHandle(thread);
2039         }
2040         return;
2041     }
2042 #else
2043     /* Silence unused function warning */
2044     (void)collect_connections_proc;
2045 #endif
2046 
2047     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2048                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2049 
2050     close_netconn(req->netconn);
2051 
2052     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2053                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2054 }
2055 
2056 static BOOL HTTP_KeepAlive(http_request_t *request)
2057 {
2058     WCHAR szVersion[10];
2059     WCHAR szConnectionResponse[20];
2060     DWORD dwBufferSize = sizeof(szVersion);
2061     BOOL keepalive = FALSE;
2062 
2063     /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
2064      * the connection is keep-alive by default */
2065     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
2066         && !strcmpiW(szVersion, g_szHttp1_1))
2067     {
2068         keepalive = TRUE;
2069     }
2070 
2071     dwBufferSize = sizeof(szConnectionResponse);
2072     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
2073         || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
2074     {
2075         keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
2076     }
2077 
2078     return keepalive;
2079 }
2080 
2081 static void HTTPREQ_CloseConnection(object_header_t *hdr)
2082 {
2083     http_request_t *req = (http_request_t*)hdr;
2084 
2085     http_release_netconn(req, drain_content(req, FALSE) == ERROR_SUCCESS);
2086 }
2087 
2088 static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL unicode)
2089 {
2090     int len;
2091     if (unicode)
2092     {
2093         WCHAR *buf = buffer;
2094 
2095         if (str) len = strlenW(str);
2096         else len = 0;
2097         if (*size < (len + 1) * sizeof(WCHAR))
2098         {
2099             *size = (len + 1) * sizeof(WCHAR);
2100             return ERROR_INSUFFICIENT_BUFFER;
2101         }
2102         if (str) strcpyW(buf, str);
2103         else buf[0] = 0;
2104 
2105         *size = len;
2106         return ERROR_SUCCESS;
2107     }
2108     else
2109     {
2110         char *buf = buffer;
2111 
2112         if (str) len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
2113         else len = 1;
2114         if (*size < len)
2115         {
2116             *size = len;
2117             return ERROR_INSUFFICIENT_BUFFER;
2118         }
2119         if (str) WideCharToMultiByte(CP_ACP, 0, str, -1, buf, *size, NULL, NULL);
2120         else buf[0] = 0;
2121 
2122         *size = len - 1;
2123         return ERROR_SUCCESS;
2124     }
2125 }
2126 
2127 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2128 {
2129     http_request_t *req = (http_request_t*)hdr;
2130 
2131     switch(option) {
2132     case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
2133     {
2134         INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
2135 
2136         FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
2137 
2138         if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
2139             return ERROR_INSUFFICIENT_BUFFER;
2140         *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
2141         /* FIXME: can't get a SOCKET from our connection since we don't use
2142          * winsock
2143          */
2144         info->Socket = 0;
2145         /* FIXME: get source port from req->netConnection */
2146         info->SourcePort = 0;
2147         info->DestPort = req->server->port;
2148         info->Flags = 0;
2149         if (HTTP_KeepAlive(req))
2150             info->Flags |= IDSI_FLAG_KEEP_ALIVE;
2151         if (req->proxy)
2152             info->Flags |= IDSI_FLAG_PROXY;
2153         if (is_valid_netconn(req->netconn) && req->netconn->secure)
2154             info->Flags |= IDSI_FLAG_SECURE;
2155 
2156         return ERROR_SUCCESS;
2157     }
2158 
2159     case 98:
2160         TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
2161         /* fall through */
2162     case INTERNET_OPTION_SECURITY_FLAGS:
2163     {
2164         DWORD flags;
2165 
2166         if (*size < sizeof(ULONG))
2167             return ERROR_INSUFFICIENT_BUFFER;
2168 
2169         *size = sizeof(DWORD);
2170         flags = is_valid_netconn(req->netconn) ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2171         *(DWORD *)buffer = flags;
2172 
2173         TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2174         return ERROR_SUCCESS;
2175     }
2176 
2177     case INTERNET_OPTION_HANDLE_TYPE:
2178         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2179 
2180         if (*size < sizeof(ULONG))
2181             return ERROR_INSUFFICIENT_BUFFER;
2182 
2183         *size = sizeof(DWORD);
2184         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2185         return ERROR_SUCCESS;
2186 
2187     case INTERNET_OPTION_URL: {
2188         WCHAR *url;
2189         DWORD res;
2190 
2191         TRACE("INTERNET_OPTION_URL\n");
2192 
2193         url = compose_request_url(req);
2194         if(!url)
2195             return ERROR_OUTOFMEMORY;
2196 
2197         res = str_to_buffer(url, buffer, size, unicode);
2198         heap_free(url);
2199         return res;
2200     }
2201     case INTERNET_OPTION_USER_AGENT:
2202         return str_to_buffer(req->session->appInfo->agent, buffer, size, unicode);
2203     case INTERNET_OPTION_USERNAME:
2204         return str_to_buffer(req->session->userName, buffer, size, unicode);
2205     case INTERNET_OPTION_PASSWORD:
2206         return str_to_buffer(req->session->password, buffer, size, unicode);
2207     case INTERNET_OPTION_PROXY_USERNAME:
2208         return str_to_buffer(req->session->appInfo->proxyUsername, buffer, size, unicode);
2209     case INTERNET_OPTION_PROXY_PASSWORD:
2210         return str_to_buffer(req->session->appInfo->proxyPassword, buffer, size, unicode);
2211 
2212     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2213         INTERNET_CACHE_ENTRY_INFOW *info;
2214         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2215         DWORD nbytes, error;
2216         BOOL ret;
2217 
2218         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2219 
2220         if(!req->req_file)
2221             return ERROR_FILE_NOT_FOUND;
2222 
2223         if (*size < sizeof(*ts))
2224         {
2225             *size = sizeof(*ts);
2226             return ERROR_INSUFFICIENT_BUFFER;
2227         }
2228 
2229         nbytes = 0;
2230         ret = GetUrlCacheEntryInfoW(req->req_file->url, NULL, &nbytes);
2231         error = GetLastError();
2232         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2233         {
2234             if (!(info = heap_alloc(nbytes)))
2235                 return ERROR_OUTOFMEMORY;
2236 
2237             GetUrlCacheEntryInfoW(req->req_file->url, info, &nbytes);
2238 
2239             ts->ftExpires = info->ExpireTime;
2240             ts->ftLastModified = info->LastModifiedTime;
2241 
2242             heap_free(info);
2243             *size = sizeof(*ts);
2244             return ERROR_SUCCESS;
2245         }
2246         return error;
2247     }
2248 
2249     case INTERNET_OPTION_DATAFILE_NAME: {
2250         DWORD req_size;
2251 
2252         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2253 
2254         if(!req->req_file) {
2255             *size = 0;
2256             return ERROR_INTERNET_ITEM_NOT_FOUND;
2257         }
2258 
2259         if(unicode) {
2260             req_size = (lstrlenW(req->req_file->file_name)+1) * sizeof(WCHAR);
2261             if(*size < req_size)
2262                 return ERROR_INSUFFICIENT_BUFFER;
2263 
2264             *size = req_size;
2265             memcpy(buffer, req->req_file->file_name, *size);
2266             return ERROR_SUCCESS;
2267         }else {
2268             req_size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name, -1, NULL, 0, NULL, NULL);
2269             if (req_size > *size)
2270                 return ERROR_INSUFFICIENT_BUFFER;
2271 
2272             *size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name,
2273                     -1, buffer, *size, NULL, NULL);
2274             return ERROR_SUCCESS;
2275         }
2276     }
2277 
2278     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2279         PCCERT_CONTEXT context;
2280 
2281         if(!req->netconn)
2282             return ERROR_INTERNET_INVALID_OPERATION;
2283 
2284         if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2285             *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2286             return ERROR_INSUFFICIENT_BUFFER;
2287         }
2288 
2289         context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2290         if(context) {
2291             INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2292             DWORD len;
2293 
2294             memset(info, 0, sizeof(*info));
2295             info->ftExpiry = context->pCertInfo->NotAfter;
2296             info->ftStart = context->pCertInfo->NotBefore;
2297             len = CertNameToStrA(context->dwCertEncodingType,
2298                      &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2299             info->lpszSubjectInfo = LocalAlloc(0, len);
2300             if(info->lpszSubjectInfo)
2301                 CertNameToStrA(context->dwCertEncodingType,
2302                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2303                          info->lpszSubjectInfo, len);
2304             len = CertNameToStrA(context->dwCertEncodingType,
2305                      &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2306             info->lpszIssuerInfo = LocalAlloc(0, len);
2307             if(info->lpszIssuerInfo)
2308                 CertNameToStrA(context->dwCertEncodingType,
2309                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2310                          info->lpszIssuerInfo, len);
2311             info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2312             CertFreeCertificateContext(context);
2313             return ERROR_SUCCESS;
2314         }
2315         return ERROR_NOT_SUPPORTED;
2316     }
2317     case INTERNET_OPTION_CONNECT_TIMEOUT:
2318         if (*size < sizeof(DWORD))
2319             return ERROR_INSUFFICIENT_BUFFER;
2320 
2321         *size = sizeof(DWORD);
2322         *(DWORD *)buffer = req->connect_timeout;
2323         return ERROR_SUCCESS;
2324     case INTERNET_OPTION_REQUEST_FLAGS: {
2325         DWORD flags = 0;
2326 
2327         if (*size < sizeof(DWORD))
2328             return ERROR_INSUFFICIENT_BUFFER;
2329 
2330         /* FIXME: Add support for:
2331          * INTERNET_REQFLAG_FROM_CACHE
2332          * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2333          */
2334 
2335         if(req->proxy)
2336             flags |= INTERNET_REQFLAG_VIA_PROXY;
2337         if(!req->status_code)
2338             flags |= INTERNET_REQFLAG_NO_HEADERS;
2339 
2340         TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2341 
2342         *size = sizeof(DWORD);
2343         *(DWORD*)buffer = flags;
2344         return ERROR_SUCCESS;
2345     }
2346     case INTERNET_OPTION_ERROR_MASK:
2347         TRACE("INTERNET_OPTION_ERROR_MASK\n");
2348 
2349         if (*size < sizeof(ULONG))
2350             return ERROR_INSUFFICIENT_BUFFER;
2351 
2352         *(ULONG*)buffer = hdr->ErrorMask;
2353         *size = sizeof(ULONG);
2354         return ERROR_SUCCESS;
2355     }
2356 
2357     return INET_QueryOption(hdr, option, buffer, size, unicode);
2358 }
2359 
2360 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2361 {
2362     http_request_t *req = (http_request_t*)hdr;
2363 
2364     switch(option) {
2365     case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2366         TRACE("Undocumented option 99\n");
2367 
2368         if (!buffer || size != sizeof(DWORD))
2369             return ERROR_INVALID_PARAMETER;
2370         if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2371             return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2372 
2373         /* fall through */
2374     case INTERNET_OPTION_SECURITY_FLAGS:
2375     {
2376         DWORD flags;
2377 
2378         if (!buffer || size != sizeof(DWORD))
2379             return ERROR_INVALID_PARAMETER;
2380         flags = *(DWORD *)buffer;
2381         TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2382         flags &= SECURITY_SET_MASK;
2383         req->security_flags |= flags;
2384         if(is_valid_netconn(req->netconn))
2385             req->netconn->security_flags |= flags;
2386         return ERROR_SUCCESS;
2387     }
2388     case INTERNET_OPTION_CONNECT_TIMEOUT:
2389         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2390         req->connect_timeout = *(DWORD *)buffer;
2391         return ERROR_SUCCESS;
2392 
2393     case INTERNET_OPTION_SEND_TIMEOUT:
2394         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2395         req->send_timeout = *(DWORD *)buffer;
2396         return ERROR_SUCCESS;
2397 
2398     case INTERNET_OPTION_RECEIVE_TIMEOUT:
2399         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2400         req->receive_timeout = *(DWORD *)buffer;
2401         return ERROR_SUCCESS;
2402 
2403     case INTERNET_OPTION_USERNAME:
2404         heap_free(req->session->userName);
2405         if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2406         return ERROR_SUCCESS;
2407 
2408     case INTERNET_OPTION_PASSWORD:
2409         heap_free(req->session->password);
2410         if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2411         return ERROR_SUCCESS;
2412 
2413     case INTERNET_OPTION_PROXY_USERNAME:
2414         heap_free(req->session->appInfo->proxyUsername);
2415         if (!(req->session->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2416         return ERROR_SUCCESS;
2417 
2418     case INTERNET_OPTION_PROXY_PASSWORD:
2419         heap_free(req->session->appInfo->proxyPassword);
2420         if (!(req->session->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2421         return ERROR_SUCCESS;
2422 
2423     }
2424 
2425     return INET_SetOption(hdr, option, buffer, size);
2426 }
2427 
2428 static void commit_cache_entry(http_request_t *req)
2429 {
2430     WCHAR *header;
2431     DWORD header_len;
2432     BOOL res;
2433 
2434     TRACE("%p\n", req);
2435 
2436     CloseHandle(req->hCacheFile);
2437     req->hCacheFile = NULL;
2438 
2439     header = build_response_header(req, TRUE);
2440     header_len = (header ? strlenW(header) : 0);
2441     res = CommitUrlCacheEntryW(req->req_file->url, req->req_file->file_name, req->expires,
2442              req->last_modified, NORMAL_CACHE_ENTRY,
2443             header, header_len, NULL, 0);
2444     if(res)
2445         req->req_file->is_committed = TRUE;
2446     else
2447         WARN("CommitUrlCacheEntry failed: %u\n", GetLastError());
2448     heap_free(header);
2449 }
2450 
2451 static void create_cache_entry(http_request_t *req)
2452 {
2453     static const WCHAR no_cacheW[] = {'n','o','-','c','a','c','h','e',0};
2454     static const WCHAR no_storeW[] = {'n','o','-','s','t','o','r','e',0};
2455 
2456     WCHAR file_name[MAX_PATH+1];
2457     WCHAR *url;
2458     BOOL b = TRUE;
2459 
2460     /* FIXME: We should free previous cache file earlier */
2461     if(req->req_file) {
2462         req_file_release(req->req_file);
2463         req->req_file = NULL;
2464     }
2465     if(req->hCacheFile) {
2466         CloseHandle(req->hCacheFile);
2467         req->hCacheFile = NULL;
2468     }
2469 
2470     if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)
2471         b = FALSE;
2472 
2473     if(b) {
2474         int header_idx;
2475 
2476         EnterCriticalSection( &req->headers_section );
2477 
2478         header_idx = HTTP_GetCustomHeaderIndex(req, szCache_Control, 0, FALSE);
2479         if(header_idx != -1) {
2480             WCHAR *ptr;
2481 
2482             for(ptr=req->custHeaders[header_idx].lpszValue; *ptr; ) {
2483                 WCHAR *end;
2484 
2485                 while(*ptr==' ' || *ptr=='\t')
2486                     ptr++;
2487 
2488                 end = strchrW(ptr, ',');
2489                 if(!end)
2490                     end = ptr + strlenW(ptr);
2491 
2492                 if(!strncmpiW(ptr, no_cacheW, ARRAY_SIZE(no_cacheW)-1)
2493                         || !strncmpiW(ptr, no_storeW, ARRAY_SIZE(no_storeW)-1)) {
2494                     b = FALSE;
2495                     break;
2496                 }
2497 
2498                 ptr = end;
2499                 if(*ptr == ',')
2500                     ptr++;
2501             }
2502         }
2503 
2504         LeaveCriticalSection( &req->headers_section );
2505     }
2506 
2507     if(!b) {
2508         if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2509             return;
2510 
2511         FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2512     }
2513 
2514     url = compose_request_url(req);
2515     if(!url) {
2516         WARN("Could not get URL\n");
2517         return;
2518     }
2519 
2520     b = CreateUrlCacheEntryW(url, req->contentLength == ~0 ? 0 : req->contentLength, NULL, file_name, 0);
2521     if(!b) {
2522         WARN("Could not create cache entry: %08x\n", GetLastError());
2523         return;
2524     }
2525 
2526     create_req_file(file_name, &req->req_file);
2527     req->req_file->url = url;
2528 
2529     req->hCacheFile = CreateFileW(file_name, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2530               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2531     if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2532         WARN("Could not create file: %u\n", GetLastError());
2533         req->hCacheFile = NULL;
2534         return;
2535     }
2536 
2537     if(req->read_size) {
2538         DWORD written;
2539 
2540         b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2541         if(!b)
2542             FIXME("WriteFile failed: %u\n", GetLastError());
2543 
2544         if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2545             commit_cache_entry(req);
2546     }
2547 }
2548 
2549 /* read some more data into the read buffer (the read section must be held) */
2550 static DWORD read_more_data( http_request_t *req, int maxlen )
2551 {
2552     DWORD res;
2553     int len;
2554 
2555     if (req->read_pos)
2556     {
2557         /* move existing data to the start of the buffer */
2558         if(req->read_size)
2559             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2560         req->read_pos = 0;
2561     }
2562 
2563     if (maxlen == -1) maxlen = sizeof(req->read_buf);
2564 
2565     res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2566                        maxlen - req->read_size, TRUE, &len );
2567     if(res == ERROR_SUCCESS)
2568         req->read_size += len;
2569 
2570     return res;
2571 }
2572 
2573 /* remove some amount of data from the read buffer (the read section must be held) */
2574 static void remove_data( http_request_t *req, int count )
2575 {
2576     if (!(req->read_size -= count)) req->read_pos = 0;
2577     else req->read_pos += count;
2578 }
2579 
2580 static DWORD read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2581 {
2582     int count, bytes_read, pos = 0;
2583     DWORD res;
2584 
2585     EnterCriticalSection( &req->read_section );
2586     for (;;)
2587     {
2588         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2589 
2590         if (eol)
2591         {
2592             count = eol - (req->read_buf + req->read_pos);
2593             bytes_read = count + 1;
2594         }
2595         else count = bytes_read = req->read_size;
2596 
2597         count = min( count, *len - pos );
2598         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2599         pos += count;
2600         remove_data( req, bytes_read );
2601         if (eol) break;
2602 
2603         if ((res = read_more_data( req, -1 )))
2604         {
2605             WARN( "read failed %u\n", res );
2606             LeaveCriticalSection( &req->read_section );
2607             return res;
2608         }
2609         if (!req->read_size)
2610         {
2611             *len = 0;
2612             TRACE( "returning empty string\n" );
2613             LeaveCriticalSection( &req->read_section );
2614             return ERROR_SUCCESS;
2615         }
2616     }
2617     LeaveCriticalSection( &req->read_section );
2618 
2619     if (pos < *len)
2620     {
2621         if (pos && buffer[pos - 1] == '\r') pos--;
2622         *len = pos + 1;
2623     }
2624     buffer[*len - 1] = 0;
2625     TRACE( "returning %s\n", debugstr_a(buffer));
2626     return ERROR_SUCCESS;
2627 }
2628 
2629 /* check if we have reached the end of the data to read (the read section must be held) */
2630 static BOOL end_of_read_data( http_request_t *req )
2631 {
2632     return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2633 }
2634 
2635 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, BOOL allow_blocking)
2636 {
2637     DWORD res;
2638 
2639     res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, allow_blocking);
2640     if(res != ERROR_SUCCESS)
2641         *read = 0;
2642     assert(*read <= size);
2643 
2644     if(req->hCacheFile) {
2645         if(*read) {
2646             BOOL bres;
2647             DWORD written;
2648 
2649             bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2650             if(!bres)
2651                 FIXME("WriteFile failed: %u\n", GetLastError());
2652         }
2653 
2654         if((res == ERROR_SUCCESS && !*read) || req->data_stream->vtbl->end_of_data(req->data_stream, req))
2655             commit_cache_entry(req);
2656     }
2657 
2658     return res;
2659 }
2660 
2661 /* fetch some more data into the read buffer (the read section must be held) */
2662 static DWORD refill_read_buffer(http_request_t *req, BOOL allow_blocking, DWORD *read_bytes)
2663 {
2664     DWORD res, read=0;
2665 
2666     if(req->read_size == sizeof(req->read_buf))
2667         return ERROR_SUCCESS;
2668 
2669     if(req->read_pos) {
2670         if(req->read_size)
2671             memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2672         req->read_pos = 0;
2673     }
2674 
2675     res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2676             &read, allow_blocking);
2677     if(res != ERROR_SUCCESS)
2678         return res;
2679 
2680     req->read_size += read;
2681 
2682     TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2683     if(read_bytes)
2684         *read_bytes = read;
2685     return res;
2686 }
2687 
2688 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2689 {
2690     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2691     return netconn_stream->content_read == netconn_stream->content_length || !is_valid_netconn(req->netconn);
2692 }
2693 
2694 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2695         DWORD *read, BOOL allow_blocking)
2696 {
2697     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2698     DWORD res = ERROR_SUCCESS;
2699     int ret = 0;
2700 
2701     size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2702 
2703     if(size && is_valid_netconn(req->netconn)) {
2704         res = NETCON_recv(req->netconn, buf, size, allow_blocking, &ret);
2705         if(res == ERROR_SUCCESS) {
2706             if(!ret)
2707                 netconn_stream->content_length = netconn_stream->content_read;
2708             netconn_stream->content_read += ret;
2709         }
2710     }
2711 
2712     TRACE("res %u read %u bytes\n", res, ret);
2713     *read = ret;
2714     return res;
2715 }
2716 
2717 static DWORD netconn_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
2718 {
2719     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2720     BYTE buf[1024];
2721     int len, res;
2722     size_t size;
2723 
2724     if(netconn_stream->content_length == ~0)
2725         return WSAEISCONN;
2726 
2727     while(netconn_stream->content_read < netconn_stream->content_length) {
2728         size = min(sizeof(buf), netconn_stream->content_length-netconn_stream->content_read);
2729         res = NETCON_recv(req->netconn, buf, size, allow_blocking, &len);
2730         if(res)
2731             return res;
2732         if(!len)
2733             return WSAECONNABORTED;
2734 
2735         netconn_stream->content_read += len;
2736     }
2737 
2738     return ERROR_SUCCESS;
2739 }
2740 
2741 static void netconn_destroy(data_stream_t *stream)
2742 {
2743 }
2744 
2745 static const data_stream_vtbl_t netconn_stream_vtbl = {
2746     netconn_end_of_data,
2747     netconn_read,
2748     netconn_drain_content,
2749     netconn_destroy
2750 };
2751 
2752 static char next_chunked_data_char(chunked_stream_t *stream)
2753 {
2754     assert(stream->buf_size);
2755 
2756     stream->buf_size--;
2757     return stream->buf[stream->buf_pos++];
2758 }
2759 
2760 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2761 {
2762     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2763     switch(chunked_stream->state) {
2764     case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2765     case CHUNKED_STREAM_STATE_END_OF_STREAM:
2766     case CHUNKED_STREAM_STATE_ERROR:
2767         return TRUE;
2768     default:
2769         return FALSE;
2770     }
2771 }
2772 
2773 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2774         DWORD *read, BOOL allow_blocking)
2775 {
2776     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2777     DWORD ret_read = 0, res = ERROR_SUCCESS;
2778     BOOL continue_read = TRUE;
2779     int read_bytes;
2780     char ch;
2781 
2782     do {
2783         TRACE("state %d\n", chunked_stream->state);
2784 
2785         /* Ensure that we have data in the buffer for states that need it. */
2786         if(!chunked_stream->buf_size) {
2787             BOOL blocking_read = allow_blocking;
2788 
2789             switch(chunked_stream->state) {
2790             case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2791             case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE:
2792                 /* never allow blocking after 0 chunk size */
2793                 if(!chunked_stream->chunk_size)
2794                     blocking_read = FALSE;
2795                 /* fall through */
2796             case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE:
2797             case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA:
2798                 chunked_stream->buf_pos = 0;
2799                 res = NETCON_recv(req->netconn, chunked_stream->buf, sizeof(chunked_stream->buf), blocking_read, &read_bytes);
2800                 if(res == ERROR_SUCCESS && read_bytes) {
2801                     chunked_stream->buf_size += read_bytes;
2802                 }else if(res == WSAEWOULDBLOCK) {
2803                     if(ret_read || allow_blocking)
2804                         res = ERROR_SUCCESS;
2805                     continue_read = FALSE;
2806                     continue;
2807                 }else {
2808                     chunked_stream->state = CHUNKED_STREAM_STATE_ERROR;
2809                 }
2810                 break;
2811             default:
2812                 break;
2813             }
2814         }
2815 
2816         switch(chunked_stream->state) {
2817         case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE:
2818             ch = next_chunked_data_char(chunked_stream);
2819 
2820             if(ch >= '0' && ch <= '9') {
2821                 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - '0';
2822             }else if(ch >= 'a' && ch <= 'f') {
2823                 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'a' + 10;
2824             }else if (ch >= 'A' && ch <= 'F') {
2825                 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'A' + 10;
2826             }else if (ch == ';' || ch == '\r' || ch == '\n') {
2827                 TRACE("reading %u byte chunk\n", chunked_stream->chunk_size);
2828                 chunked_stream->buf_size++;
2829                 chunked_stream->buf_pos--;
2830                 if(req->contentLength == ~0) req->contentLength = chunked_stream->chunk_size;
2831                 else req->contentLength += chunked_stream->chunk_size;
2832                 chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE;
2833             }
2834             break;
2835 
2836         case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE:
2837             ch = next_chunked_data_char(chunked_stream);
2838             if(ch == '\n')
2839                 chunked_stream->state = chunked_stream->chunk_size
2840                     ? CHUNKED_STREAM_STATE_READING_CHUNK
2841                     : CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END;
2842             else if(ch != '\r')
2843                 WARN("unexpected char '%c'\n", ch);
2844             break;
2845 
2846         case CHUNKED_STREAM_STATE_READING_CHUNK:
2847             assert(chunked_stream->chunk_size);
2848             if(!size) {
2849                 continue_read = FALSE;
2850                 break;
2851             }
2852             read_bytes = min(size, chunked_stream->chunk_size);
2853 
2854             if(chunked_stream->buf_size) {
2855                 if(read_bytes > chunked_stream->buf_size)
2856                     read_bytes = chunked_stream->buf_size;
2857 
2858                 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2859                 chunked_stream->buf_pos += read_bytes;
2860                 chunked_stream->buf_size -= read_bytes;
2861             }else {
2862                 res = NETCON_recv(req->netconn, (char*)buf+ret_read, read_bytes,
2863                                   allow_blocking, (int*)&read_bytes);
2864                 if(res != ERROR_SUCCESS) {
2865                     continue_read = FALSE;
2866                     break;
2867                 }
2868 
2869                 if(!read_bytes) {
2870                     chunked_stream->state = CHUNKED_STREAM_STATE_ERROR;
2871                     continue;
2872                 }
2873             }
2874 
2875             chunked_stream->chunk_size -= read_bytes;
2876             size -= read_bytes;
2877             ret_read += read_bytes;
2878             if(!chunked_stream->chunk_size)
2879                 chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA;
2880             allow_blocking = FALSE;
2881             break;
2882 
2883         case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA:
2884             ch = next_chunked_data_char(chunked_stream);
2885             if(ch == '\n')
2886                 chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE;
2887             else if(ch != '\r')
2888                 WARN("unexpected char '%c'\n", ch);
2889             break;
2890 
2891         case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2892             ch = next_chunked_data_char(chunked_stream);
2893             if(ch == '\n')
2894                 chunked_stream->state = CHUNKED_STREAM_STATE_END_OF_STREAM;
2895             else if(ch != '\r')
2896                 WARN("unexpected char '%c'\n", ch);
2897             break;
2898 
2899         case CHUNKED_STREAM_STATE_END_OF_STREAM:
2900         case CHUNKED_STREAM_STATE_ERROR:
2901             continue_read = FALSE;
2902             break;
2903         }
2904     } while(continue_read);
2905 
2906     if(ret_read)
2907         res = ERROR_SUCCESS;
2908     if(res != ERROR_SUCCESS)
2909         return res;
2910 
2911     TRACE("read %d bytes\n", ret_read);
2912     *read = ret_read;
2913     return ERROR_SUCCESS;
2914 }
2915 
2916 static DWORD chunked_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
2917 {
2918     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2919     BYTE buf[1024];
2920     DWORD size, res;
2921 
2922     while(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM
2923           && chunked_stream->state != CHUNKED_STREAM_STATE_ERROR) {
2924         res = chunked_read(stream, req, buf, sizeof(buf), &size, allow_blocking);
2925         if(res != ERROR_SUCCESS)
2926             return res;
2927     }
2928 
2929     if(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM)
2930         return ERROR_NO_DATA;
2931     return ERROR_SUCCESS;
2932 }
2933 
2934 static void chunked_destroy(data_stream_t *stream)
2935 {
2936     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2937     heap_free(chunked_stream);
2938 }
2939 
2940 static const data_stream_vtbl_t chunked_stream_vtbl = {
2941     chunked_end_of_data,
2942     chunked_read,
2943     chunked_drain_content,
2944     chunked_destroy
2945 };
2946 
2947 /* set the request content length based on the headers */
2948 static DWORD set_content_length(http_request_t *request)
2949 {
2950     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2951     static const WCHAR headW[] = {'H','E','A','D',0};
2952     WCHAR contentLength[32];
2953     WCHAR encoding[20];
2954     DWORD size;
2955 
2956     if(request->status_code == HTTP_STATUS_NO_CONTENT || !strcmpW(request->verb, headW)) {
2957         request->contentLength = request->netconn_stream.content_length = 0;
2958         return ERROR_SUCCESS;
2959     }
2960 
2961     size = sizeof(contentLength);
2962     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONTENT_LENGTH,
2963                             contentLength, &size, NULL) != ERROR_SUCCESS ||
2964         !StrToInt64ExW(contentLength, STIF_DEFAULT, (LONGLONG*)&request->contentLength)) {
2965         request->contentLength = ~0;
2966     }
2967 
2968     request->netconn_stream.content_length = request->contentLength;
2969     request->netconn_stream.content_read = request->read_size;
2970 
2971     size = sizeof(encoding);
2972     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2973         !strcmpiW(encoding, szChunked))
2974     {
2975         chunked_stream_t *chunked_stream;
2976 
2977         chunked_stream = heap_alloc(sizeof(*chunked_stream));
2978         if(!chunked_stream)
2979             return ERROR_OUTOFMEMORY;
2980 
2981         chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2982         chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2983         chunked_stream->chunk_size = 0;
2984         chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE;
2985 
2986         if(request->read_size) {
2987             memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2988             chunked_stream->buf_size = request->read_size;
2989             request->read_size = request->read_pos = 0;
2990         }
2991 
2992         request->data_stream = &chunked_stream->data_stream;
2993         request->contentLength = ~0;
2994     }
2995 
2996     if(request->hdr.decoding) {
2997         int encoding_idx;
2998 
2999         static const WCHAR deflateW[] = {'d','e','f','l','a','t','e',0};
3000         static const WCHAR gzipW[] = {'g','z','i','p',0};
3001 
3002         EnterCriticalSection( &request->headers_section );
3003 
3004         encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
3005         if(encoding_idx != -1) {
3006             if(!strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW)) {
3007                 HTTP_DeleteCustomHeader(request, encoding_idx);
3008                 LeaveCriticalSection( &request->headers_section );
3009                 return init_gzip_stream(request, TRUE);
3010             }
3011             if(!strcmpiW(request->custHeaders[encoding_idx].lpszValue, deflateW)) {
3012                 HTTP_DeleteCustomHeader(request, encoding_idx);
3013                 LeaveCriticalSection( &request->headers_section );
3014                 return init_gzip_stream(request, FALSE);
3015             }
3016         }
3017 
3018         LeaveCriticalSection( &request->headers_section );
3019     }
3020 
3021     return ERROR_SUCCESS;
3022 }
3023 
3024 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
3025 {
3026     INTERNET_ASYNC_RESULT iar;
3027 
3028     iar.dwResult = result;
3029     iar.dwError = error;
3030 
3031     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3032             sizeof(INTERNET_ASYNC_RESULT));
3033 }
3034 
3035 static void HTTP_ReceiveRequestData(http_request_t *req)
3036 {
3037     DWORD res, read = 0;
3038 
3039     TRACE("%p\n", req);
3040 
3041     EnterCriticalSection( &req->read_section );
3042 
3043     res = refill_read_buffer(req, FALSE, &read);
3044     if(res == ERROR_SUCCESS)
3045         read += req->read_size;
3046 
3047     LeaveCriticalSection( &req->read_section );
3048 
3049     if(res != WSAEWOULDBLOCK && (res != ERROR_SUCCESS || !read)) {
3050         WARN("res %u read %u, closing connection\n", res, read);
3051         http_release_netconn(req, FALSE);
3052     }
3053 
3054     if(res != ERROR_SUCCESS && res != WSAEWOULDBLOCK) {
3055         send_request_complete(req, 0, res);
3056         return;
3057     }
3058 
3059     send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, 0);
3060 }
3061 
3062 /* read data from the http connection (the read section must be held) */
3063 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL allow_blocking)
3064 {
3065     DWORD current_read = 0, ret_read = 0;
3066     DWORD res = ERROR_SUCCESS;
3067 
3068     EnterCriticalSection( &req->read_section );
3069 
3070     if(req->read_size) {
3071         ret_read = min(size, req->read_size);
3072         memcpy(buffer, req->read_buf+req->read_pos, ret_read);
3073         req->read_size -= ret_read;
3074         req->read_pos += ret_read;
3075         allow_blocking = FALSE;
3076     }
3077 
3078     if(ret_read < size) {
3079         res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, allow_blocking);
3080         if(res == ERROR_SUCCESS)
3081             ret_read += current_read;
3082         else if(res == WSAEWOULDBLOCK && ret_read)
3083             res = ERROR_SUCCESS;
3084     }
3085 
3086     LeaveCriticalSection( &req->read_section );
3087 
3088     *read = ret_read;
3089     TRACE( "retrieved %u bytes (res %u)\n", ret_read, res );
3090 
3091     if(res != WSAEWOULDBLOCK) {
3092         if(res != ERROR_SUCCESS)
3093             http_release_netconn(req, FALSE);
3094         else if(!ret_read && drain_content(req, FALSE) == ERROR_SUCCESS)
3095             http_release_netconn(req, TRUE);
3096     }
3097 
3098     return res;
3099 }
3100 
3101 static DWORD drain_content(http_request_t *req, BOOL blocking)
3102 {
3103     DWORD res;
3104 
3105     TRACE("%p\n", req->netconn);
3106 
3107     if(!is_valid_netconn(req->netconn))
3108         return ERROR_NO_DATA;
3109 
3110     if(!strcmpW(req->verb, szHEAD))
3111         return ERROR_SUCCESS;
3112 
3113     EnterCriticalSection( &req->read_section );
3114     res = req->data_stream->vtbl->drain_content(req->data_stream, req, blocking);
3115     LeaveCriticalSection( &req->read_section );
3116     return res;
3117 }
3118 
3119 typedef struct {
3120     task_header_t hdr;
3121     void *buf;
3122     DWORD size;
3123     DWORD read_pos;
3124     DWORD *ret_read;
3125 } read_file_task_t;
3126 
3127 static void async_read_file_proc(task_header_t *hdr)
3128 {
3129     read_file_task_t *task = (read_file_task_t*)hdr;
3130     http_request_t *req = (http_request_t*)task->hdr.hdr;
3131     DWORD res = ERROR_SUCCESS, read = task->read_pos, complete_arg = 0;
3132 
3133     TRACE("req %p buf %p size %u read_pos %u ret_read %p\n", req, task->buf, task->size, task->read_pos, task->ret_read);
3134 
3135     if(task->buf) {
3136         DWORD read_bytes;
3137         while (read < task->size) {
3138             res = HTTPREQ_Read(req, (char*)task->buf + read, task->size - read, &read_bytes, TRUE);
3139             if (res != ERROR_SUCCESS || !read_bytes)
3140                 break;
3141             read += read_bytes;
3142         }
3143     }else {
3144         EnterCriticalSection(&req->read_section);
3145         res = refill_read_buffer(req, TRUE, &read);
3146         LeaveCriticalSection(&req->read_section);
3147 
3148         if(task->ret_read)
3149             complete_arg = read; /* QueryDataAvailable reports read bytes in request complete notification */
3150         if(res != ERROR_SUCCESS || !read)
3151             http_release_netconn(req, drain_content(req, FALSE) == ERROR_SUCCESS);
3152     }
3153 
3154     TRACE("res %u read %u\n", res, read);
3155 
3156     if(task->ret_read)
3157         *task->ret_read = read;
3158 
3159     /* FIXME: We should report bytes transferred before decoding content. */
3160     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, &read, sizeof(read));
3161 
3162     if(res != ERROR_SUCCESS)
3163         complete_arg = res;
3164     send_request_complete(req, res == ERROR_SUCCESS, complete_arg);
3165 }
3166 
3167 static DWORD async_read(http_request_t *req, void *buf, DWORD size, DWORD read_pos, DWORD *ret_read)
3168 {
3169     read_file_task_t *task;
3170 
3171     task = alloc_async_task(&req->hdr, async_read_file_proc, sizeof(*task));
3172     if(!task)
3173         return ERROR_OUTOFMEMORY;
3174 
3175     task->buf = buf;
3176     task->size = size;
3177     task->read_pos = read_pos;
3178     task->ret_read = ret_read;
3179 
3180     INTERNET_AsyncCall(&task->hdr);
3181     return ERROR_IO_PENDING;
3182 }
3183 
3184 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
3185         DWORD flags, DWORD_PTR context)
3186 {
3187     http_request_t *req = (http_request_t*)hdr;
3188     DWORD res = ERROR_SUCCESS, read = 0, cread, error = ERROR_SUCCESS;
3189     BOOL allow_blocking, notify_received = FALSE;
3190 
3191     TRACE("(%p %p %u %x)\n", req, buf, size, flags);
3192 
3193     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3194         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3195 
3196     allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC);
3197 
3198     if(allow_blocking || TryEnterCriticalSection(&req->read_section)) {
3199         if(allow_blocking)
3200             EnterCriticalSection(&req->read_section);
3201         if(hdr->dwError == ERROR_SUCCESS)
3202             hdr->dwError = INTERNET_HANDLE_IN_USE;
3203         else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3204             hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3205 
3206         if(req->read_size) {
3207             read = min(size, req->read_size);
3208             memcpy(buf, req->read_buf + req->read_pos, read);
3209             req->read_size -= read;
3210             req->read_pos += read;
3211         }
3212 
3213         if(read < size && (!read || !(flags & IRF_NO_WAIT)) && !end_of_read_data(req)) {
3214             LeaveCriticalSection(&req->read_section);
3215             INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3216             EnterCriticalSection( &req->read_section );
3217             notify_received = TRUE;
3218 
3219             while(read < size) {
3220                 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, allow_blocking);
3221                 read += cread;
3222                 if (res != ERROR_SUCCESS || !cread)
3223                     break;
3224             }
3225         }
3226 
3227         if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3228             hdr->dwError = ERROR_SUCCESS;
3229         else
3230             error = hdr->dwError;
3231 
3232         LeaveCriticalSection( &req->read_section );
3233     }else {
3234         res = WSAEWOULDBLOCK;
3235     }
3236 
3237     if(res == WSAEWOULDBLOCK) {
3238         if(!(flags & IRF_NO_WAIT))
3239             return async_read(req, buf, size, read, ret_read);
3240         if(!read)
3241             return async_read(req, NULL, 0, 0, NULL);
3242         res = ERROR_SUCCESS;
3243     }
3244 
3245     *ret_read = read;
3246     if (res != ERROR_SUCCESS)
3247         return res;
3248 
3249     if(notify_received)
3250         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3251                 &read, sizeof(read));
3252     return error;
3253 }
3254 
3255 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3256 {
3257     DWORD res;
3258     http_request_t *request = (http_request_t*)hdr;
3259 
3260     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3261 
3262     *written = 0;
3263     res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3264     if (res == ERROR_SUCCESS)
3265         request->bytesWritten += *written;
3266 
3267     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3268     return res;
3269 }
3270 
3271 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3272 {
3273     http_request_t *req = (http_request_t*)hdr;
3274     DWORD res = ERROR_SUCCESS, avail = 0, error = ERROR_SUCCESS;
3275     BOOL allow_blocking, notify_received = FALSE;
3276 
3277     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3278 
3279     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3280         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3281 
3282     *available = 0;
3283     allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC);
3284 
3285     if(allow_blocking || TryEnterCriticalSection(&req->read_section)) {
3286         if(allow_blocking)
3287             EnterCriticalSection(&req->read_section);
3288         if(hdr->dwError == ERROR_SUCCESS)
3289             hdr->dwError = INTERNET_HANDLE_IN_USE;
3290         else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3291             hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3292 
3293         avail = req->read_size;
3294 
3295         if(!avail && !end_of_read_data(req)) {
3296             LeaveCriticalSection(&req->read_section);
3297             INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3298             EnterCriticalSection( &req->read_section );
3299             notify_received = TRUE;
3300 
3301             res = refill_read_buffer(req, allow_blocking, &avail);
3302         }
3303 
3304         if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3305             hdr->dwError = ERROR_SUCCESS;
3306         else
3307             error = hdr->dwError;
3308 
3309         LeaveCriticalSection( &req->read_section );
3310     }else {
3311         res = WSAEWOULDBLOCK;
3312     }
3313 
3314     if(res == WSAEWOULDBLOCK)
3315         return async_read(req, NULL, 0, 0, available);
3316 
3317     if (res != ERROR_SUCCESS)
3318         return res;
3319 
3320     *available = avail;
3321     if(notify_received)
3322         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3323                 &avail, sizeof(avail));
3324     return error;
3325 }
3326 
3327 static DWORD HTTPREQ_LockRequestFile(object_header_t *hdr, req_file_t **ret)
3328 {
3329     http_request_t *req = (http_request_t*)hdr;
3330 
3331     TRACE("(%p)\n", req);
3332 
3333     if(!req->req_file) {
3334         WARN("No cache file name available\n");
3335         return ERROR_FILE_NOT_FOUND;
3336     }
3337 
3338     *ret = req_file_addref(req->req_file);
3339     return ERROR_SUCCESS;
3340 }
3341 
3342 static const object_vtbl_t HTTPREQVtbl = {
3343     HTTPREQ_Destroy,
3344     HTTPREQ_CloseConnection,
3345     HTTPREQ_QueryOption,
3346     HTTPREQ_SetOption,
3347     HTTPREQ_ReadFile,
3348     HTTPREQ_WriteFile,
3349     HTTPREQ_QueryDataAvailable,
3350     NULL,
3351     HTTPREQ_LockRequestFile
3352 };
3353 
3354 /***********************************************************************
3355  *           HTTP_HttpOpenRequestW (internal)
3356  *
3357  * Open a HTTP request handle
3358  *
3359  * RETURNS
3360  *    HINTERNET  a HTTP request handle on success
3361  *    NULL 	 on failure
3362  *
3363  */
3364 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3365         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3366         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3367         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3368 {
3369     appinfo_t *hIC = session->appInfo;
3370     http_request_t *request;
3371     DWORD port, len;
3372 
3373     TRACE("-->\n");
3374 
3375     request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3376     if(!request)
3377         return ERROR_OUTOFMEMORY;
3378 
3379     request->hdr.htype = WH_HHTTPREQ;
3380     request->hdr.dwFlags = dwFlags;
3381     request->hdr.dwContext = dwContext;
3382     request->hdr.decoding = session->hdr.decoding;
3383     request->contentLength = ~0;
3384 
3385     request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3386     request->data_stream = &request->netconn_stream.data_stream;
3387     request->connect_timeout = session->connect_timeout;
3388     request->send_timeout = session->send_timeout;
3389     request->receive_timeout = session->receive_timeout;
3390 
3391     InitializeCriticalSection( &request->headers_section );
3392     request->headers_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.headers_section");
3393 
3394     InitializeCriticalSection( &request->read_section );
3395     request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3396 
3397     WININET_AddRef( &session->hdr );
3398     request->session = session;
3399     list_add_head( &session->hdr.children, &request->hdr.entry );
3400 
3401     port = session->hostPort;
3402     if (port == INTERNET_INVALID_PORT_NUMBER)
3403         port = (session->hdr.dwFlags & INTERNET_FLAG_SECURE) ?
3404                 INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3405 
3406     request->server = get_server(substrz(session->hostName), port, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3407     if(!request->server) {
3408         WININET_Release(&request->hdr);
3409         return ERROR_OUTOFMEMORY;
3410     }
3411 
3412     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3413         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3414     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3415         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3416 
3417     if (lpszObjectName && *lpszObjectName) {
3418         HRESULT rc;
3419         WCHAR dummy;
3420 
3421         len = 1;
3422         rc = UrlCanonicalizeW(lpszObjectName, &dummy, &len, URL_ESCAPE_SPACES_ONLY);
3423         if (rc != E_POINTER)
3424             len = strlenW(lpszObjectName)+1;
3425         request->path = heap_alloc(len*sizeof(WCHAR));
3426         rc = UrlCanonicalizeW(lpszObjectName, request->path, &len,
3427                    URL_ESCAPE_SPACES_ONLY);
3428         if (rc != S_OK)
3429         {
3430             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3431             strcpyW(request->path,lpszObjectName);
3432         }
3433     }else {
3434         static const WCHAR slashW[] = {'/',0};
3435 
3436         request->path = heap_strdupW(slashW);
3437     }
3438 
3439     if (lpszReferrer && *lpszReferrer)
3440         HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3441 
3442     if (lpszAcceptTypes)
3443     {
3444         int i;
3445         for (i = 0; lpszAcceptTypes[i]; i++)
3446         {
3447             if (!*lpszAcceptTypes[i]) continue;
3448             HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3449                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3450                                HTTP_ADDHDR_FLAG_REQ |
3451                                (i == 0 ? (HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD) : 0));
3452         }
3453     }
3454 
3455     request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3456     request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3457 
3458     if (hIC->proxy && hIC->proxy[0] && !HTTP_ShouldBypassProxy(hIC, session->hostName))
3459         HTTP_DealWithProxy( hIC, session, request );
3460 
3461     INTERNET_SendCallback(&session->hdr, dwContext,
3462                           INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3463                           sizeof(HINTERNET));
3464 
3465     TRACE("<-- (%p)\n", request);
3466 
3467     *ret = request->hdr.hInternet;
3468     return ERROR_SUCCESS;
3469 }
3470 
3471 /***********************************************************************
3472  *           HttpOpenRequestW (WININET.@)
3473  *
3474  * Open a HTTP request handle
3475  *
3476  * RETURNS
3477  *    HINTERNET  a HTTP request handle on success
3478  *    NULL 	 on failure
3479  *
3480  */
3481 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3482 	LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3483 	LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3484 	DWORD dwFlags, DWORD_PTR dwContext)
3485 {
3486     http_session_t *session;
3487     HINTERNET handle = NULL;
3488     DWORD res;
3489 
3490     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3491           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3492           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3493           dwFlags, dwContext);
3494     if(lpszAcceptTypes!=NULL)
3495     {
3496         int i;
3497         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3498             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3499     }
3500 
3501     session = (http_session_t*) get_handle_object( hHttpSession );
3502     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
3503     {
3504         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3505         goto lend;
3506     }
3507 
3508     /*
3509      * My tests seem to show that the windows version does not
3510      * become asynchronous until after this point. And anyhow
3511      * if this call was asynchronous then how would you get the
3512      * necessary HINTERNET pointer returned by this function.
3513      *
3514      */
3515     res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3516                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
3517                                 dwFlags, dwContext, &handle);
3518 lend:
3519     if( session )
3520         WININET_Release( &session->hdr );
3521     TRACE("returning %p\n", handle);
3522     if(res != ERROR_SUCCESS)
3523         SetLastError(res);
3524     return handle;
3525 }
3526 
3527 static const LPCWSTR header_lookup[] = {
3528     szMime_Version,		/* HTTP_QUERY_MIME_VERSION = 0 */
3529     szContent_Type,		/* HTTP_QUERY_CONTENT_TYPE = 1 */
3530     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3531     szContent_ID,		/* HTTP_QUERY_CONTENT_ID = 3 */
3532     NULL,			/* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3533     szContent_Length,		/* HTTP_QUERY_CONTENT_LENGTH =  5 */
3534     szContent_Language,		/* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
3535     szAllow,			/* HTTP_QUERY_ALLOW = 7 */
3536     szPublic,			/* HTTP_QUERY_PUBLIC = 8 */
3537     szDate,			/* HTTP_QUERY_DATE = 9 */
3538     szExpires,			/* HTTP_QUERY_EXPIRES = 10 */
3539     szLast_Modified,		/* HTTP_QUERY_LAST_MODIFIED = 11 */
3540     NULL,			/* HTTP_QUERY_MESSAGE_ID = 12 */
3541     szURI,			/* HTTP_QUERY_URI = 13 */
3542     szFrom,			/* HTTP_QUERY_DERIVED_FROM = 14 */
3543     NULL,			/* HTTP_QUERY_COST = 15 */
3544     NULL,			/* HTTP_QUERY_LINK = 16 */
3545     szPragma,			/* HTTP_QUERY_PRAGMA = 17 */
3546     NULL,			/* HTTP_QUERY_VERSION = 18 */
3547     szStatus,			/* HTTP_QUERY_STATUS_CODE = 19 */
3548     NULL,			/* HTTP_QUERY_STATUS_TEXT = 20 */
3549     NULL,			/* HTTP_QUERY_RAW_HEADERS = 21 */
3550     NULL,			/* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3551     szConnection,		/* HTTP_QUERY_CONNECTION = 23 */
3552     szAccept,			/* HTTP_QUERY_ACCEPT = 24 */
3553     szAccept_Charset,		/* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3554     szAccept_Encoding,		/* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3555     szAccept_Language,		/* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3556     szAuthorization,		/* HTTP_QUERY_AUTHORIZATION = 28 */
3557     szContent_Encoding,		/* HTTP_QUERY_CONTENT_ENCODING = 29 */
3558     NULL,			/* HTTP_QUERY_FORWARDED = 30 */
3559     NULL,			/* HTTP_QUERY_FROM = 31 */
3560     szIf_Modified_Since,	/* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3561     szLocation,			/* HTTP_QUERY_LOCATION = 33 */
3562     NULL,			/* HTTP_QUERY_ORIG_URI = 34 */
3563     szReferer,			/* HTTP_QUERY_REFERER = 35 */
3564     szRetry_After,		/* HTTP_QUERY_RETRY_AFTER = 36 */
3565     szServer,			/* HTTP_QUERY_SERVER = 37 */
3566     NULL,			/* HTTP_TITLE = 38 */
3567     szUser_Agent,		/* HTTP_QUERY_USER_AGENT = 39 */
3568     szWWW_Authenticate,		/* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3569     szProxy_Authenticate,	/* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3570     szAccept_Ranges,		/* HTTP_QUERY_ACCEPT_RANGES = 42 */
3571     szSet_Cookie,		/* HTTP_QUERY_SET_COOKIE = 43 */
3572     szCookie,			/* HTTP_QUERY_COOKIE = 44 */
3573     NULL,			/* HTTP_QUERY_REQUEST_METHOD = 45 */
3574     NULL,			/* HTTP_QUERY_REFRESH = 46 */
3575     szContent_Disposition,	/* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3576     szAge,			/* HTTP_QUERY_AGE = 48 */
3577     szCache_Control,		/* HTTP_QUERY_CACHE_CONTROL = 49 */
3578     szContent_Base,		/* HTTP_QUERY_CONTENT_BASE = 50 */
3579     szContent_Location,		/* HTTP_QUERY_CONTENT_LOCATION = 51 */
3580     szContent_MD5,		/* HTTP_QUERY_CONTENT_MD5 = 52 */
3581     szContent_Range,		/* HTTP_QUERY_CONTENT_RANGE = 53 */
3582     szETag,			/* HTTP_QUERY_ETAG = 54 */
3583     hostW,			/* HTTP_QUERY_HOST = 55 */
3584     szIf_Match,			/* HTTP_QUERY_IF_MATCH = 56 */
3585     szIf_None_Match,		/* HTTP_QUERY_IF_NONE_MATCH = 57 */
3586     szIf_Range,			/* HTTP_QUERY_IF_RANGE = 58 */
3587     szIf_Unmodified_Since,	/* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3588     szMax_Forwards,		/* HTTP_QUERY_MAX_FORWARDS = 60 */
3589     szProxy_Authorization,	/* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3590     szRange,			/* HTTP_QUERY_RANGE = 62 */
3591     szTransfer_Encoding,	/* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3592     szUpgrade,			/* HTTP_QUERY_UPGRADE = 64 */
3593     szVary,			/* HTTP_QUERY_VARY = 65 */
3594     szVia,			/* HTTP_QUERY_VIA = 66 */
3595     szWarning,			/* HTTP_QUERY_WARNING = 67 */
3596     szExpect,			/* HTTP_QUERY_EXPECT = 68 */
3597     szProxy_Connection,		/* HTTP_QUERY_PROXY_CONNECTION = 69 */
3598     szUnless_Modified_Since,	/* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3599 };
3600 
3601 /***********************************************************************
3602  *           HTTP_HttpQueryInfoW (internal)
3603  */
3604 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3605         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3606 {
3607     LPHTTPHEADERW lphttpHdr = NULL;
3608     BOOL request_only = !!(dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS);
3609     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3610     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3611     INT index = -1;
3612 
3613     EnterCriticalSection( &request->headers_section );
3614 
3615     /* Find requested header structure */
3616     switch (level)
3617     {
3618     case HTTP_QUERY_CUSTOM:
3619         if (!lpBuffer)
3620         {
3621             LeaveCriticalSection( &request->headers_section );
3622             return ERROR_INVALID_PARAMETER;
3623         }
3624         index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3625         break;
3626     case HTTP_QUERY_RAW_HEADERS_CRLF:
3627         {
3628             LPWSTR headers;
3629             DWORD len = 0;
3630             DWORD res = ERROR_INVALID_PARAMETER;
3631 
3632             if (request_only)
3633                 headers = build_request_header(request, request->verb, request->path, request->version, TRUE);
3634             else
3635                 headers = build_response_header(request, TRUE);
3636             if (!headers)
3637             {
3638                 LeaveCriticalSection( &request->headers_section );
3639                 return ERROR_OUTOFMEMORY;
3640             }
3641 
3642             len = strlenW(headers) * sizeof(WCHAR);
3643             if (len + sizeof(WCHAR) > *lpdwBufferLength)
3644             {
3645                 len += sizeof(WCHAR);
3646                 res = ERROR_INSUFFICIENT_BUFFER;
3647             }
3648             else if (lpBuffer)
3649             {
3650                 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3651                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3652                 res = ERROR_SUCCESS;
3653             }
3654             *lpdwBufferLength = len;
3655 
3656             heap_free(headers);
3657             LeaveCriticalSection( &request->headers_section );
3658             return res;
3659         }
3660     case HTTP_QUERY_RAW_HEADERS:
3661         {
3662             LPWSTR headers;
3663             DWORD len;
3664 
3665             if (request_only)
3666                 headers = build_request_header(request, request->verb, request->path, request->version, FALSE);
3667             else
3668                 headers = build_response_header(request, FALSE);
3669 
3670             if (!headers)
3671             {
3672                 LeaveCriticalSection( &request->headers_section );
3673                 return ERROR_OUTOFMEMORY;
3674             }
3675 
3676             len = strlenW(headers) * sizeof(WCHAR);
3677             if (len > *lpdwBufferLength)
3678             {
3679                 *lpdwBufferLength = len;
3680                 heap_free(headers);
3681                 LeaveCriticalSection( &request->headers_section );
3682                 return ERROR_INSUFFICIENT_BUFFER;
3683             }
3684 
3685             if (lpBuffer)
3686             {
3687                 DWORD i;
3688 
3689                 TRACE("returning data: %s\n", debugstr_wn(headers, len / sizeof(WCHAR)));
3690 
3691                 for (i = 0; i < len / sizeof(WCHAR); i++)
3692                 {
3693                     if (headers[i] == '\n')
3694                         headers[i] = 0;
3695                 }
3696                 memcpy(lpBuffer, headers, len);
3697             }
3698             *lpdwBufferLength = len - sizeof(WCHAR);
3699 
3700             heap_free(headers);
3701             LeaveCriticalSection( &request->headers_section );
3702             return ERROR_SUCCESS;
3703         }
3704     case HTTP_QUERY_STATUS_TEXT:
3705         if (request->statusText)
3706         {
3707             DWORD len = strlenW(request->statusText);
3708             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3709             {
3710                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3711                 LeaveCriticalSection( &request->headers_section );
3712                 return ERROR_INSUFFICIENT_BUFFER;
3713             }
3714             if (lpBuffer)
3715             {
3716                 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3717                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3718             }
3719             *lpdwBufferLength = len * sizeof(WCHAR);
3720             LeaveCriticalSection( &request->headers_section );
3721             return ERROR_SUCCESS;
3722         }
3723         break;
3724     case HTTP_QUERY_VERSION:
3725         if (request->version)
3726         {
3727             DWORD len = strlenW(request->version);
3728             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3729             {
3730                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3731                 LeaveCriticalSection( &request->headers_section );
3732                 return ERROR_INSUFFICIENT_BUFFER;
3733             }
3734             if (lpBuffer)
3735             {
3736                 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3737                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3738             }
3739             *lpdwBufferLength = len * sizeof(WCHAR);
3740             LeaveCriticalSection( &request->headers_section );
3741             return ERROR_SUCCESS;
3742         }
3743         break;
3744     case HTTP_QUERY_CONTENT_ENCODING:
3745         index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3746                 requested_index,request_only);
3747         break;
3748     case HTTP_QUERY_STATUS_CODE: {
3749         DWORD res = ERROR_SUCCESS;
3750 
3751         if(request_only)
3752         {
3753             LeaveCriticalSection( &request->headers_section );
3754             return ERROR_HTTP_INVALID_QUERY_REQUEST;
3755         }
3756 
3757         if(requested_index)
3758             break;
3759 
3760         if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3761             if(*lpdwBufferLength >= sizeof(DWORD))
3762                 *(DWORD*)lpBuffer = request->status_code;
3763             else
3764                 res = ERROR_INSUFFICIENT_BUFFER;
3765             *lpdwBufferLength = sizeof(DWORD);
3766         }else {
3767             WCHAR buf[12];
3768             DWORD size;
3769             static const WCHAR formatW[] = {'%','u',0};
3770 
3771             size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3772 
3773             if(size <= *lpdwBufferLength) {
3774                 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3775             }else {
3776                 size += sizeof(WCHAR);
3777                 res = ERROR_INSUFFICIENT_BUFFER;
3778             }
3779 
3780             *lpdwBufferLength = size;
3781         }
3782         LeaveCriticalSection( &request->headers_section );
3783         return res;
3784     }
3785     default:
3786         assert (ARRAY_SIZE(header_lookup) == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3787 
3788         if (level < ARRAY_SIZE(header_lookup) && header_lookup[level])
3789             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3790                                               requested_index,request_only);
3791     }
3792 
3793     if (index >= 0)
3794         lphttpHdr = &request->custHeaders[index];
3795 
3796     /* Ensure header satisfies requested attributes */
3797     if (!lphttpHdr ||
3798         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3799          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3800     {
3801         LeaveCriticalSection( &request->headers_section );
3802         return ERROR_HTTP_HEADER_NOT_FOUND;
3803     }
3804 
3805     /* coalesce value to requested type */
3806     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3807     {
3808         unsigned long value;
3809 
3810         if (*lpdwBufferLength != sizeof(DWORD))
3811         {
3812             LeaveCriticalSection( &request->headers_section );
3813             return ERROR_HTTP_INVALID_HEADER;
3814         }
3815 
3816         errno = 0;
3817         value = strtoulW( lphttpHdr->lpszValue, NULL, 10 );
3818         if (value > UINT_MAX || (value == ULONG_MAX && errno == ERANGE))
3819         {
3820             LeaveCriticalSection( &request->headers_section );
3821             return ERROR_HTTP_INVALID_HEADER;
3822         }
3823 
3824         *(DWORD *)lpBuffer = value;
3825         TRACE(" returning number: %u\n", *(DWORD *)lpBuffer);
3826     }
3827     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3828     {
3829         time_t tmpTime;
3830         struct tm tmpTM;
3831         SYSTEMTIME *STHook;
3832 
3833         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3834 
3835         tmpTM = *gmtime(&tmpTime);
3836         STHook = (SYSTEMTIME *)lpBuffer;
3837         STHook->wDay = tmpTM.tm_mday;
3838         STHook->wHour = tmpTM.tm_hour;
3839         STHook->wMilliseconds = 0;
3840         STHook->wMinute = tmpTM.tm_min;
3841         STHook->wDayOfWeek = tmpTM.tm_wday;
3842         STHook->wMonth = tmpTM.tm_mon + 1;
3843         STHook->wSecond = tmpTM.tm_sec;
3844         STHook->wYear = 1900+tmpTM.tm_year;
3845 
3846         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3847               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3848               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3849     }
3850     else if (lphttpHdr->lpszValue)
3851     {
3852         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3853 
3854         if (len > *lpdwBufferLength)
3855         {
3856             *lpdwBufferLength = len;
3857             LeaveCriticalSection( &request->headers_section );
3858             return ERROR_INSUFFICIENT_BUFFER;
3859         }
3860         if (lpBuffer)
3861         {
3862             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3863             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3864         }
3865         *lpdwBufferLength = len - sizeof(WCHAR);
3866     }
3867     if (lpdwIndex) (*lpdwIndex)++;
3868 
3869     LeaveCriticalSection( &request->headers_section );
3870     return ERROR_SUCCESS;
3871 }
3872 
3873 /***********************************************************************
3874  *           HttpQueryInfoW (WININET.@)
3875  *
3876  * Queries for information about an HTTP request
3877  *
3878  * RETURNS
3879  *    TRUE  on success
3880  *    FALSE on failure
3881  *
3882  */
3883 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3884         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3885 {
3886     http_request_t *request;
3887     DWORD res;
3888 
3889     if (TRACE_ON(wininet)) {
3890 #define FE(x) { x, #x }
3891 	static const wininet_flag_info query_flags[] = {
3892 	    FE(HTTP_QUERY_MIME_VERSION),
3893 	    FE(HTTP_QUERY_CONTENT_TYPE),
3894 	    FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3895 	    FE(HTTP_QUERY_CONTENT_ID),
3896 	    FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3897 	    FE(HTTP_QUERY_CONTENT_LENGTH),
3898 	    FE(HTTP_QUERY_CONTENT_LANGUAGE),
3899 	    FE(HTTP_QUERY_ALLOW),
3900 	    FE(HTTP_QUERY_PUBLIC),
3901 	    FE(HTTP_QUERY_DATE),
3902 	    FE(HTTP_QUERY_EXPIRES),
3903 	    FE(HTTP_QUERY_LAST_MODIFIED),
3904 	    FE(HTTP_QUERY_MESSAGE_ID),
3905 	    FE(HTTP_QUERY_URI),
3906 	    FE(HTTP_QUERY_DERIVED_FROM),
3907 	    FE(HTTP_QUERY_COST),
3908 	    FE(HTTP_QUERY_LINK),
3909 	    FE(HTTP_QUERY_PRAGMA),
3910 	    FE(HTTP_QUERY_VERSION),
3911 	    FE(HTTP_QUERY_STATUS_CODE),
3912 	    FE(HTTP_QUERY_STATUS_TEXT),
3913 	    FE(HTTP_QUERY_RAW_HEADERS),
3914 	    FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3915 	    FE(HTTP_QUERY_CONNECTION),
3916 	    FE(HTTP_QUERY_ACCEPT),
3917 	    FE(HTTP_QUERY_ACCEPT_CHARSET),
3918 	    FE(HTTP_QUERY_ACCEPT_ENCODING),
3919 	    FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3920 	    FE(HTTP_QUERY_AUTHORIZATION),
3921 	    FE(HTTP_QUERY_CONTENT_ENCODING),
3922 	    FE(HTTP_QUERY_FORWARDED),
3923 	    FE(HTTP_QUERY_FROM),
3924 	    FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3925 	    FE(HTTP_QUERY_LOCATION),
3926 	    FE(HTTP_QUERY_ORIG_URI),
3927 	    FE(HTTP_QUERY_REFERER),
3928 	    FE(HTTP_QUERY_RETRY_AFTER),
3929 	    FE(HTTP_QUERY_SERVER),
3930 	    FE(HTTP_QUERY_TITLE),
3931 	    FE(HTTP_QUERY_USER_AGENT),
3932 	    FE(HTTP_QUERY_WWW_AUTHENTICATE),
3933 	    FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3934 	    FE(HTTP_QUERY_ACCEPT_RANGES),
3935         FE(HTTP_QUERY_SET_COOKIE),
3936         FE(HTTP_QUERY_COOKIE),
3937 	    FE(HTTP_QUERY_REQUEST_METHOD),
3938 	    FE(HTTP_QUERY_REFRESH),
3939 	    FE(HTTP_QUERY_CONTENT_DISPOSITION),
3940 	    FE(HTTP_QUERY_AGE),
3941 	    FE(HTTP_QUERY_CACHE_CONTROL),
3942 	    FE(HTTP_QUERY_CONTENT_BASE),
3943 	    FE(HTTP_QUERY_CONTENT_LOCATION),
3944 	    FE(HTTP_QUERY_CONTENT_MD5),
3945 	    FE(HTTP_QUERY_CONTENT_RANGE),
3946 	    FE(HTTP_QUERY_ETAG),
3947 	    FE(HTTP_QUERY_HOST),
3948 	    FE(HTTP_QUERY_IF_MATCH),
3949 	    FE(HTTP_QUERY_IF_NONE_MATCH),
3950 	    FE(HTTP_QUERY_IF_RANGE),
3951 	    FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3952 	    FE(HTTP_QUERY_MAX_FORWARDS),
3953 	    FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3954 	    FE(HTTP_QUERY_RANGE),
3955 	    FE(HTTP_QUERY_TRANSFER_ENCODING),
3956 	    FE(HTTP_QUERY_UPGRADE),
3957 	    FE(HTTP_QUERY_VARY),
3958 	    FE(HTTP_QUERY_VIA),
3959 	    FE(HTTP_QUERY_WARNING),
3960 	    FE(HTTP_QUERY_CUSTOM)
3961 	};
3962 	static const wininet_flag_info modifier_flags[] = {
3963 	    FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3964 	    FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3965 	    FE(HTTP_QUERY_FLAG_NUMBER),
3966 	    FE(HTTP_QUERY_FLAG_COALESCE)
3967 	};
3968 #undef FE
3969 	DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3970 	DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3971 	DWORD i;
3972 
3973 	TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3974 	TRACE("  Attribute:");
3975         for (i = 0; i < ARRAY_SIZE(query_flags); i++) {
3976 	    if (query_flags[i].val == info) {
3977 		TRACE(" %s", query_flags[i].name);
3978 		break;
3979 	    }
3980 	}
3981         if (i == ARRAY_SIZE(query_flags)) {
3982 	    TRACE(" Unknown (%08x)", info);
3983 	}
3984 
3985 	TRACE(" Modifier:");
3986         for (i = 0; i < ARRAY_SIZE(modifier_flags); i++) {
3987 	    if (modifier_flags[i].val & info_mod) {
3988 		TRACE(" %s", modifier_flags[i].name);
3989 		info_mod &= ~ modifier_flags[i].val;
3990 	    }
3991 	}
3992 
3993 	if (info_mod) {
3994 	    TRACE(" Unknown (%08x)", info_mod);
3995 	}
3996 	TRACE("\n");
3997     }
3998 
3999     request = (http_request_t*) get_handle_object( hHttpRequest );
4000     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
4001     {
4002         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4003         goto lend;
4004     }
4005 
4006     if (lpBuffer == NULL)
4007         *lpdwBufferLength = 0;
4008     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
4009                                lpBuffer, lpdwBufferLength, lpdwIndex);
4010 
4011 lend:
4012     if( request )
4013          WININET_Release( &request->hdr );
4014 
4015     TRACE("%u <--\n", res);
4016 
4017     SetLastError(res);
4018     return res == ERROR_SUCCESS;
4019 }
4020 
4021 /***********************************************************************
4022  *           HttpQueryInfoA (WININET.@)
4023  *
4024  * Queries for information about an HTTP request
4025  *
4026  * RETURNS
4027  *    TRUE  on success
4028  *    FALSE on failure
4029  *
4030  */
4031 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
4032 	LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
4033 {
4034     BOOL result;
4035     DWORD len;
4036     WCHAR* bufferW;
4037 
4038     TRACE("%p %x\n", hHttpRequest, dwInfoLevel);
4039 
4040     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
4041        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
4042     {
4043         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
4044                                lpdwBufferLength, lpdwIndex );
4045     }
4046 
4047     if (lpBuffer)
4048     {
4049         DWORD alloclen;
4050         len = (*lpdwBufferLength)*sizeof(WCHAR);
4051         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
4052         {
4053             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
4054             if (alloclen < len)
4055                 alloclen = len;
4056         }
4057         else
4058             alloclen = len;
4059         bufferW = heap_alloc(alloclen);
4060         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
4061         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
4062             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
4063     } else
4064     {
4065         bufferW = NULL;
4066         len = 0;
4067     }
4068 
4069     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
4070                            &len, lpdwIndex );
4071     if( result )
4072     {
4073         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
4074                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
4075         *lpdwBufferLength = len - 1;
4076 
4077         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
4078     }
4079     else
4080         /* since the strings being returned from HttpQueryInfoW should be
4081          * only ASCII characters, it is reasonable to assume that all of
4082          * the Unicode characters can be reduced to a single byte */
4083         *lpdwBufferLength = len / sizeof(WCHAR);
4084 
4085     heap_free( bufferW );
4086     return result;
4087 }
4088 
4089 static WCHAR *get_redirect_url(http_request_t *request)
4090 {
4091     static WCHAR szHttp[] = {'h','t','t','p',0};
4092     static WCHAR szHttps[] = {'h','t','t','p','s',0};
4093     http_session_t *session = request->session;
4094     URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
4095     WCHAR *orig_url = NULL, *redirect_url = NULL, *combined_url = NULL;
4096     DWORD url_length = 0, res;
4097     BOOL b;
4098 
4099     url_length = 0;
4100     res = HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, redirect_url, &url_length, NULL);
4101     if(res == ERROR_INSUFFICIENT_BUFFER) {
4102         redirect_url = heap_alloc(url_length);
4103         res = HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, redirect_url, &url_length, NULL);
4104     }
4105     if(res != ERROR_SUCCESS) {
4106         heap_free(redirect_url);
4107         return NULL;
4108     }
4109 
4110     urlComponents.dwSchemeLength = 1;
4111     b = InternetCrackUrlW(redirect_url, url_length / sizeof(WCHAR), 0, &urlComponents);
4112     if(b && urlComponents.dwSchemeLength &&
4113        urlComponents.nScheme != INTERNET_SCHEME_HTTP && urlComponents.nScheme != INTERNET_SCHEME_HTTPS) {
4114         TRACE("redirect to non-http URL\n");
4115         return NULL;
4116     }
4117 
4118     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
4119     urlComponents.dwSchemeLength = 0;
4120     urlComponents.lpszHostName = request->server->name;
4121     urlComponents.nPort = request->server->port;
4122     urlComponents.lpszUserName = session->userName;
4123     urlComponents.lpszUrlPath = request->path;
4124 
4125     b = InternetCreateUrlW(&urlComponents, 0, NULL, &url_length);
4126     if(!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
4127         orig_url = heap_alloc(url_length);
4128 
4129         /* convert from bytes to characters */
4130         url_length = url_length / sizeof(WCHAR) - 1;
4131         b = InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length);
4132     }
4133 
4134     if(b) {
4135         url_length = 0;
4136         b = InternetCombineUrlW(orig_url, redirect_url, NULL, &url_length, ICU_ENCODE_SPACES_ONLY);
4137         if(!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
4138             combined_url = heap_alloc(url_length * sizeof(WCHAR));
4139             b = InternetCombineUrlW(orig_url, redirect_url, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY);
4140             if(!b) {
4141                 heap_free(combined_url);
4142                 combined_url = NULL;
4143             }
4144         }
4145     }
4146 
4147     heap_free(orig_url);
4148     heap_free(redirect_url);
4149     return combined_url;
4150 }
4151 
4152 
4153 /***********************************************************************
4154  *           HTTP_HandleRedirect (internal)
4155  */
4156 static DWORD HTTP_HandleRedirect(http_request_t *request, WCHAR *url)
4157 {
4158     URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
4159     http_session_t *session = request->session;
4160     size_t url_len = strlenW(url);
4161 
4162     if(url[0] == '/')
4163     {
4164         /* if it's an absolute path, keep the same session info */
4165         urlComponents.lpszUrlPath = url;
4166         urlComponents.dwUrlPathLength = url_len;
4167     }
4168     else
4169     {
4170         urlComponents.dwHostNameLength = 1;
4171         urlComponents.dwUserNameLength = 1;
4172         urlComponents.dwUrlPathLength = 1;
4173         if(!InternetCrackUrlW(url, url_len, 0, &urlComponents))
4174             return INTERNET_GetLastError();
4175 
4176         if(!urlComponents.dwHostNameLength)
4177             return ERROR_INTERNET_INVALID_URL;
4178     }
4179 
4180     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4181                           url, (url_len + 1) * sizeof(WCHAR));
4182 
4183     if(urlComponents.dwHostNameLength) {
4184         BOOL custom_port = FALSE;
4185         substr_t host;
4186 
4187         if(urlComponents.nScheme == INTERNET_SCHEME_HTTP) {
4188             if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
4189                 TRACE("redirect from secure page to non-secure page\n");
4190                 /* FIXME: warn about from secure redirect to non-secure page */
4191                 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
4192             }
4193 
4194             custom_port = urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT;
4195         }else if(urlComponents.nScheme == INTERNET_SCHEME_HTTPS) {
4196             if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
4197                 TRACE("redirect from non-secure page to secure page\n");
4198                 /* FIXME: notify about redirect to secure page */
4199                 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
4200             }
4201 
4202             custom_port = urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT;
4203         }
4204 
4205         heap_free(session->hostName);
4206 
4207         session->hostName = heap_strndupW(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
4208         session->hostPort = urlComponents.nPort;
4209 
4210         heap_free(session->userName);
4211         session->userName = NULL;
4212         if (urlComponents.dwUserNameLength)
4213             session->userName = heap_strndupW(urlComponents.lpszUserName, urlComponents.dwUserNameLength);
4214 
4215         reset_data_stream(request);
4216 
4217         host = substr(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
4218 
4219         if(host.len != strlenW(request->server->name) || strncmpiW(request->server->name, host.str, host.len)
4220            || request->server->port != urlComponents.nPort) {
4221             server_t *new_server;
4222 
4223             new_server = get_server(host, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
4224             server_release(request->server);
4225             request->server = new_server;
4226         }
4227 
4228         if (custom_port)
4229             HTTP_ProcessHeader(request, hostW, request->server->host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4230         else
4231             HTTP_ProcessHeader(request, hostW, request->server->name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4232     }
4233 
4234     heap_free(request->path);
4235     request->path = NULL;
4236     if(urlComponents.dwUrlPathLength)
4237     {
4238         DWORD needed = 1;
4239         HRESULT rc;
4240         WCHAR dummy = 0;
4241         WCHAR *path;
4242 
4243         path = heap_strndupW(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength);
4244         rc = UrlEscapeW(path, &dummy, &needed, URL_ESCAPE_SPACES_ONLY);
4245         if (rc != E_POINTER)
4246             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4247         request->path = heap_alloc(needed*sizeof(WCHAR));
4248         rc = UrlEscapeW(path, request->path, &needed,
4249                         URL_ESCAPE_SPACES_ONLY);
4250         if (rc != S_OK)
4251         {
4252             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4253             strcpyW(request->path, path);
4254         }
4255         heap_free(path);
4256     }
4257 
4258     /* Remove custom content-type/length headers on redirects.  */
4259     remove_header(request, szContent_Type, TRUE);
4260     remove_header(request, szContent_Length, TRUE);
4261 
4262     return ERROR_SUCCESS;
4263 }
4264 
4265 /***********************************************************************
4266  *           HTTP_build_req (internal)
4267  *
4268  *  concatenate all the strings in the request together
4269  */
4270 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4271 {
4272     LPCWSTR *t;
4273     LPWSTR str;
4274 
4275     for( t = list; *t ; t++  )
4276         len += strlenW( *t );
4277     len++;
4278 
4279     str = heap_alloc(len*sizeof(WCHAR));
4280     *str = 0;
4281 
4282     for( t = list; *t ; t++ )
4283         strcatW( str, *t );
4284 
4285     return str;
4286 }
4287 
4288 static void HTTP_InsertCookies(http_request_t *request)
4289 {
4290     WCHAR *cookies;
4291     DWORD res;
4292 
4293     res = get_cookie_header(request->server->name, request->path, &cookies);
4294     if(res != ERROR_SUCCESS || !cookies)
4295         return;
4296 
4297     HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies),
4298                                 HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD);
4299     heap_free(cookies);
4300 }
4301 
4302 static WORD HTTP_ParseWkday(LPCWSTR day)
4303 {
4304     static const WCHAR days[7][4] = {{ 's','u','n',0 },
4305                                      { 'm','o','n',0 },
4306                                      { 't','u','e',0 },
4307                                      { 'w','e','d',0 },
4308                                      { 't','h','u',0 },
4309                                      { 'f','r','i',0 },
4310                                      { 's','a','t',0 }};
4311     unsigned int i;
4312     for (i = 0; i < ARRAY_SIZE(days); i++)
4313         if (!strcmpiW(day, days[i]))
4314             return i;
4315 
4316     /* Invalid */
4317     return 7;
4318 }
4319 
4320 static WORD HTTP_ParseMonth(LPCWSTR month)
4321 {
4322     static const WCHAR jan[] = { 'j','a','n',0 };
4323     static const WCHAR feb[] = { 'f','e','b',0 };
4324     static const WCHAR mar[] = { 'm','a','r',0 };
4325     static const WCHAR apr[] = { 'a','p','r',0 };
4326     static const WCHAR may[] = { 'm','a','y',0 };
4327     static const WCHAR jun[] = { 'j','u','n',0 };
4328     static const WCHAR jul[] = { 'j','u','l',0 };
4329     static const WCHAR aug[] = { 'a','u','g',0 };
4330     static const WCHAR sep[] = { 's','e','p',0 };
4331     static const WCHAR oct[] = { 'o','c','t',0 };
4332     static const WCHAR nov[] = { 'n','o','v',0 };
4333     static const WCHAR dec[] = { 'd','e','c',0 };
4334 
4335     if (!strcmpiW(month, jan)) return 1;
4336     if (!strcmpiW(month, feb)) return 2;
4337     if (!strcmpiW(month, mar)) return 3;
4338     if (!strcmpiW(month, apr)) return 4;
4339     if (!strcmpiW(month, may)) return 5;
4340     if (!strcmpiW(month, jun)) return 6;
4341     if (!strcmpiW(month, jul)) return 7;
4342     if (!strcmpiW(month, aug)) return 8;
4343     if (!strcmpiW(month, sep)) return 9;
4344     if (!strcmpiW(month, oct)) return 10;
4345     if (!strcmpiW(month, nov)) return 11;
4346     if (!strcmpiW(month, dec)) return 12;
4347     /* Invalid */
4348     return 0;
4349 }
4350 
4351 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4352  * optionally preceded by whitespace.
4353  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4354  * st, and sets *str to the first character after the time format.
4355  */
4356 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4357 {
4358     LPCWSTR ptr = *str;
4359     WCHAR *nextPtr;
4360     unsigned long num;
4361 
4362     while (isspaceW(*ptr))
4363         ptr++;
4364 
4365     num = strtoulW(ptr, &nextPtr, 10);
4366     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4367     {
4368         ERR("unexpected time format %s\n", debugstr_w(ptr));
4369         return FALSE;
4370     }
4371     if (num > 23)
4372     {
4373         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4374         return FALSE;
4375     }
4376     ptr = nextPtr + 1;
4377     st->wHour = (WORD)num;
4378     num = strtoulW(ptr, &nextPtr, 10);
4379     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4380     {
4381         ERR("unexpected time format %s\n", debugstr_w(ptr));
4382         return FALSE;
4383     }
4384     if (num > 59)
4385     {
4386         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4387         return FALSE;
4388     }
4389     ptr = nextPtr + 1;
4390     st->wMinute = (WORD)num;
4391     num = strtoulW(ptr, &nextPtr, 10);
4392     if (!nextPtr || nextPtr <= ptr)
4393     {
4394         ERR("unexpected time format %s\n", debugstr_w(ptr));
4395         return FALSE;
4396     }
4397     if (num > 59)
4398     {
4399         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4400         return FALSE;
4401     }
4402     *str = nextPtr;
4403     st->wSecond = (WORD)num;
4404     return TRUE;
4405 }
4406 
4407 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4408 {
4409     static const WCHAR gmt[]= { 'G','M','T',0 };
4410     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4411     LPCWSTR ptr;
4412     SYSTEMTIME st = { 0 };
4413     unsigned long num;
4414 
4415     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4416          dayPtr - day < ARRAY_SIZE(day) - 1; ptr++, dayPtr++)
4417         *dayPtr = *ptr;
4418     *dayPtr = 0;
4419     st.wDayOfWeek = HTTP_ParseWkday(day);
4420     if (st.wDayOfWeek >= 7)
4421     {
4422         ERR("unexpected weekday %s\n", debugstr_w(day));
4423         return FALSE;
4424     }
4425 
4426     while (isspaceW(*ptr))
4427         ptr++;
4428 
4429     for (monthPtr = month; !isspaceW(*ptr) && monthPtr - month < ARRAY_SIZE(month) - 1;
4430          monthPtr++, ptr++)
4431         *monthPtr = *ptr;
4432     *monthPtr = 0;
4433     st.wMonth = HTTP_ParseMonth(month);
4434     if (!st.wMonth || st.wMonth > 12)
4435     {
4436         ERR("unexpected month %s\n", debugstr_w(month));
4437         return FALSE;
4438     }
4439 
4440     while (isspaceW(*ptr))
4441         ptr++;
4442 
4443     num = strtoulW(ptr, &nextPtr, 10);
4444     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4445     {
4446         ERR("unexpected day %s\n", debugstr_w(ptr));
4447         return FALSE;
4448     }
4449     ptr = nextPtr;
4450     st.wDay = (WORD)num;
4451 
4452     while (isspaceW(*ptr))
4453         ptr++;
4454 
4455     if (!HTTP_ParseTime(&st, &ptr))
4456         return FALSE;
4457 
4458     while (isspaceW(*ptr))
4459         ptr++;
4460 
4461     num = strtoulW(ptr, &nextPtr, 10);
4462     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4463     {
4464         ERR("unexpected year %s\n", debugstr_w(ptr));
4465         return FALSE;
4466     }
4467     ptr = nextPtr;
4468     st.wYear = (WORD)num;
4469 
4470     while (isspaceW(*ptr))
4471         ptr++;
4472 
4473     /* asctime() doesn't report a timezone, but some web servers do, so accept
4474      * with or without GMT.
4475      */
4476     if (*ptr && strcmpW(ptr, gmt))
4477     {
4478         ERR("unexpected timezone %s\n", debugstr_w(ptr));
4479         return FALSE;
4480     }
4481     return SystemTimeToFileTime(&st, ft);
4482 }
4483 
4484 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4485 {
4486     static const WCHAR gmt[]= { 'G','M','T',0 };
4487     WCHAR *nextPtr, day[4], month[4], *monthPtr;
4488     LPCWSTR ptr;
4489     unsigned long num;
4490     SYSTEMTIME st = { 0 };
4491 
4492     ptr = strchrW(value, ',');
4493     if (!ptr)
4494         return FALSE;
4495     if (ptr - value != 3)
4496     {
4497         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4498         return FALSE;
4499     }
4500     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4501     day[3] = 0;
4502     st.wDayOfWeek = HTTP_ParseWkday(day);
4503     if (st.wDayOfWeek > 6)
4504     {
4505         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4506         return FALSE;
4507     }
4508     ptr++;
4509 
4510     while (isspaceW(*ptr))
4511         ptr++;
4512 
4513     num = strtoulW(ptr, &nextPtr, 10);
4514     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4515     {
4516         WARN("unexpected day %s\n", debugstr_w(value));
4517         return FALSE;
4518     }
4519     ptr = nextPtr;
4520     st.wDay = (WORD)num;
4521 
4522     while (isspaceW(*ptr))
4523         ptr++;
4524 
4525     for (monthPtr = month; !isspaceW(*ptr) && monthPtr - month < ARRAY_SIZE(month) - 1;
4526          monthPtr++, ptr++)
4527         *monthPtr = *ptr;
4528     *monthPtr = 0;
4529     st.wMonth = HTTP_ParseMonth(month);
4530     if (!st.wMonth || st.wMonth > 12)
4531     {
4532         WARN("unexpected month %s\n", debugstr_w(month));
4533         return FALSE;
4534     }
4535 
4536     while (isspaceW(*ptr))
4537         ptr++;
4538 
4539     num = strtoulW(ptr, &nextPtr, 10);
4540     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4541     {
4542         ERR("unexpected year %s\n", debugstr_w(value));
4543         return FALSE;
4544     }
4545     ptr = nextPtr;
4546     st.wYear = (WORD)num;
4547 
4548     if (!HTTP_ParseTime(&st, &ptr))
4549         return FALSE;
4550 
4551     while (isspaceW(*ptr))
4552         ptr++;
4553 
4554     if (strcmpW(ptr, gmt))
4555     {
4556         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4557         return FALSE;
4558     }
4559     return SystemTimeToFileTime(&st, ft);
4560 }
4561 
4562 static WORD HTTP_ParseWeekday(LPCWSTR day)
4563 {
4564     static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4565                                      { 'm','o','n','d','a','y',0 },
4566                                      { 't','u','e','s','d','a','y',0 },
4567                                      { 'w','e','d','n','e','s','d','a','y',0 },
4568                                      { 't','h','u','r','s','d','a','y',0 },
4569                                      { 'f','r','i','d','a','y',0 },
4570                                      { 's','a','t','u','r','d','a','y',0 }};
4571     unsigned int i;
4572     for (i = 0; i < ARRAY_SIZE(days); i++)
4573         if (!strcmpiW(day, days[i]))
4574             return i;
4575 
4576     /* Invalid */
4577     return 7;
4578 }
4579 
4580 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4581 {
4582     static const WCHAR gmt[]= { 'G','M','T',0 };
4583     WCHAR *nextPtr, day[10], month[4], *monthPtr;
4584     LPCWSTR ptr;
4585     unsigned long num;
4586     SYSTEMTIME st = { 0 };
4587 
4588     ptr = strchrW(value, ',');
4589     if (!ptr)
4590         return FALSE;
4591     if (ptr - value == 3)
4592     {
4593         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4594         day[3] = 0;
4595         st.wDayOfWeek = HTTP_ParseWkday(day);
4596         if (st.wDayOfWeek > 6)
4597         {
4598             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4599             return FALSE;
4600         }
4601     }
4602     else if (ptr - value < ARRAY_SIZE(day))
4603     {
4604         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4605         day[ptr - value + 1] = 0;
4606         st.wDayOfWeek = HTTP_ParseWeekday(day);
4607         if (st.wDayOfWeek > 6)
4608         {
4609             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4610             return FALSE;
4611         }
4612     }
4613     else
4614     {
4615         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4616         return FALSE;
4617     }
4618     ptr++;
4619 
4620     while (isspaceW(*ptr))
4621         ptr++;
4622 
4623     num = strtoulW(ptr, &nextPtr, 10);
4624     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4625     {
4626         ERR("unexpected day %s\n", debugstr_w(value));
4627         return FALSE;
4628     }
4629     ptr = nextPtr;
4630     st.wDay = (WORD)num;
4631 
4632     if (*ptr != '-')
4633     {
4634         ERR("unexpected month format %s\n", debugstr_w(ptr));
4635         return FALSE;
4636     }
4637     ptr++;
4638 
4639     for (monthPtr = month; *ptr != '-' && monthPtr - month < ARRAY_SIZE(month) - 1;
4640          monthPtr++, ptr++)
4641         *monthPtr = *ptr;
4642     *monthPtr = 0;
4643     st.wMonth = HTTP_ParseMonth(month);
4644     if (!st.wMonth || st.wMonth > 12)
4645     {
4646         ERR("unexpected month %s\n", debugstr_w(month));
4647         return FALSE;
4648     }
4649 
4650     if (*ptr != '-')
4651     {
4652         ERR("unexpected year format %s\n", debugstr_w(ptr));
4653         return FALSE;
4654     }
4655     ptr++;
4656 
4657     num = strtoulW(ptr, &nextPtr, 10);
4658     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4659     {
4660         ERR("unexpected year %s\n", debugstr_w(value));
4661         return FALSE;
4662     }
4663     ptr = nextPtr;
4664     st.wYear = (WORD)num;
4665 
4666     if (!HTTP_ParseTime(&st, &ptr))
4667         return FALSE;
4668 
4669     while (isspaceW(*ptr))
4670         ptr++;
4671 
4672     if (strcmpW(ptr, gmt))
4673     {
4674         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4675         return FALSE;
4676     }
4677     return SystemTimeToFileTime(&st, ft);
4678 }
4679 
4680 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4681 {
4682     static const WCHAR zero[] = { '0',0 };
4683     BOOL ret;
4684 
4685     if (!strcmpW(value, zero))
4686     {
4687         ft->dwLowDateTime = ft->dwHighDateTime = 0;
4688         ret = TRUE;
4689     }
4690     else if (strchrW(value, ','))
4691     {
4692         ret = HTTP_ParseRfc1123Date(value, ft);
4693         if (!ret)
4694         {
4695             ret = HTTP_ParseRfc850Date(value, ft);
4696             if (!ret)
4697                 ERR("unexpected date format %s\n", debugstr_w(value));
4698         }
4699     }
4700     else
4701     {
4702         ret = HTTP_ParseDateAsAsctime(value, ft);
4703         if (!ret)
4704             ERR("unexpected date format %s\n", debugstr_w(value));
4705     }
4706     return ret;
4707 }
4708 
4709 static void HTTP_ProcessExpires(http_request_t *request)
4710 {
4711     BOOL expirationFound = FALSE;
4712     int headerIndex;
4713 
4714     EnterCriticalSection( &request->headers_section );
4715 
4716     /* Look for a Cache-Control header with a max-age directive, as it takes
4717      * precedence over the Expires header.
4718      */
4719     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4720     if (headerIndex != -1)
4721     {
4722         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4723         LPWSTR ptr;
4724 
4725         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4726         {
4727             LPWSTR comma = strchrW(ptr, ','), end, equal;
4728 
4729             if (comma)
4730                 end = comma;
4731             else
4732                 end = ptr + strlenW(ptr);
4733             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4734                 ;
4735             if (*equal == '=')
4736             {
4737                 static const WCHAR max_age[] = {
4738                     'm','a','x','-','a','g','e',0 };
4739 
4740                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4741                 {
4742                     LPWSTR nextPtr;
4743                     unsigned long age;
4744 
4745                     age = strtoulW(equal + 1, &nextPtr, 10);
4746                     if (nextPtr > equal + 1)
4747                     {
4748                         LARGE_INTEGER ft;
4749 
4750                         NtQuerySystemTime( &ft );
4751                         /* Age is in seconds, FILETIME resolution is in
4752                          * 100 nanosecond intervals.
4753                          */
4754                         ft.QuadPart += age * (ULONGLONG)1000000;
4755                         request->expires.dwLowDateTime = ft.u.LowPart;
4756                         request->expires.dwHighDateTime = ft.u.HighPart;
4757                         expirationFound = TRUE;
4758                     }
4759                 }
4760             }
4761             if (comma)
4762             {
4763                 ptr = comma + 1;
4764                 while (isspaceW(*ptr))
4765                     ptr++;
4766             }
4767             else
4768                 ptr = NULL;
4769         }
4770     }
4771     if (!expirationFound)
4772     {
4773         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4774         if (headerIndex != -1)
4775         {
4776             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4777             FILETIME ft;
4778 
4779             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4780             {
4781                 expirationFound = TRUE;
4782                 request->expires = ft;
4783             }
4784         }
4785     }
4786     if (!expirationFound)
4787     {
4788         LARGE_INTEGER t;
4789 
4790         /* With no known age, default to 10 minutes until expiration. */
4791         NtQuerySystemTime( &t );
4792         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4793         request->expires.dwLowDateTime = t.u.LowPart;
4794         request->expires.dwHighDateTime = t.u.HighPart;
4795     }
4796 
4797     LeaveCriticalSection( &request->headers_section );
4798 }
4799 
4800 static void HTTP_ProcessLastModified(http_request_t *request)
4801 {
4802     int headerIndex;
4803 
4804     EnterCriticalSection( &request->headers_section );
4805 
4806     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4807     if (headerIndex != -1)
4808     {
4809         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4810         FILETIME ft;
4811 
4812         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4813             request->last_modified = ft;
4814     }
4815 
4816     LeaveCriticalSection( &request->headers_section );
4817 }
4818 
4819 static void http_process_keep_alive(http_request_t *req)
4820 {
4821     int index;
4822 
4823     EnterCriticalSection( &req->headers_section );
4824 
4825     if ((index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE)) != -1)
4826         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4827     else if ((index = HTTP_GetCustomHeaderIndex(req, szProxy_Connection, 0, FALSE)) != -1)
4828         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4829     else
4830         req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4831 
4832     LeaveCriticalSection( &req->headers_section );
4833 }
4834 
4835 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4836 {
4837     netconn_t *netconn = NULL;
4838     DWORD res;
4839 
4840     if (request->netconn)
4841     {
4842         if (NETCON_is_alive(request->netconn) && drain_content(request, TRUE) == ERROR_SUCCESS)
4843         {
4844             reset_data_stream(request);
4845             *reusing = TRUE;
4846             return ERROR_SUCCESS;
4847         }
4848 
4849         TRACE("freeing netconn\n");
4850         free_netconn(request->netconn);
4851         request->netconn = NULL;
4852     }
4853 
4854     reset_data_stream(request);
4855 
4856     res = HTTP_ResolveName(request);
4857     if(res != ERROR_SUCCESS)
4858         return res;
4859 
4860     EnterCriticalSection(&connection_pool_cs);
4861 
4862     while(!list_empty(&request->server->conn_pool)) {
4863         netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4864         list_remove(&netconn->pool_entry);
4865 
4866         if(is_valid_netconn(netconn) && NETCON_is_alive(netconn))
4867             break;
4868 
4869         TRACE("connection %p closed during idle\n", netconn);
4870         free_netconn(netconn);
4871         netconn = NULL;
4872     }
4873 
4874     LeaveCriticalSection(&connection_pool_cs);
4875 
4876     if(netconn) {
4877         TRACE("<-- reusing %p netconn\n", netconn);
4878         request->netconn = netconn;
4879         *reusing = TRUE;
4880         return ERROR_SUCCESS;
4881     }
4882 
4883     TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4884           request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4885 
4886     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4887                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4888                           request->server->addr_str,
4889                           strlen(request->server->addr_str)+1);
4890 
4891     res = create_netconn(request->proxy ? request->proxy : request->server, request->security_flags,
4892                          (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4893                          request->connect_timeout, &netconn);
4894     if(res != ERROR_SUCCESS) {
4895         ERR("create_netconn failed: %u\n", res);
4896         return res;
4897     }
4898 
4899     request->netconn = netconn;
4900 
4901     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4902             INTERNET_STATUS_CONNECTED_TO_SERVER,
4903             request->server->addr_str, strlen(request->server->addr_str)+1);
4904 
4905     *reusing = FALSE;
4906     TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4907     return ERROR_SUCCESS;
4908 }
4909 
4910 static char *build_ascii_request( const WCHAR *str, void *data, DWORD data_len, DWORD *out_len )
4911 {
4912     int len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
4913     char *ret;
4914 
4915     if (!(ret = heap_alloc( len + data_len ))) return NULL;
4916     WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
4917     if (data_len) memcpy( ret + len - 1, data, data_len );
4918     *out_len = len + data_len - 1;
4919     ret[*out_len] = 0;
4920     return ret;
4921 }
4922 
4923 static void set_content_length_header( http_request_t *request, DWORD len, DWORD flags )
4924 {
4925     static const WCHAR fmtW[] =
4926         {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','u','\r','\n',0};
4927     WCHAR buf[ARRAY_SIZE(fmtW) + 10];
4928 
4929     sprintfW( buf, fmtW, len );
4930     HTTP_HttpAddRequestHeadersW( request, buf, ~0u, flags );
4931 }
4932 
4933 /***********************************************************************
4934  *           HTTP_HttpSendRequestW (internal)
4935  *
4936  * Sends the specified request to the HTTP server
4937  *
4938  * RETURNS
4939  *    ERROR_SUCCESS on success
4940  *    win32 error code on failure
4941  *
4942  */
4943 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4944 	DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4945 	DWORD dwContentLength, BOOL bEndRequest)
4946 {
4947     BOOL redirected = FALSE, secure_proxy_connect = FALSE, loop_next;
4948     WCHAR *request_header = NULL;
4949     INT responseLen, cnt;
4950     DWORD res;
4951 
4952     TRACE("--> %p\n", request);
4953 
4954     assert(request->hdr.htype == WH_HHTTPREQ);
4955 
4956     /* if the verb is NULL default to GET */
4957     if (!request->verb)
4958         request->verb = heap_strdupW(szGET);
4959 
4960     HTTP_ProcessHeader(request, hostW, request->server->canon_host_port,
4961                        HTTP_ADDREQ_FLAG_ADD_IF_NEW | HTTP_ADDHDR_FLAG_REQ);
4962 
4963     if (dwContentLength || strcmpW(request->verb, szGET))
4964     {
4965         set_content_length_header(request, dwContentLength, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4966         request->bytesToWrite = dwContentLength;
4967     }
4968     if (request->session->appInfo->agent)
4969     {
4970         WCHAR *agent_header;
4971         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4972         int len;
4973 
4974         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4975         agent_header = heap_alloc(len * sizeof(WCHAR));
4976         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4977 
4978         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4979         heap_free(agent_header);
4980     }
4981     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4982     {
4983         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4984         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4985     }
4986     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && strcmpW(request->verb, szGET))
4987     {
4988         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4989                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4990         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4991     }
4992 
4993     /* add the headers the caller supplied */
4994     if( lpszHeaders && dwHeaderLength )
4995         HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4996 
4997     do
4998     {
4999         DWORD len, data_len = dwOptionalLength;
5000         BOOL reusing_connection;
5001         char *ascii_req;
5002 
5003         loop_next = FALSE;
5004 
5005         if(redirected) {
5006             request->contentLength = ~0;
5007             request->bytesToWrite = 0;
5008         }
5009 
5010         if (TRACE_ON(wininet))
5011         {
5012             HTTPHEADERW *host;
5013 
5014             EnterCriticalSection( &request->headers_section );
5015             host = HTTP_GetHeader( request, hostW );
5016             TRACE("Going to url %s %s\n", debugstr_w(host->lpszValue), debugstr_w(request->path));
5017             LeaveCriticalSection( &request->headers_section );
5018         }
5019 
5020         HTTP_FixURL(request);
5021         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
5022         {
5023             HTTP_ProcessHeader(request, szConnection, szKeepAlive,
5024                                HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD);
5025         }
5026         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
5027         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
5028 
5029         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
5030             HTTP_InsertCookies(request);
5031 
5032         res = open_http_connection(request, &reusing_connection);
5033         if (res != ERROR_SUCCESS)
5034             break;
5035 
5036         if (!reusing_connection && (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
5037         {
5038             if (request->proxy) secure_proxy_connect = TRUE;
5039             else
5040             {
5041                 res = NETCON_secure_connect(request->netconn, request->server);
5042                 if (res != ERROR_SUCCESS)
5043                 {
5044                     WARN("failed to upgrade to secure connection\n");
5045                     http_release_netconn(request, FALSE);
5046                     break;
5047                 }
5048             }
5049         }
5050         if (secure_proxy_connect)
5051         {
5052             static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
5053             const WCHAR *target = request->server->host_port;
5054 
5055             if (HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE) >= 0)
5056                 set_content_length_header(request, 0, HTTP_ADDREQ_FLAG_REPLACE);
5057 
5058             request_header = build_request_header(request, connectW, target, g_szHttp1_1, TRUE);
5059         }
5060         else if (request->proxy && !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
5061         {
5062             WCHAR *url = build_proxy_path_url(request);
5063             request_header = build_request_header(request, request->verb, url, request->version, TRUE);
5064             heap_free(url);
5065         }
5066         else
5067         {
5068             if (request->proxy && HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE) >= 0)
5069                 set_content_length_header(request, dwContentLength, HTTP_ADDREQ_FLAG_REPLACE);
5070 
5071             request_header = build_request_header(request, request->verb, request->path, request->version, TRUE);
5072         }
5073 
5074         TRACE("Request header -> %s\n", debugstr_w(request_header) );
5075 
5076         /* send the request as ASCII, tack on the optional data */
5077         if (!lpOptional || redirected || secure_proxy_connect)
5078             data_len = 0;
5079 
5080         ascii_req = build_ascii_request(request_header, lpOptional, data_len, &len);
5081         heap_free(request_header);
5082         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
5083 
5084         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5085                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
5086 
5087         NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
5088         res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
5089         heap_free( ascii_req );
5090         if(res != ERROR_SUCCESS) {
5091             TRACE("send failed: %u\n", res);
5092             if(!reusing_connection)
5093                 break;
5094             http_release_netconn(request, FALSE);
5095             loop_next = TRUE;
5096             continue;
5097         }
5098 
5099         request->bytesWritten = data_len;
5100 
5101         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5102                               INTERNET_STATUS_REQUEST_SENT,
5103                               &len, sizeof(DWORD));
5104 
5105         if (bEndRequest)
5106         {
5107             DWORD dwBufferSize;
5108 
5109             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5110                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5111 
5112             if (HTTP_GetResponseHeaders(request, &responseLen))
5113             {
5114                 http_release_netconn(request, FALSE);
5115                 res = ERROR_INTERNET_CONNECTION_ABORTED;
5116                 goto lend;
5117             }
5118             /* FIXME: We should know that connection is closed before sending
5119              * headers. Otherwise wrong callbacks are executed */
5120             if(!responseLen && reusing_connection) {
5121                 TRACE("Connection closed by server, reconnecting\n");
5122                 http_release_netconn(request, FALSE);
5123                 loop_next = TRUE;
5124                 continue;
5125             }
5126 
5127             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5128                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
5129                                 sizeof(DWORD));
5130 
5131             http_process_keep_alive(request);
5132             HTTP_ProcessCookies(request);
5133             HTTP_ProcessExpires(request);
5134             HTTP_ProcessLastModified(request);
5135 
5136             res = set_content_length(request);
5137             if(res != ERROR_SUCCESS)
5138                 goto lend;
5139             if(!request->contentLength && !secure_proxy_connect)
5140                 http_release_netconn(request, TRUE);
5141 
5142             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
5143             {
5144                 WCHAR *new_url;
5145 
5146                 switch(request->status_code) {
5147                 case HTTP_STATUS_REDIRECT:
5148                 case HTTP_STATUS_MOVED:
5149                 case HTTP_STATUS_REDIRECT_KEEP_VERB:
5150                 case HTTP_STATUS_REDIRECT_METHOD:
5151                     new_url = get_redirect_url(request);
5152                     if(!new_url)
5153                         break;
5154 
5155                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5156                         request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5157                     {
5158                         heap_free(request->verb);
5159                         request->verb = heap_strdupW(szGET);
5160                     }
5161                     http_release_netconn(request, drain_content(request, FALSE) == ERROR_SUCCESS);
5162                     res = HTTP_HandleRedirect(request, new_url);
5163                     heap_free(new_url);
5164                     if (res == ERROR_SUCCESS)
5165                         loop_next = TRUE;
5166                     redirected = TRUE;
5167                 }
5168             }
5169             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
5170             {
5171                 WCHAR szAuthValue[2048];
5172                 dwBufferSize=2048;
5173                 if (request->status_code == HTTP_STATUS_DENIED)
5174                 {
5175                     WCHAR *host = heap_strdupW( request->server->canon_host_port );
5176                     DWORD dwIndex = 0;
5177                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5178                     {
5179                         if (HTTP_DoAuthorization(request, szAuthValue,
5180                                                  &request->authInfo,
5181                                                  request->session->userName,
5182                                                  request->session->password, host))
5183                         {
5184                             if (drain_content(request, TRUE) != ERROR_SUCCESS)
5185                             {
5186                                 FIXME("Could not drain content\n");
5187                                 http_release_netconn(request, FALSE);
5188                             }
5189                             loop_next = TRUE;
5190                             break;
5191                         }
5192                     }
5193                     heap_free( host );
5194 
5195                     if(!loop_next) {
5196                         TRACE("Cleaning wrong authorization data\n");
5197                         destroy_authinfo(request->authInfo);
5198                         request->authInfo = NULL;
5199                     }
5200                 }
5201                 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
5202                 {
5203                     DWORD dwIndex = 0;
5204                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5205                     {
5206                         if (HTTP_DoAuthorization(request, szAuthValue,
5207                                                  &request->proxyAuthInfo,
5208                                                  request->session->appInfo->proxyUsername,
5209                                                  request->session->appInfo->proxyPassword,
5210                                                  NULL))
5211                         {
5212                             if (drain_content(request, TRUE) != ERROR_SUCCESS)
5213                             {
5214                                 FIXME("Could not drain content\n");
5215                                 http_release_netconn(request, FALSE);
5216                             }
5217                             loop_next = TRUE;
5218                             break;
5219                         }
5220                     }
5221 
5222                     if(!loop_next) {
5223                         TRACE("Cleaning wrong proxy authorization data\n");
5224                         destroy_authinfo(request->proxyAuthInfo);
5225                         request->proxyAuthInfo = NULL;
5226                     }
5227                 }
5228             }
5229             if (secure_proxy_connect && request->status_code == HTTP_STATUS_OK)
5230             {
5231                 res = NETCON_secure_connect(request->netconn, request->server);
5232                 if (res != ERROR_SUCCESS)
5233                 {
5234                     WARN("failed to upgrade to secure proxy connection\n");
5235                     http_release_netconn( request, FALSE );
5236                     break;
5237                 }
5238                 remove_header(request, szProxy_Authorization, TRUE);
5239                 destroy_authinfo(request->proxyAuthInfo);
5240                 request->proxyAuthInfo = NULL;
5241                 request->contentLength = 0;
5242                 request->netconn_stream.content_length = 0;
5243 
5244                 secure_proxy_connect = FALSE;
5245                 loop_next = TRUE;
5246             }
5247         }
5248         else
5249             res = ERROR_SUCCESS;
5250     }
5251     while (loop_next);
5252 
5253 lend:
5254     /* TODO: send notification for P3P header */
5255 
5256     if(res == ERROR_SUCCESS)
5257         create_cache_entry(request);
5258 
5259     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5260     {
5261         if (res == ERROR_SUCCESS) {
5262             if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5263                 HTTP_ReceiveRequestData(request);
5264             else
5265                 send_request_complete(request,
5266                         request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5267         }else {
5268                 send_request_complete(request, 0, res);
5269         }
5270     }
5271 
5272     TRACE("<--\n");
5273     return res;
5274 }
5275 
5276 typedef struct {
5277     task_header_t hdr;
5278     WCHAR *headers;
5279     DWORD  headers_len;
5280     void  *optional;
5281     DWORD  optional_len;
5282     DWORD  content_len;
5283     BOOL   end_request;
5284 } send_request_task_t;
5285 
5286 /***********************************************************************
5287  *
5288  * Helper functions for the HttpSendRequest(Ex) functions
5289  *
5290  */
5291 static void AsyncHttpSendRequestProc(task_header_t *hdr)
5292 {
5293     send_request_task_t *task = (send_request_task_t*)hdr;
5294     http_request_t *request = (http_request_t*)task->hdr.hdr;
5295 
5296     TRACE("%p\n", request);
5297 
5298     HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
5299             task->optional_len, task->content_len, task->end_request);
5300 
5301     heap_free(task->headers);
5302 }
5303 
5304 
5305 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5306 {
5307     INT responseLen;
5308     DWORD res = ERROR_SUCCESS;
5309 
5310     if(!is_valid_netconn(request->netconn)) {
5311         WARN("Not connected\n");
5312         send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5313         return ERROR_INTERNET_OPERATION_CANCELLED;
5314     }
5315 
5316     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5317                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5318 
5319     if (HTTP_GetResponseHeaders(request, &responseLen) || !responseLen)
5320         res = ERROR_HTTP_HEADER_NOT_FOUND;
5321 
5322     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5323                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5324 
5325     /* process cookies here. Is this right? */
5326     http_process_keep_alive(request);
5327     HTTP_ProcessCookies(request);
5328     HTTP_ProcessExpires(request);
5329     HTTP_ProcessLastModified(request);
5330 
5331     if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5332         if(!request->contentLength)
5333             http_release_netconn(request, TRUE);
5334     }
5335 
5336     if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5337     {
5338         switch(request->status_code) {
5339         case HTTP_STATUS_REDIRECT:
5340         case HTTP_STATUS_MOVED:
5341         case HTTP_STATUS_REDIRECT_METHOD:
5342         case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5343             WCHAR *new_url;
5344 
5345             new_url = get_redirect_url(request);
5346             if(!new_url)
5347                 break;
5348 
5349             if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5350                 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5351             {
5352                 heap_free(request->verb);
5353                 request->verb = heap_strdupW(szGET);
5354             }
5355             http_release_netconn(request, drain_content(request, FALSE) == ERROR_SUCCESS);
5356             res = HTTP_HandleRedirect(request, new_url);
5357             heap_free(new_url);
5358             if (res == ERROR_SUCCESS)
5359                 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5360         }
5361         }
5362     }
5363 
5364     if(res == ERROR_SUCCESS)
5365         create_cache_entry(request);
5366 
5367     if (res == ERROR_SUCCESS && request->contentLength)
5368         HTTP_ReceiveRequestData(request);
5369     else
5370         send_request_complete(request, res == ERROR_SUCCESS, res);
5371 
5372     return res;
5373 }
5374 
5375 /***********************************************************************
5376  *           HttpEndRequestA (WININET.@)
5377  *
5378  * Ends an HTTP request that was started by HttpSendRequestEx
5379  *
5380  * RETURNS
5381  *    TRUE	if successful
5382  *    FALSE	on failure
5383  *
5384  */
5385 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5386         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5387 {
5388     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5389 
5390     if (lpBuffersOut)
5391     {
5392         SetLastError(ERROR_INVALID_PARAMETER);
5393         return FALSE;
5394     }
5395 
5396     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5397 }
5398 
5399 typedef struct {
5400     task_header_t hdr;
5401     DWORD flags;
5402     DWORD context;
5403 } end_request_task_t;
5404 
5405 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5406 {
5407     end_request_task_t *task = (end_request_task_t*)hdr;
5408     http_request_t *req = (http_request_t*)task->hdr.hdr;
5409 
5410     TRACE("%p\n", req);
5411 
5412     HTTP_HttpEndRequestW(req, task->flags, task->context);
5413 }
5414 
5415 /***********************************************************************
5416  *           HttpEndRequestW (WININET.@)
5417  *
5418  * Ends an HTTP request that was started by HttpSendRequestEx
5419  *
5420  * RETURNS
5421  *    TRUE	if successful
5422  *    FALSE	on failure
5423  *
5424  */
5425 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5426         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5427 {
5428     http_request_t *request;
5429     DWORD res;
5430 
5431     TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5432 
5433     if (lpBuffersOut)
5434     {
5435         SetLastError(ERROR_INVALID_PARAMETER);
5436         return FALSE;
5437     }
5438 
5439     request = (http_request_t*) get_handle_object( hRequest );
5440 
5441     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5442     {
5443         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5444         if (request)
5445             WININET_Release( &request->hdr );
5446         return FALSE;
5447     }
5448     request->hdr.dwFlags |= dwFlags;
5449 
5450     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5451     {
5452         end_request_task_t *task;
5453 
5454         task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5455         task->flags = dwFlags;
5456         task->context = dwContext;
5457 
5458         INTERNET_AsyncCall(&task->hdr);
5459         res = ERROR_IO_PENDING;
5460     }
5461     else
5462         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5463 
5464     WININET_Release( &request->hdr );
5465     TRACE("%u <--\n", res);
5466     if(res != ERROR_SUCCESS)
5467         SetLastError(res);
5468     return res == ERROR_SUCCESS;
5469 }
5470 
5471 /***********************************************************************
5472  *           HttpSendRequestExA (WININET.@)
5473  *
5474  * Sends the specified request to the HTTP server and allows chunked
5475  * transfers.
5476  *
5477  * RETURNS
5478  *  Success: TRUE
5479  *  Failure: FALSE, call GetLastError() for more information.
5480  */
5481 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5482 			       LPINTERNET_BUFFERSA lpBuffersIn,
5483 			       LPINTERNET_BUFFERSA lpBuffersOut,
5484 			       DWORD dwFlags, DWORD_PTR dwContext)
5485 {
5486     INTERNET_BUFFERSW BuffersInW;
5487     BOOL rc = FALSE;
5488     DWORD headerlen;
5489     LPWSTR header = NULL;
5490 
5491     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5492 	    lpBuffersOut, dwFlags, dwContext);
5493 
5494     if (lpBuffersIn)
5495     {
5496         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5497         if (lpBuffersIn->lpcszHeader)
5498         {
5499             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5500                     lpBuffersIn->dwHeadersLength,0,0);
5501             header = heap_alloc(headerlen*sizeof(WCHAR));
5502             if (!(BuffersInW.lpcszHeader = header))
5503             {
5504                 SetLastError(ERROR_OUTOFMEMORY);
5505                 return FALSE;
5506             }
5507             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5508                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5509                     header, headerlen);
5510         }
5511         else
5512             BuffersInW.lpcszHeader = NULL;
5513         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5514         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5515         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5516         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5517         BuffersInW.Next = NULL;
5518     }
5519 
5520     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5521 
5522     heap_free(header);
5523     return rc;
5524 }
5525 
5526 /***********************************************************************
5527  *           HttpSendRequestExW (WININET.@)
5528  *
5529  * Sends the specified request to the HTTP server and allows chunked
5530  * transfers
5531  *
5532  * RETURNS
5533  *  Success: TRUE
5534  *  Failure: FALSE, call GetLastError() for more information.
5535  */
5536 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5537                    LPINTERNET_BUFFERSW lpBuffersIn,
5538                    LPINTERNET_BUFFERSW lpBuffersOut,
5539                    DWORD dwFlags, DWORD_PTR dwContext)
5540 {
5541     http_request_t *request;
5542     http_session_t *session;
5543     appinfo_t *hIC;
5544     DWORD res;
5545 
5546     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5547             lpBuffersOut, dwFlags, dwContext);
5548 
5549     request = (http_request_t*) get_handle_object( hRequest );
5550 
5551     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5552     {
5553         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5554         goto lend;
5555     }
5556 
5557     session = request->session;
5558     assert(session->hdr.htype == WH_HHTTPSESSION);
5559     hIC = session->appInfo;
5560     assert(hIC->hdr.htype == WH_HINIT);
5561 
5562     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5563     {
5564         send_request_task_t *task;
5565 
5566         task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5567         if (lpBuffersIn)
5568         {
5569             DWORD size = 0;
5570 
5571             if (lpBuffersIn->lpcszHeader)
5572             {
5573                 if (lpBuffersIn->dwHeadersLength == ~0u)
5574                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5575                 else
5576                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5577 
5578                 task->headers = heap_alloc(size);
5579                 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5580             }
5581             else task->headers = NULL;
5582 
5583             task->headers_len = size / sizeof(WCHAR);
5584             task->optional = lpBuffersIn->lpvBuffer;
5585             task->optional_len = lpBuffersIn->dwBufferLength;
5586             task->content_len = lpBuffersIn->dwBufferTotal;
5587         }
5588         else
5589         {
5590             task->headers = NULL;
5591             task->headers_len = 0;
5592             task->optional = NULL;
5593             task->optional_len = 0;
5594             task->content_len = 0;
5595         }
5596 
5597         task->end_request = FALSE;
5598 
5599         INTERNET_AsyncCall(&task->hdr);
5600         res = ERROR_IO_PENDING;
5601     }
5602     else
5603     {
5604         if (lpBuffersIn)
5605             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5606                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5607                                         lpBuffersIn->dwBufferTotal, FALSE);
5608         else
5609             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5610     }
5611 
5612 lend:
5613     if ( request )
5614         WININET_Release( &request->hdr );
5615 
5616     TRACE("<---\n");
5617     SetLastError(res);
5618     return res == ERROR_SUCCESS;
5619 }
5620 
5621 /***********************************************************************
5622  *           HttpSendRequestW (WININET.@)
5623  *
5624  * Sends the specified request to the HTTP server
5625  *
5626  * RETURNS
5627  *    TRUE  on success
5628  *    FALSE on failure
5629  *
5630  */
5631 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5632 	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5633 {
5634     http_request_t *request;
5635     http_session_t *session = NULL;
5636     appinfo_t *hIC = NULL;
5637     DWORD res = ERROR_SUCCESS;
5638 
5639     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5640             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5641 
5642     request = (http_request_t*) get_handle_object( hHttpRequest );
5643     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5644     {
5645         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5646         goto lend;
5647     }
5648 
5649     session = request->session;
5650     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
5651     {
5652         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5653         goto lend;
5654     }
5655 
5656     hIC = session->appInfo;
5657     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
5658     {
5659         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5660         goto lend;
5661     }
5662 
5663     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5664     {
5665         send_request_task_t *task;
5666 
5667         task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5668         if (lpszHeaders)
5669         {
5670             DWORD size;
5671 
5672             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5673             else size = dwHeaderLength * sizeof(WCHAR);
5674 
5675             task->headers = heap_alloc(size);
5676             memcpy(task->headers, lpszHeaders, size);
5677         }
5678         else
5679             task->headers = NULL;
5680         task->headers_len = dwHeaderLength;
5681         task->optional = lpOptional;
5682         task->optional_len = dwOptionalLength;
5683         task->content_len = dwOptionalLength;
5684         task->end_request = TRUE;
5685 
5686         INTERNET_AsyncCall(&task->hdr);
5687         res = ERROR_IO_PENDING;
5688     }
5689     else
5690     {
5691 	res = HTTP_HttpSendRequestW(request, lpszHeaders,
5692 		dwHeaderLength, lpOptional, dwOptionalLength,
5693 		dwOptionalLength, TRUE);
5694     }
5695 lend:
5696     if( request )
5697         WININET_Release( &request->hdr );
5698 
5699     SetLastError(res);
5700     return res == ERROR_SUCCESS;
5701 }
5702 
5703 /***********************************************************************
5704  *           HttpSendRequestA (WININET.@)
5705  *
5706  * Sends the specified request to the HTTP server
5707  *
5708  * RETURNS
5709  *    TRUE  on success
5710  *    FALSE on failure
5711  *
5712  */
5713 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5714 	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5715 {
5716     BOOL result;
5717     LPWSTR szHeaders=NULL;
5718     DWORD nLen=dwHeaderLength;
5719     if(lpszHeaders!=NULL)
5720     {
5721         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5722         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5723         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5724     }
5725     result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5726     heap_free(szHeaders);
5727     return result;
5728 }
5729 
5730 /***********************************************************************
5731  *           HTTPSESSION_Destroy (internal)
5732  *
5733  * Deallocate session handle
5734  *
5735  */
5736 static void HTTPSESSION_Destroy(object_header_t *hdr)
5737 {
5738     http_session_t *session = (http_session_t*) hdr;
5739 
5740     TRACE("%p\n", session);
5741 
5742     WININET_Release(&session->appInfo->hdr);
5743 
5744     heap_free(session->hostName);
5745     heap_free(session->password);
5746     heap_free(session->userName);
5747 }
5748 
5749 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5750 {
5751     http_session_t *ses = (http_session_t *)hdr;
5752 
5753     switch(option) {
5754     case INTERNET_OPTION_HANDLE_TYPE:
5755         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5756 
5757         if (*size < sizeof(ULONG))
5758             return ERROR_INSUFFICIENT_BUFFER;
5759 
5760         *size = sizeof(DWORD);
5761         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5762         return ERROR_SUCCESS;
5763     case INTERNET_OPTION_CONNECT_TIMEOUT:
5764         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5765 
5766         if (*size < sizeof(DWORD))
5767             return ERROR_INSUFFICIENT_BUFFER;
5768 
5769         *size = sizeof(DWORD);
5770         *(DWORD *)buffer = ses->connect_timeout;
5771         return ERROR_SUCCESS;
5772 
5773     case INTERNET_OPTION_SEND_TIMEOUT:
5774         TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5775 
5776         if (*size < sizeof(DWORD))
5777             return ERROR_INSUFFICIENT_BUFFER;
5778 
5779         *size = sizeof(DWORD);
5780         *(DWORD *)buffer = ses->send_timeout;
5781         return ERROR_SUCCESS;
5782 
5783     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5784         TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5785 
5786         if (*size < sizeof(DWORD))
5787             return ERROR_INSUFFICIENT_BUFFER;
5788 
5789         *size = sizeof(DWORD);
5790         *(DWORD *)buffer = ses->receive_timeout;
5791         return ERROR_SUCCESS;
5792     }
5793 
5794     return INET_QueryOption(hdr, option, buffer, size, unicode);
5795 }
5796 
5797 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5798 {
5799     http_session_t *ses = (http_session_t*)hdr;
5800 
5801     switch(option) {
5802     case INTERNET_OPTION_USERNAME:
5803     {
5804         heap_free(ses->userName);
5805         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5806         return ERROR_SUCCESS;
5807     }
5808     case INTERNET_OPTION_PASSWORD:
5809     {
5810         heap_free(ses->password);
5811         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5812         return ERROR_SUCCESS;
5813     }
5814     case INTERNET_OPTION_PROXY_USERNAME:
5815     {
5816         heap_free(ses->appInfo->proxyUsername);
5817         if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5818         return ERROR_SUCCESS;
5819     }
5820     case INTERNET_OPTION_PROXY_PASSWORD:
5821     {
5822         heap_free(ses->appInfo->proxyPassword);
5823         if (!(ses->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5824         return ERROR_SUCCESS;
5825     }
5826     case INTERNET_OPTION_CONNECT_TIMEOUT:
5827     {
5828         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5829         ses->connect_timeout = *(DWORD *)buffer;
5830         return ERROR_SUCCESS;
5831     }
5832     case INTERNET_OPTION_SEND_TIMEOUT:
5833     {
5834         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5835         ses->send_timeout = *(DWORD *)buffer;
5836         return ERROR_SUCCESS;
5837     }
5838     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5839     {
5840         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5841         ses->receive_timeout = *(DWORD *)buffer;
5842         return ERROR_SUCCESS;
5843     }
5844     default: break;
5845     }
5846 
5847     return INET_SetOption(hdr, option, buffer, size);
5848 }
5849 
5850 static const object_vtbl_t HTTPSESSIONVtbl = {
5851     HTTPSESSION_Destroy,
5852     NULL,
5853     HTTPSESSION_QueryOption,
5854     HTTPSESSION_SetOption,
5855     NULL,
5856     NULL,
5857     NULL,
5858     NULL
5859 };
5860 
5861 
5862 /***********************************************************************
5863  *           HTTP_Connect  (internal)
5864  *
5865  * Create http session handle
5866  *
5867  * RETURNS
5868  *   HINTERNET a session handle on success
5869  *   NULL on failure
5870  *
5871  */
5872 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5873         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5874         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5875         DWORD dwInternalFlags, HINTERNET *ret)
5876 {
5877     http_session_t *session = NULL;
5878 
5879     TRACE("-->\n");
5880 
5881     if (!lpszServerName || !lpszServerName[0])
5882         return ERROR_INVALID_PARAMETER;
5883 
5884     assert( hIC->hdr.htype == WH_HINIT );
5885 
5886     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5887     if (!session)
5888         return ERROR_OUTOFMEMORY;
5889 
5890    /*
5891     * According to my tests. The name is not resolved until a request is sent
5892     */
5893 
5894     session->hdr.htype = WH_HHTTPSESSION;
5895     session->hdr.dwFlags = dwFlags;
5896     session->hdr.dwContext = dwContext;
5897     session->hdr.dwInternalFlags |= dwInternalFlags;
5898     session->hdr.decoding = hIC->hdr.decoding;
5899 
5900     WININET_AddRef( &hIC->hdr );
5901     session->appInfo = hIC;
5902     list_add_head( &hIC->hdr.children, &session->hdr.entry );
5903 
5904     session->hostName = heap_strdupW(lpszServerName);
5905     if (lpszUserName && lpszUserName[0])
5906         session->userName = heap_strdupW(lpszUserName);
5907     session->password = heap_strdupW(lpszPassword);
5908     session->hostPort = serverPort;
5909     session->connect_timeout = hIC->connect_timeout;
5910     session->send_timeout = 0;
5911     session->receive_timeout = 0;
5912 
5913     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5914     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5915     {
5916         INTERNET_SendCallback(&hIC->hdr, dwContext,
5917                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5918                               sizeof(HINTERNET));
5919     }
5920 
5921 /*
5922  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5923  * windows
5924  */
5925 
5926     TRACE("%p --> %p\n", hIC, session);
5927 
5928     *ret = session->hdr.hInternet;
5929     return ERROR_SUCCESS;
5930 }
5931 
5932 /***********************************************************************
5933  *           HTTP_clear_response_headers (internal)
5934  *
5935  * clear out any old response headers
5936  */
5937 static void HTTP_clear_response_headers( http_request_t *request )
5938 {
5939     DWORD i;
5940 
5941     EnterCriticalSection( &request->headers_section );
5942 
5943     for( i=0; i<request->nCustHeaders; i++)
5944     {
5945         if( !request->custHeaders[i].lpszField )
5946             continue;
5947         if( !request->custHeaders[i].lpszValue )
5948             continue;
5949         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5950             continue;
5951         HTTP_DeleteCustomHeader( request, i );
5952         i--;
5953     }
5954 
5955     LeaveCriticalSection( &request->headers_section );
5956 }
5957 
5958 /***********************************************************************
5959  *           HTTP_GetResponseHeaders (internal)
5960  *
5961  * Read server response
5962  *
5963  * RETURNS
5964  *
5965  *   TRUE  on success
5966  *   FALSE on error
5967  */
5968 static DWORD HTTP_GetResponseHeaders(http_request_t *request, INT *len)
5969 {
5970     INT cbreaks = 0;
5971     WCHAR buffer[MAX_REPLY_LEN];
5972     DWORD buflen = MAX_REPLY_LEN;
5973     INT  rc = 0;
5974     char bufferA[MAX_REPLY_LEN];
5975     LPWSTR status_code = NULL, status_text = NULL;
5976     DWORD res = ERROR_HTTP_INVALID_SERVER_RESPONSE;
5977     BOOL codeHundred = FALSE;
5978 
5979     TRACE("-->\n");
5980 
5981     if(!is_valid_netconn(request->netconn))
5982         goto lend;
5983 
5984     /* clear old response headers (eg. from a redirect response) */
5985     HTTP_clear_response_headers( request );
5986 
5987     NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5988     do {
5989         /*
5990          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5991          */
5992         buflen = MAX_REPLY_LEN;
5993         if ((res = read_line(request, bufferA, &buflen)))
5994             goto lend;
5995 
5996         if (!buflen) goto lend;
5997 
5998         rc += buflen;
5999         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
6000         /* check is this a status code line? */
6001         if (!strncmpW(buffer, g_szHttp1_0, 4))
6002         {
6003             /* split the version from the status code */
6004             status_code = strchrW( buffer, ' ' );
6005             if( !status_code )
6006                 goto lend;
6007             *status_code++=0;
6008 
6009             /* split the status code from the status text */
6010             status_text = strchrW( status_code, ' ' );
6011             if( status_text )
6012                 *status_text++=0;
6013 
6014             request->status_code = atoiW(status_code);
6015 
6016             TRACE("version [%s] status code [%s] status text [%s]\n",
6017                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
6018 
6019             codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
6020         }
6021         else if (!codeHundred)
6022         {
6023             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
6024 
6025             heap_free(request->version);
6026             heap_free(request->statusText);
6027 
6028             request->status_code = HTTP_STATUS_OK;
6029             request->version = heap_strdupW(g_szHttp1_0);
6030             request->statusText = heap_strdupW(szOK);
6031 
6032             goto lend;
6033         }
6034     } while (codeHundred);
6035 
6036     /* Add status code */
6037     HTTP_ProcessHeader(request, szStatus, status_code,
6038                        HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD);
6039 
6040     heap_free(request->version);
6041     heap_free(request->statusText);
6042 
6043     request->version = heap_strdupW(buffer);
6044     request->statusText = heap_strdupW(status_text ? status_text : emptyW);
6045 
6046     /* Restore the spaces */
6047     *(status_code-1) = ' ';
6048     if (status_text)
6049         *(status_text-1) = ' ';
6050 
6051     /* Parse each response line */
6052     do
6053     {
6054         buflen = MAX_REPLY_LEN;
6055         if (!read_line(request, bufferA, &buflen) && buflen)
6056         {
6057             LPWSTR * pFieldAndValue;
6058 
6059             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
6060 
6061             if (!bufferA[0]) break;
6062             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
6063 
6064             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
6065             if (pFieldAndValue)
6066             {
6067                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
6068                                    HTTP_ADDREQ_FLAG_ADD );
6069                 HTTP_FreeTokens(pFieldAndValue);
6070             }
6071         }
6072         else
6073         {
6074             cbreaks++;
6075             if (cbreaks >= 2)
6076                 break;
6077         }
6078     }while(1);
6079 
6080     res = ERROR_SUCCESS;
6081 
6082 lend:
6083 
6084     *len = rc;
6085     TRACE("<--\n");
6086     return res;
6087 }
6088 
6089 /***********************************************************************
6090  *           HTTP_InterpretHttpHeader (internal)
6091  *
6092  * Parse server response
6093  *
6094  * RETURNS
6095  *
6096  *   Pointer to array of field, value, NULL on success.
6097  *   NULL on error.
6098  */
6099 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
6100 {
6101     LPWSTR * pTokenPair;
6102     LPWSTR pszColon;
6103     INT len;
6104 
6105     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
6106 
6107     pszColon = strchrW(buffer, ':');
6108     /* must have two tokens */
6109     if (!pszColon)
6110     {
6111         HTTP_FreeTokens(pTokenPair);
6112         if (buffer[0])
6113             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
6114         return NULL;
6115     }
6116 
6117     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
6118     if (!pTokenPair[0])
6119     {
6120         HTTP_FreeTokens(pTokenPair);
6121         return NULL;
6122     }
6123     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
6124     pTokenPair[0][pszColon - buffer] = '\0';
6125 
6126     /* skip colon */
6127     pszColon++;
6128     len = strlenW(pszColon);
6129     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
6130     if (!pTokenPair[1])
6131     {
6132         HTTP_FreeTokens(pTokenPair);
6133         return NULL;
6134     }
6135     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
6136 
6137     strip_spaces(pTokenPair[0]);
6138     strip_spaces(pTokenPair[1]);
6139 
6140     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
6141     return pTokenPair;
6142 }
6143 
6144 /***********************************************************************
6145  *           HTTP_ProcessHeader (internal)
6146  *
6147  * Stuff header into header tables according to <dwModifier>
6148  *
6149  */
6150 
6151 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6152 
6153 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
6154 {
6155     LPHTTPHEADERW lphttpHdr;
6156     INT index;
6157     BOOL request_only = !!(dwModifier & HTTP_ADDHDR_FLAG_REQ);
6158     DWORD res = ERROR_SUCCESS;
6159 
6160     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
6161 
6162     EnterCriticalSection( &request->headers_section );
6163 
6164     index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
6165     if (index >= 0)
6166     {
6167         lphttpHdr = &request->custHeaders[index];
6168 
6169         /* replace existing header if FLAG_REPLACE is given */
6170         if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6171         {
6172             HTTP_DeleteCustomHeader( request, index );
6173 
6174             if (value && value[0])
6175             {
6176                 HTTPHEADERW hdr;
6177 
6178                 hdr.lpszField = (LPWSTR)field;
6179                 hdr.lpszValue = (LPWSTR)value;
6180                 hdr.wFlags = hdr.wCount = 0;
6181 
6182                 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6183                     hdr.wFlags |= HDR_ISREQUEST;
6184 
6185                 res = HTTP_InsertCustomHeader( request, &hdr );
6186             }
6187 
6188             goto out;
6189         }
6190 
6191         /* do not add new header if FLAG_ADD_IF_NEW is set */
6192         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
6193         {
6194             res = ERROR_HTTP_INVALID_HEADER; /* FIXME */
6195             goto out;
6196         }
6197 
6198         /* handle appending to existing header */
6199         if (dwModifier & COALESCEFLAGS)
6200         {
6201             LPWSTR lpsztmp;
6202             WCHAR ch = 0;
6203             INT len = 0;
6204             INT origlen = strlenW(lphttpHdr->lpszValue);
6205             INT valuelen = strlenW(value);
6206 
6207             /* FIXME: Should it really clear HDR_ISREQUEST? */
6208             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6209                 lphttpHdr->wFlags |= HDR_ISREQUEST;
6210             else
6211                 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6212 
6213             if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6214             {
6215                 ch = ',';
6216                 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6217             }
6218             else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6219             {
6220                 ch = ';';
6221                 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6222             }
6223 
6224             len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6225 
6226             lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6227             if (lpsztmp)
6228             {
6229                 lphttpHdr->lpszValue = lpsztmp;
6230                 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6231                 if (ch > 0)
6232                 {
6233                     lphttpHdr->lpszValue[origlen] = ch;
6234                     origlen++;
6235                     lphttpHdr->lpszValue[origlen] = ' ';
6236                     origlen++;
6237                 }
6238 
6239                 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6240                 lphttpHdr->lpszValue[len] = '\0';
6241             }
6242             else
6243             {
6244                 WARN("heap_realloc (%d bytes) failed\n",len+1);
6245                 res = ERROR_OUTOFMEMORY;
6246             }
6247 
6248             goto out;
6249         }
6250     }
6251 
6252     /* FIXME: What about other combinations? */
6253     if ((dwModifier & ~HTTP_ADDHDR_FLAG_REQ) == HTTP_ADDHDR_FLAG_REPLACE)
6254     {
6255         res = ERROR_HTTP_HEADER_NOT_FOUND;
6256         goto out;
6257     }
6258 
6259     /* FIXME: What if value == ""? */
6260     if (value)
6261     {
6262         HTTPHEADERW hdr;
6263 
6264         hdr.lpszField = (LPWSTR)field;
6265         hdr.lpszValue = (LPWSTR)value;
6266         hdr.wFlags = hdr.wCount = 0;
6267 
6268         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6269             hdr.wFlags |= HDR_ISREQUEST;
6270 
6271         res = HTTP_InsertCustomHeader( request, &hdr );
6272         goto out;
6273     }
6274 
6275     /* FIXME: What if value == NULL? */
6276 out:
6277     TRACE("<-- %d\n", res);
6278     LeaveCriticalSection( &request->headers_section );
6279     return res;
6280 }
6281 
6282 /***********************************************************************
6283  *           HTTP_GetCustomHeaderIndex (internal)
6284  *
6285  * Return index of custom header from header array
6286  * Headers section must be held
6287  */
6288 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6289                                      int requested_index, BOOL request_only)
6290 {
6291     DWORD index;
6292 
6293     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6294 
6295     for (index = 0; index < request->nCustHeaders; index++)
6296     {
6297         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6298             continue;
6299 
6300         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6301             continue;
6302 
6303         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6304             continue;
6305 
6306         if (requested_index == 0)
6307             break;
6308         requested_index --;
6309     }
6310 
6311     if (index >= request->nCustHeaders)
6312 	index = -1;
6313 
6314     TRACE("Return: %d\n", index);
6315     return index;
6316 }
6317 
6318 
6319 /***********************************************************************
6320  *           HTTP_InsertCustomHeader (internal)
6321  *
6322  * Insert header into array
6323  * Headers section must be held
6324  */
6325 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6326 {
6327     INT count;
6328     LPHTTPHEADERW lph = NULL;
6329 
6330     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6331     count = request->nCustHeaders + 1;
6332     if (count > 1)
6333 	lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6334     else
6335 	lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6336 
6337     if (!lph)
6338         return ERROR_OUTOFMEMORY;
6339 
6340     request->custHeaders = lph;
6341     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6342     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6343     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6344     request->custHeaders[count-1].wCount= lpHdr->wCount;
6345     request->nCustHeaders++;
6346 
6347     return ERROR_SUCCESS;
6348 }
6349 
6350 
6351 /***********************************************************************
6352  *           HTTP_DeleteCustomHeader (internal)
6353  *
6354  * Delete header from array
6355  * If this function is called, the index may change.
6356  * Headers section must be held
6357  */
6358 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6359 {
6360     if( request->nCustHeaders <= 0 )
6361         return FALSE;
6362     if( index >= request->nCustHeaders )
6363         return FALSE;
6364     request->nCustHeaders--;
6365 
6366     heap_free(request->custHeaders[index].lpszField);
6367     heap_free(request->custHeaders[index].lpszValue);
6368 
6369     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6370              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6371     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6372 
6373     return TRUE;
6374 }
6375 
6376 
6377 /***********************************************************************
6378  *          IsHostInProxyBypassList (@)
6379  *
6380  * Undocumented
6381  *
6382  */
6383 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6384 {
6385    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6386    return FALSE;
6387 }
6388