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