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