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