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