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