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