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