wininet: Get rid of the clear parameter to HTTP_GetResponseHeaders.
[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);
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) {
2311             WCHAR *ptr;
2312
2313             for(ptr=req->custHeaders[header_idx].lpszValue; *ptr; ) {
2314                 WCHAR *end;
2315
2316                 while(*ptr==' ' || *ptr=='\t')
2317                     ptr++;
2318
2319                 end = strchrW(ptr, ',');
2320                 if(!end)
2321                     end = ptr + strlenW(ptr);
2322
2323                 if(!strncmpiW(ptr, no_cacheW, sizeof(no_cacheW)/sizeof(*no_cacheW)-1)
2324                         || !strncmpiW(ptr, no_storeW, sizeof(no_storeW)/sizeof(*no_storeW)-1)) {
2325                     b = FALSE;
2326                     break;
2327                 }
2328
2329                 ptr = end;
2330                 if(*ptr == ',')
2331                     ptr++;
2332             }
2333         }
2334     }
2335
2336     if(!b) {
2337         if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2338             return;
2339
2340         FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2341     }
2342
2343     b = HTTP_GetRequestURL(req, url);
2344     if(!b) {
2345         WARN("Could not get URL\n");
2346         return;
2347     }
2348
2349     b = CreateUrlCacheEntryW(url, req->contentLength == ~0u ? 0 : req->contentLength, NULL, file_name, 0);
2350     if(!b) {
2351         WARN("Could not create cache entry: %08x\n", GetLastError());
2352         return;
2353     }
2354
2355     req->cacheFile = heap_strdupW(file_name);
2356     req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2357               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2358     if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2359         WARN("Could not create file: %u\n", GetLastError());
2360         req->hCacheFile = NULL;
2361         return;
2362     }
2363
2364     if(req->read_size) {
2365         DWORD written;
2366
2367         b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2368         if(!b)
2369             FIXME("WriteFile failed: %u\n", GetLastError());
2370
2371         if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2372             commit_cache_entry(req);
2373     }
2374 }
2375
2376 /* read some more data into the read buffer (the read section must be held) */
2377 static DWORD read_more_data( http_request_t *req, int maxlen )
2378 {
2379     DWORD res;
2380     int len;
2381
2382     if (req->read_pos)
2383     {
2384         /* move existing data to the start of the buffer */
2385         if(req->read_size)
2386             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2387         req->read_pos = 0;
2388     }
2389
2390     if (maxlen == -1) maxlen = sizeof(req->read_buf);
2391
2392     res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2393                        maxlen - req->read_size, 0, &len );
2394     if(res == ERROR_SUCCESS)
2395         req->read_size += len;
2396
2397     return res;
2398 }
2399
2400 /* remove some amount of data from the read buffer (the read section must be held) */
2401 static void remove_data( http_request_t *req, int count )
2402 {
2403     if (!(req->read_size -= count)) req->read_pos = 0;
2404     else req->read_pos += count;
2405 }
2406
2407 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2408 {
2409     int count, bytes_read, pos = 0;
2410     DWORD res;
2411
2412     EnterCriticalSection( &req->read_section );
2413     for (;;)
2414     {
2415         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2416
2417         if (eol)
2418         {
2419             count = eol - (req->read_buf + req->read_pos);
2420             bytes_read = count + 1;
2421         }
2422         else count = bytes_read = req->read_size;
2423
2424         count = min( count, *len - pos );
2425         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2426         pos += count;
2427         remove_data( req, bytes_read );
2428         if (eol) break;
2429
2430         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2431         {
2432             *len = 0;
2433             TRACE( "returning empty string %u\n", res);
2434             LeaveCriticalSection( &req->read_section );
2435             INTERNET_SetLastError(res);
2436             return FALSE;
2437         }
2438     }
2439     LeaveCriticalSection( &req->read_section );
2440
2441     if (pos < *len)
2442     {
2443         if (pos && buffer[pos - 1] == '\r') pos--;
2444         *len = pos + 1;
2445     }
2446     buffer[*len - 1] = 0;
2447     TRACE( "returning %s\n", debugstr_a(buffer));
2448     return TRUE;
2449 }
2450
2451 /* check if we have reached the end of the data to read (the read section must be held) */
2452 static BOOL end_of_read_data( http_request_t *req )
2453 {
2454     return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2455 }
2456
2457 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
2458 {
2459     DWORD res;
2460
2461     res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
2462     assert(*read <= size);
2463
2464     if(req->hCacheFile) {
2465         if(*read) {
2466             BOOL bres;
2467             DWORD written;
2468
2469             bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2470             if(!bres)
2471                 FIXME("WriteFile failed: %u\n", GetLastError());
2472         }
2473
2474         if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2475             commit_cache_entry(req);
2476     }
2477
2478     return res;
2479 }
2480
2481 /* fetch some more data into the read buffer (the read section must be held) */
2482 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2483 {
2484     DWORD res, read=0;
2485
2486     if(req->read_size == sizeof(req->read_buf))
2487         return ERROR_SUCCESS;
2488
2489     if(req->read_pos) {
2490         if(req->read_size)
2491             memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2492         req->read_pos = 0;
2493     }
2494
2495     res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2496             &read, read_mode);
2497     req->read_size += read;
2498
2499     TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2500     if(read_bytes)
2501         *read_bytes = read;
2502     return res;
2503 }
2504
2505 /* return the size of data available to be read immediately (the read section must be held) */
2506 static DWORD get_avail_data( http_request_t *req )
2507 {
2508     return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2509 }
2510
2511 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2512 {
2513     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2514     DWORD avail = 0;
2515
2516     if(req->netconn)
2517         NETCON_query_data_available(req->netconn, &avail);
2518     return netconn_stream->content_length == ~0u
2519         ? avail
2520         : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2521 }
2522
2523 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2524 {
2525     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2526     return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2527 }
2528
2529 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2530         DWORD *read, read_mode_t read_mode)
2531 {
2532     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2533     int len = 0;
2534
2535     size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2536
2537     if(read_mode == READMODE_NOBLOCK) {
2538         DWORD avail = netconn_get_avail_data(stream, req);
2539         if (size > avail)
2540             size = avail;
2541     }
2542
2543     if(size && req->netconn) {
2544         if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2545             len = 0;
2546         if(!len)
2547             netconn_stream->content_length = netconn_stream->content_read;
2548     }
2549
2550     netconn_stream->content_read += *read = len;
2551     TRACE("read %u bytes\n", len);
2552     return ERROR_SUCCESS;
2553 }
2554
2555 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2556 {
2557     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2558     BYTE buf[1024];
2559     DWORD avail;
2560     int len;
2561
2562     if(netconn_end_of_data(stream, req))
2563         return TRUE;
2564
2565     do {
2566         avail = netconn_get_avail_data(stream, req);
2567         if(!avail)
2568             return FALSE;
2569
2570         if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2571             return FALSE;
2572
2573         netconn_stream->content_read += len;
2574     }while(netconn_stream->content_read < netconn_stream->content_length);
2575
2576     return TRUE;
2577 }
2578
2579 static void netconn_destroy(data_stream_t *stream)
2580 {
2581 }
2582
2583 static const data_stream_vtbl_t netconn_stream_vtbl = {
2584     netconn_get_avail_data,
2585     netconn_end_of_data,
2586     netconn_read,
2587     netconn_drain_content,
2588     netconn_destroy
2589 };
2590
2591 /* read some more data into the read buffer (the read section must be held) */
2592 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2593 {
2594     DWORD res;
2595     int len;
2596
2597     if (stream->buf_pos)
2598     {
2599         /* move existing data to the start of the buffer */
2600         if(stream->buf_size)
2601             memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2602         stream->buf_pos = 0;
2603     }
2604
2605     if (maxlen == -1) maxlen = sizeof(stream->buf);
2606
2607     res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2608                        maxlen - stream->buf_size, 0, &len );
2609     if(res == ERROR_SUCCESS)
2610         stream->buf_size += len;
2611
2612     return res;
2613 }
2614
2615 /* remove some amount of data from the read buffer (the read section must be held) */
2616 static void remove_chunked_data(chunked_stream_t *stream, int count)
2617 {
2618     if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2619     else stream->buf_pos += count;
2620 }
2621
2622 /* discard data contents until we reach end of line (the read section must be held) */
2623 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2624 {
2625     DWORD res;
2626
2627     do
2628     {
2629         BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2630         if (eol)
2631         {
2632             remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2633             break;
2634         }
2635         stream->buf_pos = stream->buf_size = 0;  /* discard everything */
2636         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2637     } while (stream->buf_size);
2638     return ERROR_SUCCESS;
2639 }
2640
2641 /* read the size of the next chunk (the read section must be held) */
2642 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2643 {
2644     /* TODOO */
2645     DWORD chunk_size = 0, res;
2646
2647     if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2648         return res;
2649
2650     for (;;)
2651     {
2652         while (stream->buf_size)
2653         {
2654             char ch = stream->buf[stream->buf_pos];
2655             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2656             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2657             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2658             else if (ch == ';' || ch == '\r' || ch == '\n')
2659             {
2660                 TRACE( "reading %u byte chunk\n", chunk_size );
2661                 stream->chunk_size = chunk_size;
2662                 req->contentLength += chunk_size;
2663                 return discard_chunked_eol(stream, req);
2664             }
2665             remove_chunked_data(stream, 1);
2666         }
2667         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2668         if (!stream->buf_size)
2669         {
2670             stream->chunk_size = 0;
2671             return ERROR_SUCCESS;
2672         }
2673     }
2674 }
2675
2676 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2677 {
2678     /* Allow reading only from read buffer */
2679     return 0;
2680 }
2681
2682 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2683 {
2684     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2685     return !chunked_stream->chunk_size;
2686 }
2687
2688 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2689         DWORD *read, read_mode_t read_mode)
2690 {
2691     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2692     DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2693
2694     if(chunked_stream->chunk_size == ~0u) {
2695         res = start_next_chunk(chunked_stream, req);
2696         if(res != ERROR_SUCCESS)
2697             return res;
2698     }
2699
2700     while(size && chunked_stream->chunk_size) {
2701         if(chunked_stream->buf_size) {
2702             read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2703
2704             /* this could block */
2705             if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2706                 break;
2707
2708             memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2709             remove_chunked_data(chunked_stream, read_bytes);
2710         }else {
2711             read_bytes = min(size, chunked_stream->chunk_size);
2712
2713             if(read_mode == READMODE_NOBLOCK) {
2714                 DWORD avail;
2715
2716                 if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
2717                     break;
2718                 if(read_bytes > avail)
2719                     read_bytes = avail;
2720
2721                 /* this could block */
2722                 if(read_bytes == chunked_stream->chunk_size)
2723                     break;
2724             }
2725
2726             res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2727             if(res != ERROR_SUCCESS)
2728                 break;
2729         }
2730
2731         chunked_stream->chunk_size -= read_bytes;
2732         size -= read_bytes;
2733         ret_read += read_bytes;
2734         if(!chunked_stream->chunk_size) {
2735             assert(read_mode != READMODE_NOBLOCK);
2736             res = start_next_chunk(chunked_stream, req);
2737             if(res != ERROR_SUCCESS)
2738                 break;
2739         }
2740
2741         if(read_mode == READMODE_ASYNC)
2742             read_mode = READMODE_NOBLOCK;
2743     }
2744
2745     TRACE("read %u bytes\n", ret_read);
2746     *read = ret_read;
2747     return res;
2748 }
2749
2750 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2751 {
2752     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2753
2754     /* FIXME: we can do better */
2755     return !chunked_stream->chunk_size;
2756 }
2757
2758 static void chunked_destroy(data_stream_t *stream)
2759 {
2760     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2761     heap_free(chunked_stream);
2762 }
2763
2764 static const data_stream_vtbl_t chunked_stream_vtbl = {
2765     chunked_get_avail_data,
2766     chunked_end_of_data,
2767     chunked_read,
2768     chunked_drain_content,
2769     chunked_destroy
2770 };
2771
2772 /* set the request content length based on the headers */
2773 static DWORD set_content_length(http_request_t *request)
2774 {
2775     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2776     WCHAR encoding[20];
2777     DWORD size;
2778
2779     if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2780         request->contentLength = request->netconn_stream.content_length = 0;
2781         return ERROR_SUCCESS;
2782     }
2783
2784     size = sizeof(request->contentLength);
2785     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2786                             &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2787         request->contentLength = ~0u;
2788     request->netconn_stream.content_length = request->contentLength;
2789     request->netconn_stream.content_read = request->read_size;
2790
2791     size = sizeof(encoding);
2792     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2793         !strcmpiW(encoding, szChunked))
2794     {
2795         chunked_stream_t *chunked_stream;
2796
2797         chunked_stream = heap_alloc(sizeof(*chunked_stream));
2798         if(!chunked_stream)
2799             return ERROR_OUTOFMEMORY;
2800
2801         chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2802         chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2803         chunked_stream->chunk_size = ~0u;
2804
2805         if(request->read_size) {
2806             memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2807             chunked_stream->buf_size = request->read_size;
2808             request->read_size = request->read_pos = 0;
2809         }
2810
2811         request->data_stream = &chunked_stream->data_stream;
2812         request->contentLength = ~0u;
2813         request->read_chunked = TRUE;
2814     }
2815
2816     if(request->decoding) {
2817         int encoding_idx;
2818
2819         static const WCHAR gzipW[] = {'g','z','i','p',0};
2820
2821         encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2822         if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2823             return init_gzip_stream(request);
2824     }
2825
2826     return ERROR_SUCCESS;
2827 }
2828
2829 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2830 {
2831     INTERNET_ASYNC_RESULT iar;
2832
2833     iar.dwResult = result;
2834     iar.dwError = error;
2835
2836     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2837             sizeof(INTERNET_ASYNC_RESULT));
2838 }
2839
2840 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif, DWORD *ret_size)
2841 {
2842     DWORD res, read = 0, avail = 0;
2843     read_mode_t mode;
2844
2845     TRACE("%p\n", req);
2846
2847     EnterCriticalSection( &req->read_section );
2848
2849     mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2850     res = refill_read_buffer(req, mode, &read);
2851     if(res == ERROR_SUCCESS)
2852         avail = get_avail_data(req);
2853
2854     LeaveCriticalSection( &req->read_section );
2855
2856     if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2857         WARN("res %u read %u, closing connection\n", res, read);
2858         http_release_netconn(req, FALSE);
2859     }
2860
2861     if(res != ERROR_SUCCESS) {
2862         send_request_complete(req, 0, res);
2863         return;
2864     }
2865
2866     if(ret_size)
2867         *ret_size = avail;
2868     if(first_notif)
2869         avail = 0;
2870
2871     send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2872 }
2873
2874 /* read data from the http connection (the read section must be held) */
2875 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2876 {
2877     DWORD current_read = 0, ret_read = 0;
2878     read_mode_t read_mode;
2879     DWORD res = ERROR_SUCCESS;
2880
2881     read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2882
2883     EnterCriticalSection( &req->read_section );
2884
2885     if(req->read_size) {
2886         ret_read = min(size, req->read_size);
2887         memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2888         req->read_size -= ret_read;
2889         req->read_pos += ret_read;
2890         if(read_mode == READMODE_ASYNC)
2891             read_mode = READMODE_NOBLOCK;
2892     }
2893
2894     if(ret_read < size) {
2895         res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2896         ret_read += current_read;
2897     }
2898
2899     LeaveCriticalSection( &req->read_section );
2900
2901     *read = ret_read;
2902     TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2903
2904     if(size && !ret_read)
2905         http_release_netconn(req, res == ERROR_SUCCESS);
2906
2907     return res;
2908 }
2909
2910 static BOOL drain_content(http_request_t *req, BOOL blocking)
2911 {
2912     BOOL ret;
2913
2914     if(!req->netconn || req->contentLength == -1)
2915         return FALSE;
2916
2917     if(!strcmpW(req->verb, szHEAD))
2918         return TRUE;
2919
2920     if(!blocking)
2921         return req->data_stream->vtbl->drain_content(req->data_stream, req);
2922
2923     EnterCriticalSection( &req->read_section );
2924
2925     while(1) {
2926         DWORD bytes_read, res;
2927         BYTE buf[4096];
2928
2929         res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
2930         if(res != ERROR_SUCCESS) {
2931             ret = FALSE;
2932             break;
2933         }
2934         if(!bytes_read) {
2935             ret = TRUE;
2936             break;
2937         }
2938     }
2939
2940     LeaveCriticalSection( &req->read_section );
2941     return ret;
2942 }
2943
2944 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2945 {
2946     http_request_t *req = (http_request_t*)hdr;
2947     DWORD res;
2948
2949     EnterCriticalSection( &req->read_section );
2950     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2951         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2952
2953     res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2954     if(res == ERROR_SUCCESS)
2955         res = hdr->dwError;
2956     LeaveCriticalSection( &req->read_section );
2957
2958     return res;
2959 }
2960
2961 typedef struct {
2962     task_header_t hdr;
2963     void *buf;
2964     DWORD size;
2965     DWORD *ret_read;
2966 } read_file_ex_task_t;
2967
2968 static void AsyncReadFileExProc(task_header_t *hdr)
2969 {
2970     read_file_ex_task_t *task = (read_file_ex_task_t*)hdr;
2971     http_request_t *req = (http_request_t*)task->hdr.hdr;
2972     DWORD res;
2973
2974     TRACE("INTERNETREADFILEEXW %p\n", task->hdr.hdr);
2975
2976     res = HTTPREQ_Read(req, task->buf, task->size, task->ret_read, TRUE);
2977     send_request_complete(req, res == ERROR_SUCCESS, res);
2978 }
2979
2980 static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2981         DWORD flags, DWORD_PTR context)
2982 {
2983
2984     http_request_t *req = (http_request_t*)hdr;
2985     DWORD res, read, cread, error = ERROR_SUCCESS;
2986
2987     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2988         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2989
2990     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2991
2992     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2993     {
2994         read_file_ex_task_t *task;
2995
2996         if (TryEnterCriticalSection( &req->read_section ))
2997         {
2998             if (get_avail_data(req))
2999             {
3000                 res = HTTPREQ_Read(req, buf, size, &read, FALSE);
3001                 LeaveCriticalSection( &req->read_section );
3002                 goto done;
3003             }
3004             LeaveCriticalSection( &req->read_section );
3005         }
3006
3007         task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
3008         task->buf = buf;
3009         task->size = size;
3010         task->ret_read = ret_read;
3011
3012         INTERNET_AsyncCall(&task->hdr);
3013
3014         return ERROR_IO_PENDING;
3015     }
3016
3017     read = 0;
3018
3019     EnterCriticalSection( &req->read_section );
3020     if(hdr->dwError == ERROR_SUCCESS)
3021         hdr->dwError = INTERNET_HANDLE_IN_USE;
3022     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3023         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3024
3025     while(1) {
3026         res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
3027         if(res != ERROR_SUCCESS)
3028             break;
3029
3030         read += cread;
3031         if(read == size || end_of_read_data(req))
3032             break;
3033
3034         LeaveCriticalSection( &req->read_section );
3035
3036         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3037                 &cread, sizeof(cread));
3038         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
3039                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3040
3041         EnterCriticalSection( &req->read_section );
3042     }
3043
3044     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3045         hdr->dwError = ERROR_SUCCESS;
3046     else
3047         error = hdr->dwError;
3048
3049     LeaveCriticalSection( &req->read_section );
3050
3051 done:
3052     *ret_read = read;
3053     if (res == ERROR_SUCCESS) {
3054         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3055                 &read, sizeof(read));
3056     }
3057
3058     return res==ERROR_SUCCESS ? error : res;
3059 }
3060
3061 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3062 {
3063     DWORD res;
3064     http_request_t *request = (http_request_t*)hdr;
3065
3066     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3067
3068     *written = 0;
3069     res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3070     if (res == ERROR_SUCCESS)
3071         request->bytesWritten += *written;
3072
3073     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3074     return res;
3075 }
3076
3077 typedef struct {
3078     task_header_t hdr;
3079     DWORD *ret_size;
3080 } http_data_available_task_t;
3081
3082 static void AsyncQueryDataAvailableProc(task_header_t *hdr)
3083 {
3084     http_data_available_task_t *task = (http_data_available_task_t*)hdr;
3085
3086     HTTP_ReceiveRequestData((http_request_t*)task->hdr.hdr, FALSE, task->ret_size);
3087 }
3088
3089 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3090 {
3091     http_request_t *req = (http_request_t*)hdr;
3092
3093     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3094
3095     if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3096     {
3097         http_data_available_task_t *task;
3098
3099         /* never wait, if we can't enter the section we queue an async request right away */
3100         if (TryEnterCriticalSection( &req->read_section ))
3101         {
3102             refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3103             if ((*available = get_avail_data( req ))) goto done;
3104             if (end_of_read_data( req )) goto done;
3105             LeaveCriticalSection( &req->read_section );
3106         }
3107
3108         task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
3109         task->ret_size = available;
3110         INTERNET_AsyncCall(&task->hdr);
3111         return ERROR_IO_PENDING;
3112     }
3113
3114     EnterCriticalSection( &req->read_section );
3115
3116     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3117     {
3118         refill_read_buffer( req, READMODE_ASYNC, NULL );
3119         *available = get_avail_data( req );
3120     }
3121
3122 done:
3123     LeaveCriticalSection( &req->read_section );
3124
3125     TRACE( "returning %u\n", *available );
3126     return ERROR_SUCCESS;
3127 }
3128
3129 static const object_vtbl_t HTTPREQVtbl = {
3130     HTTPREQ_Destroy,
3131     HTTPREQ_CloseConnection,
3132     HTTPREQ_QueryOption,
3133     HTTPREQ_SetOption,
3134     HTTPREQ_ReadFile,
3135     HTTPREQ_ReadFileEx,
3136     HTTPREQ_WriteFile,
3137     HTTPREQ_QueryDataAvailable,
3138     NULL
3139 };
3140
3141 /***********************************************************************
3142  *           HTTP_HttpOpenRequestW (internal)
3143  *
3144  * Open a HTTP request handle
3145  *
3146  * RETURNS
3147  *    HINTERNET  a HTTP request handle on success
3148  *    NULL       on failure
3149  *
3150  */
3151 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3152         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3153         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3154         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3155 {
3156     appinfo_t *hIC = session->appInfo;
3157     http_request_t *request;
3158     DWORD len, res = ERROR_SUCCESS;
3159
3160     TRACE("-->\n");
3161
3162     request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3163     if(!request)
3164         return ERROR_OUTOFMEMORY;
3165
3166     request->hdr.htype = WH_HHTTPREQ;
3167     request->hdr.dwFlags = dwFlags;
3168     request->hdr.dwContext = dwContext;
3169     request->contentLength = ~0u;
3170
3171     request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3172     request->data_stream = &request->netconn_stream.data_stream;
3173     request->connect_timeout = session->connect_timeout;
3174     request->send_timeout = session->send_timeout;
3175     request->receive_timeout = session->receive_timeout;
3176
3177     InitializeCriticalSection( &request->read_section );
3178     request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3179
3180     WININET_AddRef( &session->hdr );
3181     request->session = session;
3182     list_add_head( &session->hdr.children, &request->hdr.entry );
3183
3184     request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3185     if(!request->server) {
3186         WININET_Release(&request->hdr);
3187         return ERROR_OUTOFMEMORY;
3188     }
3189
3190     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3191         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3192     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3193         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3194
3195     if (lpszObjectName && *lpszObjectName) {
3196         HRESULT rc;
3197
3198         len = 0;
3199         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3200         if (rc != E_POINTER)
3201             len = strlenW(lpszObjectName)+1;
3202         request->path = heap_alloc(len*sizeof(WCHAR));
3203         rc = UrlEscapeW(lpszObjectName, request->path, &len,
3204                    URL_ESCAPE_SPACES_ONLY);
3205         if (rc != S_OK)
3206         {
3207             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3208             strcpyW(request->path,lpszObjectName);
3209         }
3210     }else {
3211         static const WCHAR slashW[] = {'/',0};
3212
3213         request->path = heap_strdupW(slashW);
3214     }
3215
3216     if (lpszReferrer && *lpszReferrer)
3217         HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3218
3219     if (lpszAcceptTypes)
3220     {
3221         int i;
3222         for (i = 0; lpszAcceptTypes[i]; i++)
3223         {
3224             if (!*lpszAcceptTypes[i]) continue;
3225             HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3226                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3227                                HTTP_ADDHDR_FLAG_REQ |
3228                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3229         }
3230     }
3231
3232     request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3233     request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3234
3235     HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3236
3237     if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3238         session->hostPort = INTERNET_DEFAULT_HTTP_PORT;
3239
3240     if (hIC->proxy && hIC->proxy[0])
3241         HTTP_DealWithProxy( hIC, session, request );
3242
3243     INTERNET_SendCallback(&session->hdr, dwContext,
3244                           INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3245                           sizeof(HINTERNET));
3246
3247     TRACE("<-- %u (%p)\n", res, request);
3248
3249     if(res != ERROR_SUCCESS) {
3250         WININET_Release( &request->hdr );
3251         *ret = NULL;
3252         return res;
3253     }
3254
3255     *ret = request->hdr.hInternet;
3256     return ERROR_SUCCESS;
3257 }
3258
3259 /***********************************************************************
3260  *           HttpOpenRequestW (WININET.@)
3261  *
3262  * Open a HTTP request handle
3263  *
3264  * RETURNS
3265  *    HINTERNET  a HTTP request handle on success
3266  *    NULL       on failure
3267  *
3268  */
3269 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3270         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3271         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3272         DWORD dwFlags, DWORD_PTR dwContext)
3273 {
3274     http_session_t *session;
3275     HINTERNET handle = NULL;
3276     DWORD res;
3277
3278     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3279           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3280           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3281           dwFlags, dwContext);
3282     if(lpszAcceptTypes!=NULL)
3283     {
3284         int i;
3285         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3286             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3287     }
3288
3289     session = (http_session_t*) get_handle_object( hHttpSession );
3290     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
3291     {
3292         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3293         goto lend;
3294     }
3295
3296     /*
3297      * My tests seem to show that the windows version does not
3298      * become asynchronous until after this point. And anyhow
3299      * if this call was asynchronous then how would you get the
3300      * necessary HINTERNET pointer returned by this function.
3301      *
3302      */
3303     res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3304                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
3305                                 dwFlags, dwContext, &handle);
3306 lend:
3307     if( session )
3308         WININET_Release( &session->hdr );
3309     TRACE("returning %p\n", handle);
3310     if(res != ERROR_SUCCESS)
3311         SetLastError(res);
3312     return handle;
3313 }
3314
3315 static const LPCWSTR header_lookup[] = {
3316     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
3317     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
3318     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3319     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
3320     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3321     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
3322     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
3323     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
3324     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
3325     szDate,                     /* HTTP_QUERY_DATE = 9 */
3326     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
3327     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
3328     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
3329     szURI,                      /* HTTP_QUERY_URI = 13 */
3330     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
3331     NULL,                       /* HTTP_QUERY_COST = 15 */
3332     NULL,                       /* HTTP_QUERY_LINK = 16 */
3333     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
3334     NULL,                       /* HTTP_QUERY_VERSION = 18 */
3335     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
3336     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
3337     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
3338     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3339     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
3340     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
3341     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3342     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3343     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3344     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
3345     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3346     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
3347     NULL,                       /* HTTP_QUERY_FROM = 31 */
3348     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3349     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
3350     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
3351     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
3352     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
3353     szServer,                   /* HTTP_QUERY_SERVER = 37 */
3354     NULL,                       /* HTTP_TITLE = 38 */
3355     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
3356     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3357     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3358     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3359     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
3360     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
3361     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
3362     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
3363     szContent_Disposition,      /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3364     szAge,                      /* HTTP_QUERY_AGE = 48 */
3365     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
3366     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
3367     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3368     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
3369     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
3370     szETag,                     /* HTTP_QUERY_ETAG = 54 */
3371     hostW,                      /* HTTP_QUERY_HOST = 55 */
3372     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
3373     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3374     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
3375     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3376     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
3377     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3378     szRange,                    /* HTTP_QUERY_RANGE = 62 */
3379     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3380     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
3381     szVary,                     /* HTTP_QUERY_VARY = 65 */
3382     szVia,                      /* HTTP_QUERY_VIA = 66 */
3383     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
3384     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
3385     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3386     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3387 };
3388
3389 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3390
3391 /***********************************************************************
3392  *           HTTP_HttpQueryInfoW (internal)
3393  */
3394 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3395         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3396 {
3397     LPHTTPHEADERW lphttpHdr = NULL;
3398     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3399     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3400     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3401     INT index = -1;
3402
3403     /* Find requested header structure */
3404     switch (level)
3405     {
3406     case HTTP_QUERY_CUSTOM:
3407         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3408         index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3409         break;
3410     case HTTP_QUERY_RAW_HEADERS_CRLF:
3411         {
3412             LPWSTR headers;
3413             DWORD len = 0;
3414             DWORD res = ERROR_INVALID_PARAMETER;
3415
3416             if (request_only)
3417                 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3418             else
3419                 headers = request->rawHeaders;
3420
3421             if (headers)
3422                 len = strlenW(headers) * sizeof(WCHAR);
3423
3424             if (len + sizeof(WCHAR) > *lpdwBufferLength)
3425             {
3426                 len += sizeof(WCHAR);
3427                 res = ERROR_INSUFFICIENT_BUFFER;
3428             }
3429             else if (lpBuffer)
3430             {
3431                 if (headers)
3432                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3433                 else
3434                 {
3435                     len = strlenW(szCrLf) * sizeof(WCHAR);
3436                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3437                 }
3438                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3439                 res = ERROR_SUCCESS;
3440             }
3441             *lpdwBufferLength = len;
3442
3443             if (request_only) heap_free(headers);
3444             return res;
3445         }
3446     case HTTP_QUERY_RAW_HEADERS:
3447         {
3448             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3449             DWORD i, size = 0;
3450             LPWSTR pszString = lpBuffer;
3451
3452             for (i = 0; ppszRawHeaderLines[i]; i++)
3453                 size += strlenW(ppszRawHeaderLines[i]) + 1;
3454
3455             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3456             {
3457                 HTTP_FreeTokens(ppszRawHeaderLines);
3458                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3459                 return ERROR_INSUFFICIENT_BUFFER;
3460             }
3461             if (pszString)
3462             {
3463                 for (i = 0; ppszRawHeaderLines[i]; i++)
3464                 {
3465                     DWORD len = strlenW(ppszRawHeaderLines[i]);
3466                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3467                     pszString += len+1;
3468                 }
3469                 *pszString = '\0';
3470                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3471             }
3472             *lpdwBufferLength = size * sizeof(WCHAR);
3473             HTTP_FreeTokens(ppszRawHeaderLines);
3474
3475             return ERROR_SUCCESS;
3476         }
3477     case HTTP_QUERY_STATUS_TEXT:
3478         if (request->statusText)
3479         {
3480             DWORD len = strlenW(request->statusText);
3481             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3482             {
3483                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3484                 return ERROR_INSUFFICIENT_BUFFER;
3485             }
3486             if (lpBuffer)
3487             {
3488                 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3489                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3490             }
3491             *lpdwBufferLength = len * sizeof(WCHAR);
3492             return ERROR_SUCCESS;
3493         }
3494         break;
3495     case HTTP_QUERY_VERSION:
3496         if (request->version)
3497         {
3498             DWORD len = strlenW(request->version);
3499             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3500             {
3501                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3502                 return ERROR_INSUFFICIENT_BUFFER;
3503             }
3504             if (lpBuffer)
3505             {
3506                 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3507                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3508             }
3509             *lpdwBufferLength = len * sizeof(WCHAR);
3510             return ERROR_SUCCESS;
3511         }
3512         break;
3513     case HTTP_QUERY_CONTENT_ENCODING:
3514         index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3515                 requested_index,request_only);
3516         break;
3517     case HTTP_QUERY_STATUS_CODE: {
3518         DWORD res = ERROR_SUCCESS;
3519
3520         if(request_only)
3521             return ERROR_HTTP_INVALID_QUERY_REQUEST;
3522
3523         if(requested_index)
3524             break;
3525
3526         if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3527             if(*lpdwBufferLength >= sizeof(DWORD))
3528                 *(DWORD*)lpBuffer = request->status_code;
3529             else
3530                 res = ERROR_INSUFFICIENT_BUFFER;
3531             *lpdwBufferLength = sizeof(DWORD);
3532         }else {
3533             WCHAR buf[12];
3534             DWORD size;
3535             static const WCHAR formatW[] = {'%','u',0};
3536
3537             size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3538
3539             if(size <= *lpdwBufferLength) {
3540                 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3541             }else {
3542                 size += sizeof(WCHAR);
3543                 res = ERROR_INSUFFICIENT_BUFFER;
3544             }
3545
3546             *lpdwBufferLength = size;
3547         }
3548         return res;
3549     }
3550     default:
3551         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3552
3553         if (level < LAST_TABLE_HEADER && header_lookup[level])
3554             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3555                                               requested_index,request_only);
3556     }
3557
3558     if (index >= 0)
3559         lphttpHdr = &request->custHeaders[index];
3560
3561     /* Ensure header satisfies requested attributes */
3562     if (!lphttpHdr ||
3563         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3564          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3565     {
3566         return ERROR_HTTP_HEADER_NOT_FOUND;
3567     }
3568
3569     if (lpdwIndex) (*lpdwIndex)++;
3570
3571     /* coalesce value to requested type */
3572     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3573     {
3574         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3575         TRACE(" returning number: %d\n", *(int *)lpBuffer);
3576      }
3577     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3578     {
3579         time_t tmpTime;
3580         struct tm tmpTM;
3581         SYSTEMTIME *STHook;
3582
3583         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3584
3585         tmpTM = *gmtime(&tmpTime);
3586         STHook = (SYSTEMTIME *)lpBuffer;
3587         STHook->wDay = tmpTM.tm_mday;
3588         STHook->wHour = tmpTM.tm_hour;
3589         STHook->wMilliseconds = 0;
3590         STHook->wMinute = tmpTM.tm_min;
3591         STHook->wDayOfWeek = tmpTM.tm_wday;
3592         STHook->wMonth = tmpTM.tm_mon + 1;
3593         STHook->wSecond = tmpTM.tm_sec;
3594         STHook->wYear = 1900+tmpTM.tm_year;
3595
3596         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3597               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3598               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3599     }
3600     else if (lphttpHdr->lpszValue)
3601     {
3602         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3603
3604         if (len > *lpdwBufferLength)
3605         {
3606             *lpdwBufferLength = len;
3607             return ERROR_INSUFFICIENT_BUFFER;
3608         }
3609         if (lpBuffer)
3610         {
3611             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3612             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3613         }
3614         *lpdwBufferLength = len - sizeof(WCHAR);
3615     }
3616     return ERROR_SUCCESS;
3617 }
3618
3619 /***********************************************************************
3620  *           HttpQueryInfoW (WININET.@)
3621  *
3622  * Queries for information about an HTTP request
3623  *
3624  * RETURNS
3625  *    TRUE  on success
3626  *    FALSE on failure
3627  *
3628  */
3629 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3630         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3631 {
3632     http_request_t *request;
3633     DWORD res;
3634
3635     if (TRACE_ON(wininet)) {
3636 #define FE(x) { x, #x }
3637         static const wininet_flag_info query_flags[] = {
3638             FE(HTTP_QUERY_MIME_VERSION),
3639             FE(HTTP_QUERY_CONTENT_TYPE),
3640             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3641             FE(HTTP_QUERY_CONTENT_ID),
3642             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3643             FE(HTTP_QUERY_CONTENT_LENGTH),
3644             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3645             FE(HTTP_QUERY_ALLOW),
3646             FE(HTTP_QUERY_PUBLIC),
3647             FE(HTTP_QUERY_DATE),
3648             FE(HTTP_QUERY_EXPIRES),
3649             FE(HTTP_QUERY_LAST_MODIFIED),
3650             FE(HTTP_QUERY_MESSAGE_ID),
3651             FE(HTTP_QUERY_URI),
3652             FE(HTTP_QUERY_DERIVED_FROM),
3653             FE(HTTP_QUERY_COST),
3654             FE(HTTP_QUERY_LINK),
3655             FE(HTTP_QUERY_PRAGMA),
3656             FE(HTTP_QUERY_VERSION),
3657             FE(HTTP_QUERY_STATUS_CODE),
3658             FE(HTTP_QUERY_STATUS_TEXT),
3659             FE(HTTP_QUERY_RAW_HEADERS),
3660             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3661             FE(HTTP_QUERY_CONNECTION),
3662             FE(HTTP_QUERY_ACCEPT),
3663             FE(HTTP_QUERY_ACCEPT_CHARSET),
3664             FE(HTTP_QUERY_ACCEPT_ENCODING),
3665             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3666             FE(HTTP_QUERY_AUTHORIZATION),
3667             FE(HTTP_QUERY_CONTENT_ENCODING),
3668             FE(HTTP_QUERY_FORWARDED),
3669             FE(HTTP_QUERY_FROM),
3670             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3671             FE(HTTP_QUERY_LOCATION),
3672             FE(HTTP_QUERY_ORIG_URI),
3673             FE(HTTP_QUERY_REFERER),
3674             FE(HTTP_QUERY_RETRY_AFTER),
3675             FE(HTTP_QUERY_SERVER),
3676             FE(HTTP_QUERY_TITLE),
3677             FE(HTTP_QUERY_USER_AGENT),
3678             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3679             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3680             FE(HTTP_QUERY_ACCEPT_RANGES),
3681         FE(HTTP_QUERY_SET_COOKIE),
3682         FE(HTTP_QUERY_COOKIE),
3683             FE(HTTP_QUERY_REQUEST_METHOD),
3684             FE(HTTP_QUERY_REFRESH),
3685             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3686             FE(HTTP_QUERY_AGE),
3687             FE(HTTP_QUERY_CACHE_CONTROL),
3688             FE(HTTP_QUERY_CONTENT_BASE),
3689             FE(HTTP_QUERY_CONTENT_LOCATION),
3690             FE(HTTP_QUERY_CONTENT_MD5),
3691             FE(HTTP_QUERY_CONTENT_RANGE),
3692             FE(HTTP_QUERY_ETAG),
3693             FE(HTTP_QUERY_HOST),
3694             FE(HTTP_QUERY_IF_MATCH),
3695             FE(HTTP_QUERY_IF_NONE_MATCH),
3696             FE(HTTP_QUERY_IF_RANGE),
3697             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3698             FE(HTTP_QUERY_MAX_FORWARDS),
3699             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3700             FE(HTTP_QUERY_RANGE),
3701             FE(HTTP_QUERY_TRANSFER_ENCODING),
3702             FE(HTTP_QUERY_UPGRADE),
3703             FE(HTTP_QUERY_VARY),
3704             FE(HTTP_QUERY_VIA),
3705             FE(HTTP_QUERY_WARNING),
3706             FE(HTTP_QUERY_CUSTOM)
3707         };
3708         static const wininet_flag_info modifier_flags[] = {
3709             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3710             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3711             FE(HTTP_QUERY_FLAG_NUMBER),
3712             FE(HTTP_QUERY_FLAG_COALESCE)
3713         };
3714 #undef FE
3715         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3716         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3717         DWORD i;
3718
3719         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3720         TRACE("  Attribute:");
3721         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3722             if (query_flags[i].val == info) {
3723                 TRACE(" %s", query_flags[i].name);
3724                 break;
3725             }
3726         }
3727         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3728             TRACE(" Unknown (%08x)", info);
3729         }
3730
3731         TRACE(" Modifier:");
3732         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3733             if (modifier_flags[i].val & info_mod) {
3734                 TRACE(" %s", modifier_flags[i].name);
3735                 info_mod &= ~ modifier_flags[i].val;
3736             }
3737         }
3738         
3739         if (info_mod) {
3740             TRACE(" Unknown (%08x)", info_mod);
3741         }
3742         TRACE("\n");
3743     }
3744     
3745     request = (http_request_t*) get_handle_object( hHttpRequest );
3746     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
3747     {
3748         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3749         goto lend;
3750     }
3751
3752     if (lpBuffer == NULL)
3753         *lpdwBufferLength = 0;
3754     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3755                                lpBuffer, lpdwBufferLength, lpdwIndex);
3756
3757 lend:
3758     if( request )
3759          WININET_Release( &request->hdr );
3760
3761     TRACE("%u <--\n", res);
3762     if(res != ERROR_SUCCESS)
3763         SetLastError(res);
3764     return res == ERROR_SUCCESS;
3765 }
3766
3767 /***********************************************************************
3768  *           HttpQueryInfoA (WININET.@)
3769  *
3770  * Queries for information about an HTTP request
3771  *
3772  * RETURNS
3773  *    TRUE  on success
3774  *    FALSE on failure
3775  *
3776  */
3777 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3778         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3779 {
3780     BOOL result;
3781     DWORD len;
3782     WCHAR* bufferW;
3783
3784     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3785        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3786     {
3787         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3788                                lpdwBufferLength, lpdwIndex );
3789     }
3790
3791     if (lpBuffer)
3792     {
3793         DWORD alloclen;
3794         len = (*lpdwBufferLength)*sizeof(WCHAR);
3795         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3796         {
3797             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3798             if (alloclen < len)
3799                 alloclen = len;
3800         }
3801         else
3802             alloclen = len;
3803         bufferW = heap_alloc(alloclen);
3804         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3805         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3806             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3807     } else
3808     {
3809         bufferW = NULL;
3810         len = 0;
3811     }
3812
3813     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3814                            &len, lpdwIndex );
3815     if( result )
3816     {
3817         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3818                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3819         *lpdwBufferLength = len - 1;
3820
3821         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3822     }
3823     else
3824         /* since the strings being returned from HttpQueryInfoW should be
3825          * only ASCII characters, it is reasonable to assume that all of
3826          * the Unicode characters can be reduced to a single byte */
3827         *lpdwBufferLength = len / sizeof(WCHAR);
3828
3829     heap_free( bufferW );
3830     return result;
3831 }
3832
3833 /***********************************************************************
3834  *           HTTP_GetRedirectURL (internal)
3835  */
3836 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3837 {
3838     static WCHAR szHttp[] = {'h','t','t','p',0};
3839     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3840     http_session_t *session = request->session;
3841     URL_COMPONENTSW urlComponents;
3842     DWORD url_length = 0;
3843     LPWSTR orig_url;
3844     LPWSTR combined_url;
3845
3846     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3847     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3848     urlComponents.dwSchemeLength = 0;
3849     urlComponents.lpszHostName = session->hostName;
3850     urlComponents.dwHostNameLength = 0;
3851     urlComponents.nPort = session->hostPort;
3852     urlComponents.lpszUserName = session->userName;
3853     urlComponents.dwUserNameLength = 0;
3854     urlComponents.lpszPassword = NULL;
3855     urlComponents.dwPasswordLength = 0;
3856     urlComponents.lpszUrlPath = request->path;
3857     urlComponents.dwUrlPathLength = 0;
3858     urlComponents.lpszExtraInfo = NULL;
3859     urlComponents.dwExtraInfoLength = 0;
3860
3861     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3862         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3863         return NULL;
3864
3865     orig_url = heap_alloc(url_length);
3866
3867     /* convert from bytes to characters */
3868     url_length = url_length / sizeof(WCHAR) - 1;
3869     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3870     {
3871         heap_free(orig_url);
3872         return NULL;
3873     }
3874
3875     url_length = 0;
3876     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3877         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3878     {
3879         heap_free(orig_url);
3880         return NULL;
3881     }
3882     combined_url = heap_alloc(url_length * sizeof(WCHAR));
3883
3884     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3885     {
3886         heap_free(orig_url);
3887         heap_free(combined_url);
3888         return NULL;
3889     }
3890     heap_free(orig_url);
3891     return combined_url;
3892 }
3893
3894
3895 /***********************************************************************
3896  *           HTTP_HandleRedirect (internal)
3897  */
3898 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3899 {
3900     http_session_t *session = request->session;
3901     WCHAR path[INTERNET_MAX_PATH_LENGTH];
3902     int index;
3903
3904     if(lpszUrl[0]=='/')
3905     {
3906         /* if it's an absolute path, keep the same session info */
3907         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3908     }
3909     else
3910     {
3911         URL_COMPONENTSW urlComponents;
3912         WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3913         WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3914         WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3915         BOOL custom_port = FALSE;
3916
3917         static WCHAR httpW[] = {'h','t','t','p',0};
3918         static WCHAR httpsW[] = {'h','t','t','p','s',0};
3919
3920         userName[0] = 0;
3921         hostName[0] = 0;
3922         protocol[0] = 0;
3923
3924         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3925         urlComponents.lpszScheme = protocol;
3926         urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3927         urlComponents.lpszHostName = hostName;
3928         urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3929         urlComponents.lpszUserName = userName;
3930         urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3931         urlComponents.lpszPassword = NULL;
3932         urlComponents.dwPasswordLength = 0;
3933         urlComponents.lpszUrlPath = path;
3934         urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3935         urlComponents.lpszExtraInfo = NULL;
3936         urlComponents.dwExtraInfoLength = 0;
3937         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3938             return INTERNET_GetLastError();
3939
3940         if(!strcmpiW(protocol, httpW)) {
3941             if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3942                 TRACE("redirect from secure page to non-secure page\n");
3943                 /* FIXME: warn about from secure redirect to non-secure page */
3944                 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3945             }
3946
3947             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3948                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3949             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3950                 custom_port = TRUE;
3951         }else if(!strcmpiW(protocol, httpsW)) {
3952             if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3953                 TRACE("redirect from non-secure page to secure page\n");
3954                 /* FIXME: notify about redirect to secure page */
3955                 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3956             }
3957
3958             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3959                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3960             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3961                 custom_port = TRUE;
3962         }
3963
3964         heap_free(session->hostName);
3965
3966         if(custom_port) {
3967             int len;
3968             static const WCHAR fmt[] = {'%','s',':','%','u',0};
3969             len = lstrlenW(hostName);
3970             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3971             session->hostName = heap_alloc(len*sizeof(WCHAR));
3972             sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3973         }
3974         else
3975             session->hostName = heap_strdupW(hostName);
3976
3977         HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3978
3979         heap_free(session->userName);
3980         session->userName = NULL;
3981         if (userName[0])
3982             session->userName = heap_strdupW(userName);
3983
3984         reset_data_stream(request);
3985
3986         if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3987             server_t *new_server;
3988
3989             new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3990             server_release(request->server);
3991             request->server = new_server;
3992         }
3993     }
3994     heap_free(request->path);
3995     request->path=NULL;
3996     if (*path)
3997     {
3998         DWORD needed = 0;
3999         HRESULT rc;
4000
4001         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
4002         if (rc != E_POINTER)
4003             needed = strlenW(path)+1;
4004         request->path = heap_alloc(needed*sizeof(WCHAR));
4005         rc = UrlEscapeW(path, request->path, &needed,
4006                         URL_ESCAPE_SPACES_ONLY);
4007         if (rc != S_OK)
4008         {
4009             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4010             strcpyW(request->path,path);
4011         }
4012     }
4013
4014     /* Remove custom content-type/length headers on redirects.  */
4015     index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
4016     if (0 <= index)
4017         HTTP_DeleteCustomHeader(request, index);
4018     index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
4019     if (0 <= index)
4020         HTTP_DeleteCustomHeader(request, index);
4021
4022     return ERROR_SUCCESS;
4023 }
4024
4025 /***********************************************************************
4026  *           HTTP_build_req (internal)
4027  *
4028  *  concatenate all the strings in the request together
4029  */
4030 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4031 {
4032     LPCWSTR *t;
4033     LPWSTR str;
4034
4035     for( t = list; *t ; t++  )
4036         len += strlenW( *t );
4037     len++;
4038
4039     str = heap_alloc(len*sizeof(WCHAR));
4040     *str = 0;
4041
4042     for( t = list; *t ; t++ )
4043         strcatW( str, *t );
4044
4045     return str;
4046 }
4047
4048 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
4049 {
4050     server_t *server = request->server;
4051     LPWSTR requestString;
4052     INT len;
4053     INT cnt;
4054     INT responseLen;
4055     char *ascii_req;
4056     DWORD res;
4057
4058     static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
4059
4060     TRACE("\n");
4061
4062     requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
4063
4064     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4065                                 NULL, 0, NULL, NULL );
4066     len--; /* the nul terminator isn't needed */
4067     ascii_req = heap_alloc(len);
4068     WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4069     heap_free( requestString );
4070
4071     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4072
4073     NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4074     res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4075     heap_free( ascii_req );
4076     if (res != ERROR_SUCCESS)
4077         return res;
4078
4079     responseLen = HTTP_GetResponseHeaders( request );
4080     if (!responseLen)
4081         return ERROR_HTTP_INVALID_HEADER;
4082
4083     return ERROR_SUCCESS;
4084 }
4085
4086 static void HTTP_InsertCookies(http_request_t *request)
4087 {
4088     DWORD cookie_size, size, cnt = 0;
4089     HTTPHEADERW *host;
4090     WCHAR *cookies;
4091
4092     static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4093
4094     host = HTTP_GetHeader(request, hostW);
4095     if(!host)
4096         return;
4097
4098     if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4099         return;
4100
4101     size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4102     if(!(cookies = heap_alloc(size)))
4103         return;
4104
4105     cnt += sprintfW(cookies, cookieW);
4106     get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4107     strcatW(cookies, szCrLf);
4108
4109     HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4110
4111     heap_free(cookies);
4112 }
4113
4114 static WORD HTTP_ParseWkday(LPCWSTR day)
4115 {
4116     static const WCHAR days[7][4] = {{ 's','u','n',0 },
4117                                      { 'm','o','n',0 },
4118                                      { 't','u','e',0 },
4119                                      { 'w','e','d',0 },
4120                                      { 't','h','u',0 },
4121                                      { 'f','r','i',0 },
4122                                      { 's','a','t',0 }};
4123     unsigned int i;
4124     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4125         if (!strcmpiW(day, days[i]))
4126             return i;
4127
4128     /* Invalid */
4129     return 7;
4130 }
4131
4132 static WORD HTTP_ParseMonth(LPCWSTR month)
4133 {
4134     static const WCHAR jan[] = { 'j','a','n',0 };
4135     static const WCHAR feb[] = { 'f','e','b',0 };
4136     static const WCHAR mar[] = { 'm','a','r',0 };
4137     static const WCHAR apr[] = { 'a','p','r',0 };
4138     static const WCHAR may[] = { 'm','a','y',0 };
4139     static const WCHAR jun[] = { 'j','u','n',0 };
4140     static const WCHAR jul[] = { 'j','u','l',0 };
4141     static const WCHAR aug[] = { 'a','u','g',0 };
4142     static const WCHAR sep[] = { 's','e','p',0 };
4143     static const WCHAR oct[] = { 'o','c','t',0 };
4144     static const WCHAR nov[] = { 'n','o','v',0 };
4145     static const WCHAR dec[] = { 'd','e','c',0 };
4146
4147     if (!strcmpiW(month, jan)) return 1;
4148     if (!strcmpiW(month, feb)) return 2;
4149     if (!strcmpiW(month, mar)) return 3;
4150     if (!strcmpiW(month, apr)) return 4;
4151     if (!strcmpiW(month, may)) return 5;
4152     if (!strcmpiW(month, jun)) return 6;
4153     if (!strcmpiW(month, jul)) return 7;
4154     if (!strcmpiW(month, aug)) return 8;
4155     if (!strcmpiW(month, sep)) return 9;
4156     if (!strcmpiW(month, oct)) return 10;
4157     if (!strcmpiW(month, nov)) return 11;
4158     if (!strcmpiW(month, dec)) return 12;
4159     /* Invalid */
4160     return 0;
4161 }
4162
4163 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4164  * optionally preceded by whitespace.
4165  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4166  * st, and sets *str to the first character after the time format.
4167  */
4168 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4169 {
4170     LPCWSTR ptr = *str;
4171     WCHAR *nextPtr;
4172     unsigned long num;
4173
4174     while (isspaceW(*ptr))
4175         ptr++;
4176
4177     num = strtoulW(ptr, &nextPtr, 10);
4178     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4179     {
4180         ERR("unexpected time format %s\n", debugstr_w(ptr));
4181         return FALSE;
4182     }
4183     if (num > 23)
4184     {
4185         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4186         return FALSE;
4187     }
4188     ptr = nextPtr + 1;
4189     st->wHour = (WORD)num;
4190     num = strtoulW(ptr, &nextPtr, 10);
4191     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4192     {
4193         ERR("unexpected time format %s\n", debugstr_w(ptr));
4194         return FALSE;
4195     }
4196     if (num > 59)
4197     {
4198         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4199         return FALSE;
4200     }
4201     ptr = nextPtr + 1;
4202     st->wMinute = (WORD)num;
4203     num = strtoulW(ptr, &nextPtr, 10);
4204     if (!nextPtr || nextPtr <= ptr)
4205     {
4206         ERR("unexpected time format %s\n", debugstr_w(ptr));
4207         return FALSE;
4208     }
4209     if (num > 59)
4210     {
4211         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4212         return FALSE;
4213     }
4214     ptr = nextPtr + 1;
4215     *str = ptr;
4216     st->wSecond = (WORD)num;
4217     return TRUE;
4218 }
4219
4220 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4221 {
4222     static const WCHAR gmt[]= { 'G','M','T',0 };
4223     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4224     LPCWSTR ptr;
4225     SYSTEMTIME st = { 0 };
4226     unsigned long num;
4227
4228     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4229          dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4230         *dayPtr = *ptr;
4231     *dayPtr = 0;
4232     st.wDayOfWeek = HTTP_ParseWkday(day);
4233     if (st.wDayOfWeek >= 7)
4234     {
4235         ERR("unexpected weekday %s\n", debugstr_w(day));
4236         return FALSE;
4237     }
4238
4239     while (isspaceW(*ptr))
4240         ptr++;
4241
4242     for (monthPtr = month; !isspace(*ptr) &&
4243          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4244          monthPtr++, ptr++)
4245         *monthPtr = *ptr;
4246     *monthPtr = 0;
4247     st.wMonth = HTTP_ParseMonth(month);
4248     if (!st.wMonth || st.wMonth > 12)
4249     {
4250         ERR("unexpected month %s\n", debugstr_w(month));
4251         return FALSE;
4252     }
4253
4254     while (isspaceW(*ptr))
4255         ptr++;
4256
4257     num = strtoulW(ptr, &nextPtr, 10);
4258     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4259     {
4260         ERR("unexpected day %s\n", debugstr_w(ptr));
4261         return FALSE;
4262     }
4263     ptr = nextPtr;
4264     st.wDay = (WORD)num;
4265
4266     while (isspaceW(*ptr))
4267         ptr++;
4268
4269     if (!HTTP_ParseTime(&st, &ptr))
4270         return FALSE;
4271
4272     while (isspaceW(*ptr))
4273         ptr++;
4274
4275     num = strtoulW(ptr, &nextPtr, 10);
4276     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4277     {
4278         ERR("unexpected year %s\n", debugstr_w(ptr));
4279         return FALSE;
4280     }
4281     ptr = nextPtr;
4282     st.wYear = (WORD)num;
4283
4284     while (isspaceW(*ptr))
4285         ptr++;
4286
4287     /* asctime() doesn't report a timezone, but some web servers do, so accept
4288      * with or without GMT.
4289      */
4290     if (*ptr && strcmpW(ptr, gmt))
4291     {
4292         ERR("unexpected timezone %s\n", debugstr_w(ptr));
4293         return FALSE;
4294     }
4295     return SystemTimeToFileTime(&st, ft);
4296 }
4297
4298 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4299 {
4300     static const WCHAR gmt[]= { 'G','M','T',0 };
4301     WCHAR *nextPtr, day[4], month[4], *monthPtr;
4302     LPCWSTR ptr;
4303     unsigned long num;
4304     SYSTEMTIME st = { 0 };
4305
4306     ptr = strchrW(value, ',');
4307     if (!ptr)
4308         return FALSE;
4309     if (ptr - value != 3)
4310     {
4311         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4312         return FALSE;
4313     }
4314     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4315     day[3] = 0;
4316     st.wDayOfWeek = HTTP_ParseWkday(day);
4317     if (st.wDayOfWeek > 6)
4318     {
4319         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4320         return FALSE;
4321     }
4322     ptr++;
4323
4324     while (isspaceW(*ptr))
4325         ptr++;
4326
4327     num = strtoulW(ptr, &nextPtr, 10);
4328     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4329     {
4330         WARN("unexpected day %s\n", debugstr_w(value));
4331         return FALSE;
4332     }
4333     ptr = nextPtr;
4334     st.wDay = (WORD)num;
4335
4336     while (isspaceW(*ptr))
4337         ptr++;
4338
4339     for (monthPtr = month; !isspace(*ptr) &&
4340          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4341          monthPtr++, ptr++)
4342         *monthPtr = *ptr;
4343     *monthPtr = 0;
4344     st.wMonth = HTTP_ParseMonth(month);
4345     if (!st.wMonth || st.wMonth > 12)
4346     {
4347         WARN("unexpected month %s\n", debugstr_w(month));
4348         return FALSE;
4349     }
4350
4351     while (isspaceW(*ptr))
4352         ptr++;
4353
4354     num = strtoulW(ptr, &nextPtr, 10);
4355     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4356     {
4357         ERR("unexpected year %s\n", debugstr_w(value));
4358         return FALSE;
4359     }
4360     ptr = nextPtr;
4361     st.wYear = (WORD)num;
4362
4363     if (!HTTP_ParseTime(&st, &ptr))
4364         return FALSE;
4365
4366     while (isspaceW(*ptr))
4367         ptr++;
4368
4369     if (strcmpW(ptr, gmt))
4370     {
4371         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4372         return FALSE;
4373     }
4374     return SystemTimeToFileTime(&st, ft);
4375 }
4376
4377 static WORD HTTP_ParseWeekday(LPCWSTR day)
4378 {
4379     static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4380                                      { 'm','o','n','d','a','y',0 },
4381                                      { 't','u','e','s','d','a','y',0 },
4382                                      { 'w','e','d','n','e','s','d','a','y',0 },
4383                                      { 't','h','u','r','s','d','a','y',0 },
4384                                      { 'f','r','i','d','a','y',0 },
4385                                      { 's','a','t','u','r','d','a','y',0 }};
4386     unsigned int i;
4387     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4388         if (!strcmpiW(day, days[i]))
4389             return i;
4390
4391     /* Invalid */
4392     return 7;
4393 }
4394
4395 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4396 {
4397     static const WCHAR gmt[]= { 'G','M','T',0 };
4398     WCHAR *nextPtr, day[10], month[4], *monthPtr;
4399     LPCWSTR ptr;
4400     unsigned long num;
4401     SYSTEMTIME st = { 0 };
4402
4403     ptr = strchrW(value, ',');
4404     if (!ptr)
4405         return FALSE;
4406     if (ptr - value == 3)
4407     {
4408         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4409         day[3] = 0;
4410         st.wDayOfWeek = HTTP_ParseWkday(day);
4411         if (st.wDayOfWeek > 6)
4412         {
4413             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4414             return FALSE;
4415         }
4416     }
4417     else if (ptr - value < sizeof(day) / sizeof(day[0]))
4418     {
4419         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4420         day[ptr - value + 1] = 0;
4421         st.wDayOfWeek = HTTP_ParseWeekday(day);
4422         if (st.wDayOfWeek > 6)
4423         {
4424             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4425             return FALSE;
4426         }
4427     }
4428     else
4429     {
4430         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4431         return FALSE;
4432     }
4433     ptr++;
4434
4435     while (isspaceW(*ptr))
4436         ptr++;
4437
4438     num = strtoulW(ptr, &nextPtr, 10);
4439     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4440     {
4441         ERR("unexpected day %s\n", debugstr_w(value));
4442         return FALSE;
4443     }
4444     ptr = nextPtr;
4445     st.wDay = (WORD)num;
4446
4447     if (*ptr != '-')
4448     {
4449         ERR("unexpected month format %s\n", debugstr_w(ptr));
4450         return FALSE;
4451     }
4452     ptr++;
4453
4454     for (monthPtr = month; *ptr != '-' &&
4455          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4456          monthPtr++, ptr++)
4457         *monthPtr = *ptr;
4458     *monthPtr = 0;
4459     st.wMonth = HTTP_ParseMonth(month);
4460     if (!st.wMonth || st.wMonth > 12)
4461     {
4462         ERR("unexpected month %s\n", debugstr_w(month));
4463         return FALSE;
4464     }
4465
4466     if (*ptr != '-')
4467     {
4468         ERR("unexpected year format %s\n", debugstr_w(ptr));
4469         return FALSE;
4470     }
4471     ptr++;
4472
4473     num = strtoulW(ptr, &nextPtr, 10);
4474     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4475     {
4476         ERR("unexpected year %s\n", debugstr_w(value));
4477         return FALSE;
4478     }
4479     ptr = nextPtr;
4480     st.wYear = (WORD)num;
4481
4482     if (!HTTP_ParseTime(&st, &ptr))
4483         return FALSE;
4484
4485     while (isspaceW(*ptr))
4486         ptr++;
4487
4488     if (strcmpW(ptr, gmt))
4489     {
4490         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4491         return FALSE;
4492     }
4493     return SystemTimeToFileTime(&st, ft);
4494 }
4495
4496 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4497 {
4498     static const WCHAR zero[] = { '0',0 };
4499     BOOL ret;
4500
4501     if (!strcmpW(value, zero))
4502     {
4503         ft->dwLowDateTime = ft->dwHighDateTime = 0;
4504         ret = TRUE;
4505     }
4506     else if (strchrW(value, ','))
4507     {
4508         ret = HTTP_ParseRfc1123Date(value, ft);
4509         if (!ret)
4510         {
4511             ret = HTTP_ParseRfc850Date(value, ft);
4512             if (!ret)
4513                 ERR("unexpected date format %s\n", debugstr_w(value));
4514         }
4515     }
4516     else
4517     {
4518         ret = HTTP_ParseDateAsAsctime(value, ft);
4519         if (!ret)
4520             ERR("unexpected date format %s\n", debugstr_w(value));
4521     }
4522     return ret;
4523 }
4524
4525 static void HTTP_ProcessExpires(http_request_t *request)
4526 {
4527     BOOL expirationFound = FALSE;
4528     int headerIndex;
4529
4530     /* Look for a Cache-Control header with a max-age directive, as it takes
4531      * precedence over the Expires header.
4532      */
4533     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4534     if (headerIndex != -1)
4535     {
4536         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4537         LPWSTR ptr;
4538
4539         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4540         {
4541             LPWSTR comma = strchrW(ptr, ','), end, equal;
4542
4543             if (comma)
4544                 end = comma;
4545             else
4546                 end = ptr + strlenW(ptr);
4547             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4548                 ;
4549             if (*equal == '=')
4550             {
4551                 static const WCHAR max_age[] = {
4552                     'm','a','x','-','a','g','e',0 };
4553
4554                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4555                 {
4556                     LPWSTR nextPtr;
4557                     unsigned long age;
4558
4559                     age = strtoulW(equal + 1, &nextPtr, 10);
4560                     if (nextPtr > equal + 1)
4561                     {
4562                         LARGE_INTEGER ft;
4563
4564                         NtQuerySystemTime( &ft );
4565                         /* Age is in seconds, FILETIME resolution is in
4566                          * 100 nanosecond intervals.
4567                          */
4568                         ft.QuadPart += age * (ULONGLONG)1000000;
4569                         request->expires.dwLowDateTime = ft.u.LowPart;
4570                         request->expires.dwHighDateTime = ft.u.HighPart;
4571                         expirationFound = TRUE;
4572                     }
4573                 }
4574             }
4575             if (comma)
4576             {
4577                 ptr = comma + 1;
4578                 while (isspaceW(*ptr))
4579                     ptr++;
4580             }
4581             else
4582                 ptr = NULL;
4583         }
4584     }
4585     if (!expirationFound)
4586     {
4587         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4588         if (headerIndex != -1)
4589         {
4590             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4591             FILETIME ft;
4592
4593             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4594             {
4595                 expirationFound = TRUE;
4596                 request->expires = ft;
4597             }
4598         }
4599     }
4600     if (!expirationFound)
4601     {
4602         LARGE_INTEGER t;
4603
4604         /* With no known age, default to 10 minutes until expiration. */
4605         NtQuerySystemTime( &t );
4606         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4607         request->expires.dwLowDateTime = t.u.LowPart;
4608         request->expires.dwHighDateTime = t.u.HighPart;
4609     }
4610 }
4611
4612 static void HTTP_ProcessLastModified(http_request_t *request)
4613 {
4614     int headerIndex;
4615
4616     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4617     if (headerIndex != -1)
4618     {
4619         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4620         FILETIME ft;
4621
4622         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4623             request->last_modified = ft;
4624     }
4625 }
4626
4627 static void http_process_keep_alive(http_request_t *req)
4628 {
4629     int index;
4630
4631     if ((index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE)) != -1)
4632         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4633     else if ((index = HTTP_GetCustomHeaderIndex(req, szProxy_Connection, 0, FALSE)) != -1)
4634         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4635     else
4636         req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4637 }
4638
4639 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4640 {
4641     const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4642     netconn_t *netconn = NULL;
4643     DWORD res;
4644
4645     assert(!request->netconn);
4646     reset_data_stream(request);
4647
4648     res = HTTP_ResolveName(request);
4649     if(res != ERROR_SUCCESS)
4650         return res;
4651
4652     EnterCriticalSection(&connection_pool_cs);
4653
4654     while(!list_empty(&request->server->conn_pool)) {
4655         netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4656         list_remove(&netconn->pool_entry);
4657
4658         if(NETCON_is_alive(netconn))
4659             break;
4660
4661         TRACE("connection %p closed during idle\n", netconn);
4662         free_netconn(netconn);
4663         netconn = NULL;
4664     }
4665
4666     LeaveCriticalSection(&connection_pool_cs);
4667
4668     if(netconn) {
4669         TRACE("<-- reusing %p netconn\n", netconn);
4670         request->netconn = netconn;
4671         *reusing = TRUE;
4672         return ERROR_SUCCESS;
4673     }
4674
4675     TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4676           request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4677
4678     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4679                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4680                           request->server->addr_str,
4681                           strlen(request->server->addr_str)+1);
4682
4683     res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4684                          (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4685                          request->connect_timeout, &netconn);
4686     if(res != ERROR_SUCCESS) {
4687         ERR("create_netconn failed: %u\n", res);
4688         return res;
4689     }
4690
4691     request->netconn = netconn;
4692
4693     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4694             INTERNET_STATUS_CONNECTED_TO_SERVER,
4695             request->server->addr_str, strlen(request->server->addr_str)+1);
4696
4697     if(is_https) {
4698         /* Note: we differ from Microsoft's WinINet here. they seem to have
4699          * a bug that causes no status callbacks to be sent when starting
4700          * a tunnel to a proxy server using the CONNECT verb. i believe our
4701          * behaviour to be more correct and to not cause any incompatibilities
4702          * because using a secure connection through a proxy server is a rare
4703          * case that would be hard for anyone to depend on */
4704         if(request->proxy)
4705             res = HTTP_SecureProxyConnect(request);
4706         if(res == ERROR_SUCCESS)
4707             res = NETCON_secure_connect(request->netconn, request->server);
4708     }
4709
4710     if(res != ERROR_SUCCESS) {
4711         http_release_netconn(request, FALSE);
4712         return res;
4713     }
4714
4715     *reusing = FALSE;
4716     TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4717     return ERROR_SUCCESS;
4718 }
4719
4720 /***********************************************************************
4721  *           HTTP_HttpSendRequestW (internal)
4722  *
4723  * Sends the specified request to the HTTP server
4724  *
4725  * RETURNS
4726  *    ERROR_SUCCESS on success
4727  *    win32 error code on failure
4728  *
4729  */
4730 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4731         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4732         DWORD dwContentLength, BOOL bEndRequest)
4733 {
4734     INT cnt;
4735     BOOL redirected = FALSE;
4736     LPWSTR requestString = NULL;
4737     INT responseLen;
4738     BOOL loop_next;
4739     static const WCHAR szPost[] = { 'P','O','S','T',0 };
4740     static const WCHAR szContentLength[] =
4741         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4742     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4743     DWORD res;
4744
4745     TRACE("--> %p\n", request);
4746
4747     assert(request->hdr.htype == WH_HHTTPREQ);
4748
4749     /* if the verb is NULL default to GET */
4750     if (!request->verb)
4751         request->verb = heap_strdupW(szGET);
4752
4753     if (dwContentLength || strcmpW(request->verb, szGET))
4754     {
4755         sprintfW(contentLengthStr, szContentLength, dwContentLength);
4756         HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4757         request->bytesToWrite = dwContentLength;
4758     }
4759     if (request->session->appInfo->agent)
4760     {
4761         WCHAR *agent_header;
4762         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4763         int len;
4764
4765         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4766         agent_header = heap_alloc(len * sizeof(WCHAR));
4767         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4768
4769         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4770         heap_free(agent_header);
4771     }
4772     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4773     {
4774         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4775         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4776     }
4777     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4778     {
4779         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4780                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4781         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4782     }
4783
4784     /* add the headers the caller supplied */
4785     if( lpszHeaders && dwHeaderLength )
4786         HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4787
4788     do
4789     {
4790         DWORD len;
4791         BOOL reusing_connection;
4792         char *ascii_req;
4793
4794         loop_next = FALSE;
4795         reusing_connection = request->netconn != NULL;
4796
4797         if(redirected) {
4798             request->contentLength = ~0u;
4799             request->bytesToWrite = 0;
4800         }
4801
4802         if (TRACE_ON(wininet))
4803         {
4804             LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4805             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4806         }
4807
4808         HTTP_FixURL(request);
4809         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4810         {
4811             HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4812         }
4813         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4814         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4815
4816         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4817             HTTP_InsertCookies(request);
4818
4819         if (request->proxy)
4820         {
4821             WCHAR *url = build_proxy_path_url(request);
4822             requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4823             heap_free(url);
4824         }
4825         else
4826             requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4827
4828  
4829         TRACE("Request header -> %s\n", debugstr_w(requestString) );
4830
4831         if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4832             break;
4833
4834         /* send the request as ASCII, tack on the optional data */
4835         if (!lpOptional || redirected)
4836             dwOptionalLength = 0;
4837         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4838                                    NULL, 0, NULL, NULL );
4839         ascii_req = heap_alloc(len + dwOptionalLength);
4840         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4841                              ascii_req, len, NULL, NULL );
4842         if( lpOptional )
4843             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4844         len = (len + dwOptionalLength - 1);
4845         ascii_req[len] = 0;
4846         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4847
4848         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4849                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4850
4851         NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4852         res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4853         heap_free( ascii_req );
4854         if(res != ERROR_SUCCESS) {
4855             TRACE("send failed: %u\n", res);
4856             if(!reusing_connection)
4857                 break;
4858             http_release_netconn(request, FALSE);
4859             loop_next = TRUE;
4860             continue;
4861         }
4862
4863         request->bytesWritten = dwOptionalLength;
4864
4865         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4866                               INTERNET_STATUS_REQUEST_SENT,
4867                               &len, sizeof(DWORD));
4868
4869         if (bEndRequest)
4870         {
4871             DWORD dwBufferSize;
4872
4873             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4874                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4875     
4876             responseLen = HTTP_GetResponseHeaders(request);
4877             /* FIXME: We should know that connection is closed before sending
4878              * headers. Otherwise wrong callbacks are executed */
4879             if(!responseLen && reusing_connection) {
4880                 TRACE("Connection closed by server, reconnecting\n");
4881                 http_release_netconn(request, FALSE);
4882                 loop_next = TRUE;
4883                 continue;
4884             }
4885
4886             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4887                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4888                                 sizeof(DWORD));
4889
4890             http_process_keep_alive(request);
4891             HTTP_ProcessCookies(request);
4892             HTTP_ProcessExpires(request);
4893             HTTP_ProcessLastModified(request);
4894
4895             res = set_content_length(request);
4896             if(res != ERROR_SUCCESS)
4897                 goto lend;
4898             if(!request->contentLength)
4899                 http_release_netconn(request, TRUE);
4900
4901             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4902             {
4903                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4904                 dwBufferSize=sizeof(szNewLocation);
4905                 switch(request->status_code) {
4906                 case HTTP_STATUS_REDIRECT:
4907                 case HTTP_STATUS_MOVED:
4908                 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4909                 case HTTP_STATUS_REDIRECT_METHOD:
4910                     if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4911                         break;
4912
4913                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4914                         request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4915                     {
4916                         heap_free(request->verb);
4917                         request->verb = heap_strdupW(szGET);
4918                     }
4919                     http_release_netconn(request, drain_content(request, FALSE));
4920                     if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4921                     {
4922                         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4923                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4924                         res = HTTP_HandleRedirect(request, new_url);
4925                         if (res == ERROR_SUCCESS)
4926                         {
4927                             heap_free(requestString);
4928                             loop_next = TRUE;
4929                         }
4930                         heap_free( new_url );
4931                     }
4932                     redirected = TRUE;
4933                 }
4934             }
4935             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4936             {
4937                 WCHAR szAuthValue[2048];
4938                 dwBufferSize=2048;
4939                 if (request->status_code == HTTP_STATUS_DENIED)
4940                 {
4941                     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4942                     DWORD dwIndex = 0;
4943                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4944                     {
4945                         if (HTTP_DoAuthorization(request, szAuthValue,
4946                                                  &request->authInfo,
4947                                                  request->session->userName,
4948                                                  request->session->password,
4949                                                  Host->lpszValue))
4950                         {
4951                             heap_free(requestString);
4952                             if(!drain_content(request, TRUE)) {
4953                                 FIXME("Could not drain content\n");
4954                                 http_release_netconn(request, FALSE);
4955                             }
4956                             loop_next = TRUE;
4957                             break;
4958                         }
4959                     }
4960
4961                     if(!loop_next) {
4962                         TRACE("Cleaning wrong authorization data\n");
4963                         destroy_authinfo(request->authInfo);
4964                         request->authInfo = NULL;
4965                     }
4966                 }
4967                 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4968                 {
4969                     DWORD dwIndex = 0;
4970                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4971                     {
4972                         if (HTTP_DoAuthorization(request, szAuthValue,
4973                                                  &request->proxyAuthInfo,
4974                                                  request->session->appInfo->proxyUsername,
4975                                                  request->session->appInfo->proxyPassword,
4976                                                  NULL))
4977                         {
4978                             heap_free(requestString);
4979                             if(!drain_content(request, TRUE)) {
4980                                 FIXME("Could not drain content\n");
4981                                 http_release_netconn(request, FALSE);
4982                             }
4983                             loop_next = TRUE;
4984                             break;
4985                         }
4986                     }
4987
4988                     if(!loop_next) {
4989                         TRACE("Cleaning wrong proxy authorization data\n");
4990                         destroy_authinfo(request->proxyAuthInfo);
4991                         request->proxyAuthInfo = NULL;
4992                     }
4993                 }
4994             }
4995         }
4996         else
4997             res = ERROR_SUCCESS;
4998     }
4999     while (loop_next);
5000
5001 lend:
5002     heap_free(requestString);
5003
5004     /* TODO: send notification for P3P header */
5005
5006     if(res == ERROR_SUCCESS)
5007         create_cache_entry(request);
5008
5009     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5010     {
5011         if (res == ERROR_SUCCESS) {
5012             if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5013                 HTTP_ReceiveRequestData(request, TRUE, NULL);
5014             else
5015                 send_request_complete(request,
5016                         request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5017         }else {
5018                 send_request_complete(request, 0, res);
5019         }
5020     }
5021
5022     TRACE("<--\n");
5023     return res;
5024 }
5025
5026 typedef struct {
5027     task_header_t hdr;
5028     WCHAR *headers;
5029     DWORD  headers_len;
5030     void  *optional;
5031     DWORD  optional_len;
5032     DWORD  content_len;
5033     BOOL   end_request;
5034 } send_request_task_t;
5035
5036 /***********************************************************************
5037  *
5038  * Helper functions for the HttpSendRequest(Ex) functions
5039  *
5040  */
5041 static void AsyncHttpSendRequestProc(task_header_t *hdr)
5042 {
5043     send_request_task_t *task = (send_request_task_t*)hdr;
5044     http_request_t *request = (http_request_t*)task->hdr.hdr;
5045
5046     TRACE("%p\n", request);
5047
5048     HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
5049             task->optional_len, task->content_len, task->end_request);
5050
5051     heap_free(task->headers);
5052 }
5053
5054
5055 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5056 {
5057     DWORD dwBufferSize;
5058     INT responseLen;
5059     DWORD res = ERROR_SUCCESS;
5060
5061     if(!request->netconn) {
5062         WARN("Not connected\n");
5063         send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5064         return ERROR_INTERNET_OPERATION_CANCELLED;
5065     }
5066
5067     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5068                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5069
5070     responseLen = HTTP_GetResponseHeaders(request);
5071     if (!responseLen)
5072         res = ERROR_HTTP_HEADER_NOT_FOUND;
5073
5074     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5075                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5076
5077     /* process cookies here. Is this right? */
5078     http_process_keep_alive(request);
5079     HTTP_ProcessCookies(request);
5080     HTTP_ProcessExpires(request);
5081     HTTP_ProcessLastModified(request);
5082
5083     if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5084         if(!request->contentLength)
5085             http_release_netconn(request, TRUE);
5086     }
5087
5088     if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5089     {
5090         switch(request->status_code) {
5091         case HTTP_STATUS_REDIRECT:
5092         case HTTP_STATUS_MOVED:
5093         case HTTP_STATUS_REDIRECT_METHOD:
5094         case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5095             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5096             dwBufferSize=sizeof(szNewLocation);
5097             if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5098                 break;
5099
5100             if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5101                 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5102             {
5103                 heap_free(request->verb);
5104                 request->verb = heap_strdupW(szGET);
5105             }
5106             http_release_netconn(request, drain_content(request, FALSE));
5107             if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5108             {
5109                 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5110                                       new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5111                 res = HTTP_HandleRedirect(request, new_url);
5112                 if (res == ERROR_SUCCESS)
5113                     res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5114                 heap_free( new_url );
5115             }
5116         }
5117         }
5118     }
5119
5120     if(res == ERROR_SUCCESS)
5121         create_cache_entry(request);
5122
5123     if (res == ERROR_SUCCESS && request->contentLength)
5124         HTTP_ReceiveRequestData(request, TRUE, NULL);
5125     else
5126         send_request_complete(request, res == ERROR_SUCCESS, res);
5127
5128     return res;
5129 }
5130
5131 /***********************************************************************
5132  *           HttpEndRequestA (WININET.@)
5133  *
5134  * Ends an HTTP request that was started by HttpSendRequestEx
5135  *
5136  * RETURNS
5137  *    TRUE      if successful
5138  *    FALSE     on failure
5139  *
5140  */
5141 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5142         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5143 {
5144     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5145
5146     if (lpBuffersOut)
5147     {
5148         SetLastError(ERROR_INVALID_PARAMETER);
5149         return FALSE;
5150     }
5151
5152     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5153 }
5154
5155 typedef struct {
5156     task_header_t hdr;
5157     DWORD flags;
5158     DWORD context;
5159 } end_request_task_t;
5160
5161 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5162 {
5163     end_request_task_t *task = (end_request_task_t*)hdr;
5164     http_request_t *req = (http_request_t*)task->hdr.hdr;
5165
5166     TRACE("%p\n", req);
5167
5168     HTTP_HttpEndRequestW(req, task->flags, task->context);
5169 }
5170
5171 /***********************************************************************
5172  *           HttpEndRequestW (WININET.@)
5173  *
5174  * Ends an HTTP request that was started by HttpSendRequestEx
5175  *
5176  * RETURNS
5177  *    TRUE      if successful
5178  *    FALSE     on failure
5179  *
5180  */
5181 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5182         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5183 {
5184     http_request_t *request;
5185     DWORD res;
5186
5187     TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5188
5189     if (lpBuffersOut)
5190     {
5191         SetLastError(ERROR_INVALID_PARAMETER);
5192         return FALSE;
5193     }
5194
5195     request = (http_request_t*) get_handle_object( hRequest );
5196
5197     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5198     {
5199         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5200         if (request)
5201             WININET_Release( &request->hdr );
5202         return FALSE;
5203     }
5204     request->hdr.dwFlags |= dwFlags;
5205
5206     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5207     {
5208         end_request_task_t *task;
5209
5210         task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5211         task->flags = dwFlags;
5212         task->context = dwContext;
5213
5214         INTERNET_AsyncCall(&task->hdr);
5215         res = ERROR_IO_PENDING;
5216     }
5217     else
5218         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5219
5220     WININET_Release( &request->hdr );
5221     TRACE("%u <--\n", res);
5222     if(res != ERROR_SUCCESS)
5223         SetLastError(res);
5224     return res == ERROR_SUCCESS;
5225 }
5226
5227 /***********************************************************************
5228  *           HttpSendRequestExA (WININET.@)
5229  *
5230  * Sends the specified request to the HTTP server and allows chunked
5231  * transfers.
5232  *
5233  * RETURNS
5234  *  Success: TRUE
5235  *  Failure: FALSE, call GetLastError() for more information.
5236  */
5237 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5238                                LPINTERNET_BUFFERSA lpBuffersIn,
5239                                LPINTERNET_BUFFERSA lpBuffersOut,
5240                                DWORD dwFlags, DWORD_PTR dwContext)
5241 {
5242     INTERNET_BUFFERSW BuffersInW;
5243     BOOL rc = FALSE;
5244     DWORD headerlen;
5245     LPWSTR header = NULL;
5246
5247     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5248             lpBuffersOut, dwFlags, dwContext);
5249
5250     if (lpBuffersIn)
5251     {
5252         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5253         if (lpBuffersIn->lpcszHeader)
5254         {
5255             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5256                     lpBuffersIn->dwHeadersLength,0,0);
5257             header = heap_alloc(headerlen*sizeof(WCHAR));
5258             if (!(BuffersInW.lpcszHeader = header))
5259             {
5260                 SetLastError(ERROR_OUTOFMEMORY);
5261                 return FALSE;
5262             }
5263             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5264                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5265                     header, headerlen);
5266         }
5267         else
5268             BuffersInW.lpcszHeader = NULL;
5269         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5270         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5271         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5272         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5273         BuffersInW.Next = NULL;
5274     }
5275
5276     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5277
5278     heap_free(header);
5279     return rc;
5280 }
5281
5282 /***********************************************************************
5283  *           HttpSendRequestExW (WININET.@)
5284  *
5285  * Sends the specified request to the HTTP server and allows chunked
5286  * transfers
5287  *
5288  * RETURNS
5289  *  Success: TRUE
5290  *  Failure: FALSE, call GetLastError() for more information.
5291  */
5292 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5293                    LPINTERNET_BUFFERSW lpBuffersIn,
5294                    LPINTERNET_BUFFERSW lpBuffersOut,
5295                    DWORD dwFlags, DWORD_PTR dwContext)
5296 {
5297     http_request_t *request;
5298     http_session_t *session;
5299     appinfo_t *hIC;
5300     DWORD res;
5301
5302     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5303             lpBuffersOut, dwFlags, dwContext);
5304
5305     request = (http_request_t*) get_handle_object( hRequest );
5306
5307     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5308     {
5309         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5310         goto lend;
5311     }
5312
5313     session = request->session;
5314     assert(session->hdr.htype == WH_HHTTPSESSION);
5315     hIC = session->appInfo;
5316     assert(hIC->hdr.htype == WH_HINIT);
5317
5318     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5319     {
5320         send_request_task_t *task;
5321
5322         task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5323         if (lpBuffersIn)
5324         {
5325             DWORD size = 0;
5326
5327             if (lpBuffersIn->lpcszHeader)
5328             {
5329                 if (lpBuffersIn->dwHeadersLength == ~0u)
5330                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5331                 else
5332                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5333
5334                 task->headers = heap_alloc(size);
5335                 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5336             }
5337             else task->headers = NULL;
5338
5339             task->headers_len = size / sizeof(WCHAR);
5340             task->optional = lpBuffersIn->lpvBuffer;
5341             task->optional_len = lpBuffersIn->dwBufferLength;
5342             task->content_len = lpBuffersIn->dwBufferTotal;
5343         }
5344         else
5345         {
5346             task->headers = NULL;
5347             task->headers_len = 0;
5348             task->optional = NULL;
5349             task->optional_len = 0;
5350             task->content_len = 0;
5351         }
5352
5353         task->end_request = FALSE;
5354
5355         INTERNET_AsyncCall(&task->hdr);
5356         res = ERROR_IO_PENDING;
5357     }
5358     else
5359     {
5360         if (lpBuffersIn)
5361             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5362                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5363                                         lpBuffersIn->dwBufferTotal, FALSE);
5364         else
5365             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5366     }
5367
5368 lend:
5369     if ( request )
5370         WININET_Release( &request->hdr );
5371
5372     TRACE("<---\n");
5373     SetLastError(res);
5374     return res == ERROR_SUCCESS;
5375 }
5376
5377 /***********************************************************************
5378  *           HttpSendRequestW (WININET.@)
5379  *
5380  * Sends the specified request to the HTTP server
5381  *
5382  * RETURNS
5383  *    TRUE  on success
5384  *    FALSE on failure
5385  *
5386  */
5387 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5388         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5389 {
5390     http_request_t *request;
5391     http_session_t *session = NULL;
5392     appinfo_t *hIC = NULL;
5393     DWORD res = ERROR_SUCCESS;
5394
5395     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5396             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5397
5398     request = (http_request_t*) get_handle_object( hHttpRequest );
5399     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5400     {
5401         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5402         goto lend;
5403     }
5404
5405     session = request->session;
5406     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
5407     {
5408         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5409         goto lend;
5410     }
5411
5412     hIC = session->appInfo;
5413     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
5414     {
5415         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5416         goto lend;
5417     }
5418
5419     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5420     {
5421         send_request_task_t *task;
5422
5423         task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5424         if (lpszHeaders)
5425         {
5426             DWORD size;
5427
5428             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5429             else size = dwHeaderLength * sizeof(WCHAR);
5430
5431             task->headers = heap_alloc(size);
5432             memcpy(task->headers, lpszHeaders, size);
5433         }
5434         else
5435             task->headers = NULL;
5436         task->headers_len = dwHeaderLength;
5437         task->optional = lpOptional;
5438         task->optional_len = dwOptionalLength;
5439         task->content_len = dwOptionalLength;
5440         task->end_request = TRUE;
5441
5442         INTERNET_AsyncCall(&task->hdr);
5443         res = ERROR_IO_PENDING;
5444     }
5445     else
5446     {
5447         res = HTTP_HttpSendRequestW(request, lpszHeaders,
5448                 dwHeaderLength, lpOptional, dwOptionalLength,
5449                 dwOptionalLength, TRUE);
5450     }
5451 lend:
5452     if( request )
5453         WININET_Release( &request->hdr );
5454
5455     SetLastError(res);
5456     return res == ERROR_SUCCESS;
5457 }
5458
5459 /***********************************************************************
5460  *           HttpSendRequestA (WININET.@)
5461  *
5462  * Sends the specified request to the HTTP server
5463  *
5464  * RETURNS
5465  *    TRUE  on success
5466  *    FALSE on failure
5467  *
5468  */
5469 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5470         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5471 {
5472     BOOL result;
5473     LPWSTR szHeaders=NULL;
5474     DWORD nLen=dwHeaderLength;
5475     if(lpszHeaders!=NULL)
5476     {
5477         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5478         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5479         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5480     }
5481     result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5482     heap_free(szHeaders);
5483     return result;
5484 }
5485
5486 /***********************************************************************
5487  *           HTTPSESSION_Destroy (internal)
5488  *
5489  * Deallocate session handle
5490  *
5491  */
5492 static void HTTPSESSION_Destroy(object_header_t *hdr)
5493 {
5494     http_session_t *session = (http_session_t*) hdr;
5495
5496     TRACE("%p\n", session);
5497
5498     WININET_Release(&session->appInfo->hdr);
5499
5500     heap_free(session->hostName);
5501     heap_free(session->password);
5502     heap_free(session->userName);
5503 }
5504
5505 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5506 {
5507     http_session_t *ses = (http_session_t *)hdr;
5508
5509     switch(option) {
5510     case INTERNET_OPTION_HANDLE_TYPE:
5511         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5512
5513         if (*size < sizeof(ULONG))
5514             return ERROR_INSUFFICIENT_BUFFER;
5515
5516         *size = sizeof(DWORD);
5517         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5518         return ERROR_SUCCESS;
5519     case INTERNET_OPTION_CONNECT_TIMEOUT:
5520         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5521
5522         if (*size < sizeof(DWORD))
5523             return ERROR_INSUFFICIENT_BUFFER;
5524
5525         *size = sizeof(DWORD);
5526         *(DWORD *)buffer = ses->connect_timeout;
5527         return ERROR_SUCCESS;
5528
5529     case INTERNET_OPTION_SEND_TIMEOUT:
5530         TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5531
5532         if (*size < sizeof(DWORD))
5533             return ERROR_INSUFFICIENT_BUFFER;
5534
5535         *size = sizeof(DWORD);
5536         *(DWORD *)buffer = ses->send_timeout;
5537         return ERROR_SUCCESS;
5538
5539     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5540         TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5541
5542         if (*size < sizeof(DWORD))
5543             return ERROR_INSUFFICIENT_BUFFER;
5544
5545         *size = sizeof(DWORD);
5546         *(DWORD *)buffer = ses->receive_timeout;
5547         return ERROR_SUCCESS;
5548     }
5549
5550     return INET_QueryOption(hdr, option, buffer, size, unicode);
5551 }
5552
5553 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5554 {
5555     http_session_t *ses = (http_session_t*)hdr;
5556
5557     switch(option) {
5558     case INTERNET_OPTION_USERNAME:
5559     {
5560         heap_free(ses->userName);
5561         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5562         return ERROR_SUCCESS;
5563     }
5564     case INTERNET_OPTION_PASSWORD:
5565     {
5566         heap_free(ses->password);
5567         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5568         return ERROR_SUCCESS;
5569     }
5570     case INTERNET_OPTION_PROXY_USERNAME:
5571     {
5572         heap_free(ses->appInfo->proxyUsername);
5573         if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5574         return ERROR_SUCCESS;
5575     }
5576     case INTERNET_OPTION_PROXY_PASSWORD:
5577     {
5578         heap_free(ses->appInfo->proxyPassword);
5579         if (!(ses->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5580         return ERROR_SUCCESS;
5581     }
5582     case INTERNET_OPTION_CONNECT_TIMEOUT:
5583     {
5584         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5585         ses->connect_timeout = *(DWORD *)buffer;
5586         return ERROR_SUCCESS;
5587     }
5588     case INTERNET_OPTION_SEND_TIMEOUT:
5589     {
5590         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5591         ses->send_timeout = *(DWORD *)buffer;
5592         return ERROR_SUCCESS;
5593     }
5594     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5595     {
5596         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5597         ses->receive_timeout = *(DWORD *)buffer;
5598         return ERROR_SUCCESS;
5599     }
5600     default: break;
5601     }
5602
5603     return INET_SetOption(hdr, option, buffer, size);
5604 }
5605
5606 static const object_vtbl_t HTTPSESSIONVtbl = {
5607     HTTPSESSION_Destroy,
5608     NULL,
5609     HTTPSESSION_QueryOption,
5610     HTTPSESSION_SetOption,
5611     NULL,
5612     NULL,
5613     NULL,
5614     NULL,
5615     NULL
5616 };
5617
5618
5619 /***********************************************************************
5620  *           HTTP_Connect  (internal)
5621  *
5622  * Create http session handle
5623  *
5624  * RETURNS
5625  *   HINTERNET a session handle on success
5626  *   NULL on failure
5627  *
5628  */
5629 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5630         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5631         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5632         DWORD dwInternalFlags, HINTERNET *ret)
5633 {
5634     http_session_t *session = NULL;
5635
5636     TRACE("-->\n");
5637
5638     if (!lpszServerName || !lpszServerName[0])
5639         return ERROR_INVALID_PARAMETER;
5640
5641     assert( hIC->hdr.htype == WH_HINIT );
5642
5643     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5644     if (!session)
5645         return ERROR_OUTOFMEMORY;
5646
5647    /*
5648     * According to my tests. The name is not resolved until a request is sent
5649     */
5650
5651     session->hdr.htype = WH_HHTTPSESSION;
5652     session->hdr.dwFlags = dwFlags;
5653     session->hdr.dwContext = dwContext;
5654     session->hdr.dwInternalFlags |= dwInternalFlags;
5655
5656     WININET_AddRef( &hIC->hdr );
5657     session->appInfo = hIC;
5658     list_add_head( &hIC->hdr.children, &session->hdr.entry );
5659
5660     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5661         if(hIC->proxyBypass)
5662             FIXME("Proxy bypass is ignored.\n");
5663     }
5664     session->hostName = heap_strdupW(lpszServerName);
5665     if (lpszUserName && lpszUserName[0])
5666         session->userName = heap_strdupW(lpszUserName);
5667     if (lpszPassword && lpszPassword[0])
5668         session->password = heap_strdupW(lpszPassword);
5669     session->hostPort = serverPort;
5670     session->connect_timeout = hIC->connect_timeout;
5671     session->send_timeout = INFINITE;
5672     session->receive_timeout = INFINITE;
5673
5674     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5675     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5676     {
5677         INTERNET_SendCallback(&hIC->hdr, dwContext,
5678                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5679                               sizeof(HINTERNET));
5680     }
5681
5682 /*
5683  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5684  * windows
5685  */
5686
5687     TRACE("%p --> %p\n", hIC, session);
5688
5689     *ret = session->hdr.hInternet;
5690     return ERROR_SUCCESS;
5691 }
5692
5693 /***********************************************************************
5694  *           HTTP_clear_response_headers (internal)
5695  *
5696  * clear out any old response headers
5697  */
5698 static void HTTP_clear_response_headers( http_request_t *request )
5699 {
5700     DWORD i;
5701
5702     for( i=0; i<request->nCustHeaders; i++)
5703     {
5704         if( !request->custHeaders[i].lpszField )
5705             continue;
5706         if( !request->custHeaders[i].lpszValue )
5707             continue;
5708         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5709             continue;
5710         HTTP_DeleteCustomHeader( request, i );
5711         i--;
5712     }
5713 }
5714
5715 /***********************************************************************
5716  *           HTTP_GetResponseHeaders (internal)
5717  *
5718  * Read server response
5719  *
5720  * RETURNS
5721  *
5722  *   TRUE  on success
5723  *   FALSE on error
5724  */
5725 static INT HTTP_GetResponseHeaders(http_request_t *request)
5726 {
5727     INT cbreaks = 0;
5728     WCHAR buffer[MAX_REPLY_LEN];
5729     DWORD buflen = MAX_REPLY_LEN;
5730     BOOL bSuccess = FALSE;
5731     INT  rc = 0;
5732     char bufferA[MAX_REPLY_LEN];
5733     LPWSTR status_code = NULL, status_text = NULL;
5734     DWORD cchMaxRawHeaders = 1024;
5735     LPWSTR lpszRawHeaders = NULL;
5736     LPWSTR temp;
5737     DWORD cchRawHeaders = 0;
5738     BOOL codeHundred = FALSE;
5739
5740     TRACE("-->\n");
5741
5742     if(!request->netconn)
5743         goto lend;
5744
5745     /* clear old response headers (eg. from a redirect response) */
5746     HTTP_clear_response_headers( request );
5747
5748     NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5749     do {
5750         /*
5751          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5752          */
5753         buflen = MAX_REPLY_LEN;
5754         if (!read_line(request, bufferA, &buflen))
5755             goto lend;
5756
5757         rc += buflen;
5758         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5759         /* check is this a status code line? */
5760         if (!strncmpW(buffer, g_szHttp1_0, 4))
5761         {
5762             /* split the version from the status code */
5763             status_code = strchrW( buffer, ' ' );
5764             if( !status_code )
5765                 goto lend;
5766             *status_code++=0;
5767
5768             /* split the status code from the status text */
5769             status_text = strchrW( status_code, ' ' );
5770             if( !status_text )
5771                 goto lend;
5772             *status_text++=0;
5773
5774             request->status_code = atoiW(status_code);
5775
5776             TRACE("version [%s] status code [%s] status text [%s]\n",
5777                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5778
5779             codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5780         }
5781         else if (!codeHundred)
5782         {
5783             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5784
5785             heap_free(request->version);
5786             heap_free(request->statusText);
5787
5788             request->status_code = HTTP_STATUS_OK;
5789             request->version = heap_strdupW(g_szHttp1_0);
5790             request->statusText = heap_strdupW(szOK);
5791
5792             heap_free(request->rawHeaders);
5793             request->rawHeaders = heap_strdupW(szDefaultHeader);
5794
5795             bSuccess = TRUE;
5796             goto lend;
5797         }
5798     } while (codeHundred);
5799
5800     /* Add status code */
5801     HTTP_ProcessHeader(request, szStatus, status_code,
5802             HTTP_ADDHDR_FLAG_REPLACE);
5803
5804     heap_free(request->version);
5805     heap_free(request->statusText);
5806
5807     request->version = heap_strdupW(buffer);
5808     request->statusText = heap_strdupW(status_text);
5809
5810     /* Restore the spaces */
5811     *(status_code-1) = ' ';
5812     *(status_text-1) = ' ';
5813
5814     /* regenerate raw headers */
5815     lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5816     if (!lpszRawHeaders) goto lend;
5817
5818     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5819         cchMaxRawHeaders *= 2;
5820     temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5821     if (temp == NULL) goto lend;
5822     lpszRawHeaders = temp;
5823     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5824     cchRawHeaders += (buflen-1);
5825     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5826     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5827     lpszRawHeaders[cchRawHeaders] = '\0';
5828
5829     /* Parse each response line */
5830     do
5831     {
5832         buflen = MAX_REPLY_LEN;
5833         if (read_line(request, bufferA, &buflen))
5834         {
5835             LPWSTR * pFieldAndValue;
5836
5837             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5838
5839             if (!bufferA[0]) break;
5840             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5841
5842             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5843             if (pFieldAndValue)
5844             {
5845                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5846                     cchMaxRawHeaders *= 2;
5847                 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5848                 if (temp == NULL) goto lend;
5849                 lpszRawHeaders = temp;
5850                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5851                 cchRawHeaders += (buflen-1);
5852                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5853                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5854                 lpszRawHeaders[cchRawHeaders] = '\0';
5855
5856                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5857                                    HTTP_ADDREQ_FLAG_ADD );
5858
5859                 HTTP_FreeTokens(pFieldAndValue);
5860             }
5861         }
5862         else
5863         {
5864             cbreaks++;
5865             if (cbreaks >= 2)
5866                break;
5867         }
5868     }while(1);
5869
5870     /* make sure the response header is terminated with an empty line.  Some apps really
5871        truly care about that empty line being there for some reason.  Just add it to the
5872        header. */
5873     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5874     {
5875         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5876         temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5877         if (temp == NULL) goto lend;
5878         lpszRawHeaders = temp;
5879     }
5880
5881     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5882
5883     heap_free(request->rawHeaders);
5884     request->rawHeaders = lpszRawHeaders;
5885     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5886     bSuccess = TRUE;
5887
5888 lend:
5889
5890     TRACE("<--\n");
5891     if (bSuccess)
5892         return rc;
5893     else
5894     {
5895         heap_free(lpszRawHeaders);
5896         return 0;
5897     }
5898 }
5899
5900 /***********************************************************************
5901  *           HTTP_InterpretHttpHeader (internal)
5902  *
5903  * Parse server response
5904  *
5905  * RETURNS
5906  *
5907  *   Pointer to array of field, value, NULL on success.
5908  *   NULL on error.
5909  */
5910 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5911 {
5912     LPWSTR * pTokenPair;
5913     LPWSTR pszColon;
5914     INT len;
5915
5916     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5917
5918     pszColon = strchrW(buffer, ':');
5919     /* must have two tokens */
5920     if (!pszColon)
5921     {
5922         HTTP_FreeTokens(pTokenPair);
5923         if (buffer[0])
5924             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5925         return NULL;
5926     }
5927
5928     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5929     if (!pTokenPair[0])
5930     {
5931         HTTP_FreeTokens(pTokenPair);
5932         return NULL;
5933     }
5934     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5935     pTokenPair[0][pszColon - buffer] = '\0';
5936
5937     /* skip colon */
5938     pszColon++;
5939     len = strlenW(pszColon);
5940     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5941     if (!pTokenPair[1])
5942     {
5943         HTTP_FreeTokens(pTokenPair);
5944         return NULL;
5945     }
5946     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5947
5948     strip_spaces(pTokenPair[0]);
5949     strip_spaces(pTokenPair[1]);
5950
5951     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5952     return pTokenPair;
5953 }
5954
5955 /***********************************************************************
5956  *           HTTP_ProcessHeader (internal)
5957  *
5958  * Stuff header into header tables according to <dwModifier>
5959  *
5960  */
5961
5962 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5963
5964 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5965 {
5966     LPHTTPHEADERW lphttpHdr = NULL;
5967     INT index = -1;
5968     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5969     DWORD res = ERROR_HTTP_INVALID_HEADER;
5970
5971     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5972
5973     /* REPLACE wins out over ADD */
5974     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5975         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5976     
5977     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5978         index = -1;
5979     else
5980         index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5981
5982     if (index >= 0)
5983     {
5984         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5985             return ERROR_HTTP_INVALID_HEADER;
5986         lphttpHdr = &request->custHeaders[index];
5987     }
5988     else if (value)
5989     {
5990         HTTPHEADERW hdr;
5991
5992         hdr.lpszField = (LPWSTR)field;
5993         hdr.lpszValue = (LPWSTR)value;
5994         hdr.wFlags = hdr.wCount = 0;
5995
5996         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5997             hdr.wFlags |= HDR_ISREQUEST;
5998
5999         return HTTP_InsertCustomHeader(request, &hdr);
6000     }
6001     /* no value to delete */
6002     else return ERROR_SUCCESS;
6003
6004     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6005             lphttpHdr->wFlags |= HDR_ISREQUEST;
6006     else
6007         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6008
6009     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6010     {
6011         HTTP_DeleteCustomHeader( request, index );
6012
6013         if (value)
6014         {
6015             HTTPHEADERW hdr;
6016
6017             hdr.lpszField = (LPWSTR)field;
6018             hdr.lpszValue = (LPWSTR)value;
6019             hdr.wFlags = hdr.wCount = 0;
6020
6021             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6022                 hdr.wFlags |= HDR_ISREQUEST;
6023
6024             return HTTP_InsertCustomHeader(request, &hdr);
6025         }
6026
6027         return ERROR_SUCCESS;
6028     }
6029     else if (dwModifier & COALESCEFLAGS)
6030     {
6031         LPWSTR lpsztmp;
6032         WCHAR ch = 0;
6033         INT len = 0;
6034         INT origlen = strlenW(lphttpHdr->lpszValue);
6035         INT valuelen = strlenW(value);
6036
6037         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6038         {
6039             ch = ',';
6040             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6041         }
6042         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6043         {
6044             ch = ';';
6045             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6046         }
6047
6048         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6049
6050         lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6051         if (lpsztmp)
6052         {
6053             lphttpHdr->lpszValue = lpsztmp;
6054     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6055             if (ch > 0)
6056             {
6057                 lphttpHdr->lpszValue[origlen] = ch;
6058                 origlen++;
6059                 lphttpHdr->lpszValue[origlen] = ' ';
6060                 origlen++;
6061             }
6062
6063             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6064             lphttpHdr->lpszValue[len] = '\0';
6065             res = ERROR_SUCCESS;
6066         }
6067         else
6068         {
6069             WARN("heap_realloc (%d bytes) failed\n",len+1);
6070             res = ERROR_OUTOFMEMORY;
6071         }
6072     }
6073     TRACE("<-- %d\n", res);
6074     return res;
6075 }
6076
6077 /***********************************************************************
6078  *           HTTP_GetCustomHeaderIndex (internal)
6079  *
6080  * Return index of custom header from header array
6081  *
6082  */
6083 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6084                                      int requested_index, BOOL request_only)
6085 {
6086     DWORD index;
6087
6088     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6089
6090     for (index = 0; index < request->nCustHeaders; index++)
6091     {
6092         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6093             continue;
6094
6095         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6096             continue;
6097
6098         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6099             continue;
6100
6101         if (requested_index == 0)
6102             break;
6103         requested_index --;
6104     }
6105
6106     if (index >= request->nCustHeaders)
6107         index = -1;
6108
6109     TRACE("Return: %d\n", index);
6110     return index;
6111 }
6112
6113
6114 /***********************************************************************
6115  *           HTTP_InsertCustomHeader (internal)
6116  *
6117  * Insert header into array
6118  *
6119  */
6120 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6121 {
6122     INT count;
6123     LPHTTPHEADERW lph = NULL;
6124
6125     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6126     count = request->nCustHeaders + 1;
6127     if (count > 1)
6128         lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6129     else
6130         lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6131
6132     if (!lph)
6133         return ERROR_OUTOFMEMORY;
6134
6135     request->custHeaders = lph;
6136     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6137     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6138     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6139     request->custHeaders[count-1].wCount= lpHdr->wCount;
6140     request->nCustHeaders++;
6141
6142     return ERROR_SUCCESS;
6143 }
6144
6145
6146 /***********************************************************************
6147  *           HTTP_DeleteCustomHeader (internal)
6148  *
6149  * Delete header from array
6150  *  If this function is called, the indexs may change.
6151  */
6152 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6153 {
6154     if( request->nCustHeaders <= 0 )
6155         return FALSE;
6156     if( index >= request->nCustHeaders )
6157         return FALSE;
6158     request->nCustHeaders--;
6159
6160     heap_free(request->custHeaders[index].lpszField);
6161     heap_free(request->custHeaders[index].lpszValue);
6162
6163     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6164              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6165     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6166
6167     return TRUE;
6168 }
6169
6170
6171 /***********************************************************************
6172  *           HTTP_VerifyValidHeader (internal)
6173  *
6174  * Verify the given header is not invalid for the given http request
6175  *
6176  */
6177 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6178 {
6179     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6180     if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6181         return ERROR_HTTP_INVALID_HEADER;
6182
6183     return ERROR_SUCCESS;
6184 }
6185
6186 /***********************************************************************
6187  *          IsHostInProxyBypassList (@)
6188  *
6189  * Undocumented
6190  *
6191  */
6192 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6193 {
6194    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6195    return FALSE;
6196 }