ole32: Add a NULL pointer check in CoDisconnectObject.
[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)
3428             return ERROR_HTTP_INVALID_QUERY_REQUEST;
3429
3430         if(requested_index)
3431             break;
3432
3433         if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3434             if(*lpdwBufferLength >= sizeof(DWORD))
3435                 *(DWORD*)lpBuffer = request->status_code;
3436             else
3437                 res = ERROR_INSUFFICIENT_BUFFER;
3438             *lpdwBufferLength = sizeof(DWORD);
3439         }else {
3440             WCHAR buf[12];
3441             DWORD size;
3442             static const WCHAR formatW[] = {'%','u',0};
3443
3444             size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3445
3446             if(size <= *lpdwBufferLength)
3447                 memcpy(lpBuffer, buf, size);
3448             else
3449                 res = ERROR_INSUFFICIENT_BUFFER;
3450
3451             *lpdwBufferLength = size;
3452         }
3453         return res;
3454     }
3455     default:
3456         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3457
3458         if (level < LAST_TABLE_HEADER && header_lookup[level])
3459             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3460                                               requested_index,request_only);
3461     }
3462
3463     if (index >= 0)
3464         lphttpHdr = &request->custHeaders[index];
3465
3466     /* Ensure header satisfies requested attributes */
3467     if (!lphttpHdr ||
3468         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3469          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3470     {
3471         return ERROR_HTTP_HEADER_NOT_FOUND;
3472     }
3473
3474     if (lpdwIndex) (*lpdwIndex)++;
3475
3476     /* coalesce value to requested type */
3477     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3478     {
3479         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3480         TRACE(" returning number: %d\n", *(int *)lpBuffer);
3481      }
3482     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3483     {
3484         time_t tmpTime;
3485         struct tm tmpTM;
3486         SYSTEMTIME *STHook;
3487
3488         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3489
3490         tmpTM = *gmtime(&tmpTime);
3491         STHook = (SYSTEMTIME *)lpBuffer;
3492         STHook->wDay = tmpTM.tm_mday;
3493         STHook->wHour = tmpTM.tm_hour;
3494         STHook->wMilliseconds = 0;
3495         STHook->wMinute = tmpTM.tm_min;
3496         STHook->wDayOfWeek = tmpTM.tm_wday;
3497         STHook->wMonth = tmpTM.tm_mon + 1;
3498         STHook->wSecond = tmpTM.tm_sec;
3499         STHook->wYear = tmpTM.tm_year;
3500
3501         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3502               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3503               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3504     }
3505     else if (lphttpHdr->lpszValue)
3506     {
3507         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3508
3509         if (len > *lpdwBufferLength)
3510         {
3511             *lpdwBufferLength = len;
3512             return ERROR_INSUFFICIENT_BUFFER;
3513         }
3514         if (lpBuffer)
3515         {
3516             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3517             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3518         }
3519         *lpdwBufferLength = len - sizeof(WCHAR);
3520     }
3521     return ERROR_SUCCESS;
3522 }
3523
3524 /***********************************************************************
3525  *           HttpQueryInfoW (WININET.@)
3526  *
3527  * Queries for information about an HTTP request
3528  *
3529  * RETURNS
3530  *    TRUE  on success
3531  *    FALSE on failure
3532  *
3533  */
3534 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3535         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3536 {
3537     http_request_t *request;
3538     DWORD res;
3539
3540     if (TRACE_ON(wininet)) {
3541 #define FE(x) { x, #x }
3542         static const wininet_flag_info query_flags[] = {
3543             FE(HTTP_QUERY_MIME_VERSION),
3544             FE(HTTP_QUERY_CONTENT_TYPE),
3545             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3546             FE(HTTP_QUERY_CONTENT_ID),
3547             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3548             FE(HTTP_QUERY_CONTENT_LENGTH),
3549             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3550             FE(HTTP_QUERY_ALLOW),
3551             FE(HTTP_QUERY_PUBLIC),
3552             FE(HTTP_QUERY_DATE),
3553             FE(HTTP_QUERY_EXPIRES),
3554             FE(HTTP_QUERY_LAST_MODIFIED),
3555             FE(HTTP_QUERY_MESSAGE_ID),
3556             FE(HTTP_QUERY_URI),
3557             FE(HTTP_QUERY_DERIVED_FROM),
3558             FE(HTTP_QUERY_COST),
3559             FE(HTTP_QUERY_LINK),
3560             FE(HTTP_QUERY_PRAGMA),
3561             FE(HTTP_QUERY_VERSION),
3562             FE(HTTP_QUERY_STATUS_CODE),
3563             FE(HTTP_QUERY_STATUS_TEXT),
3564             FE(HTTP_QUERY_RAW_HEADERS),
3565             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3566             FE(HTTP_QUERY_CONNECTION),
3567             FE(HTTP_QUERY_ACCEPT),
3568             FE(HTTP_QUERY_ACCEPT_CHARSET),
3569             FE(HTTP_QUERY_ACCEPT_ENCODING),
3570             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3571             FE(HTTP_QUERY_AUTHORIZATION),
3572             FE(HTTP_QUERY_CONTENT_ENCODING),
3573             FE(HTTP_QUERY_FORWARDED),
3574             FE(HTTP_QUERY_FROM),
3575             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3576             FE(HTTP_QUERY_LOCATION),
3577             FE(HTTP_QUERY_ORIG_URI),
3578             FE(HTTP_QUERY_REFERER),
3579             FE(HTTP_QUERY_RETRY_AFTER),
3580             FE(HTTP_QUERY_SERVER),
3581             FE(HTTP_QUERY_TITLE),
3582             FE(HTTP_QUERY_USER_AGENT),
3583             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3584             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3585             FE(HTTP_QUERY_ACCEPT_RANGES),
3586         FE(HTTP_QUERY_SET_COOKIE),
3587         FE(HTTP_QUERY_COOKIE),
3588             FE(HTTP_QUERY_REQUEST_METHOD),
3589             FE(HTTP_QUERY_REFRESH),
3590             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3591             FE(HTTP_QUERY_AGE),
3592             FE(HTTP_QUERY_CACHE_CONTROL),
3593             FE(HTTP_QUERY_CONTENT_BASE),
3594             FE(HTTP_QUERY_CONTENT_LOCATION),
3595             FE(HTTP_QUERY_CONTENT_MD5),
3596             FE(HTTP_QUERY_CONTENT_RANGE),
3597             FE(HTTP_QUERY_ETAG),
3598             FE(HTTP_QUERY_HOST),
3599             FE(HTTP_QUERY_IF_MATCH),
3600             FE(HTTP_QUERY_IF_NONE_MATCH),
3601             FE(HTTP_QUERY_IF_RANGE),
3602             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3603             FE(HTTP_QUERY_MAX_FORWARDS),
3604             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3605             FE(HTTP_QUERY_RANGE),
3606             FE(HTTP_QUERY_TRANSFER_ENCODING),
3607             FE(HTTP_QUERY_UPGRADE),
3608             FE(HTTP_QUERY_VARY),
3609             FE(HTTP_QUERY_VIA),
3610             FE(HTTP_QUERY_WARNING),
3611             FE(HTTP_QUERY_CUSTOM)
3612         };
3613         static const wininet_flag_info modifier_flags[] = {
3614             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3615             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3616             FE(HTTP_QUERY_FLAG_NUMBER),
3617             FE(HTTP_QUERY_FLAG_COALESCE)
3618         };
3619 #undef FE
3620         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3621         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3622         DWORD i;
3623
3624         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3625         TRACE("  Attribute:");
3626         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3627             if (query_flags[i].val == info) {
3628                 TRACE(" %s", query_flags[i].name);
3629                 break;
3630             }
3631         }
3632         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3633             TRACE(" Unknown (%08x)", info);
3634         }
3635
3636         TRACE(" Modifier:");
3637         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3638             if (modifier_flags[i].val & info_mod) {
3639                 TRACE(" %s", modifier_flags[i].name);
3640                 info_mod &= ~ modifier_flags[i].val;
3641             }
3642         }
3643         
3644         if (info_mod) {
3645             TRACE(" Unknown (%08x)", info_mod);
3646         }
3647         TRACE("\n");
3648     }
3649     
3650     request = (http_request_t*) get_handle_object( hHttpRequest );
3651     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
3652     {
3653         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3654         goto lend;
3655     }
3656
3657     if (lpBuffer == NULL)
3658         *lpdwBufferLength = 0;
3659     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3660                                lpBuffer, lpdwBufferLength, lpdwIndex);
3661
3662 lend:
3663     if( request )
3664          WININET_Release( &request->hdr );
3665
3666     TRACE("%u <--\n", res);
3667     if(res != ERROR_SUCCESS)
3668         SetLastError(res);
3669     return res == ERROR_SUCCESS;
3670 }
3671
3672 /***********************************************************************
3673  *           HttpQueryInfoA (WININET.@)
3674  *
3675  * Queries for information about an HTTP request
3676  *
3677  * RETURNS
3678  *    TRUE  on success
3679  *    FALSE on failure
3680  *
3681  */
3682 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3683         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3684 {
3685     BOOL result;
3686     DWORD len;
3687     WCHAR* bufferW;
3688
3689     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3690        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3691     {
3692         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3693                                lpdwBufferLength, lpdwIndex );
3694     }
3695
3696     if (lpBuffer)
3697     {
3698         DWORD alloclen;
3699         len = (*lpdwBufferLength)*sizeof(WCHAR);
3700         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3701         {
3702             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3703             if (alloclen < len)
3704                 alloclen = len;
3705         }
3706         else
3707             alloclen = len;
3708         bufferW = heap_alloc(alloclen);
3709         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3710         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3711             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3712     } else
3713     {
3714         bufferW = NULL;
3715         len = 0;
3716     }
3717
3718     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3719                            &len, lpdwIndex );
3720     if( result )
3721     {
3722         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3723                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3724         *lpdwBufferLength = len - 1;
3725
3726         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3727     }
3728     else
3729         /* since the strings being returned from HttpQueryInfoW should be
3730          * only ASCII characters, it is reasonable to assume that all of
3731          * the Unicode characters can be reduced to a single byte */
3732         *lpdwBufferLength = len / sizeof(WCHAR);
3733
3734     heap_free( bufferW );
3735     return result;
3736 }
3737
3738 /***********************************************************************
3739  *           HTTP_GetRedirectURL (internal)
3740  */
3741 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3742 {
3743     static WCHAR szHttp[] = {'h','t','t','p',0};
3744     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3745     http_session_t *session = request->session;
3746     URL_COMPONENTSW urlComponents;
3747     DWORD url_length = 0;
3748     LPWSTR orig_url;
3749     LPWSTR combined_url;
3750
3751     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3752     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3753     urlComponents.dwSchemeLength = 0;
3754     urlComponents.lpszHostName = session->hostName;
3755     urlComponents.dwHostNameLength = 0;
3756     urlComponents.nPort = session->hostPort;
3757     urlComponents.lpszUserName = session->userName;
3758     urlComponents.dwUserNameLength = 0;
3759     urlComponents.lpszPassword = NULL;
3760     urlComponents.dwPasswordLength = 0;
3761     urlComponents.lpszUrlPath = request->path;
3762     urlComponents.dwUrlPathLength = 0;
3763     urlComponents.lpszExtraInfo = NULL;
3764     urlComponents.dwExtraInfoLength = 0;
3765
3766     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3767         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3768         return NULL;
3769
3770     orig_url = heap_alloc(url_length);
3771
3772     /* convert from bytes to characters */
3773     url_length = url_length / sizeof(WCHAR) - 1;
3774     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3775     {
3776         heap_free(orig_url);
3777         return NULL;
3778     }
3779
3780     url_length = 0;
3781     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3782         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3783     {
3784         heap_free(orig_url);
3785         return NULL;
3786     }
3787     combined_url = heap_alloc(url_length * sizeof(WCHAR));
3788
3789     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3790     {
3791         heap_free(orig_url);
3792         heap_free(combined_url);
3793         return NULL;
3794     }
3795     heap_free(orig_url);
3796     return combined_url;
3797 }
3798
3799
3800 /***********************************************************************
3801  *           HTTP_HandleRedirect (internal)
3802  */
3803 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3804 {
3805     http_session_t *session = request->session;
3806     appinfo_t *hIC = session->appInfo;
3807     BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3808     WCHAR path[INTERNET_MAX_PATH_LENGTH];
3809     int index;
3810
3811     if(lpszUrl[0]=='/')
3812     {
3813         /* if it's an absolute path, keep the same session info */
3814         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3815     }
3816     else
3817     {
3818         URL_COMPONENTSW urlComponents;
3819         WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3820         WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3821         WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3822         BOOL custom_port = FALSE;
3823
3824         static WCHAR httpW[] = {'h','t','t','p',0};
3825         static WCHAR httpsW[] = {'h','t','t','p','s',0};
3826
3827         userName[0] = 0;
3828         hostName[0] = 0;
3829         protocol[0] = 0;
3830
3831         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3832         urlComponents.lpszScheme = protocol;
3833         urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3834         urlComponents.lpszHostName = hostName;
3835         urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3836         urlComponents.lpszUserName = userName;
3837         urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3838         urlComponents.lpszPassword = NULL;
3839         urlComponents.dwPasswordLength = 0;
3840         urlComponents.lpszUrlPath = path;
3841         urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3842         urlComponents.lpszExtraInfo = NULL;
3843         urlComponents.dwExtraInfoLength = 0;
3844         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3845             return INTERNET_GetLastError();
3846
3847         if(!strcmpiW(protocol, httpW)) {
3848             if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3849                 TRACE("redirect from secure page to non-secure page\n");
3850                 /* FIXME: warn about from secure redirect to non-secure page */
3851                 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3852             }
3853
3854             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3855                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3856             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3857                 custom_port = TRUE;
3858         }else if(!strcmpiW(protocol, httpsW)) {
3859             if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3860                 TRACE("redirect from non-secure page to secure page\n");
3861                 /* FIXME: notify about redirect to secure page */
3862                 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3863             }
3864
3865             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3866                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3867             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3868                 custom_port = TRUE;
3869         }
3870
3871         heap_free(session->hostName);
3872
3873         if(custom_port) {
3874             int len;
3875             static const WCHAR fmt[] = {'%','s',':','%','u',0};
3876             len = lstrlenW(hostName);
3877             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3878             session->hostName = heap_alloc(len*sizeof(WCHAR));
3879             sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3880         }
3881         else
3882             session->hostName = heap_strdupW(hostName);
3883
3884         HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3885
3886         heap_free(session->userName);
3887         session->userName = NULL;
3888         if (userName[0])
3889             session->userName = heap_strdupW(userName);
3890
3891         reset_data_stream(request);
3892
3893         if(!using_proxy) {
3894             if(strcmpiW(session->serverName, hostName)) {
3895                 heap_free(session->serverName);
3896                 session->serverName = heap_strdupW(hostName);
3897             }
3898             session->serverPort = urlComponents.nPort;
3899         }
3900     }
3901     heap_free(request->path);
3902     request->path=NULL;
3903     if (*path)
3904     {
3905         DWORD needed = 0;
3906         HRESULT rc;
3907
3908         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3909         if (rc != E_POINTER)
3910             needed = strlenW(path)+1;
3911         request->path = heap_alloc(needed*sizeof(WCHAR));
3912         rc = UrlEscapeW(path, request->path, &needed,
3913                         URL_ESCAPE_SPACES_ONLY);
3914         if (rc != S_OK)
3915         {
3916             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3917             strcpyW(request->path,path);
3918         }
3919     }
3920
3921     /* Remove custom content-type/length headers on redirects.  */
3922     index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3923     if (0 <= index)
3924         HTTP_DeleteCustomHeader(request, index);
3925     index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3926     if (0 <= index)
3927         HTTP_DeleteCustomHeader(request, index);
3928
3929     return ERROR_SUCCESS;
3930 }
3931
3932 /***********************************************************************
3933  *           HTTP_build_req (internal)
3934  *
3935  *  concatenate all the strings in the request together
3936  */
3937 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3938 {
3939     LPCWSTR *t;
3940     LPWSTR str;
3941
3942     for( t = list; *t ; t++  )
3943         len += strlenW( *t );
3944     len++;
3945
3946     str = heap_alloc(len*sizeof(WCHAR));
3947     *str = 0;
3948
3949     for( t = list; *t ; t++ )
3950         strcatW( str, *t );
3951
3952     return str;
3953 }
3954
3955 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3956 {
3957     LPWSTR lpszPath;
3958     LPWSTR requestString;
3959     INT len;
3960     INT cnt;
3961     INT responseLen;
3962     char *ascii_req;
3963     DWORD res;
3964     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3965     static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3966     http_session_t *session = request->session;
3967
3968     TRACE("\n");
3969
3970     lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3971     sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3972     requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3973     heap_free( lpszPath );
3974
3975     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3976                                 NULL, 0, NULL, NULL );
3977     len--; /* the nul terminator isn't needed */
3978     ascii_req = heap_alloc(len);
3979     WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3980     heap_free( requestString );
3981
3982     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3983
3984     NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3985     res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3986     heap_free( ascii_req );
3987     if (res != ERROR_SUCCESS)
3988         return res;
3989
3990     responseLen = HTTP_GetResponseHeaders( request, TRUE );
3991     if (!responseLen)
3992         return ERROR_HTTP_INVALID_HEADER;
3993
3994     return ERROR_SUCCESS;
3995 }
3996
3997 static void HTTP_InsertCookies(http_request_t *request)
3998 {
3999     DWORD cookie_size, size, cnt = 0;
4000     HTTPHEADERW *host;
4001     WCHAR *cookies;
4002
4003     static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4004
4005     host = HTTP_GetHeader(request, hostW);
4006     if(!host)
4007         return;
4008
4009     if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4010         return;
4011
4012     size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4013     if(!(cookies = heap_alloc(size)))
4014         return;
4015
4016     cnt += sprintfW(cookies, cookieW);
4017     get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4018     strcatW(cookies, szCrLf);
4019
4020     HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4021
4022     heap_free(cookies);
4023 }
4024
4025 static WORD HTTP_ParseWkday(LPCWSTR day)
4026 {
4027     static const WCHAR days[7][4] = {{ 's','u','n',0 },
4028                                      { 'm','o','n',0 },
4029                                      { 't','u','e',0 },
4030                                      { 'w','e','d',0 },
4031                                      { 't','h','u',0 },
4032                                      { 'f','r','i',0 },
4033                                      { 's','a','t',0 }};
4034     int i;
4035     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4036         if (!strcmpiW(day, days[i]))
4037             return i;
4038
4039     /* Invalid */
4040     return 7;
4041 }
4042
4043 static WORD HTTP_ParseMonth(LPCWSTR month)
4044 {
4045     static const WCHAR jan[] = { 'j','a','n',0 };
4046     static const WCHAR feb[] = { 'f','e','b',0 };
4047     static const WCHAR mar[] = { 'm','a','r',0 };
4048     static const WCHAR apr[] = { 'a','p','r',0 };
4049     static const WCHAR may[] = { 'm','a','y',0 };
4050     static const WCHAR jun[] = { 'j','u','n',0 };
4051     static const WCHAR jul[] = { 'j','u','l',0 };
4052     static const WCHAR aug[] = { 'a','u','g',0 };
4053     static const WCHAR sep[] = { 's','e','p',0 };
4054     static const WCHAR oct[] = { 'o','c','t',0 };
4055     static const WCHAR nov[] = { 'n','o','v',0 };
4056     static const WCHAR dec[] = { 'd','e','c',0 };
4057
4058     if (!strcmpiW(month, jan)) return 1;
4059     if (!strcmpiW(month, feb)) return 2;
4060     if (!strcmpiW(month, mar)) return 3;
4061     if (!strcmpiW(month, apr)) return 4;
4062     if (!strcmpiW(month, may)) return 5;
4063     if (!strcmpiW(month, jun)) return 6;
4064     if (!strcmpiW(month, jul)) return 7;
4065     if (!strcmpiW(month, aug)) return 8;
4066     if (!strcmpiW(month, sep)) return 9;
4067     if (!strcmpiW(month, oct)) return 10;
4068     if (!strcmpiW(month, nov)) return 11;
4069     if (!strcmpiW(month, dec)) return 12;
4070     /* Invalid */
4071     return 0;
4072 }
4073
4074 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4075  * optionally preceded by whitespace.
4076  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4077  * st, and sets *str to the first character after the time format.
4078  */
4079 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4080 {
4081     LPCWSTR ptr = *str;
4082     WCHAR *nextPtr;
4083     unsigned long num;
4084
4085     while (isspaceW(*ptr))
4086         ptr++;
4087
4088     num = strtoulW(ptr, &nextPtr, 10);
4089     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4090     {
4091         ERR("unexpected time format %s\n", debugstr_w(ptr));
4092         return FALSE;
4093     }
4094     if (num > 23)
4095     {
4096         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4097         return FALSE;
4098     }
4099     ptr = nextPtr + 1;
4100     st->wHour = (WORD)num;
4101     num = strtoulW(ptr, &nextPtr, 10);
4102     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4103     {
4104         ERR("unexpected time format %s\n", debugstr_w(ptr));
4105         return FALSE;
4106     }
4107     if (num > 59)
4108     {
4109         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4110         return FALSE;
4111     }
4112     ptr = nextPtr + 1;
4113     st->wMinute = (WORD)num;
4114     num = strtoulW(ptr, &nextPtr, 10);
4115     if (!nextPtr || nextPtr <= ptr)
4116     {
4117         ERR("unexpected time format %s\n", debugstr_w(ptr));
4118         return FALSE;
4119     }
4120     if (num > 59)
4121     {
4122         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4123         return FALSE;
4124     }
4125     ptr = nextPtr + 1;
4126     *str = ptr;
4127     st->wSecond = (WORD)num;
4128     return TRUE;
4129 }
4130
4131 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4132 {
4133     static const WCHAR gmt[]= { 'G','M','T',0 };
4134     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4135     LPCWSTR ptr;
4136     SYSTEMTIME st = { 0 };
4137     unsigned long num;
4138
4139     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4140          dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4141         *dayPtr = *ptr;
4142     *dayPtr = 0;
4143     st.wDayOfWeek = HTTP_ParseWkday(day);
4144     if (st.wDayOfWeek >= 7)
4145     {
4146         ERR("unexpected weekday %s\n", debugstr_w(day));
4147         return FALSE;
4148     }
4149
4150     while (isspaceW(*ptr))
4151         ptr++;
4152
4153     for (monthPtr = month; !isspace(*ptr) &&
4154          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4155          monthPtr++, ptr++)
4156         *monthPtr = *ptr;
4157     *monthPtr = 0;
4158     st.wMonth = HTTP_ParseMonth(month);
4159     if (!st.wMonth || st.wMonth > 12)
4160     {
4161         ERR("unexpected month %s\n", debugstr_w(month));
4162         return FALSE;
4163     }
4164
4165     while (isspaceW(*ptr))
4166         ptr++;
4167
4168     num = strtoulW(ptr, &nextPtr, 10);
4169     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4170     {
4171         ERR("unexpected day %s\n", debugstr_w(ptr));
4172         return FALSE;
4173     }
4174     ptr = nextPtr;
4175     st.wDay = (WORD)num;
4176
4177     while (isspaceW(*ptr))
4178         ptr++;
4179
4180     if (!HTTP_ParseTime(&st, &ptr))
4181         return FALSE;
4182
4183     while (isspaceW(*ptr))
4184         ptr++;
4185
4186     num = strtoulW(ptr, &nextPtr, 10);
4187     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4188     {
4189         ERR("unexpected year %s\n", debugstr_w(ptr));
4190         return FALSE;
4191     }
4192     ptr = nextPtr;
4193     st.wYear = (WORD)num;
4194
4195     while (isspaceW(*ptr))
4196         ptr++;
4197
4198     /* asctime() doesn't report a timezone, but some web servers do, so accept
4199      * with or without GMT.
4200      */
4201     if (*ptr && strcmpW(ptr, gmt))
4202     {
4203         ERR("unexpected timezone %s\n", debugstr_w(ptr));
4204         return FALSE;
4205     }
4206     return SystemTimeToFileTime(&st, ft);
4207 }
4208
4209 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4210 {
4211     static const WCHAR gmt[]= { 'G','M','T',0 };
4212     WCHAR *nextPtr, day[4], month[4], *monthPtr;
4213     LPCWSTR ptr;
4214     unsigned long num;
4215     SYSTEMTIME st = { 0 };
4216
4217     ptr = strchrW(value, ',');
4218     if (!ptr)
4219         return FALSE;
4220     if (ptr - value != 3)
4221     {
4222         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4223         return FALSE;
4224     }
4225     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4226     day[3] = 0;
4227     st.wDayOfWeek = HTTP_ParseWkday(day);
4228     if (st.wDayOfWeek > 6)
4229     {
4230         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4231         return FALSE;
4232     }
4233     ptr++;
4234
4235     while (isspaceW(*ptr))
4236         ptr++;
4237
4238     num = strtoulW(ptr, &nextPtr, 10);
4239     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4240     {
4241         WARN("unexpected day %s\n", debugstr_w(value));
4242         return FALSE;
4243     }
4244     ptr = nextPtr;
4245     st.wDay = (WORD)num;
4246
4247     while (isspaceW(*ptr))
4248         ptr++;
4249
4250     for (monthPtr = month; !isspace(*ptr) &&
4251          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4252          monthPtr++, ptr++)
4253         *monthPtr = *ptr;
4254     *monthPtr = 0;
4255     st.wMonth = HTTP_ParseMonth(month);
4256     if (!st.wMonth || st.wMonth > 12)
4257     {
4258         WARN("unexpected month %s\n", debugstr_w(month));
4259         return FALSE;
4260     }
4261
4262     while (isspaceW(*ptr))
4263         ptr++;
4264
4265     num = strtoulW(ptr, &nextPtr, 10);
4266     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4267     {
4268         ERR("unexpected year %s\n", debugstr_w(value));
4269         return FALSE;
4270     }
4271     ptr = nextPtr;
4272     st.wYear = (WORD)num;
4273
4274     if (!HTTP_ParseTime(&st, &ptr))
4275         return FALSE;
4276
4277     while (isspaceW(*ptr))
4278         ptr++;
4279
4280     if (strcmpW(ptr, gmt))
4281     {
4282         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4283         return FALSE;
4284     }
4285     return SystemTimeToFileTime(&st, ft);
4286 }
4287
4288 static WORD HTTP_ParseWeekday(LPCWSTR day)
4289 {
4290     static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4291                                      { 'm','o','n','d','a','y',0 },
4292                                      { 't','u','e','s','d','a','y',0 },
4293                                      { 'w','e','d','n','e','s','d','a','y',0 },
4294                                      { 't','h','u','r','s','d','a','y',0 },
4295                                      { 'f','r','i','d','a','y',0 },
4296                                      { 's','a','t','u','r','d','a','y',0 }};
4297     int i;
4298     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4299         if (!strcmpiW(day, days[i]))
4300             return i;
4301
4302     /* Invalid */
4303     return 7;
4304 }
4305
4306 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4307 {
4308     static const WCHAR gmt[]= { 'G','M','T',0 };
4309     WCHAR *nextPtr, day[10], month[4], *monthPtr;
4310     LPCWSTR ptr;
4311     unsigned long num;
4312     SYSTEMTIME st = { 0 };
4313
4314     ptr = strchrW(value, ',');
4315     if (!ptr)
4316         return FALSE;
4317     if (ptr - value == 3)
4318     {
4319         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4320         day[3] = 0;
4321         st.wDayOfWeek = HTTP_ParseWkday(day);
4322         if (st.wDayOfWeek > 6)
4323         {
4324             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4325             return FALSE;
4326         }
4327     }
4328     else if (ptr - value < sizeof(day) / sizeof(day[0]))
4329     {
4330         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4331         day[ptr - value + 1] = 0;
4332         st.wDayOfWeek = HTTP_ParseWeekday(day);
4333         if (st.wDayOfWeek > 6)
4334         {
4335             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4336             return FALSE;
4337         }
4338     }
4339     else
4340     {
4341         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4342         return FALSE;
4343     }
4344     ptr++;
4345
4346     while (isspaceW(*ptr))
4347         ptr++;
4348
4349     num = strtoulW(ptr, &nextPtr, 10);
4350     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4351     {
4352         ERR("unexpected day %s\n", debugstr_w(value));
4353         return FALSE;
4354     }
4355     ptr = nextPtr;
4356     st.wDay = (WORD)num;
4357
4358     if (*ptr != '-')
4359     {
4360         ERR("unexpected month format %s\n", debugstr_w(ptr));
4361         return FALSE;
4362     }
4363     ptr++;
4364
4365     for (monthPtr = month; *ptr != '-' &&
4366          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4367          monthPtr++, ptr++)
4368         *monthPtr = *ptr;
4369     *monthPtr = 0;
4370     st.wMonth = HTTP_ParseMonth(month);
4371     if (!st.wMonth || st.wMonth > 12)
4372     {
4373         ERR("unexpected month %s\n", debugstr_w(month));
4374         return FALSE;
4375     }
4376
4377     if (*ptr != '-')
4378     {
4379         ERR("unexpected year format %s\n", debugstr_w(ptr));
4380         return FALSE;
4381     }
4382     ptr++;
4383
4384     num = strtoulW(ptr, &nextPtr, 10);
4385     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4386     {
4387         ERR("unexpected year %s\n", debugstr_w(value));
4388         return FALSE;
4389     }
4390     ptr = nextPtr;
4391     st.wYear = (WORD)num;
4392
4393     if (!HTTP_ParseTime(&st, &ptr))
4394         return FALSE;
4395
4396     while (isspaceW(*ptr))
4397         ptr++;
4398
4399     if (strcmpW(ptr, gmt))
4400     {
4401         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4402         return FALSE;
4403     }
4404     return SystemTimeToFileTime(&st, ft);
4405 }
4406
4407 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4408 {
4409     static const WCHAR zero[] = { '0',0 };
4410     BOOL ret;
4411
4412     if (!strcmpW(value, zero))
4413     {
4414         ft->dwLowDateTime = ft->dwHighDateTime = 0;
4415         ret = TRUE;
4416     }
4417     else if (strchrW(value, ','))
4418     {
4419         ret = HTTP_ParseRfc1123Date(value, ft);
4420         if (!ret)
4421         {
4422             ret = HTTP_ParseRfc850Date(value, ft);
4423             if (!ret)
4424                 ERR("unexpected date format %s\n", debugstr_w(value));
4425         }
4426     }
4427     else
4428     {
4429         ret = HTTP_ParseDateAsAsctime(value, ft);
4430         if (!ret)
4431             ERR("unexpected date format %s\n", debugstr_w(value));
4432     }
4433     return ret;
4434 }
4435
4436 static void HTTP_ProcessExpires(http_request_t *request)
4437 {
4438     BOOL expirationFound = FALSE;
4439     int headerIndex;
4440
4441     /* Look for a Cache-Control header with a max-age directive, as it takes
4442      * precedence over the Expires header.
4443      */
4444     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4445     if (headerIndex != -1)
4446     {
4447         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4448         LPWSTR ptr;
4449
4450         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4451         {
4452             LPWSTR comma = strchrW(ptr, ','), end, equal;
4453
4454             if (comma)
4455                 end = comma;
4456             else
4457                 end = ptr + strlenW(ptr);
4458             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4459                 ;
4460             if (*equal == '=')
4461             {
4462                 static const WCHAR max_age[] = {
4463                     'm','a','x','-','a','g','e',0 };
4464
4465                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4466                 {
4467                     LPWSTR nextPtr;
4468                     unsigned long age;
4469
4470                     age = strtoulW(equal + 1, &nextPtr, 10);
4471                     if (nextPtr > equal + 1)
4472                     {
4473                         LARGE_INTEGER ft;
4474
4475                         NtQuerySystemTime( &ft );
4476                         /* Age is in seconds, FILETIME resolution is in
4477                          * 100 nanosecond intervals.
4478                          */
4479                         ft.QuadPart += age * (ULONGLONG)1000000;
4480                         request->expires.dwLowDateTime = ft.u.LowPart;
4481                         request->expires.dwHighDateTime = ft.u.HighPart;
4482                         expirationFound = TRUE;
4483                     }
4484                 }
4485             }
4486             if (comma)
4487             {
4488                 ptr = comma + 1;
4489                 while (isspaceW(*ptr))
4490                     ptr++;
4491             }
4492             else
4493                 ptr = NULL;
4494         }
4495     }
4496     if (!expirationFound)
4497     {
4498         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4499         if (headerIndex != -1)
4500         {
4501             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4502             FILETIME ft;
4503
4504             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4505             {
4506                 expirationFound = TRUE;
4507                 request->expires = ft;
4508             }
4509         }
4510     }
4511     if (!expirationFound)
4512     {
4513         LARGE_INTEGER t;
4514
4515         /* With no known age, default to 10 minutes until expiration. */
4516         NtQuerySystemTime( &t );
4517         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4518         request->expires.dwLowDateTime = t.u.LowPart;
4519         request->expires.dwHighDateTime = t.u.HighPart;
4520     }
4521 }
4522
4523 static void HTTP_ProcessLastModified(http_request_t *request)
4524 {
4525     int headerIndex;
4526
4527     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4528     if (headerIndex != -1)
4529     {
4530         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4531         FILETIME ft;
4532
4533         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4534             request->last_modified = ft;
4535     }
4536 }
4537
4538 static void http_process_keep_alive(http_request_t *req)
4539 {
4540     int index;
4541
4542     index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4543     if(index != -1)
4544         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4545     else
4546         req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4547 }
4548
4549 static void HTTP_CacheRequest(http_request_t *request)
4550 {
4551     WCHAR url[INTERNET_MAX_URL_LENGTH];
4552     WCHAR cacheFileName[MAX_PATH+1];
4553     BOOL b;
4554
4555     b = HTTP_GetRequestURL(request, url);
4556     if(!b) {
4557         WARN("Could not get URL\n");
4558         return;
4559     }
4560
4561     b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4562     if(b) {
4563         heap_free(request->cacheFile);
4564         CloseHandle(request->hCacheFile);
4565
4566         request->cacheFile = heap_strdupW(cacheFileName);
4567         request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4568                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4569         if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4570             WARN("Could not create file: %u\n", GetLastError());
4571             request->hCacheFile = NULL;
4572         }
4573     }else {
4574         WARN("Could not create cache entry: %08x\n", GetLastError());
4575     }
4576 }
4577
4578 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4579 {
4580     const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4581     http_session_t *session = request->session;
4582     netconn_t *netconn = NULL;
4583     server_t *server;
4584     DWORD res;
4585
4586     assert(!request->netconn);
4587     reset_data_stream(request);
4588
4589     server = get_server(session->serverName, session->serverPort);
4590     if(!server)
4591         return ERROR_OUTOFMEMORY;
4592
4593     res = HTTP_ResolveName(request, server);
4594     if(res != ERROR_SUCCESS) {
4595         server_release(server);
4596         return res;
4597     }
4598
4599     EnterCriticalSection(&connection_pool_cs);
4600
4601     while(!list_empty(&server->conn_pool)) {
4602         netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4603         list_remove(&netconn->pool_entry);
4604
4605         if(NETCON_is_alive(netconn))
4606             break;
4607
4608         TRACE("connection %p closed during idle\n", netconn);
4609         free_netconn(netconn);
4610         netconn = NULL;
4611     }
4612
4613     LeaveCriticalSection(&connection_pool_cs);
4614
4615     if(netconn) {
4616         TRACE("<-- reusing %p netconn\n", netconn);
4617         request->netconn = netconn;
4618         *reusing = TRUE;
4619         return ERROR_SUCCESS;
4620     }
4621
4622     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4623                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4624                           server->addr_str,
4625                           strlen(server->addr_str)+1);
4626
4627     res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4628     server_release(server);
4629     if(res != ERROR_SUCCESS) {
4630         ERR("create_netconn failed: %u\n", res);
4631         return res;
4632     }
4633
4634     request->netconn = netconn;
4635
4636     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4637             INTERNET_STATUS_CONNECTED_TO_SERVER,
4638             server->addr_str, strlen(server->addr_str)+1);
4639
4640     if(is_https) {
4641         /* Note: we differ from Microsoft's WinINet here. they seem to have
4642          * a bug that causes no status callbacks to be sent when starting
4643          * a tunnel to a proxy server using the CONNECT verb. i believe our
4644          * behaviour to be more correct and to not cause any incompatibilities
4645          * because using a secure connection through a proxy server is a rare
4646          * case that would be hard for anyone to depend on */
4647         if(session->appInfo->proxy)
4648             res = HTTP_SecureProxyConnect(request);
4649         if(res == ERROR_SUCCESS)
4650             res = NETCON_secure_connect(request->netconn);
4651         if(res != ERROR_SUCCESS)
4652         {
4653             WARN("Couldn't connect securely to host\n");
4654
4655             if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4656                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4657                     || res == ERROR_INTERNET_INVALID_CA
4658                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
4659                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4660                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
4661                     || res == ERROR_INTERNET_SEC_INVALID_CERT
4662                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4663                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4664         }
4665     }
4666
4667     if(res != ERROR_SUCCESS) {
4668         http_release_netconn(request, FALSE);
4669         return res;
4670     }
4671
4672     *reusing = FALSE;
4673     TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4674     return ERROR_SUCCESS;
4675 }
4676
4677 /***********************************************************************
4678  *           HTTP_HttpSendRequestW (internal)
4679  *
4680  * Sends the specified request to the HTTP server
4681  *
4682  * RETURNS
4683  *    ERROR_SUCCESS on success
4684  *    win32 error code on failure
4685  *
4686  */
4687 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4688         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4689         DWORD dwContentLength, BOOL bEndRequest)
4690 {
4691     INT cnt;
4692     BOOL redirected = FALSE;
4693     LPWSTR requestString = NULL;
4694     INT responseLen;
4695     BOOL loop_next;
4696     static const WCHAR szPost[] = { 'P','O','S','T',0 };
4697     static const WCHAR szContentLength[] =
4698         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4699     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4700     DWORD res;
4701
4702     TRACE("--> %p\n", request);
4703
4704     assert(request->hdr.htype == WH_HHTTPREQ);
4705
4706     /* if the verb is NULL default to GET */
4707     if (!request->verb)
4708         request->verb = heap_strdupW(szGET);
4709
4710     if (dwContentLength || strcmpW(request->verb, szGET))
4711     {
4712         sprintfW(contentLengthStr, szContentLength, dwContentLength);
4713         HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4714         request->bytesToWrite = dwContentLength;
4715     }
4716     if (request->session->appInfo->agent)
4717     {
4718         WCHAR *agent_header;
4719         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4720         int len;
4721
4722         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4723         agent_header = heap_alloc(len * sizeof(WCHAR));
4724         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4725
4726         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4727         heap_free(agent_header);
4728     }
4729     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4730     {
4731         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4732         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4733     }
4734     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4735     {
4736         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4737                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4738         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4739     }
4740
4741     /* add the headers the caller supplied */
4742     if( lpszHeaders && dwHeaderLength )
4743         HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4744
4745     do
4746     {
4747         DWORD len;
4748         BOOL reusing_connection;
4749         char *ascii_req;
4750
4751         loop_next = FALSE;
4752
4753         /* like native, just in case the caller forgot to call InternetReadFile
4754          * for all the data */
4755         drain_content(request);
4756         if(redirected) {
4757             request->contentLength = ~0u;
4758             request->bytesToWrite = 0;
4759         }
4760
4761         if (TRACE_ON(wininet))
4762         {
4763             LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4764             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4765         }
4766
4767         HTTP_FixURL(request);
4768         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4769         {
4770             HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4771         }
4772         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4773         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4774
4775         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4776             HTTP_InsertCookies(request);
4777
4778         if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4779         {
4780             WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4781             requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4782             heap_free(url);
4783         }
4784         else
4785             requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4786
4787  
4788         TRACE("Request header -> %s\n", debugstr_w(requestString) );
4789
4790         if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4791             break;
4792
4793         /* send the request as ASCII, tack on the optional data */
4794         if (!lpOptional || redirected)
4795             dwOptionalLength = 0;
4796         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4797                                    NULL, 0, NULL, NULL );
4798         ascii_req = heap_alloc(len + dwOptionalLength);
4799         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4800                              ascii_req, len, NULL, NULL );
4801         if( lpOptional )
4802             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4803         len = (len + dwOptionalLength - 1);
4804         ascii_req[len] = 0;
4805         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4806
4807         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4808                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4809
4810         NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4811         res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4812         heap_free( ascii_req );
4813         if(res != ERROR_SUCCESS) {
4814             TRACE("send failed: %u\n", res);
4815             if(!reusing_connection)
4816                 break;
4817             http_release_netconn(request, FALSE);
4818             loop_next = TRUE;
4819             continue;
4820         }
4821
4822         request->bytesWritten = dwOptionalLength;
4823
4824         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4825                               INTERNET_STATUS_REQUEST_SENT,
4826                               &len, sizeof(DWORD));
4827
4828         if (bEndRequest)
4829         {
4830             DWORD dwBufferSize;
4831
4832             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4833                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4834     
4835             responseLen = HTTP_GetResponseHeaders(request, TRUE);
4836             /* FIXME: We should know that connection is closed before sending
4837              * headers. Otherwise wrong callbacks are executed */
4838             if(!responseLen && reusing_connection) {
4839                 TRACE("Connection closed by server, reconnecting\n");
4840                 http_release_netconn(request, FALSE);
4841                 loop_next = TRUE;
4842                 continue;
4843             }
4844
4845             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4846                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4847                                 sizeof(DWORD));
4848
4849             http_process_keep_alive(request);
4850             HTTP_ProcessCookies(request);
4851             HTTP_ProcessExpires(request);
4852             HTTP_ProcessLastModified(request);
4853
4854             res = set_content_length(request);
4855             if(res != ERROR_SUCCESS)
4856                 goto lend;
4857             if(!request->contentLength)
4858                 http_release_netconn(request, TRUE);
4859
4860             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4861             {
4862                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4863                 dwBufferSize=sizeof(szNewLocation);
4864                 switch(request->status_code) {
4865                 case HTTP_STATUS_REDIRECT:
4866                 case HTTP_STATUS_MOVED:
4867                 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4868                 case HTTP_STATUS_REDIRECT_METHOD:
4869                     if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4870                         break;
4871
4872                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4873                         request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4874                     {
4875                         heap_free(request->verb);
4876                         request->verb = heap_strdupW(szGET);
4877                     }
4878                     drain_content(request);
4879                     if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4880                     {
4881                         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4882                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4883                         res = HTTP_HandleRedirect(request, new_url);
4884                         if (res == ERROR_SUCCESS)
4885                         {
4886                             heap_free(requestString);
4887                             loop_next = TRUE;
4888                         }
4889                         heap_free( new_url );
4890                     }
4891                     redirected = TRUE;
4892                 }
4893             }
4894             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4895             {
4896                 WCHAR szAuthValue[2048];
4897                 dwBufferSize=2048;
4898                 if (request->status_code == HTTP_STATUS_DENIED)
4899                 {
4900                     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4901                     DWORD dwIndex = 0;
4902                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4903                     {
4904                         if (HTTP_DoAuthorization(request, szAuthValue,
4905                                                  &request->authInfo,
4906                                                  request->session->userName,
4907                                                  request->session->password,
4908                                                  Host->lpszValue))
4909                         {
4910                             heap_free(requestString);
4911                             loop_next = TRUE;
4912                             break;
4913                         }
4914                     }
4915
4916                     if(!loop_next) {
4917                         TRACE("Cleaning wrong authorization data\n");
4918                         destroy_authinfo(request->authInfo);
4919                         request->authInfo = NULL;
4920                     }
4921                 }
4922                 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4923                 {
4924                     DWORD dwIndex = 0;
4925                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4926                     {
4927                         if (HTTP_DoAuthorization(request, szAuthValue,
4928                                                  &request->proxyAuthInfo,
4929                                                  request->session->appInfo->proxyUsername,
4930                                                  request->session->appInfo->proxyPassword,
4931                                                  NULL))
4932                         {
4933                             loop_next = TRUE;
4934                             break;
4935                         }
4936                     }
4937
4938                     if(!loop_next) {
4939                         TRACE("Cleaning wrong proxy authorization data\n");
4940                         destroy_authinfo(request->proxyAuthInfo);
4941                         request->proxyAuthInfo = NULL;
4942                     }
4943                 }
4944             }
4945         }
4946         else
4947             res = ERROR_SUCCESS;
4948     }
4949     while (loop_next);
4950
4951     if(res == ERROR_SUCCESS)
4952         HTTP_CacheRequest(request);
4953
4954 lend:
4955     heap_free(requestString);
4956
4957     /* TODO: send notification for P3P header */
4958
4959     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4960     {
4961         if (res == ERROR_SUCCESS) {
4962             if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4963                 HTTP_ReceiveRequestData(request, TRUE);
4964             else
4965                 send_request_complete(request,
4966                         request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4967         }else {
4968                 send_request_complete(request, 0, res);
4969         }
4970     }
4971
4972     TRACE("<--\n");
4973     return res;
4974 }
4975
4976 /***********************************************************************
4977  *
4978  * Helper functions for the HttpSendRequest(Ex) functions
4979  *
4980  */
4981 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4982 {
4983     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4984     http_request_t *request = (http_request_t*) workRequest->hdr;
4985
4986     TRACE("%p\n", request);
4987
4988     HTTP_HttpSendRequestW(request, req->lpszHeader,
4989             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4990             req->dwContentLength, req->bEndRequest);
4991
4992     heap_free(req->lpszHeader);
4993 }
4994
4995
4996 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4997 {
4998     DWORD dwBufferSize;
4999     INT responseLen;
5000     DWORD res = ERROR_SUCCESS;
5001
5002     if(!request->netconn) {
5003         WARN("Not connected\n");
5004         send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5005         return ERROR_INTERNET_OPERATION_CANCELLED;
5006     }
5007
5008     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5009                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5010
5011     responseLen = HTTP_GetResponseHeaders(request, TRUE);
5012     if (!responseLen)
5013         res = ERROR_HTTP_HEADER_NOT_FOUND;
5014
5015     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5016                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5017
5018     /* process cookies here. Is this right? */
5019     http_process_keep_alive(request);
5020     HTTP_ProcessCookies(request);
5021     HTTP_ProcessExpires(request);
5022     HTTP_ProcessLastModified(request);
5023
5024     if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5025         if(!request->contentLength)
5026             http_release_netconn(request, TRUE);
5027     }
5028
5029     if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5030     {
5031         switch(request->status_code) {
5032         case HTTP_STATUS_REDIRECT:
5033         case HTTP_STATUS_MOVED:
5034         case HTTP_STATUS_REDIRECT_METHOD:
5035         case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5036             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5037             dwBufferSize=sizeof(szNewLocation);
5038             if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5039                 break;
5040
5041             if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5042                 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5043             {
5044                 heap_free(request->verb);
5045                 request->verb = heap_strdupW(szGET);
5046             }
5047             drain_content(request);
5048             if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5049             {
5050                 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5051                                       new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5052                 res = HTTP_HandleRedirect(request, new_url);
5053                 if (res == ERROR_SUCCESS)
5054                     res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5055                 heap_free( new_url );
5056             }
5057         }
5058         }
5059     }
5060
5061     if (res == ERROR_SUCCESS && request->contentLength)
5062         HTTP_ReceiveRequestData(request, TRUE);
5063     else
5064         send_request_complete(request, res == ERROR_SUCCESS, res);
5065
5066     return res;
5067 }
5068
5069 /***********************************************************************
5070  *           HttpEndRequestA (WININET.@)
5071  *
5072  * Ends an HTTP request that was started by HttpSendRequestEx
5073  *
5074  * RETURNS
5075  *    TRUE      if successful
5076  *    FALSE     on failure
5077  *
5078  */
5079 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5080         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5081 {
5082     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5083
5084     if (lpBuffersOut)
5085     {
5086         SetLastError(ERROR_INVALID_PARAMETER);
5087         return FALSE;
5088     }
5089
5090     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5091 }
5092
5093 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5094 {
5095     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5096     http_request_t *request = (http_request_t*)work->hdr;
5097
5098     TRACE("%p\n", request);
5099
5100     HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5101 }
5102
5103 /***********************************************************************
5104  *           HttpEndRequestW (WININET.@)
5105  *
5106  * Ends an HTTP request that was started by HttpSendRequestEx
5107  *
5108  * RETURNS
5109  *    TRUE      if successful
5110  *    FALSE     on failure
5111  *
5112  */
5113 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5114         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5115 {
5116     http_request_t *request;
5117     DWORD res;
5118
5119     TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5120
5121     if (lpBuffersOut)
5122     {
5123         SetLastError(ERROR_INVALID_PARAMETER);
5124         return FALSE;
5125     }
5126
5127     request = (http_request_t*) get_handle_object( hRequest );
5128
5129     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5130     {
5131         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5132         if (request)
5133             WININET_Release( &request->hdr );
5134         return FALSE;
5135     }
5136     request->hdr.dwFlags |= dwFlags;
5137
5138     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5139     {
5140         WORKREQUEST work;
5141         struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5142
5143         work.asyncproc = AsyncHttpEndRequestProc;
5144         work.hdr = WININET_AddRef( &request->hdr );
5145
5146         work_endrequest = &work.u.HttpEndRequestW;
5147         work_endrequest->dwFlags = dwFlags;
5148         work_endrequest->dwContext = dwContext;
5149
5150         INTERNET_AsyncCall(&work);
5151         res = ERROR_IO_PENDING;
5152     }
5153     else
5154         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5155
5156     WININET_Release( &request->hdr );
5157     TRACE("%u <--\n", res);
5158     if(res != ERROR_SUCCESS)
5159         SetLastError(res);
5160     return res == ERROR_SUCCESS;
5161 }
5162
5163 /***********************************************************************
5164  *           HttpSendRequestExA (WININET.@)
5165  *
5166  * Sends the specified request to the HTTP server and allows chunked
5167  * transfers.
5168  *
5169  * RETURNS
5170  *  Success: TRUE
5171  *  Failure: FALSE, call GetLastError() for more information.
5172  */
5173 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5174                                LPINTERNET_BUFFERSA lpBuffersIn,
5175                                LPINTERNET_BUFFERSA lpBuffersOut,
5176                                DWORD dwFlags, DWORD_PTR dwContext)
5177 {
5178     INTERNET_BUFFERSW BuffersInW;
5179     BOOL rc = FALSE;
5180     DWORD headerlen;
5181     LPWSTR header = NULL;
5182
5183     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5184             lpBuffersOut, dwFlags, dwContext);
5185
5186     if (lpBuffersIn)
5187     {
5188         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5189         if (lpBuffersIn->lpcszHeader)
5190         {
5191             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5192                     lpBuffersIn->dwHeadersLength,0,0);
5193             header = heap_alloc(headerlen*sizeof(WCHAR));
5194             if (!(BuffersInW.lpcszHeader = header))
5195             {
5196                 SetLastError(ERROR_OUTOFMEMORY);
5197                 return FALSE;
5198             }
5199             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5200                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5201                     header, headerlen);
5202         }
5203         else
5204             BuffersInW.lpcszHeader = NULL;
5205         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5206         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5207         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5208         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5209         BuffersInW.Next = NULL;
5210     }
5211
5212     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5213
5214     heap_free(header);
5215     return rc;
5216 }
5217
5218 /***********************************************************************
5219  *           HttpSendRequestExW (WININET.@)
5220  *
5221  * Sends the specified request to the HTTP server and allows chunked
5222  * transfers
5223  *
5224  * RETURNS
5225  *  Success: TRUE
5226  *  Failure: FALSE, call GetLastError() for more information.
5227  */
5228 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5229                    LPINTERNET_BUFFERSW lpBuffersIn,
5230                    LPINTERNET_BUFFERSW lpBuffersOut,
5231                    DWORD dwFlags, DWORD_PTR dwContext)
5232 {
5233     http_request_t *request;
5234     http_session_t *session;
5235     appinfo_t *hIC;
5236     DWORD res;
5237
5238     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5239             lpBuffersOut, dwFlags, dwContext);
5240
5241     request = (http_request_t*) get_handle_object( hRequest );
5242
5243     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5244     {
5245         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5246         goto lend;
5247     }
5248
5249     session = request->session;
5250     assert(session->hdr.htype == WH_HHTTPSESSION);
5251     hIC = session->appInfo;
5252     assert(hIC->hdr.htype == WH_HINIT);
5253
5254     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5255     {
5256         WORKREQUEST workRequest;
5257         struct WORKREQ_HTTPSENDREQUESTW *req;
5258
5259         workRequest.asyncproc = AsyncHttpSendRequestProc;
5260         workRequest.hdr = WININET_AddRef( &request->hdr );
5261         req = &workRequest.u.HttpSendRequestW;
5262         if (lpBuffersIn)
5263         {
5264             DWORD size = 0;
5265
5266             if (lpBuffersIn->lpcszHeader)
5267             {
5268                 if (lpBuffersIn->dwHeadersLength == ~0u)
5269                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5270                 else
5271                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5272
5273                 req->lpszHeader = heap_alloc(size);
5274                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5275             }
5276             else req->lpszHeader = NULL;
5277
5278             req->dwHeaderLength = size / sizeof(WCHAR);
5279             req->lpOptional = lpBuffersIn->lpvBuffer;
5280             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5281             req->dwContentLength = lpBuffersIn->dwBufferTotal;
5282         }
5283         else
5284         {
5285             req->lpszHeader = NULL;
5286             req->dwHeaderLength = 0;
5287             req->lpOptional = NULL;
5288             req->dwOptionalLength = 0;
5289             req->dwContentLength = 0;
5290         }
5291
5292         req->bEndRequest = FALSE;
5293
5294         INTERNET_AsyncCall(&workRequest);
5295         /*
5296          * This is from windows.
5297          */
5298         res = ERROR_IO_PENDING;
5299     }
5300     else
5301     {
5302         if (lpBuffersIn)
5303             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5304                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5305                                         lpBuffersIn->dwBufferTotal, FALSE);
5306         else
5307             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5308     }
5309
5310 lend:
5311     if ( request )
5312         WININET_Release( &request->hdr );
5313
5314     TRACE("<---\n");
5315     SetLastError(res);
5316     return res == ERROR_SUCCESS;
5317 }
5318
5319 /***********************************************************************
5320  *           HttpSendRequestW (WININET.@)
5321  *
5322  * Sends the specified request to the HTTP server
5323  *
5324  * RETURNS
5325  *    TRUE  on success
5326  *    FALSE on failure
5327  *
5328  */
5329 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5330         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5331 {
5332     http_request_t *request;
5333     http_session_t *session = NULL;
5334     appinfo_t *hIC = NULL;
5335     DWORD res = ERROR_SUCCESS;
5336
5337     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5338             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5339
5340     request = (http_request_t*) get_handle_object( hHttpRequest );
5341     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5342     {
5343         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5344         goto lend;
5345     }
5346
5347     session = request->session;
5348     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
5349     {
5350         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5351         goto lend;
5352     }
5353
5354     hIC = session->appInfo;
5355     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
5356     {
5357         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5358         goto lend;
5359     }
5360
5361     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5362     {
5363         WORKREQUEST workRequest;
5364         struct WORKREQ_HTTPSENDREQUESTW *req;
5365
5366         workRequest.asyncproc = AsyncHttpSendRequestProc;
5367         workRequest.hdr = WININET_AddRef( &request->hdr );
5368         req = &workRequest.u.HttpSendRequestW;
5369         if (lpszHeaders)
5370         {
5371             DWORD size;
5372
5373             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5374             else size = dwHeaderLength * sizeof(WCHAR);
5375
5376             req->lpszHeader = heap_alloc(size);
5377             memcpy(req->lpszHeader, lpszHeaders, size);
5378         }
5379         else
5380             req->lpszHeader = 0;
5381         req->dwHeaderLength = dwHeaderLength;
5382         req->lpOptional = lpOptional;
5383         req->dwOptionalLength = dwOptionalLength;
5384         req->dwContentLength = dwOptionalLength;
5385         req->bEndRequest = TRUE;
5386
5387         INTERNET_AsyncCall(&workRequest);
5388         /*
5389          * This is from windows.
5390          */
5391         res = ERROR_IO_PENDING;
5392     }
5393     else
5394     {
5395         res = HTTP_HttpSendRequestW(request, lpszHeaders,
5396                 dwHeaderLength, lpOptional, dwOptionalLength,
5397                 dwOptionalLength, TRUE);
5398     }
5399 lend:
5400     if( request )
5401         WININET_Release( &request->hdr );
5402
5403     SetLastError(res);
5404     return res == ERROR_SUCCESS;
5405 }
5406
5407 /***********************************************************************
5408  *           HttpSendRequestA (WININET.@)
5409  *
5410  * Sends the specified request to the HTTP server
5411  *
5412  * RETURNS
5413  *    TRUE  on success
5414  *    FALSE on failure
5415  *
5416  */
5417 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5418         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5419 {
5420     BOOL result;
5421     LPWSTR szHeaders=NULL;
5422     DWORD nLen=dwHeaderLength;
5423     if(lpszHeaders!=NULL)
5424     {
5425         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5426         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5427         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5428     }
5429     result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5430     heap_free(szHeaders);
5431     return result;
5432 }
5433
5434 /***********************************************************************
5435  *           HTTPSESSION_Destroy (internal)
5436  *
5437  * Deallocate session handle
5438  *
5439  */
5440 static void HTTPSESSION_Destroy(object_header_t *hdr)
5441 {
5442     http_session_t *session = (http_session_t*) hdr;
5443
5444     TRACE("%p\n", session);
5445
5446     WININET_Release(&session->appInfo->hdr);
5447
5448     heap_free(session->hostName);
5449     heap_free(session->serverName);
5450     heap_free(session->password);
5451     heap_free(session->userName);
5452 }
5453
5454 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5455 {
5456     http_session_t *ses = (http_session_t *)hdr;
5457
5458     switch(option) {
5459     case INTERNET_OPTION_HANDLE_TYPE:
5460         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5461
5462         if (*size < sizeof(ULONG))
5463             return ERROR_INSUFFICIENT_BUFFER;
5464
5465         *size = sizeof(DWORD);
5466         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5467         return ERROR_SUCCESS;
5468     case INTERNET_OPTION_CONNECT_TIMEOUT:
5469         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5470
5471         if (*size < sizeof(DWORD))
5472             return ERROR_INSUFFICIENT_BUFFER;
5473
5474         *size = sizeof(DWORD);
5475         *(DWORD *)buffer = ses->connect_timeout;
5476         return ERROR_SUCCESS;
5477
5478     case INTERNET_OPTION_SEND_TIMEOUT:
5479         TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5480
5481         if (*size < sizeof(DWORD))
5482             return ERROR_INSUFFICIENT_BUFFER;
5483
5484         *size = sizeof(DWORD);
5485         *(DWORD *)buffer = ses->send_timeout;
5486         return ERROR_SUCCESS;
5487
5488     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5489         TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5490
5491         if (*size < sizeof(DWORD))
5492             return ERROR_INSUFFICIENT_BUFFER;
5493
5494         *size = sizeof(DWORD);
5495         *(DWORD *)buffer = ses->receive_timeout;
5496         return ERROR_SUCCESS;
5497     }
5498
5499     return INET_QueryOption(hdr, option, buffer, size, unicode);
5500 }
5501
5502 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5503 {
5504     http_session_t *ses = (http_session_t*)hdr;
5505
5506     switch(option) {
5507     case INTERNET_OPTION_USERNAME:
5508     {
5509         heap_free(ses->userName);
5510         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5511         return ERROR_SUCCESS;
5512     }
5513     case INTERNET_OPTION_PASSWORD:
5514     {
5515         heap_free(ses->password);
5516         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5517         return ERROR_SUCCESS;
5518     }
5519     case INTERNET_OPTION_CONNECT_TIMEOUT:
5520     {
5521         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5522         ses->connect_timeout = *(DWORD *)buffer;
5523         return ERROR_SUCCESS;
5524     }
5525     case INTERNET_OPTION_SEND_TIMEOUT:
5526     {
5527         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5528         ses->send_timeout = *(DWORD *)buffer;
5529         return ERROR_SUCCESS;
5530     }
5531     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5532     {
5533         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5534         ses->receive_timeout = *(DWORD *)buffer;
5535         return ERROR_SUCCESS;
5536     }
5537     default: break;
5538     }
5539
5540     return INET_SetOption(hdr, option, buffer, size);
5541 }
5542
5543 static const object_vtbl_t HTTPSESSIONVtbl = {
5544     HTTPSESSION_Destroy,
5545     NULL,
5546     HTTPSESSION_QueryOption,
5547     HTTPSESSION_SetOption,
5548     NULL,
5549     NULL,
5550     NULL,
5551     NULL,
5552     NULL
5553 };
5554
5555
5556 /***********************************************************************
5557  *           HTTP_Connect  (internal)
5558  *
5559  * Create http session handle
5560  *
5561  * RETURNS
5562  *   HINTERNET a session handle on success
5563  *   NULL on failure
5564  *
5565  */
5566 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5567         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5568         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5569         DWORD dwInternalFlags, HINTERNET *ret)
5570 {
5571     http_session_t *session = NULL;
5572
5573     TRACE("-->\n");
5574
5575     if (!lpszServerName || !lpszServerName[0])
5576         return ERROR_INVALID_PARAMETER;
5577
5578     assert( hIC->hdr.htype == WH_HINIT );
5579
5580     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5581     if (!session)
5582         return ERROR_OUTOFMEMORY;
5583
5584    /*
5585     * According to my tests. The name is not resolved until a request is sent
5586     */
5587
5588     session->hdr.htype = WH_HHTTPSESSION;
5589     session->hdr.dwFlags = dwFlags;
5590     session->hdr.dwContext = dwContext;
5591     session->hdr.dwInternalFlags |= dwInternalFlags;
5592
5593     WININET_AddRef( &hIC->hdr );
5594     session->appInfo = hIC;
5595     list_add_head( &hIC->hdr.children, &session->hdr.entry );
5596
5597     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5598         if(hIC->proxyBypass)
5599             FIXME("Proxy bypass is ignored.\n");
5600     }
5601     session->serverName = heap_strdupW(lpszServerName);
5602     session->hostName = heap_strdupW(lpszServerName);
5603     if (lpszUserName && lpszUserName[0])
5604         session->userName = heap_strdupW(lpszUserName);
5605     if (lpszPassword && lpszPassword[0])
5606         session->password = heap_strdupW(lpszPassword);
5607     session->serverPort = serverPort;
5608     session->hostPort = serverPort;
5609     session->connect_timeout = hIC->connect_timeout;
5610     session->send_timeout = INFINITE;
5611     session->receive_timeout = INFINITE;
5612
5613     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5614     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5615     {
5616         INTERNET_SendCallback(&hIC->hdr, dwContext,
5617                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5618                               sizeof(HINTERNET));
5619     }
5620
5621 /*
5622  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5623  * windows
5624  */
5625
5626     TRACE("%p --> %p\n", hIC, session);
5627
5628     *ret = session->hdr.hInternet;
5629     return ERROR_SUCCESS;
5630 }
5631
5632 /***********************************************************************
5633  *           HTTP_clear_response_headers (internal)
5634  *
5635  * clear out any old response headers
5636  */
5637 static void HTTP_clear_response_headers( http_request_t *request )
5638 {
5639     DWORD i;
5640
5641     for( i=0; i<request->nCustHeaders; i++)
5642     {
5643         if( !request->custHeaders[i].lpszField )
5644             continue;
5645         if( !request->custHeaders[i].lpszValue )
5646             continue;
5647         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5648             continue;
5649         HTTP_DeleteCustomHeader( request, i );
5650         i--;
5651     }
5652 }
5653
5654 /***********************************************************************
5655  *           HTTP_GetResponseHeaders (internal)
5656  *
5657  * Read server response
5658  *
5659  * RETURNS
5660  *
5661  *   TRUE  on success
5662  *   FALSE on error
5663  */
5664 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5665 {
5666     INT cbreaks = 0;
5667     WCHAR buffer[MAX_REPLY_LEN];
5668     DWORD buflen = MAX_REPLY_LEN;
5669     BOOL bSuccess = FALSE;
5670     INT  rc = 0;
5671     char bufferA[MAX_REPLY_LEN];
5672     LPWSTR status_code = NULL, status_text = NULL;
5673     DWORD cchMaxRawHeaders = 1024;
5674     LPWSTR lpszRawHeaders = NULL;
5675     LPWSTR temp;
5676     DWORD cchRawHeaders = 0;
5677     BOOL codeHundred = FALSE;
5678
5679     TRACE("-->\n");
5680
5681     if(!request->netconn)
5682         goto lend;
5683
5684     NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5685     do {
5686         static const WCHAR szHundred[] = {'1','0','0',0};
5687         /*
5688          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5689          */
5690         buflen = MAX_REPLY_LEN;
5691         if (!read_line(request, bufferA, &buflen))
5692             goto lend;
5693
5694         /* clear old response headers (eg. from a redirect response) */
5695         if (clear) {
5696             HTTP_clear_response_headers( request );
5697             clear = FALSE;
5698         }
5699
5700         rc += buflen;
5701         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5702         /* check is this a status code line? */
5703         if (!strncmpW(buffer, g_szHttp1_0, 4))
5704         {
5705             /* split the version from the status code */
5706             status_code = strchrW( buffer, ' ' );
5707             if( !status_code )
5708                 goto lend;
5709             *status_code++=0;
5710
5711             /* split the status code from the status text */
5712             status_text = strchrW( status_code, ' ' );
5713             if( !status_text )
5714                 goto lend;
5715             *status_text++=0;
5716
5717             request->status_code = atoiW(status_code);
5718
5719             TRACE("version [%s] status code [%s] status text [%s]\n",
5720                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5721
5722             codeHundred = (!strcmpW(status_code, szHundred));
5723         }
5724         else if (!codeHundred)
5725         {
5726             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5727
5728             heap_free(request->version);
5729             heap_free(request->statusText);
5730
5731             request->status_code = HTTP_STATUS_OK;
5732             request->version = heap_strdupW(g_szHttp1_0);
5733             request->statusText = heap_strdupW(szOK);
5734
5735             heap_free(request->rawHeaders);
5736             request->rawHeaders = heap_strdupW(szDefaultHeader);
5737
5738             bSuccess = TRUE;
5739             goto lend;
5740         }
5741     } while (codeHundred);
5742
5743     /* Add status code */
5744     HTTP_ProcessHeader(request, szStatus, status_code,
5745             HTTP_ADDHDR_FLAG_REPLACE);
5746
5747     heap_free(request->version);
5748     heap_free(request->statusText);
5749
5750     request->version = heap_strdupW(buffer);
5751     request->statusText = heap_strdupW(status_text);
5752
5753     /* Restore the spaces */
5754     *(status_code-1) = ' ';
5755     *(status_text-1) = ' ';
5756
5757     /* regenerate raw headers */
5758     lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5759     if (!lpszRawHeaders) goto lend;
5760
5761     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5762         cchMaxRawHeaders *= 2;
5763     temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5764     if (temp == NULL) goto lend;
5765     lpszRawHeaders = temp;
5766     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5767     cchRawHeaders += (buflen-1);
5768     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5769     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5770     lpszRawHeaders[cchRawHeaders] = '\0';
5771
5772     /* Parse each response line */
5773     do
5774     {
5775         buflen = MAX_REPLY_LEN;
5776         if (read_line(request, bufferA, &buflen))
5777         {
5778             LPWSTR * pFieldAndValue;
5779
5780             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5781
5782             if (!bufferA[0]) break;
5783             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5784
5785             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5786             if (pFieldAndValue)
5787             {
5788                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5789                     cchMaxRawHeaders *= 2;
5790                 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5791                 if (temp == NULL) goto lend;
5792                 lpszRawHeaders = temp;
5793                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5794                 cchRawHeaders += (buflen-1);
5795                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5796                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5797                 lpszRawHeaders[cchRawHeaders] = '\0';
5798
5799                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5800                                    HTTP_ADDREQ_FLAG_ADD );
5801
5802                 HTTP_FreeTokens(pFieldAndValue);
5803             }
5804         }
5805         else
5806         {
5807             cbreaks++;
5808             if (cbreaks >= 2)
5809                break;
5810         }
5811     }while(1);
5812
5813     /* make sure the response header is terminated with an empty line.  Some apps really
5814        truly care about that empty line being there for some reason.  Just add it to the
5815        header. */
5816     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5817     {
5818         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5819         temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5820         if (temp == NULL) goto lend;
5821         lpszRawHeaders = temp;
5822     }
5823
5824     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5825
5826     heap_free(request->rawHeaders);
5827     request->rawHeaders = lpszRawHeaders;
5828     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5829     bSuccess = TRUE;
5830
5831 lend:
5832
5833     TRACE("<--\n");
5834     if (bSuccess)
5835         return rc;
5836     else
5837     {
5838         heap_free(lpszRawHeaders);
5839         return 0;
5840     }
5841 }
5842
5843 /***********************************************************************
5844  *           HTTP_InterpretHttpHeader (internal)
5845  *
5846  * Parse server response
5847  *
5848  * RETURNS
5849  *
5850  *   Pointer to array of field, value, NULL on success.
5851  *   NULL on error.
5852  */
5853 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5854 {
5855     LPWSTR * pTokenPair;
5856     LPWSTR pszColon;
5857     INT len;
5858
5859     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5860
5861     pszColon = strchrW(buffer, ':');
5862     /* must have two tokens */
5863     if (!pszColon)
5864     {
5865         HTTP_FreeTokens(pTokenPair);
5866         if (buffer[0])
5867             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5868         return NULL;
5869     }
5870
5871     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5872     if (!pTokenPair[0])
5873     {
5874         HTTP_FreeTokens(pTokenPair);
5875         return NULL;
5876     }
5877     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5878     pTokenPair[0][pszColon - buffer] = '\0';
5879
5880     /* skip colon */
5881     pszColon++;
5882     len = strlenW(pszColon);
5883     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5884     if (!pTokenPair[1])
5885     {
5886         HTTP_FreeTokens(pTokenPair);
5887         return NULL;
5888     }
5889     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5890
5891     strip_spaces(pTokenPair[0]);
5892     strip_spaces(pTokenPair[1]);
5893
5894     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5895     return pTokenPair;
5896 }
5897
5898 /***********************************************************************
5899  *           HTTP_ProcessHeader (internal)
5900  *
5901  * Stuff header into header tables according to <dwModifier>
5902  *
5903  */
5904
5905 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5906
5907 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5908 {
5909     LPHTTPHEADERW lphttpHdr = NULL;
5910     INT index = -1;
5911     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5912     DWORD res = ERROR_HTTP_INVALID_HEADER;
5913
5914     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5915
5916     /* REPLACE wins out over ADD */
5917     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5918         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5919     
5920     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5921         index = -1;
5922     else
5923         index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5924
5925     if (index >= 0)
5926     {
5927         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5928             return ERROR_HTTP_INVALID_HEADER;
5929         lphttpHdr = &request->custHeaders[index];
5930     }
5931     else if (value)
5932     {
5933         HTTPHEADERW hdr;
5934
5935         hdr.lpszField = (LPWSTR)field;
5936         hdr.lpszValue = (LPWSTR)value;
5937         hdr.wFlags = hdr.wCount = 0;
5938
5939         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5940             hdr.wFlags |= HDR_ISREQUEST;
5941
5942         return HTTP_InsertCustomHeader(request, &hdr);
5943     }
5944     /* no value to delete */
5945     else return ERROR_SUCCESS;
5946
5947     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5948             lphttpHdr->wFlags |= HDR_ISREQUEST;
5949     else
5950         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5951
5952     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5953     {
5954         HTTP_DeleteCustomHeader( request, index );
5955
5956         if (value)
5957         {
5958             HTTPHEADERW hdr;
5959
5960             hdr.lpszField = (LPWSTR)field;
5961             hdr.lpszValue = (LPWSTR)value;
5962             hdr.wFlags = hdr.wCount = 0;
5963
5964             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5965                 hdr.wFlags |= HDR_ISREQUEST;
5966
5967             return HTTP_InsertCustomHeader(request, &hdr);
5968         }
5969
5970         return ERROR_SUCCESS;
5971     }
5972     else if (dwModifier & COALESCEFLAGS)
5973     {
5974         LPWSTR lpsztmp;
5975         WCHAR ch = 0;
5976         INT len = 0;
5977         INT origlen = strlenW(lphttpHdr->lpszValue);
5978         INT valuelen = strlenW(value);
5979
5980         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5981         {
5982             ch = ',';
5983             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5984         }
5985         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5986         {
5987             ch = ';';
5988             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5989         }
5990
5991         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5992
5993         lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5994         if (lpsztmp)
5995         {
5996             lphttpHdr->lpszValue = lpsztmp;
5997     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5998             if (ch > 0)
5999             {
6000                 lphttpHdr->lpszValue[origlen] = ch;
6001                 origlen++;
6002                 lphttpHdr->lpszValue[origlen] = ' ';
6003                 origlen++;
6004             }
6005
6006             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6007             lphttpHdr->lpszValue[len] = '\0';
6008             res = ERROR_SUCCESS;
6009         }
6010         else
6011         {
6012             WARN("heap_realloc (%d bytes) failed\n",len+1);
6013             res = ERROR_OUTOFMEMORY;
6014         }
6015     }
6016     TRACE("<-- %d\n", res);
6017     return res;
6018 }
6019
6020 /***********************************************************************
6021  *           HTTP_GetCustomHeaderIndex (internal)
6022  *
6023  * Return index of custom header from header array
6024  *
6025  */
6026 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6027                                      int requested_index, BOOL request_only)
6028 {
6029     DWORD index;
6030
6031     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6032
6033     for (index = 0; index < request->nCustHeaders; index++)
6034     {
6035         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6036             continue;
6037
6038         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6039             continue;
6040
6041         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6042             continue;
6043
6044         if (requested_index == 0)
6045             break;
6046         requested_index --;
6047     }
6048
6049     if (index >= request->nCustHeaders)
6050         index = -1;
6051
6052     TRACE("Return: %d\n", index);
6053     return index;
6054 }
6055
6056
6057 /***********************************************************************
6058  *           HTTP_InsertCustomHeader (internal)
6059  *
6060  * Insert header into array
6061  *
6062  */
6063 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6064 {
6065     INT count;
6066     LPHTTPHEADERW lph = NULL;
6067
6068     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6069     count = request->nCustHeaders + 1;
6070     if (count > 1)
6071         lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6072     else
6073         lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6074
6075     if (!lph)
6076         return ERROR_OUTOFMEMORY;
6077
6078     request->custHeaders = lph;
6079     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6080     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6081     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6082     request->custHeaders[count-1].wCount= lpHdr->wCount;
6083     request->nCustHeaders++;
6084
6085     return ERROR_SUCCESS;
6086 }
6087
6088
6089 /***********************************************************************
6090  *           HTTP_DeleteCustomHeader (internal)
6091  *
6092  * Delete header from array
6093  *  If this function is called, the indexs may change.
6094  */
6095 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6096 {
6097     if( request->nCustHeaders <= 0 )
6098         return FALSE;
6099     if( index >= request->nCustHeaders )
6100         return FALSE;
6101     request->nCustHeaders--;
6102
6103     heap_free(request->custHeaders[index].lpszField);
6104     heap_free(request->custHeaders[index].lpszValue);
6105
6106     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6107              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6108     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6109
6110     return TRUE;
6111 }
6112
6113
6114 /***********************************************************************
6115  *           HTTP_VerifyValidHeader (internal)
6116  *
6117  * Verify the given header is not invalid for the given http request
6118  *
6119  */
6120 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6121 {
6122     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6123     if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6124         return ERROR_HTTP_INVALID_HEADER;
6125
6126     return ERROR_SUCCESS;
6127 }
6128
6129 /***********************************************************************
6130  *          IsHostInProxyBypassList (@)
6131  *
6132  * Undocumented
6133  *
6134  */
6135 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6136 {
6137    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6138    return FALSE;
6139 }
6140
6141 /***********************************************************************
6142  *           InternetShowSecurityInfoByURLA (@)
6143  */
6144 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6145 {
6146    FIXME("stub: %s %p\n", url, window);
6147    return FALSE;
6148 }
6149
6150 /***********************************************************************
6151  *           InternetShowSecurityInfoByURLW (@)
6152  */
6153 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6154 {
6155    FIXME("stub: %s %p\n", debugstr_w(url), window);
6156    return FALSE;
6157 }
6158
6159 /***********************************************************************
6160  *           ShowX509EncodedCertificate (@)
6161  */
6162 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6163 {
6164     PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6165         cert, len);
6166     DWORD ret;
6167
6168     if (certContext)
6169     {
6170         CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6171
6172         memset(&view, 0, sizeof(view));
6173         view.hwndParent = parent;
6174         view.pCertContext = certContext;
6175         if (CryptUIDlgViewCertificateW(&view, NULL))
6176             ret = ERROR_SUCCESS;
6177         else
6178             ret = GetLastError();
6179         CertFreeCertificateContext(certContext);
6180     }
6181     else
6182         ret = GetLastError();
6183     return ret;
6184 }