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