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