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