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