wininet: Added real implementation of InternetQueryOption(INTERNET_OPTION_REQUEST_FLAGS).
[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
223 static CRITICAL_SECTION connection_pool_cs;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug =
225 {
226     0, 0, &connection_pool_cs,
227     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
228       0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
229 };
230 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
231
232 static struct list connection_pool = LIST_INIT(connection_pool);
233 static BOOL collector_running;
234
235 void server_addref(server_t *server)
236 {
237     InterlockedIncrement(&server->ref);
238 }
239
240 void server_release(server_t *server)
241 {
242     if(InterlockedDecrement(&server->ref))
243         return;
244
245     if(!server->ref)
246         server->keep_until = GetTickCount64() + COLLECT_TIME;
247 }
248
249 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
250 {
251     server_t *iter, *server = NULL;
252
253     EnterCriticalSection(&connection_pool_cs);
254
255     LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
256         if(iter->port == port && !strcmpW(iter->name, name)) {
257             server = iter;
258             server_addref(server);
259             break;
260         }
261     }
262
263     if(!server) {
264         server = heap_alloc(sizeof(*server));
265         if(server) {
266             server->addr_len = 0;
267             server->ref = 1;
268             server->port = port;
269             list_init(&server->conn_pool);
270             server->name = heap_strdupW(name);
271             if(server->name) {
272                 list_add_head(&connection_pool, &server->entry);
273             }else {
274                 heap_free(server);
275                 server = NULL;
276             }
277         }
278     }
279
280     LeaveCriticalSection(&connection_pool_cs);
281
282     return server;
283 }
284
285 BOOL collect_connections(BOOL collect_all)
286 {
287     netconn_t *netconn, *netconn_safe;
288     server_t *server, *server_safe;
289     BOOL remaining = FALSE;
290     DWORD64 now;
291
292     now = GetTickCount64();
293
294     LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
295         LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
296             if(collect_all || netconn->keep_until < now) {
297                 TRACE("freeing %p\n", netconn);
298                 list_remove(&netconn->pool_entry);
299                 free_netconn(netconn);
300             }else {
301                 remaining = TRUE;
302             }
303         }
304
305         if(!server->ref) {
306             if(collect_all || server->keep_until < now) {
307                 list_remove(&server->entry);
308
309                 heap_free(server->name);
310                 heap_free(server);
311             }else {
312                 remaining = TRUE;
313             }
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(FALSE);
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_SYSTEM_DEFAULT, 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->session->serverName : 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     static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1702     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1703     static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704
1705     memset( &UrlComponents, 0, sizeof UrlComponents );
1706     UrlComponents.dwStructSize = sizeof UrlComponents;
1707     UrlComponents.lpszHostName = buf;
1708     UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1709
1710     if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1711         return FALSE;
1712     if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1713                                  protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1714         sprintfW(proxy, szFormat, protoProxy);
1715     else
1716         strcpyW(proxy, protoProxy);
1717     if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1718         return FALSE;
1719     if( UrlComponents.dwHostNameLength == 0 )
1720         return FALSE;
1721
1722     if( !request->path )
1723         request->path = szNul;
1724
1725     if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1726         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1727
1728     heap_free(session->serverName);
1729     session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1730     session->serverPort = UrlComponents.nPort;
1731
1732     TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1733     return TRUE;
1734 }
1735
1736 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1737 {
1738     socklen_t addr_len;
1739     const void *addr;
1740
1741     if(server->addr_len)
1742         return ERROR_SUCCESS;
1743
1744     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1745                           INTERNET_STATUS_RESOLVING_NAME,
1746                           server->name,
1747                           (strlenW(server->name)+1) * sizeof(WCHAR));
1748
1749     addr_len = sizeof(server->addr);
1750     if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1751         return ERROR_INTERNET_NAME_NOT_RESOLVED;
1752
1753     switch(server->addr.ss_family) {
1754     case AF_INET:
1755         addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1756         break;
1757     case AF_INET6:
1758         addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1759         break;
1760     default:
1761         WARN("unsupported family %d\n", server->addr.ss_family);
1762         return ERROR_INTERNET_NAME_NOT_RESOLVED;
1763     }
1764
1765     server->addr_len = addr_len;
1766     inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1767     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1768                           INTERNET_STATUS_NAME_RESOLVED,
1769                           server->addr_str, strlen(server->addr_str)+1);
1770
1771     TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1772     return ERROR_SUCCESS;
1773 }
1774
1775 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1776 {
1777     static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1778     static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1779     static const WCHAR slash[] = { '/',0 };
1780     LPHTTPHEADERW host_header;
1781     LPCWSTR scheme;
1782
1783     host_header = HTTP_GetHeader(req, hostW);
1784     if(!host_header)
1785         return FALSE;
1786
1787     if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1788         scheme = https;
1789     else
1790         scheme = http;
1791     strcpyW(buf, scheme);
1792     strcatW(buf, host_header->lpszValue);
1793     if (req->path[0] != '/')
1794         strcatW(buf, slash);
1795     strcatW(buf, req->path);
1796     return TRUE;
1797 }
1798
1799
1800 /***********************************************************************
1801  *           HTTPREQ_Destroy (internal)
1802  *
1803  * Deallocate request handle
1804  *
1805  */
1806 static void HTTPREQ_Destroy(object_header_t *hdr)
1807 {
1808     http_request_t *request = (http_request_t*) hdr;
1809     DWORD i;
1810
1811     TRACE("\n");
1812
1813     if(request->hCacheFile) {
1814         WCHAR url[INTERNET_MAX_URL_LENGTH];
1815
1816         CloseHandle(request->hCacheFile);
1817
1818         if(HTTP_GetRequestURL(request, url)) {
1819             DWORD headersLen;
1820
1821             headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1822             CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1823                     request->last_modified, NORMAL_CACHE_ENTRY,
1824                     request->rawHeaders, headersLen, NULL, 0);
1825         }
1826     }
1827     heap_free(request->cacheFile);
1828
1829     request->read_section.DebugInfo->Spare[0] = 0;
1830     DeleteCriticalSection( &request->read_section );
1831     WININET_Release(&request->session->hdr);
1832
1833     destroy_authinfo(request->authInfo);
1834     destroy_authinfo(request->proxyAuthInfo);
1835
1836     heap_free(request->path);
1837     heap_free(request->verb);
1838     heap_free(request->rawHeaders);
1839     heap_free(request->version);
1840     heap_free(request->statusText);
1841
1842     for (i = 0; i < request->nCustHeaders; i++)
1843     {
1844         heap_free(request->custHeaders[i].lpszField);
1845         heap_free(request->custHeaders[i].lpszValue);
1846     }
1847     destroy_data_stream(request->data_stream);
1848     heap_free(request->custHeaders);
1849 }
1850
1851 static void http_release_netconn(http_request_t *req, BOOL reuse)
1852 {
1853     TRACE("%p %p\n",req, req->netconn);
1854
1855     if(!req->netconn)
1856         return;
1857
1858     if(reuse && req->netconn->keep_alive) {
1859         BOOL run_collector;
1860
1861         EnterCriticalSection(&connection_pool_cs);
1862
1863         list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1864         req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1865         req->netconn = NULL;
1866
1867         run_collector = !collector_running;
1868         collector_running = TRUE;
1869
1870         LeaveCriticalSection(&connection_pool_cs);
1871
1872         if(run_collector) {
1873             HANDLE thread = NULL;
1874             HMODULE module;
1875
1876             GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1877             if(module)
1878                 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1879             if(!thread) {
1880                 EnterCriticalSection(&connection_pool_cs);
1881                 collector_running = FALSE;
1882                 LeaveCriticalSection(&connection_pool_cs);
1883
1884                 if(module)
1885                     FreeLibrary(module);
1886             }
1887             else
1888                 CloseHandle(thread);
1889         }
1890         return;
1891     }
1892
1893     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1894                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1895
1896     free_netconn(req->netconn);
1897     req->netconn = NULL;
1898
1899     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1900                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1901 }
1902
1903 static void drain_content(http_request_t *req)
1904 {
1905     BOOL try_reuse;
1906
1907     if (!req->netconn) return;
1908
1909     if (req->contentLength == -1)
1910         try_reuse = FALSE;
1911     else if(!strcmpW(req->verb, szHEAD))
1912         try_reuse = TRUE;
1913     else
1914         try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1915
1916     http_release_netconn(req, try_reuse);
1917 }
1918
1919 static BOOL HTTP_KeepAlive(http_request_t *request)
1920 {
1921     WCHAR szVersion[10];
1922     WCHAR szConnectionResponse[20];
1923     DWORD dwBufferSize = sizeof(szVersion);
1924     BOOL keepalive = FALSE;
1925
1926     /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1927      * the connection is keep-alive by default */
1928     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1929         && !strcmpiW(szVersion, g_szHttp1_1))
1930     {
1931         keepalive = TRUE;
1932     }
1933
1934     dwBufferSize = sizeof(szConnectionResponse);
1935     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1936         || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1937     {
1938         keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1939     }
1940
1941     return keepalive;
1942 }
1943
1944 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1945 {
1946     http_request_t *req = (http_request_t*)hdr;
1947
1948     drain_content(req);
1949 }
1950
1951 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1952 {
1953     http_request_t *req = (http_request_t*)hdr;
1954
1955     switch(option) {
1956     case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1957     {
1958         http_session_t *session = req->session;
1959         INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1960
1961         FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1962
1963         if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1964             return ERROR_INSUFFICIENT_BUFFER;
1965         *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1966         /* FIXME: can't get a SOCKET from our connection since we don't use
1967          * winsock
1968          */
1969         info->Socket = 0;
1970         /* FIXME: get source port from req->netConnection */
1971         info->SourcePort = 0;
1972         info->DestPort = session->hostPort;
1973         info->Flags = 0;
1974         if (HTTP_KeepAlive(req))
1975             info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1976         if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1977             info->Flags |= IDSI_FLAG_PROXY;
1978         if (req->netconn->useSSL)
1979             info->Flags |= IDSI_FLAG_SECURE;
1980
1981         return ERROR_SUCCESS;
1982     }
1983
1984     case INTERNET_OPTION_SECURITY_FLAGS:
1985     {
1986         DWORD flags;
1987
1988         if (*size < sizeof(ULONG))
1989             return ERROR_INSUFFICIENT_BUFFER;
1990
1991         *size = sizeof(DWORD);
1992         flags = 0;
1993         if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1994             flags |= SECURITY_FLAG_SECURE;
1995         flags |= req->security_flags;
1996         if(req->netconn) {
1997             int bits = NETCON_GetCipherStrength(req->netconn);
1998             if (bits >= 128)
1999                 flags |= SECURITY_FLAG_STRENGTH_STRONG;
2000             else if (bits >= 56)
2001                 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
2002             else
2003                 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2004         }
2005         *(DWORD *)buffer = flags;
2006         return ERROR_SUCCESS;
2007     }
2008
2009     case INTERNET_OPTION_HANDLE_TYPE:
2010         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011
2012         if (*size < sizeof(ULONG))
2013             return ERROR_INSUFFICIENT_BUFFER;
2014
2015         *size = sizeof(DWORD);
2016         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2017         return ERROR_SUCCESS;
2018
2019     case INTERNET_OPTION_URL: {
2020         WCHAR url[INTERNET_MAX_URL_LENGTH];
2021         HTTPHEADERW *host;
2022         DWORD len;
2023         WCHAR *pch;
2024
2025         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2026
2027         TRACE("INTERNET_OPTION_URL\n");
2028
2029         host = HTTP_GetHeader(req, hostW);
2030         strcpyW(url, httpW);
2031         strcatW(url, host->lpszValue);
2032         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2033             *pch = 0;
2034         strcatW(url, req->path);
2035
2036         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2037
2038         if(unicode) {
2039             len = (strlenW(url)+1) * sizeof(WCHAR);
2040             if(*size < len)
2041                 return ERROR_INSUFFICIENT_BUFFER;
2042
2043             *size = len;
2044             strcpyW(buffer, url);
2045             return ERROR_SUCCESS;
2046         }else {
2047             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2048             if(len > *size)
2049                 return ERROR_INSUFFICIENT_BUFFER;
2050
2051             *size = len;
2052             return ERROR_SUCCESS;
2053         }
2054     }
2055
2056     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2057         INTERNET_CACHE_ENTRY_INFOW *info;
2058         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2059         WCHAR url[INTERNET_MAX_URL_LENGTH];
2060         DWORD nbytes, error;
2061         BOOL ret;
2062
2063         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064
2065         if (*size < sizeof(*ts))
2066         {
2067             *size = sizeof(*ts);
2068             return ERROR_INSUFFICIENT_BUFFER;
2069         }
2070         nbytes = 0;
2071         HTTP_GetRequestURL(req, url);
2072         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2073         error = GetLastError();
2074         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2075         {
2076             if (!(info = heap_alloc(nbytes)))
2077                 return ERROR_OUTOFMEMORY;
2078
2079             GetUrlCacheEntryInfoW(url, info, &nbytes);
2080
2081             ts->ftExpires = info->ExpireTime;
2082             ts->ftLastModified = info->LastModifiedTime;
2083
2084             heap_free(info);
2085             *size = sizeof(*ts);
2086             return ERROR_SUCCESS;
2087         }
2088         return error;
2089     }
2090
2091     case INTERNET_OPTION_DATAFILE_NAME: {
2092         DWORD req_size;
2093
2094         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095
2096         if(!req->cacheFile) {
2097             *size = 0;
2098             return ERROR_INTERNET_ITEM_NOT_FOUND;
2099         }
2100
2101         if(unicode) {
2102             req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2103             if(*size < req_size)
2104                 return ERROR_INSUFFICIENT_BUFFER;
2105
2106             *size = req_size;
2107             memcpy(buffer, req->cacheFile, *size);
2108             return ERROR_SUCCESS;
2109         }else {
2110             req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2111             if (req_size > *size)
2112                 return ERROR_INSUFFICIENT_BUFFER;
2113
2114             *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2115                     -1, buffer, *size, NULL, NULL);
2116             return ERROR_SUCCESS;
2117         }
2118     }
2119
2120     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2121         PCCERT_CONTEXT context;
2122
2123         if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2124             *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2125             return ERROR_INSUFFICIENT_BUFFER;
2126         }
2127
2128         context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2129         if(context) {
2130             INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2131             DWORD len;
2132
2133             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2134             info->ftExpiry = context->pCertInfo->NotAfter;
2135             info->ftStart = context->pCertInfo->NotBefore;
2136             len = CertNameToStrA(context->dwCertEncodingType,
2137                      &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2138             info->lpszSubjectInfo = LocalAlloc(0, len);
2139             if(info->lpszSubjectInfo)
2140                 CertNameToStrA(context->dwCertEncodingType,
2141                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2142                          info->lpszSubjectInfo, len);
2143             len = CertNameToStrA(context->dwCertEncodingType,
2144                      &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2145             info->lpszIssuerInfo = LocalAlloc(0, len);
2146             if(info->lpszIssuerInfo)
2147                 CertNameToStrA(context->dwCertEncodingType,
2148                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2149                          info->lpszIssuerInfo, len);
2150             info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2151             CertFreeCertificateContext(context);
2152             return ERROR_SUCCESS;
2153         }
2154     }
2155     case INTERNET_OPTION_CONNECT_TIMEOUT:
2156         if (*size < sizeof(DWORD))
2157             return ERROR_INSUFFICIENT_BUFFER;
2158
2159         *size = sizeof(DWORD);
2160         *(DWORD *)buffer = req->connect_timeout;
2161         return ERROR_SUCCESS;
2162     case INTERNET_OPTION_REQUEST_FLAGS: {
2163         DWORD flags = 0;
2164
2165         if (*size < sizeof(DWORD))
2166             return ERROR_INSUFFICIENT_BUFFER;
2167
2168         /* FIXME: Add support for:
2169          * INTERNET_REQFLAG_FROM_CACHE
2170          * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2171          */
2172
2173         if(req->session->appInfo->proxy)
2174             flags |= INTERNET_REQFLAG_VIA_PROXY;
2175         if(!req->rawHeaders)
2176             flags |= INTERNET_REQFLAG_NO_HEADERS;
2177
2178         TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2179
2180         *size = sizeof(DWORD);
2181         *(DWORD*)buffer = flags;
2182         return ERROR_SUCCESS;
2183     }
2184     }
2185
2186     return INET_QueryOption(hdr, option, buffer, size, unicode);
2187 }
2188
2189 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2190 {
2191     http_request_t *req = (http_request_t*)hdr;
2192
2193     switch(option) {
2194     case INTERNET_OPTION_SECURITY_FLAGS:
2195     {
2196         DWORD flags;
2197
2198         if (!buffer || size != sizeof(DWORD))
2199             return ERROR_INVALID_PARAMETER;
2200         flags = *(DWORD *)buffer;
2201         TRACE("%08x\n", flags);
2202         req->security_flags = flags;
2203         if(req->netconn)
2204             req->netconn->security_flags = flags;
2205         return ERROR_SUCCESS;
2206     }
2207     case INTERNET_OPTION_CONNECT_TIMEOUT:
2208         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2209         req->connect_timeout = *(DWORD *)buffer;
2210         return ERROR_SUCCESS;
2211
2212     case INTERNET_OPTION_SEND_TIMEOUT:
2213         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2214         req->send_timeout = *(DWORD *)buffer;
2215         return ERROR_SUCCESS;
2216
2217     case INTERNET_OPTION_RECEIVE_TIMEOUT:
2218         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2219         req->receive_timeout = *(DWORD *)buffer;
2220         return ERROR_SUCCESS;
2221
2222     case INTERNET_OPTION_USERNAME:
2223         heap_free(req->session->userName);
2224         if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2225         return ERROR_SUCCESS;
2226
2227     case INTERNET_OPTION_PASSWORD:
2228         heap_free(req->session->password);
2229         if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2230         return ERROR_SUCCESS;
2231     case INTERNET_OPTION_HTTP_DECODING:
2232         if(size != sizeof(BOOL))
2233             return ERROR_INVALID_PARAMETER;
2234         req->decoding = *(BOOL*)buffer;
2235         return ERROR_SUCCESS;
2236     }
2237
2238     return INET_SetOption(hdr, option, buffer, size);
2239 }
2240
2241 /* read some more data into the read buffer (the read section must be held) */
2242 static DWORD read_more_data( http_request_t *req, int maxlen )
2243 {
2244     DWORD res;
2245     int len;
2246
2247     if (req->read_pos)
2248     {
2249         /* move existing data to the start of the buffer */
2250         if(req->read_size)
2251             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2252         req->read_pos = 0;
2253     }
2254
2255     if (maxlen == -1) maxlen = sizeof(req->read_buf);
2256
2257     res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2258                        maxlen - req->read_size, 0, &len );
2259     if(res == ERROR_SUCCESS)
2260         req->read_size += len;
2261
2262     return res;
2263 }
2264
2265 /* remove some amount of data from the read buffer (the read section must be held) */
2266 static void remove_data( http_request_t *req, int count )
2267 {
2268     if (!(req->read_size -= count)) req->read_pos = 0;
2269     else req->read_pos += count;
2270 }
2271
2272 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2273 {
2274     int count, bytes_read, pos = 0;
2275     DWORD res;
2276
2277     EnterCriticalSection( &req->read_section );
2278     for (;;)
2279     {
2280         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2281
2282         if (eol)
2283         {
2284             count = eol - (req->read_buf + req->read_pos);
2285             bytes_read = count + 1;
2286         }
2287         else count = bytes_read = req->read_size;
2288
2289         count = min( count, *len - pos );
2290         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2291         pos += count;
2292         remove_data( req, bytes_read );
2293         if (eol) break;
2294
2295         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2296         {
2297             *len = 0;
2298             TRACE( "returning empty string %u\n", res);
2299             LeaveCriticalSection( &req->read_section );
2300             INTERNET_SetLastError(res);
2301             return FALSE;
2302         }
2303     }
2304     LeaveCriticalSection( &req->read_section );
2305
2306     if (pos < *len)
2307     {
2308         if (pos && buffer[pos - 1] == '\r') pos--;
2309         *len = pos + 1;
2310     }
2311     buffer[*len - 1] = 0;
2312     TRACE( "returning %s\n", debugstr_a(buffer));
2313     return TRUE;
2314 }
2315
2316 /* check if we have reached the end of the data to read (the read section must be held) */
2317 static BOOL end_of_read_data( http_request_t *req )
2318 {
2319     return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2320 }
2321
2322 /* fetch some more data into the read buffer (the read section must be held) */
2323 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2324 {
2325     DWORD res, read=0;
2326
2327     if(req->read_size == sizeof(req->read_buf))
2328         return ERROR_SUCCESS;
2329
2330     if(req->read_pos) {
2331         if(req->read_size)
2332             memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2333         req->read_pos = 0;
2334     }
2335
2336     res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2337             sizeof(req->read_buf)-req->read_size, &read, read_mode);
2338     req->read_size += read;
2339
2340     TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2341     if(read_bytes)
2342         *read_bytes = read;
2343     return res;
2344 }
2345
2346 /* return the size of data available to be read immediately (the read section must be held) */
2347 static DWORD get_avail_data( http_request_t *req )
2348 {
2349     return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2350 }
2351
2352 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2353 {
2354     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2355     DWORD avail = 0;
2356
2357     if(req->netconn)
2358         NETCON_query_data_available(req->netconn, &avail);
2359     return netconn_stream->content_length == ~0u
2360         ? avail
2361         : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2362 }
2363
2364 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2365 {
2366     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2367     return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2368 }
2369
2370 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2371         DWORD *read, read_mode_t read_mode)
2372 {
2373     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2374     int len = 0;
2375
2376     size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2377
2378     if(read_mode == READMODE_NOBLOCK)
2379         size = min(size, netconn_get_avail_data(stream, req));
2380
2381     if(size && req->netconn) {
2382         if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2383             len = 0;
2384         if(!len)
2385             netconn_stream->content_length = netconn_stream->content_read;
2386     }
2387
2388     netconn_stream->content_read += *read = len;
2389     TRACE("read %u bytes\n", len);
2390     return ERROR_SUCCESS;
2391 }
2392
2393 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2394 {
2395     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2396     BYTE buf[1024];
2397     DWORD avail;
2398     int len;
2399
2400     if(netconn_end_of_data(stream, req))
2401         return TRUE;
2402
2403     do {
2404         avail = netconn_get_avail_data(stream, req);
2405         if(!avail)
2406             return FALSE;
2407
2408         if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2409             return FALSE;
2410
2411         netconn_stream->content_read += len;
2412     }while(netconn_stream->content_read < netconn_stream->content_length);
2413
2414     return TRUE;
2415 }
2416
2417 static void netconn_destroy(data_stream_t *stream)
2418 {
2419 }
2420
2421 static const data_stream_vtbl_t netconn_stream_vtbl = {
2422     netconn_get_avail_data,
2423     netconn_end_of_data,
2424     netconn_read,
2425     netconn_drain_content,
2426     netconn_destroy
2427 };
2428
2429 /* read some more data into the read buffer (the read section must be held) */
2430 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2431 {
2432     DWORD res;
2433     int len;
2434
2435     if (stream->buf_pos)
2436     {
2437         /* move existing data to the start of the buffer */
2438         if(stream->buf_size)
2439             memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2440         stream->buf_pos = 0;
2441     }
2442
2443     if (maxlen == -1) maxlen = sizeof(stream->buf);
2444
2445     res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2446                        maxlen - stream->buf_size, 0, &len );
2447     if(res == ERROR_SUCCESS)
2448         stream->buf_size += len;
2449
2450     return res;
2451 }
2452
2453 /* remove some amount of data from the read buffer (the read section must be held) */
2454 static void remove_chunked_data(chunked_stream_t *stream, int count)
2455 {
2456     if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2457     else stream->buf_pos += count;
2458 }
2459
2460 /* discard data contents until we reach end of line (the read section must be held) */
2461 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2462 {
2463     DWORD res;
2464
2465     do
2466     {
2467         BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2468         if (eol)
2469         {
2470             remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2471             break;
2472         }
2473         stream->buf_pos = stream->buf_size = 0;  /* discard everything */
2474         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2475     } while (stream->buf_size);
2476     return ERROR_SUCCESS;
2477 }
2478
2479 /* read the size of the next chunk (the read section must be held) */
2480 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2481 {
2482     /* TODOO */
2483     DWORD chunk_size = 0, res;
2484
2485     if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2486         return res;
2487
2488     for (;;)
2489     {
2490         while (stream->buf_size)
2491         {
2492             char ch = stream->buf[stream->buf_pos];
2493             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2494             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2495             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2496             else if (ch == ';' || ch == '\r' || ch == '\n')
2497             {
2498                 TRACE( "reading %u byte chunk\n", chunk_size );
2499                 stream->chunk_size = chunk_size;
2500                 req->contentLength += chunk_size;
2501                 return discard_chunked_eol(stream, req);
2502             }
2503             remove_chunked_data(stream, 1);
2504         }
2505         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2506         if (!stream->buf_size)
2507         {
2508             stream->chunk_size = 0;
2509             return ERROR_SUCCESS;
2510         }
2511     }
2512 }
2513
2514 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2515 {
2516     /* Allow reading only from read buffer */
2517     return 0;
2518 }
2519
2520 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2521 {
2522     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2523     return !chunked_stream->chunk_size;
2524 }
2525
2526 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2527         DWORD *read, read_mode_t read_mode)
2528 {
2529     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2530     DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2531
2532     if(chunked_stream->chunk_size == ~0u) {
2533         res = start_next_chunk(chunked_stream, req);
2534         if(res != ERROR_SUCCESS)
2535             return res;
2536     }
2537
2538     while(size && chunked_stream->chunk_size) {
2539         if(chunked_stream->buf_size) {
2540             read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2541
2542             /* this could block */
2543             if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2544                 break;
2545
2546             memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2547             remove_chunked_data(chunked_stream, read_bytes);
2548         }else {
2549             read_bytes = min(size, chunked_stream->chunk_size);
2550
2551             if(read_mode == READMODE_NOBLOCK) {
2552                 DWORD avail;
2553
2554                 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2555                     break;
2556                 if(read_bytes > avail)
2557                     read_bytes = avail;
2558
2559                 /* this could block */
2560                 if(read_bytes == chunked_stream->chunk_size)
2561                     break;
2562             }
2563
2564             res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2565             if(res != ERROR_SUCCESS)
2566                 break;
2567         }
2568
2569         chunked_stream->chunk_size -= read_bytes;
2570         size -= read_bytes;
2571         ret_read += read_bytes;
2572         if(!chunked_stream->chunk_size) {
2573             assert(read_mode != READMODE_NOBLOCK);
2574             res = start_next_chunk(chunked_stream, req);
2575             if(res != ERROR_SUCCESS)
2576                 break;
2577         }
2578
2579         if(read_mode == READMODE_ASYNC)
2580             read_mode = READMODE_NOBLOCK;
2581     }
2582
2583     TRACE("read %u bytes\n", ret_read);
2584     *read = ret_read;
2585     return res;
2586 }
2587
2588 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2589 {
2590     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2591
2592     /* FIXME: we can do better */
2593     return !chunked_stream->chunk_size;
2594 }
2595
2596 static void chunked_destroy(data_stream_t *stream)
2597 {
2598     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2599     heap_free(chunked_stream);
2600 }
2601
2602 static const data_stream_vtbl_t chunked_stream_vtbl = {
2603     chunked_get_avail_data,
2604     chunked_end_of_data,
2605     chunked_read,
2606     chunked_drain_content,
2607     chunked_destroy
2608 };
2609
2610 /* set the request content length based on the headers */
2611 static DWORD set_content_length(http_request_t *request)
2612 {
2613     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2614     WCHAR encoding[20];
2615     DWORD size;
2616
2617     if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2618         request->contentLength = request->netconn_stream.content_length = 0;
2619         return ERROR_SUCCESS;
2620     }
2621
2622     size = sizeof(request->contentLength);
2623     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2624                             &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2625         request->contentLength = ~0u;
2626     request->netconn_stream.content_length = request->contentLength;
2627     request->netconn_stream.content_read = request->read_size;
2628
2629     size = sizeof(encoding);
2630     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2631         !strcmpiW(encoding, szChunked))
2632     {
2633         chunked_stream_t *chunked_stream;
2634
2635         chunked_stream = heap_alloc(sizeof(*chunked_stream));
2636         if(!chunked_stream)
2637             return ERROR_OUTOFMEMORY;
2638
2639         chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2640         chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2641         chunked_stream->chunk_size = ~0u;
2642
2643         if(request->read_size) {
2644             memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2645             chunked_stream->buf_size = request->read_size;
2646             request->read_size = request->read_pos = 0;
2647         }
2648
2649         request->data_stream = &chunked_stream->data_stream;
2650         request->contentLength = ~0u;
2651         request->read_chunked = TRUE;
2652     }
2653
2654     if(request->decoding) {
2655         int encoding_idx;
2656
2657         static const WCHAR gzipW[] = {'g','z','i','p',0};
2658
2659         encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2660         if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2661             return init_gzip_stream(request);
2662     }
2663
2664     return ERROR_SUCCESS;
2665 }
2666
2667 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2668 {
2669     INTERNET_ASYNC_RESULT iar;
2670
2671     iar.dwResult = result;
2672     iar.dwError = error;
2673
2674     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2675             sizeof(INTERNET_ASYNC_RESULT));
2676 }
2677
2678 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2679 {
2680     DWORD res, read = 0, avail = 0;
2681     read_mode_t mode;
2682
2683     TRACE("%p\n", req);
2684
2685     EnterCriticalSection( &req->read_section );
2686
2687     mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2688     res = refill_read_buffer(req, mode, &read);
2689     if(res == ERROR_SUCCESS && !first_notif)
2690         avail = get_avail_data(req);
2691
2692     LeaveCriticalSection( &req->read_section );
2693
2694     if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2695         WARN("res %u read %u, closing connection\n", res, read);
2696         http_release_netconn(req, FALSE);
2697     }
2698
2699     if(res == ERROR_SUCCESS)
2700         send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2701     else
2702         send_request_complete(req, 0, res);
2703 }
2704
2705 /* read data from the http connection (the read section must be held) */
2706 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2707 {
2708     DWORD current_read = 0, ret_read = 0;
2709     read_mode_t read_mode;
2710     DWORD res = ERROR_SUCCESS;
2711
2712     read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2713
2714     EnterCriticalSection( &req->read_section );
2715
2716     if(req->read_size) {
2717         ret_read = min(size, req->read_size);
2718         memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2719         req->read_size -= ret_read;
2720         req->read_pos += ret_read;
2721         if(read_mode == READMODE_ASYNC)
2722             read_mode = READMODE_NOBLOCK;
2723     }
2724
2725     if(ret_read < size) {
2726         res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2727         ret_read += current_read;
2728     }
2729
2730     LeaveCriticalSection( &req->read_section );
2731
2732     *read = ret_read;
2733     TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2734
2735     if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2736         BOOL res;
2737         DWORD written;
2738
2739         res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2740         if(!res)
2741             WARN("WriteFile failed: %u\n", GetLastError());
2742     }
2743
2744     if(size && !ret_read)
2745         http_release_netconn(req, res == ERROR_SUCCESS);
2746
2747     return res;
2748 }
2749
2750
2751 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2752 {
2753     http_request_t *req = (http_request_t*)hdr;
2754     DWORD res;
2755
2756     EnterCriticalSection( &req->read_section );
2757     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2758         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2759
2760     res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2761     if(res == ERROR_SUCCESS)
2762         res = hdr->dwError;
2763     LeaveCriticalSection( &req->read_section );
2764
2765     return res;
2766 }
2767
2768 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2769 {
2770     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2771     http_request_t *req = (http_request_t*)workRequest->hdr;
2772     DWORD res;
2773
2774     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2775
2776     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2777             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2778
2779     send_request_complete(req, res == ERROR_SUCCESS, res);
2780 }
2781
2782 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2783         DWORD flags, DWORD_PTR context)
2784 {
2785     http_request_t *req = (http_request_t*)hdr;
2786     DWORD res, size, read, error = ERROR_SUCCESS;
2787
2788     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2789         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2790
2791     if (buffers->dwStructSize != sizeof(*buffers))
2792         return ERROR_INVALID_PARAMETER;
2793
2794     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2795
2796     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2797     {
2798         WORKREQUEST workRequest;
2799
2800         if (TryEnterCriticalSection( &req->read_section ))
2801         {
2802             if (get_avail_data(req))
2803             {
2804                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2805                                    &buffers->dwBufferLength, FALSE);
2806                 size = buffers->dwBufferLength;
2807                 LeaveCriticalSection( &req->read_section );
2808                 goto done;
2809             }
2810             LeaveCriticalSection( &req->read_section );
2811         }
2812
2813         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2814         workRequest.hdr = WININET_AddRef(&req->hdr);
2815         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2816
2817         INTERNET_AsyncCall(&workRequest);
2818
2819         return ERROR_IO_PENDING;
2820     }
2821
2822     read = 0;
2823     size = buffers->dwBufferLength;
2824
2825     EnterCriticalSection( &req->read_section );
2826     if(hdr->dwError == ERROR_SUCCESS)
2827         hdr->dwError = INTERNET_HANDLE_IN_USE;
2828     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2829         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2830
2831     while(1) {
2832         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2833                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2834         if(res != ERROR_SUCCESS)
2835             break;
2836
2837         read += buffers->dwBufferLength;
2838         if(read == size || end_of_read_data(req))
2839             break;
2840
2841         LeaveCriticalSection( &req->read_section );
2842
2843         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2844                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2845         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2846                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2847
2848         EnterCriticalSection( &req->read_section );
2849     }
2850
2851     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2852         hdr->dwError = ERROR_SUCCESS;
2853     else
2854         error = hdr->dwError;
2855
2856     LeaveCriticalSection( &req->read_section );
2857     size = buffers->dwBufferLength;
2858     buffers->dwBufferLength = read;
2859
2860 done:
2861     if (res == ERROR_SUCCESS) {
2862         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2863                 &size, sizeof(size));
2864     }
2865
2866     return res==ERROR_SUCCESS ? error : res;
2867 }
2868
2869 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2870 {
2871     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2872     http_request_t *req = (http_request_t*)workRequest->hdr;
2873     DWORD res;
2874
2875     TRACE("INTERNETREADFILEEXW %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_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2884         DWORD flags, DWORD_PTR context)
2885 {
2886
2887     http_request_t *req = (http_request_t*)hdr;
2888     DWORD res, size, read, error = ERROR_SUCCESS;
2889
2890     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2891         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2892
2893     if (buffers->dwStructSize != sizeof(*buffers))
2894         return ERROR_INVALID_PARAMETER;
2895
2896     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2897
2898     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2899     {
2900         WORKREQUEST workRequest;
2901
2902         if (TryEnterCriticalSection( &req->read_section ))
2903         {
2904             if (get_avail_data(req))
2905             {
2906                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2907                                    &buffers->dwBufferLength, FALSE);
2908                 size = buffers->dwBufferLength;
2909                 LeaveCriticalSection( &req->read_section );
2910                 goto done;
2911             }
2912             LeaveCriticalSection( &req->read_section );
2913         }
2914
2915         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2916         workRequest.hdr = WININET_AddRef(&req->hdr);
2917         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2918
2919         INTERNET_AsyncCall(&workRequest);
2920
2921         return ERROR_IO_PENDING;
2922     }
2923
2924     read = 0;
2925     size = buffers->dwBufferLength;
2926
2927     EnterCriticalSection( &req->read_section );
2928     if(hdr->dwError == ERROR_SUCCESS)
2929         hdr->dwError = INTERNET_HANDLE_IN_USE;
2930     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2931         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2932
2933     while(1) {
2934         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2935                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2936         if(res != ERROR_SUCCESS)
2937             break;
2938
2939         read += buffers->dwBufferLength;
2940         if(read == size || end_of_read_data(req))
2941             break;
2942
2943         LeaveCriticalSection( &req->read_section );
2944
2945         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2946                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2947         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2948                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2949
2950         EnterCriticalSection( &req->read_section );
2951     }
2952
2953     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2954         hdr->dwError = ERROR_SUCCESS;
2955     else
2956         error = hdr->dwError;
2957
2958     LeaveCriticalSection( &req->read_section );
2959     size = buffers->dwBufferLength;
2960     buffers->dwBufferLength = read;
2961
2962 done:
2963     if (res == ERROR_SUCCESS) {
2964         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2965                 &size, sizeof(size));
2966     }
2967
2968     return res==ERROR_SUCCESS ? error : res;
2969 }
2970
2971 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2972 {
2973     DWORD res;
2974     http_request_t *request = (http_request_t*)hdr;
2975
2976     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2977
2978     *written = 0;
2979     res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2980     if (res == ERROR_SUCCESS)
2981         request->bytesWritten += *written;
2982
2983     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2984     return res;
2985 }
2986
2987 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2988 {
2989     http_request_t *req = (http_request_t*)workRequest->hdr;
2990
2991     HTTP_ReceiveRequestData(req, FALSE);
2992 }
2993
2994 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2995 {
2996     http_request_t *req = (http_request_t*)hdr;
2997
2998     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2999
3000     if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3001     {
3002         WORKREQUEST workRequest;
3003
3004         /* never wait, if we can't enter the section we queue an async request right away */
3005         if (TryEnterCriticalSection( &req->read_section ))
3006         {
3007             refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3008             if ((*available = get_avail_data( req ))) goto done;
3009             if (end_of_read_data( req )) goto done;
3010             LeaveCriticalSection( &req->read_section );
3011         }
3012
3013         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3014         workRequest.hdr = WININET_AddRef( &req->hdr );
3015
3016         INTERNET_AsyncCall(&workRequest);
3017
3018         return ERROR_IO_PENDING;
3019     }
3020
3021     EnterCriticalSection( &req->read_section );
3022
3023     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3024     {
3025         refill_read_buffer( req, READMODE_ASYNC, NULL );
3026         *available = get_avail_data( req );
3027     }
3028
3029 done:
3030     LeaveCriticalSection( &req->read_section );
3031
3032     TRACE( "returning %u\n", *available );
3033     return ERROR_SUCCESS;
3034 }
3035
3036 static const object_vtbl_t HTTPREQVtbl = {
3037     HTTPREQ_Destroy,
3038     HTTPREQ_CloseConnection,
3039     HTTPREQ_QueryOption,
3040     HTTPREQ_SetOption,
3041     HTTPREQ_ReadFile,
3042     HTTPREQ_ReadFileExA,
3043     HTTPREQ_ReadFileExW,
3044     HTTPREQ_WriteFile,
3045     HTTPREQ_QueryDataAvailable,
3046     NULL
3047 };
3048
3049 /***********************************************************************
3050  *           HTTP_HttpOpenRequestW (internal)
3051  *
3052  * Open a HTTP request handle
3053  *
3054  * RETURNS
3055  *    HINTERNET  a HTTP request handle on success
3056  *    NULL       on failure
3057  *
3058  */
3059 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3060         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3061         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3062         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3063 {
3064     appinfo_t *hIC = session->appInfo;
3065     http_request_t *request;
3066     DWORD len, res = ERROR_SUCCESS;
3067
3068     TRACE("-->\n");
3069
3070     request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3071     if(!request)
3072         return ERROR_OUTOFMEMORY;
3073
3074     request->hdr.htype = WH_HHTTPREQ;
3075     request->hdr.dwFlags = dwFlags;
3076     request->hdr.dwContext = dwContext;
3077     request->contentLength = ~0u;
3078
3079     request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3080     request->data_stream = &request->netconn_stream.data_stream;
3081     request->connect_timeout = session->connect_timeout;
3082     request->send_timeout = session->send_timeout;
3083     request->receive_timeout = session->receive_timeout;
3084
3085     InitializeCriticalSection( &request->read_section );
3086     request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3087
3088     WININET_AddRef( &session->hdr );
3089     request->session = session;
3090     list_add_head( &session->hdr.children, &request->hdr.entry );
3091
3092     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3093         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3094     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3095         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3096
3097     if (lpszObjectName && *lpszObjectName) {
3098         HRESULT rc;
3099
3100         len = 0;
3101         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3102         if (rc != E_POINTER)
3103             len = strlenW(lpszObjectName)+1;
3104         request->path = heap_alloc(len*sizeof(WCHAR));
3105         rc = UrlEscapeW(lpszObjectName, request->path, &len,
3106                    URL_ESCAPE_SPACES_ONLY);
3107         if (rc != S_OK)
3108         {
3109             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3110             strcpyW(request->path,lpszObjectName);
3111         }
3112     }else {
3113         static const WCHAR slashW[] = {'/',0};
3114
3115         request->path = heap_strdupW(slashW);
3116     }
3117
3118     if (lpszReferrer && *lpszReferrer)
3119         HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3120
3121     if (lpszAcceptTypes)
3122     {
3123         int i;
3124         for (i = 0; lpszAcceptTypes[i]; i++)
3125         {
3126             if (!*lpszAcceptTypes[i]) continue;
3127             HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3128                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3129                                HTTP_ADDHDR_FLAG_REQ |
3130                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3131         }
3132     }
3133
3134     request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3135     request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3136
3137     if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3138         session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3139         session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3140     {
3141         WCHAR *host_name;
3142
3143         static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3144
3145         host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3146         if (!host_name) {
3147             res = ERROR_OUTOFMEMORY;
3148             goto lend;
3149         }
3150
3151         sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3152         HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3153         heap_free(host_name);
3154     }
3155     else
3156         HTTP_ProcessHeader(request, hostW, session->hostName,
3157                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3158
3159     if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3160         session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3161                         INTERNET_DEFAULT_HTTPS_PORT :
3162                         INTERNET_DEFAULT_HTTP_PORT);
3163
3164     if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3165         session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3166                         INTERNET_DEFAULT_HTTPS_PORT :
3167                         INTERNET_DEFAULT_HTTP_PORT);
3168
3169     if (hIC->proxy && hIC->proxy[0])
3170         HTTP_DealWithProxy( hIC, session, request );
3171
3172     INTERNET_SendCallback(&session->hdr, dwContext,
3173                           INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3174                           sizeof(HINTERNET));
3175
3176 lend:
3177     TRACE("<-- %u (%p)\n", res, request);
3178
3179     if(res != ERROR_SUCCESS) {
3180         WININET_Release( &request->hdr );
3181         *ret = NULL;
3182         return res;
3183     }
3184
3185     *ret = request->hdr.hInternet;
3186     return ERROR_SUCCESS;
3187 }
3188
3189 /***********************************************************************
3190  *           HttpOpenRequestW (WININET.@)
3191  *
3192  * Open a HTTP request handle
3193  *
3194  * RETURNS
3195  *    HINTERNET  a HTTP request handle on success
3196  *    NULL       on failure
3197  *
3198  */
3199 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3200         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3201         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3202         DWORD dwFlags, DWORD_PTR dwContext)
3203 {
3204     http_session_t *session;
3205     HINTERNET handle = NULL;
3206     DWORD res;
3207
3208     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3209           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3210           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3211           dwFlags, dwContext);
3212     if(lpszAcceptTypes!=NULL)
3213     {
3214         int i;
3215         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3216             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3217     }
3218
3219     session = (http_session_t*) get_handle_object( hHttpSession );
3220     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
3221     {
3222         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3223         goto lend;
3224     }
3225
3226     /*
3227      * My tests seem to show that the windows version does not
3228      * become asynchronous until after this point. And anyhow
3229      * if this call was asynchronous then how would you get the
3230      * necessary HINTERNET pointer returned by this function.
3231      *
3232      */
3233     res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3234                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
3235                                 dwFlags, dwContext, &handle);
3236 lend:
3237     if( session )
3238         WININET_Release( &session->hdr );
3239     TRACE("returning %p\n", handle);
3240     if(res != ERROR_SUCCESS)
3241         SetLastError(res);
3242     return handle;
3243 }
3244
3245 static const LPCWSTR header_lookup[] = {
3246     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
3247     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
3248     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3249     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
3250     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3251     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
3252     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
3253     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
3254     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
3255     szDate,                     /* HTTP_QUERY_DATE = 9 */
3256     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
3257     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
3258     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
3259     szURI,                      /* HTTP_QUERY_URI = 13 */
3260     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
3261     NULL,                       /* HTTP_QUERY_COST = 15 */
3262     NULL,                       /* HTTP_QUERY_LINK = 16 */
3263     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
3264     NULL,                       /* HTTP_QUERY_VERSION = 18 */
3265     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
3266     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
3267     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
3268     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3269     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
3270     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
3271     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3272     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3273     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3274     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
3275     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3276     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
3277     NULL,                       /* HTTP_QUERY_FROM = 31 */
3278     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3279     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
3280     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
3281     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
3282     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
3283     szServer,                   /* HTTP_QUERY_SERVER = 37 */
3284     NULL,                       /* HTTP_TITLE = 38 */
3285     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
3286     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3287     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3288     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3289     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
3290     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
3291     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
3292     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
3293     szContent_Disposition,      /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3294     szAge,                      /* HTTP_QUERY_AGE = 48 */
3295     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
3296     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
3297     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3298     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
3299     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
3300     szETag,                     /* HTTP_QUERY_ETAG = 54 */
3301     hostW,                      /* HTTP_QUERY_HOST = 55 */
3302     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
3303     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3304     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
3305     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3306     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
3307     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3308     szRange,                    /* HTTP_QUERY_RANGE = 62 */
3309     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3310     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
3311     szVary,                     /* HTTP_QUERY_VARY = 65 */
3312     szVia,                      /* HTTP_QUERY_VIA = 66 */
3313     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
3314     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
3315     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3316     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3317 };
3318
3319 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3320
3321 /***********************************************************************
3322  *           HTTP_HttpQueryInfoW (internal)
3323  */
3324 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3325         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3326 {
3327     LPHTTPHEADERW lphttpHdr = NULL;
3328     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3329     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3330     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3331     INT index = -1;
3332
3333     /* Find requested header structure */
3334     switch (level)
3335     {
3336     case HTTP_QUERY_CUSTOM:
3337         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3338         index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3339         break;
3340     case HTTP_QUERY_RAW_HEADERS_CRLF:
3341         {
3342             LPWSTR headers;
3343             DWORD len = 0;
3344             DWORD res = ERROR_INVALID_PARAMETER;
3345
3346             if (request_only)
3347                 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3348             else
3349                 headers = request->rawHeaders;
3350
3351             if (headers)
3352                 len = strlenW(headers) * sizeof(WCHAR);
3353
3354             if (len + sizeof(WCHAR) > *lpdwBufferLength)
3355             {
3356                 len += sizeof(WCHAR);
3357                 res = ERROR_INSUFFICIENT_BUFFER;
3358             }
3359             else if (lpBuffer)
3360             {
3361                 if (headers)
3362                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3363                 else
3364                 {
3365                     len = strlenW(szCrLf) * sizeof(WCHAR);
3366                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3367                 }
3368                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3369                 res = ERROR_SUCCESS;
3370             }
3371             *lpdwBufferLength = len;
3372
3373             if (request_only) heap_free(headers);
3374             return res;
3375         }
3376     case HTTP_QUERY_RAW_HEADERS:
3377         {
3378             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3379             DWORD i, size = 0;
3380             LPWSTR pszString = lpBuffer;
3381
3382             for (i = 0; ppszRawHeaderLines[i]; i++)
3383                 size += strlenW(ppszRawHeaderLines[i]) + 1;
3384
3385             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3386             {
3387                 HTTP_FreeTokens(ppszRawHeaderLines);
3388                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3389                 return ERROR_INSUFFICIENT_BUFFER;
3390             }
3391             if (pszString)
3392             {
3393                 for (i = 0; ppszRawHeaderLines[i]; i++)
3394                 {
3395                     DWORD len = strlenW(ppszRawHeaderLines[i]);
3396                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3397                     pszString += len+1;
3398                 }
3399                 *pszString = '\0';
3400                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3401             }
3402             *lpdwBufferLength = size * sizeof(WCHAR);
3403             HTTP_FreeTokens(ppszRawHeaderLines);
3404
3405             return ERROR_SUCCESS;
3406         }
3407     case HTTP_QUERY_STATUS_TEXT:
3408         if (request->statusText)
3409         {
3410             DWORD len = strlenW(request->statusText);
3411             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3412             {
3413                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3414                 return ERROR_INSUFFICIENT_BUFFER;
3415             }
3416             if (lpBuffer)
3417             {
3418                 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3419                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3420             }
3421             *lpdwBufferLength = len * sizeof(WCHAR);
3422             return ERROR_SUCCESS;
3423         }
3424         break;
3425     case HTTP_QUERY_VERSION:
3426         if (request->version)
3427         {
3428             DWORD len = strlenW(request->version);
3429             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3430             {
3431                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3432                 return ERROR_INSUFFICIENT_BUFFER;
3433             }
3434             if (lpBuffer)
3435             {
3436                 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3437                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3438             }
3439             *lpdwBufferLength = len * sizeof(WCHAR);
3440             return ERROR_SUCCESS;
3441         }
3442         break;
3443     case HTTP_QUERY_CONTENT_ENCODING:
3444         index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3445                 requested_index,request_only);
3446         break;
3447     case HTTP_QUERY_STATUS_CODE: {
3448         DWORD res = ERROR_SUCCESS;
3449
3450         if(request_only)
3451             return ERROR_HTTP_INVALID_QUERY_REQUEST;
3452
3453         if(requested_index)
3454             break;
3455
3456         if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3457             if(*lpdwBufferLength >= sizeof(DWORD))
3458                 *(DWORD*)lpBuffer = request->status_code;
3459             else
3460                 res = ERROR_INSUFFICIENT_BUFFER;
3461             *lpdwBufferLength = sizeof(DWORD);
3462         }else {
3463             WCHAR buf[12];
3464             DWORD size;
3465             static const WCHAR formatW[] = {'%','u',0};
3466
3467             size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3468
3469             if(size <= *lpdwBufferLength)
3470                 memcpy(lpBuffer, buf, size);
3471             else
3472                 res = ERROR_INSUFFICIENT_BUFFER;
3473
3474             *lpdwBufferLength = size;
3475         }
3476         return res;
3477     }
3478     default:
3479         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3480
3481         if (level < LAST_TABLE_HEADER && header_lookup[level])
3482             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3483                                               requested_index,request_only);
3484     }
3485
3486     if (index >= 0)
3487         lphttpHdr = &request->custHeaders[index];
3488
3489     /* Ensure header satisfies requested attributes */
3490     if (!lphttpHdr ||
3491         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3492          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3493     {
3494         return ERROR_HTTP_HEADER_NOT_FOUND;
3495     }
3496
3497     if (lpdwIndex) (*lpdwIndex)++;
3498
3499     /* coalesce value to requested type */
3500     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3501     {
3502         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3503         TRACE(" returning number: %d\n", *(int *)lpBuffer);
3504      }
3505     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3506     {
3507         time_t tmpTime;
3508         struct tm tmpTM;
3509         SYSTEMTIME *STHook;
3510
3511         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3512
3513         tmpTM = *gmtime(&tmpTime);
3514         STHook = (SYSTEMTIME *)lpBuffer;
3515         STHook->wDay = tmpTM.tm_mday;
3516         STHook->wHour = tmpTM.tm_hour;
3517         STHook->wMilliseconds = 0;
3518         STHook->wMinute = tmpTM.tm_min;
3519         STHook->wDayOfWeek = tmpTM.tm_wday;
3520         STHook->wMonth = tmpTM.tm_mon + 1;
3521         STHook->wSecond = tmpTM.tm_sec;
3522         STHook->wYear = tmpTM.tm_year;
3523
3524         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3525               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3526               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3527     }
3528     else if (lphttpHdr->lpszValue)
3529     {
3530         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3531
3532         if (len > *lpdwBufferLength)
3533         {
3534             *lpdwBufferLength = len;
3535             return ERROR_INSUFFICIENT_BUFFER;
3536         }
3537         if (lpBuffer)
3538         {
3539             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3540             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3541         }
3542         *lpdwBufferLength = len - sizeof(WCHAR);
3543     }
3544     return ERROR_SUCCESS;
3545 }
3546
3547 /***********************************************************************
3548  *           HttpQueryInfoW (WININET.@)
3549  *
3550  * Queries for information about an HTTP request
3551  *
3552  * RETURNS
3553  *    TRUE  on success
3554  *    FALSE on failure
3555  *
3556  */
3557 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3558         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3559 {
3560     http_request_t *request;
3561     DWORD res;
3562
3563     if (TRACE_ON(wininet)) {
3564 #define FE(x) { x, #x }
3565         static const wininet_flag_info query_flags[] = {
3566             FE(HTTP_QUERY_MIME_VERSION),
3567             FE(HTTP_QUERY_CONTENT_TYPE),
3568             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3569             FE(HTTP_QUERY_CONTENT_ID),
3570             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3571             FE(HTTP_QUERY_CONTENT_LENGTH),
3572             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3573             FE(HTTP_QUERY_ALLOW),
3574             FE(HTTP_QUERY_PUBLIC),
3575             FE(HTTP_QUERY_DATE),
3576             FE(HTTP_QUERY_EXPIRES),
3577             FE(HTTP_QUERY_LAST_MODIFIED),
3578             FE(HTTP_QUERY_MESSAGE_ID),
3579             FE(HTTP_QUERY_URI),
3580             FE(HTTP_QUERY_DERIVED_FROM),
3581             FE(HTTP_QUERY_COST),
3582             FE(HTTP_QUERY_LINK),
3583             FE(HTTP_QUERY_PRAGMA),
3584             FE(HTTP_QUERY_VERSION),
3585             FE(HTTP_QUERY_STATUS_CODE),
3586             FE(HTTP_QUERY_STATUS_TEXT),
3587             FE(HTTP_QUERY_RAW_HEADERS),
3588             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3589             FE(HTTP_QUERY_CONNECTION),
3590             FE(HTTP_QUERY_ACCEPT),
3591             FE(HTTP_QUERY_ACCEPT_CHARSET),
3592             FE(HTTP_QUERY_ACCEPT_ENCODING),
3593             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3594             FE(HTTP_QUERY_AUTHORIZATION),
3595             FE(HTTP_QUERY_CONTENT_ENCODING),
3596             FE(HTTP_QUERY_FORWARDED),
3597             FE(HTTP_QUERY_FROM),
3598             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3599             FE(HTTP_QUERY_LOCATION),
3600             FE(HTTP_QUERY_ORIG_URI),
3601             FE(HTTP_QUERY_REFERER),
3602             FE(HTTP_QUERY_RETRY_AFTER),
3603             FE(HTTP_QUERY_SERVER),
3604             FE(HTTP_QUERY_TITLE),
3605             FE(HTTP_QUERY_USER_AGENT),
3606             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3607             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3608             FE(HTTP_QUERY_ACCEPT_RANGES),
3609         FE(HTTP_QUERY_SET_COOKIE),
3610         FE(HTTP_QUERY_COOKIE),
3611             FE(HTTP_QUERY_REQUEST_METHOD),
3612             FE(HTTP_QUERY_REFRESH),
3613             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3614             FE(HTTP_QUERY_AGE),
3615             FE(HTTP_QUERY_CACHE_CONTROL),
3616             FE(HTTP_QUERY_CONTENT_BASE),
3617             FE(HTTP_QUERY_CONTENT_LOCATION),
3618             FE(HTTP_QUERY_CONTENT_MD5),
3619             FE(HTTP_QUERY_CONTENT_RANGE),
3620             FE(HTTP_QUERY_ETAG),
3621             FE(HTTP_QUERY_HOST),
3622             FE(HTTP_QUERY_IF_MATCH),
3623             FE(HTTP_QUERY_IF_NONE_MATCH),
3624             FE(HTTP_QUERY_IF_RANGE),
3625             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3626             FE(HTTP_QUERY_MAX_FORWARDS),
3627             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3628             FE(HTTP_QUERY_RANGE),
3629             FE(HTTP_QUERY_TRANSFER_ENCODING),
3630             FE(HTTP_QUERY_UPGRADE),
3631             FE(HTTP_QUERY_VARY),
3632             FE(HTTP_QUERY_VIA),
3633             FE(HTTP_QUERY_WARNING),
3634             FE(HTTP_QUERY_CUSTOM)
3635         };
3636         static const wininet_flag_info modifier_flags[] = {
3637             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3638             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3639             FE(HTTP_QUERY_FLAG_NUMBER),
3640             FE(HTTP_QUERY_FLAG_COALESCE)
3641         };
3642 #undef FE
3643         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3644         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3645         DWORD i;
3646
3647         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3648         TRACE("  Attribute:");
3649         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3650             if (query_flags[i].val == info) {
3651                 TRACE(" %s", query_flags[i].name);
3652                 break;
3653             }
3654         }
3655         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3656             TRACE(" Unknown (%08x)", info);
3657         }
3658
3659         TRACE(" Modifier:");
3660         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3661             if (modifier_flags[i].val & info_mod) {
3662                 TRACE(" %s", modifier_flags[i].name);
3663                 info_mod &= ~ modifier_flags[i].val;
3664             }
3665         }
3666         
3667         if (info_mod) {
3668             TRACE(" Unknown (%08x)", info_mod);
3669         }
3670         TRACE("\n");
3671     }
3672     
3673     request = (http_request_t*) get_handle_object( hHttpRequest );
3674     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
3675     {
3676         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3677         goto lend;
3678     }
3679
3680     if (lpBuffer == NULL)
3681         *lpdwBufferLength = 0;
3682     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3683                                lpBuffer, lpdwBufferLength, lpdwIndex);
3684
3685 lend:
3686     if( request )
3687          WININET_Release( &request->hdr );
3688
3689     TRACE("%u <--\n", res);
3690     if(res != ERROR_SUCCESS)
3691         SetLastError(res);
3692     return res == ERROR_SUCCESS;
3693 }
3694
3695 /***********************************************************************
3696  *           HttpQueryInfoA (WININET.@)
3697  *
3698  * Queries for information about an HTTP request
3699  *
3700  * RETURNS
3701  *    TRUE  on success
3702  *    FALSE on failure
3703  *
3704  */
3705 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3706         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3707 {
3708     BOOL result;
3709     DWORD len;
3710     WCHAR* bufferW;
3711
3712     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3713        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3714     {
3715         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3716                                lpdwBufferLength, lpdwIndex );
3717     }
3718
3719     if (lpBuffer)
3720     {
3721         DWORD alloclen;
3722         len = (*lpdwBufferLength)*sizeof(WCHAR);
3723         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3724         {
3725             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3726             if (alloclen < len)
3727                 alloclen = len;
3728         }
3729         else
3730             alloclen = len;
3731         bufferW = heap_alloc(alloclen);
3732         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3733         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3734             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3735     } else
3736     {
3737         bufferW = NULL;
3738         len = 0;
3739     }
3740
3741     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3742                            &len, lpdwIndex );
3743     if( result )
3744     {
3745         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3746                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3747         *lpdwBufferLength = len - 1;
3748
3749         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3750     }
3751     else
3752         /* since the strings being returned from HttpQueryInfoW should be
3753          * only ASCII characters, it is reasonable to assume that all of
3754          * the Unicode characters can be reduced to a single byte */
3755         *lpdwBufferLength = len / sizeof(WCHAR);
3756
3757     heap_free( bufferW );
3758     return result;
3759 }
3760
3761 /***********************************************************************
3762  *           HTTP_GetRedirectURL (internal)
3763  */
3764 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3765 {
3766     static WCHAR szHttp[] = {'h','t','t','p',0};
3767     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3768     http_session_t *session = request->session;
3769     URL_COMPONENTSW urlComponents;
3770     DWORD url_length = 0;
3771     LPWSTR orig_url;
3772     LPWSTR combined_url;
3773
3774     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3775     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3776     urlComponents.dwSchemeLength = 0;
3777     urlComponents.lpszHostName = session->hostName;
3778     urlComponents.dwHostNameLength = 0;
3779     urlComponents.nPort = session->hostPort;
3780     urlComponents.lpszUserName = session->userName;
3781     urlComponents.dwUserNameLength = 0;
3782     urlComponents.lpszPassword = NULL;
3783     urlComponents.dwPasswordLength = 0;
3784     urlComponents.lpszUrlPath = request->path;
3785     urlComponents.dwUrlPathLength = 0;
3786     urlComponents.lpszExtraInfo = NULL;
3787     urlComponents.dwExtraInfoLength = 0;
3788
3789     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3790         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3791         return NULL;
3792
3793     orig_url = heap_alloc(url_length);
3794
3795     /* convert from bytes to characters */
3796     url_length = url_length / sizeof(WCHAR) - 1;
3797     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3798     {
3799         heap_free(orig_url);
3800         return NULL;
3801     }
3802
3803     url_length = 0;
3804     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3805         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3806     {
3807         heap_free(orig_url);
3808         return NULL;
3809     }
3810     combined_url = heap_alloc(url_length * sizeof(WCHAR));
3811
3812     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3813     {
3814         heap_free(orig_url);
3815         heap_free(combined_url);
3816         return NULL;
3817     }
3818     heap_free(orig_url);
3819     return combined_url;
3820 }
3821
3822
3823 /***********************************************************************
3824  *           HTTP_HandleRedirect (internal)
3825  */
3826 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3827 {
3828     http_session_t *session = request->session;
3829     appinfo_t *hIC = session->appInfo;
3830     BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3831     WCHAR path[INTERNET_MAX_PATH_LENGTH];
3832     int index;
3833
3834     if(lpszUrl[0]=='/')
3835     {
3836         /* if it's an absolute path, keep the same session info */
3837         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3838     }
3839     else
3840     {
3841         URL_COMPONENTSW urlComponents;
3842         WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3843         WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3844         WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3845         BOOL custom_port = FALSE;
3846
3847         static WCHAR httpW[] = {'h','t','t','p',0};
3848         static WCHAR httpsW[] = {'h','t','t','p','s',0};
3849
3850         userName[0] = 0;
3851         hostName[0] = 0;
3852         protocol[0] = 0;
3853
3854         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3855         urlComponents.lpszScheme = protocol;
3856         urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3857         urlComponents.lpszHostName = hostName;
3858         urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3859         urlComponents.lpszUserName = userName;
3860         urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3861         urlComponents.lpszPassword = NULL;
3862         urlComponents.dwPasswordLength = 0;
3863         urlComponents.lpszUrlPath = path;
3864         urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3865         urlComponents.lpszExtraInfo = NULL;
3866         urlComponents.dwExtraInfoLength = 0;
3867         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3868             return INTERNET_GetLastError();
3869
3870         if(!strcmpiW(protocol, httpW)) {
3871             if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3872                 TRACE("redirect from secure page to non-secure page\n");
3873                 /* FIXME: warn about from secure redirect to non-secure page */
3874                 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3875             }
3876
3877             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3878                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3879             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3880                 custom_port = TRUE;
3881         }else if(!strcmpiW(protocol, httpsW)) {
3882             if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3883                 TRACE("redirect from non-secure page to secure page\n");
3884                 /* FIXME: notify about redirect to secure page */
3885                 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3886             }
3887
3888             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3889                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3890             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3891                 custom_port = TRUE;
3892         }
3893
3894         heap_free(session->hostName);
3895
3896         if(custom_port) {
3897             int len;
3898             static const WCHAR fmt[] = {'%','s',':','%','u',0};
3899             len = lstrlenW(hostName);
3900             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3901             session->hostName = heap_alloc(len*sizeof(WCHAR));
3902             sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3903         }
3904         else
3905             session->hostName = heap_strdupW(hostName);
3906
3907         HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3908
3909         heap_free(session->userName);
3910         session->userName = NULL;
3911         if (userName[0])
3912             session->userName = heap_strdupW(userName);
3913
3914         reset_data_stream(request);
3915
3916         if(!using_proxy) {
3917             if(strcmpiW(session->serverName, hostName)) {
3918                 heap_free(session->serverName);
3919                 session->serverName = heap_strdupW(hostName);
3920             }
3921             session->serverPort = urlComponents.nPort;
3922         }
3923     }
3924     heap_free(request->path);
3925     request->path=NULL;
3926     if (*path)
3927     {
3928         DWORD needed = 0;
3929         HRESULT rc;
3930
3931         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3932         if (rc != E_POINTER)
3933             needed = strlenW(path)+1;
3934         request->path = heap_alloc(needed*sizeof(WCHAR));
3935         rc = UrlEscapeW(path, request->path, &needed,
3936                         URL_ESCAPE_SPACES_ONLY);
3937         if (rc != S_OK)
3938         {
3939             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3940             strcpyW(request->path,path);
3941         }
3942     }
3943
3944     /* Remove custom content-type/length headers on redirects.  */
3945     index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3946     if (0 <= index)
3947         HTTP_DeleteCustomHeader(request, index);
3948     index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3949     if (0 <= index)
3950         HTTP_DeleteCustomHeader(request, index);
3951
3952     return ERROR_SUCCESS;
3953 }
3954
3955 /***********************************************************************
3956  *           HTTP_build_req (internal)
3957  *
3958  *  concatenate all the strings in the request together
3959  */
3960 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3961 {
3962     LPCWSTR *t;
3963     LPWSTR str;
3964
3965     for( t = list; *t ; t++  )
3966         len += strlenW( *t );
3967     len++;
3968
3969     str = heap_alloc(len*sizeof(WCHAR));
3970     *str = 0;
3971
3972     for( t = list; *t ; t++ )
3973         strcatW( str, *t );
3974
3975     return str;
3976 }
3977
3978 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3979 {
3980     LPWSTR lpszPath;
3981     LPWSTR requestString;
3982     INT len;
3983     INT cnt;
3984     INT responseLen;
3985     char *ascii_req;
3986     DWORD res;
3987     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3988     static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3989     http_session_t *session = request->session;
3990
3991     TRACE("\n");
3992
3993     lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3994     sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3995     requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3996     heap_free( lpszPath );
3997
3998     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3999                                 NULL, 0, NULL, NULL );
4000     len--; /* the nul terminator isn't needed */
4001     ascii_req = heap_alloc(len);
4002     WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4003     heap_free( requestString );
4004
4005     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4006
4007     NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4008     res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4009     heap_free( ascii_req );
4010     if (res != ERROR_SUCCESS)
4011         return res;
4012
4013     responseLen = HTTP_GetResponseHeaders( request, TRUE );
4014     if (!responseLen)
4015         return ERROR_HTTP_INVALID_HEADER;
4016
4017     return ERROR_SUCCESS;
4018 }
4019
4020 static void HTTP_InsertCookies(http_request_t *request)
4021 {
4022     DWORD cookie_size, size, cnt = 0;
4023     HTTPHEADERW *host;
4024     WCHAR *cookies;
4025
4026     static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4027
4028     host = HTTP_GetHeader(request, hostW);
4029     if(!host)
4030         return;
4031
4032     if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4033         return;
4034
4035     size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4036     if(!(cookies = heap_alloc(size)))
4037         return;
4038
4039     cnt += sprintfW(cookies, cookieW);
4040     get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4041     strcatW(cookies, szCrLf);
4042
4043     HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4044
4045     heap_free(cookies);
4046 }
4047
4048 static WORD HTTP_ParseWkday(LPCWSTR day)
4049 {
4050     static const WCHAR days[7][4] = {{ 's','u','n',0 },
4051                                      { 'm','o','n',0 },
4052                                      { 't','u','e',0 },
4053                                      { 'w','e','d',0 },
4054                                      { 't','h','u',0 },
4055                                      { 'f','r','i',0 },
4056                                      { 's','a','t',0 }};
4057     int i;
4058     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4059         if (!strcmpiW(day, days[i]))
4060             return i;
4061
4062     /* Invalid */
4063     return 7;
4064 }
4065
4066 static WORD HTTP_ParseMonth(LPCWSTR month)
4067 {
4068     static const WCHAR jan[] = { 'j','a','n',0 };
4069     static const WCHAR feb[] = { 'f','e','b',0 };
4070     static const WCHAR mar[] = { 'm','a','r',0 };
4071     static const WCHAR apr[] = { 'a','p','r',0 };
4072     static const WCHAR may[] = { 'm','a','y',0 };
4073     static const WCHAR jun[] = { 'j','u','n',0 };
4074     static const WCHAR jul[] = { 'j','u','l',0 };
4075     static const WCHAR aug[] = { 'a','u','g',0 };
4076     static const WCHAR sep[] = { 's','e','p',0 };
4077     static const WCHAR oct[] = { 'o','c','t',0 };
4078     static const WCHAR nov[] = { 'n','o','v',0 };
4079     static const WCHAR dec[] = { 'd','e','c',0 };
4080
4081     if (!strcmpiW(month, jan)) return 1;
4082     if (!strcmpiW(month, feb)) return 2;
4083     if (!strcmpiW(month, mar)) return 3;
4084     if (!strcmpiW(month, apr)) return 4;
4085     if (!strcmpiW(month, may)) return 5;
4086     if (!strcmpiW(month, jun)) return 6;
4087     if (!strcmpiW(month, jul)) return 7;
4088     if (!strcmpiW(month, aug)) return 8;
4089     if (!strcmpiW(month, sep)) return 9;
4090     if (!strcmpiW(month, oct)) return 10;
4091     if (!strcmpiW(month, nov)) return 11;
4092     if (!strcmpiW(month, dec)) return 12;
4093     /* Invalid */
4094     return 0;
4095 }
4096
4097 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4098  * optionally preceded by whitespace.
4099  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4100  * st, and sets *str to the first character after the time format.
4101  */
4102 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4103 {
4104     LPCWSTR ptr = *str;
4105     WCHAR *nextPtr;
4106     unsigned long num;
4107
4108     while (isspaceW(*ptr))
4109         ptr++;
4110
4111     num = strtoulW(ptr, &nextPtr, 10);
4112     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4113     {
4114         ERR("unexpected time format %s\n", debugstr_w(ptr));
4115         return FALSE;
4116     }
4117     if (num > 23)
4118     {
4119         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4120         return FALSE;
4121     }
4122     ptr = nextPtr + 1;
4123     st->wHour = (WORD)num;
4124     num = strtoulW(ptr, &nextPtr, 10);
4125     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4126     {
4127         ERR("unexpected time format %s\n", debugstr_w(ptr));
4128         return FALSE;
4129     }
4130     if (num > 59)
4131     {
4132         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4133         return FALSE;
4134     }
4135     ptr = nextPtr + 1;
4136     st->wMinute = (WORD)num;
4137     num = strtoulW(ptr, &nextPtr, 10);
4138     if (!nextPtr || nextPtr <= ptr)
4139     {
4140         ERR("unexpected time format %s\n", debugstr_w(ptr));
4141         return FALSE;
4142     }
4143     if (num > 59)
4144     {
4145         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4146         return FALSE;
4147     }
4148     ptr = nextPtr + 1;
4149     *str = ptr;
4150     st->wSecond = (WORD)num;
4151     return TRUE;
4152 }
4153
4154 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4155 {
4156     static const WCHAR gmt[]= { 'G','M','T',0 };
4157     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4158     LPCWSTR ptr;
4159     SYSTEMTIME st = { 0 };
4160     unsigned long num;
4161
4162     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4163          dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4164         *dayPtr = *ptr;
4165     *dayPtr = 0;
4166     st.wDayOfWeek = HTTP_ParseWkday(day);
4167     if (st.wDayOfWeek >= 7)
4168     {
4169         ERR("unexpected weekday %s\n", debugstr_w(day));
4170         return FALSE;
4171     }
4172
4173     while (isspaceW(*ptr))
4174         ptr++;
4175
4176     for (monthPtr = month; !isspace(*ptr) &&
4177          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4178          monthPtr++, ptr++)
4179         *monthPtr = *ptr;
4180     *monthPtr = 0;
4181     st.wMonth = HTTP_ParseMonth(month);
4182     if (!st.wMonth || st.wMonth > 12)
4183     {
4184         ERR("unexpected month %s\n", debugstr_w(month));
4185         return FALSE;
4186     }
4187
4188     while (isspaceW(*ptr))
4189         ptr++;
4190
4191     num = strtoulW(ptr, &nextPtr, 10);
4192     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4193     {
4194         ERR("unexpected day %s\n", debugstr_w(ptr));
4195         return FALSE;
4196     }
4197     ptr = nextPtr;
4198     st.wDay = (WORD)num;
4199
4200     while (isspaceW(*ptr))
4201         ptr++;
4202
4203     if (!HTTP_ParseTime(&st, &ptr))
4204         return FALSE;
4205
4206     while (isspaceW(*ptr))
4207         ptr++;
4208
4209     num = strtoulW(ptr, &nextPtr, 10);
4210     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4211     {
4212         ERR("unexpected year %s\n", debugstr_w(ptr));
4213         return FALSE;
4214     }
4215     ptr = nextPtr;
4216     st.wYear = (WORD)num;
4217
4218     while (isspaceW(*ptr))
4219         ptr++;
4220
4221     /* asctime() doesn't report a timezone, but some web servers do, so accept
4222      * with or without GMT.
4223      */
4224     if (*ptr && strcmpW(ptr, gmt))
4225     {
4226         ERR("unexpected timezone %s\n", debugstr_w(ptr));
4227         return FALSE;
4228     }
4229     return SystemTimeToFileTime(&st, ft);
4230 }
4231
4232 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4233 {
4234     static const WCHAR gmt[]= { 'G','M','T',0 };
4235     WCHAR *nextPtr, day[4], month[4], *monthPtr;
4236     LPCWSTR ptr;
4237     unsigned long num;
4238     SYSTEMTIME st = { 0 };
4239
4240     ptr = strchrW(value, ',');
4241     if (!ptr)
4242         return FALSE;
4243     if (ptr - value != 3)
4244     {
4245         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4246         return FALSE;
4247     }
4248     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4249     day[3] = 0;
4250     st.wDayOfWeek = HTTP_ParseWkday(day);
4251     if (st.wDayOfWeek > 6)
4252     {
4253         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4254         return FALSE;
4255     }
4256     ptr++;
4257
4258     while (isspaceW(*ptr))
4259         ptr++;
4260
4261     num = strtoulW(ptr, &nextPtr, 10);
4262     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4263     {
4264         WARN("unexpected day %s\n", debugstr_w(value));
4265         return FALSE;
4266     }
4267     ptr = nextPtr;
4268     st.wDay = (WORD)num;
4269
4270     while (isspaceW(*ptr))
4271         ptr++;
4272
4273     for (monthPtr = month; !isspace(*ptr) &&
4274          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4275          monthPtr++, ptr++)
4276         *monthPtr = *ptr;
4277     *monthPtr = 0;
4278     st.wMonth = HTTP_ParseMonth(month);
4279     if (!st.wMonth || st.wMonth > 12)
4280     {
4281         WARN("unexpected month %s\n", debugstr_w(month));
4282         return FALSE;
4283     }
4284
4285     while (isspaceW(*ptr))
4286         ptr++;
4287
4288     num = strtoulW(ptr, &nextPtr, 10);
4289     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4290     {
4291         ERR("unexpected year %s\n", debugstr_w(value));
4292         return FALSE;
4293     }
4294     ptr = nextPtr;
4295     st.wYear = (WORD)num;
4296
4297     if (!HTTP_ParseTime(&st, &ptr))
4298         return FALSE;
4299
4300     while (isspaceW(*ptr))
4301         ptr++;
4302
4303     if (strcmpW(ptr, gmt))
4304     {
4305         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4306         return FALSE;
4307     }
4308     return SystemTimeToFileTime(&st, ft);
4309 }
4310
4311 static WORD HTTP_ParseWeekday(LPCWSTR day)
4312 {
4313     static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4314                                      { 'm','o','n','d','a','y',0 },
4315                                      { 't','u','e','s','d','a','y',0 },
4316                                      { 'w','e','d','n','e','s','d','a','y',0 },
4317                                      { 't','h','u','r','s','d','a','y',0 },
4318                                      { 'f','r','i','d','a','y',0 },
4319                                      { 's','a','t','u','r','d','a','y',0 }};
4320     int i;
4321     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4322         if (!strcmpiW(day, days[i]))
4323             return i;
4324
4325     /* Invalid */
4326     return 7;
4327 }
4328
4329 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4330 {
4331     static const WCHAR gmt[]= { 'G','M','T',0 };
4332     WCHAR *nextPtr, day[10], month[4], *monthPtr;
4333     LPCWSTR ptr;
4334     unsigned long num;
4335     SYSTEMTIME st = { 0 };
4336
4337     ptr = strchrW(value, ',');
4338     if (!ptr)
4339         return FALSE;
4340     if (ptr - value == 3)
4341     {
4342         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4343         day[3] = 0;
4344         st.wDayOfWeek = HTTP_ParseWkday(day);
4345         if (st.wDayOfWeek > 6)
4346         {
4347             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4348             return FALSE;
4349         }
4350     }
4351     else if (ptr - value < sizeof(day) / sizeof(day[0]))
4352     {
4353         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4354         day[ptr - value + 1] = 0;
4355         st.wDayOfWeek = HTTP_ParseWeekday(day);
4356         if (st.wDayOfWeek > 6)
4357         {
4358             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4359             return FALSE;
4360         }
4361     }
4362     else
4363     {
4364         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4365         return FALSE;
4366     }
4367     ptr++;
4368
4369     while (isspaceW(*ptr))
4370         ptr++;
4371
4372     num = strtoulW(ptr, &nextPtr, 10);
4373     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4374     {
4375         ERR("unexpected day %s\n", debugstr_w(value));
4376         return FALSE;
4377     }
4378     ptr = nextPtr;
4379     st.wDay = (WORD)num;
4380
4381     if (*ptr != '-')
4382     {
4383         ERR("unexpected month format %s\n", debugstr_w(ptr));
4384         return FALSE;
4385     }
4386     ptr++;
4387
4388     for (monthPtr = month; *ptr != '-' &&
4389          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4390          monthPtr++, ptr++)
4391         *monthPtr = *ptr;
4392     *monthPtr = 0;
4393     st.wMonth = HTTP_ParseMonth(month);
4394     if (!st.wMonth || st.wMonth > 12)
4395     {
4396         ERR("unexpected month %s\n", debugstr_w(month));
4397         return FALSE;
4398     }
4399
4400     if (*ptr != '-')
4401     {
4402         ERR("unexpected year format %s\n", debugstr_w(ptr));
4403         return FALSE;
4404     }
4405     ptr++;
4406
4407     num = strtoulW(ptr, &nextPtr, 10);
4408     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4409     {
4410         ERR("unexpected year %s\n", debugstr_w(value));
4411         return FALSE;
4412     }
4413     ptr = nextPtr;
4414     st.wYear = (WORD)num;
4415
4416     if (!HTTP_ParseTime(&st, &ptr))
4417         return FALSE;
4418
4419     while (isspaceW(*ptr))
4420         ptr++;
4421
4422     if (strcmpW(ptr, gmt))
4423     {
4424         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4425         return FALSE;
4426     }
4427     return SystemTimeToFileTime(&st, ft);
4428 }
4429
4430 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4431 {
4432     static const WCHAR zero[] = { '0',0 };
4433     BOOL ret;
4434
4435     if (!strcmpW(value, zero))
4436     {
4437         ft->dwLowDateTime = ft->dwHighDateTime = 0;
4438         ret = TRUE;
4439     }
4440     else if (strchrW(value, ','))
4441     {
4442         ret = HTTP_ParseRfc1123Date(value, ft);
4443         if (!ret)
4444         {
4445             ret = HTTP_ParseRfc850Date(value, ft);
4446             if (!ret)
4447                 ERR("unexpected date format %s\n", debugstr_w(value));
4448         }
4449     }
4450     else
4451     {
4452         ret = HTTP_ParseDateAsAsctime(value, ft);
4453         if (!ret)
4454             ERR("unexpected date format %s\n", debugstr_w(value));
4455     }
4456     return ret;
4457 }
4458
4459 static void HTTP_ProcessExpires(http_request_t *request)
4460 {
4461     BOOL expirationFound = FALSE;
4462     int headerIndex;
4463
4464     /* Look for a Cache-Control header with a max-age directive, as it takes
4465      * precedence over the Expires header.
4466      */
4467     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4468     if (headerIndex != -1)
4469     {
4470         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4471         LPWSTR ptr;
4472
4473         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4474         {
4475             LPWSTR comma = strchrW(ptr, ','), end, equal;
4476
4477             if (comma)
4478                 end = comma;
4479             else
4480                 end = ptr + strlenW(ptr);
4481             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4482                 ;
4483             if (*equal == '=')
4484             {
4485                 static const WCHAR max_age[] = {
4486                     'm','a','x','-','a','g','e',0 };
4487
4488                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4489                 {
4490                     LPWSTR nextPtr;
4491                     unsigned long age;
4492
4493                     age = strtoulW(equal + 1, &nextPtr, 10);
4494                     if (nextPtr > equal + 1)
4495                     {
4496                         LARGE_INTEGER ft;
4497
4498                         NtQuerySystemTime( &ft );
4499                         /* Age is in seconds, FILETIME resolution is in
4500                          * 100 nanosecond intervals.
4501                          */
4502                         ft.QuadPart += age * (ULONGLONG)1000000;
4503                         request->expires.dwLowDateTime = ft.u.LowPart;
4504                         request->expires.dwHighDateTime = ft.u.HighPart;
4505                         expirationFound = TRUE;
4506                     }
4507                 }
4508             }
4509             if (comma)
4510             {
4511                 ptr = comma + 1;
4512                 while (isspaceW(*ptr))
4513                     ptr++;
4514             }
4515             else
4516                 ptr = NULL;
4517         }
4518     }
4519     if (!expirationFound)
4520     {
4521         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4522         if (headerIndex != -1)
4523         {
4524             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4525             FILETIME ft;
4526
4527             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4528             {
4529                 expirationFound = TRUE;
4530                 request->expires = ft;
4531             }
4532         }
4533     }
4534     if (!expirationFound)
4535     {
4536         LARGE_INTEGER t;
4537
4538         /* With no known age, default to 10 minutes until expiration. */
4539         NtQuerySystemTime( &t );
4540         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4541         request->expires.dwLowDateTime = t.u.LowPart;
4542         request->expires.dwHighDateTime = t.u.HighPart;
4543     }
4544 }
4545
4546 static void HTTP_ProcessLastModified(http_request_t *request)
4547 {
4548     int headerIndex;
4549
4550     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4551     if (headerIndex != -1)
4552     {
4553         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4554         FILETIME ft;
4555
4556         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4557             request->last_modified = ft;
4558     }
4559 }
4560
4561 static void http_process_keep_alive(http_request_t *req)
4562 {
4563     int index;
4564
4565     index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4566     if(index != -1)
4567         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4568     else
4569         req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4570 }
4571
4572 static void HTTP_CacheRequest(http_request_t *request)
4573 {
4574     WCHAR url[INTERNET_MAX_URL_LENGTH];
4575     WCHAR cacheFileName[MAX_PATH+1];
4576     BOOL b;
4577
4578     b = HTTP_GetRequestURL(request, url);
4579     if(!b) {
4580         WARN("Could not get URL\n");
4581         return;
4582     }
4583
4584     b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4585     if(b) {
4586         heap_free(request->cacheFile);
4587         CloseHandle(request->hCacheFile);
4588
4589         request->cacheFile = heap_strdupW(cacheFileName);
4590         request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4591                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4592         if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4593             WARN("Could not create file: %u\n", GetLastError());
4594             request->hCacheFile = NULL;
4595         }
4596     }else {
4597         WARN("Could not create cache entry: %08x\n", GetLastError());
4598     }
4599 }
4600
4601 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4602 {
4603     const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4604     http_session_t *session = request->session;
4605     netconn_t *netconn = NULL;
4606     server_t *server;
4607     DWORD res;
4608
4609     assert(!request->netconn);
4610     reset_data_stream(request);
4611
4612     server = get_server(session->serverName, session->serverPort);
4613     if(!server)
4614         return ERROR_OUTOFMEMORY;
4615
4616     res = HTTP_ResolveName(request, server);
4617     if(res != ERROR_SUCCESS) {
4618         server_release(server);
4619         return res;
4620     }
4621
4622     EnterCriticalSection(&connection_pool_cs);
4623
4624     while(!list_empty(&server->conn_pool)) {
4625         netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4626         list_remove(&netconn->pool_entry);
4627
4628         if(NETCON_is_alive(netconn))
4629             break;
4630
4631         TRACE("connection %p closed during idle\n", netconn);
4632         free_netconn(netconn);
4633         netconn = NULL;
4634     }
4635
4636     LeaveCriticalSection(&connection_pool_cs);
4637
4638     if(netconn) {
4639         TRACE("<-- reusing %p netconn\n", netconn);
4640         request->netconn = netconn;
4641         *reusing = TRUE;
4642         return ERROR_SUCCESS;
4643     }
4644
4645     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4646                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4647                           server->addr_str,
4648                           strlen(server->addr_str)+1);
4649
4650     res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4651     server_release(server);
4652     if(res != ERROR_SUCCESS) {
4653         ERR("create_netconn failed: %u\n", res);
4654         return res;
4655     }
4656
4657     request->netconn = netconn;
4658
4659     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4660             INTERNET_STATUS_CONNECTED_TO_SERVER,
4661             server->addr_str, strlen(server->addr_str)+1);
4662
4663     if(is_https) {
4664         /* Note: we differ from Microsoft's WinINet here. they seem to have
4665          * a bug that causes no status callbacks to be sent when starting
4666          * a tunnel to a proxy server using the CONNECT verb. i believe our
4667          * behaviour to be more correct and to not cause any incompatibilities
4668          * because using a secure connection through a proxy server is a rare
4669          * case that would be hard for anyone to depend on */
4670         if(session->appInfo->proxy)
4671             res = HTTP_SecureProxyConnect(request);
4672         if(res == ERROR_SUCCESS)
4673             res = NETCON_secure_connect(request->netconn);
4674         if(res != ERROR_SUCCESS)
4675         {
4676             WARN("Couldn't connect securely to host\n");
4677
4678             if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4679                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4680                     || res == ERROR_INTERNET_INVALID_CA
4681                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
4682                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4683                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
4684                     || res == ERROR_INTERNET_SEC_INVALID_CERT
4685                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4686                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4687         }
4688     }
4689
4690     if(res != ERROR_SUCCESS) {
4691         http_release_netconn(request, FALSE);
4692         return res;
4693     }
4694
4695     *reusing = FALSE;
4696     TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4697     return ERROR_SUCCESS;
4698 }
4699
4700 /***********************************************************************
4701  *           HTTP_HttpSendRequestW (internal)
4702  *
4703  * Sends the specified request to the HTTP server
4704  *
4705  * RETURNS
4706  *    ERROR_SUCCESS on success
4707  *    win32 error code on failure
4708  *
4709  */
4710 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4711         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4712         DWORD dwContentLength, BOOL bEndRequest)
4713 {
4714     INT cnt;
4715     BOOL redirected = FALSE;
4716     LPWSTR requestString = NULL;
4717     INT responseLen;
4718     BOOL loop_next;
4719     static const WCHAR szPost[] = { 'P','O','S','T',0 };
4720     static const WCHAR szContentLength[] =
4721         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4722     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4723     DWORD res;
4724
4725     TRACE("--> %p\n", request);
4726
4727     assert(request->hdr.htype == WH_HHTTPREQ);
4728
4729     /* if the verb is NULL default to GET */
4730     if (!request->verb)
4731         request->verb = heap_strdupW(szGET);
4732
4733     if (dwContentLength || strcmpW(request->verb, szGET))
4734     {
4735         sprintfW(contentLengthStr, szContentLength, dwContentLength);
4736         HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4737         request->bytesToWrite = dwContentLength;
4738     }
4739     if (request->session->appInfo->agent)
4740     {
4741         WCHAR *agent_header;
4742         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4743         int len;
4744
4745         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4746         agent_header = heap_alloc(len * sizeof(WCHAR));
4747         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4748
4749         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4750         heap_free(agent_header);
4751     }
4752     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4753     {
4754         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4755         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4756     }
4757     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4758     {
4759         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4760                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4761         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4762     }
4763
4764     /* add the headers the caller supplied */
4765     if( lpszHeaders && dwHeaderLength )
4766         HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4767
4768     do
4769     {
4770         DWORD len;
4771         BOOL reusing_connection;
4772         char *ascii_req;
4773
4774         loop_next = FALSE;
4775
4776         /* like native, just in case the caller forgot to call InternetReadFile
4777          * for all the data */
4778         drain_content(request);
4779         if(redirected) {
4780             request->contentLength = ~0u;
4781             request->bytesToWrite = 0;
4782         }
4783
4784         if (TRACE_ON(wininet))
4785         {
4786             LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4787             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4788         }
4789
4790         HTTP_FixURL(request);
4791         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4792         {
4793             HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4794         }
4795         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4796         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4797
4798         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4799             HTTP_InsertCookies(request);
4800
4801         if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4802         {
4803             WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4804             requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4805             heap_free(url);
4806         }
4807         else
4808             requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4809
4810  
4811         TRACE("Request header -> %s\n", debugstr_w(requestString) );
4812
4813         if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4814             break;
4815
4816         /* send the request as ASCII, tack on the optional data */
4817         if (!lpOptional || redirected)
4818             dwOptionalLength = 0;
4819         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4820                                    NULL, 0, NULL, NULL );
4821         ascii_req = heap_alloc(len + dwOptionalLength);
4822         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4823                              ascii_req, len, NULL, NULL );
4824         if( lpOptional )
4825             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4826         len = (len + dwOptionalLength - 1);
4827         ascii_req[len] = 0;
4828         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4829
4830         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4831                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4832
4833         NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4834         res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4835         heap_free( ascii_req );
4836         if(res != ERROR_SUCCESS) {
4837             TRACE("send failed: %u\n", res);
4838             if(!reusing_connection)
4839                 break;
4840             http_release_netconn(request, FALSE);
4841             loop_next = TRUE;
4842             continue;
4843         }
4844
4845         request->bytesWritten = dwOptionalLength;
4846
4847         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4848                               INTERNET_STATUS_REQUEST_SENT,
4849                               &len, sizeof(DWORD));
4850
4851         if (bEndRequest)
4852         {
4853             DWORD dwBufferSize;
4854
4855             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4856                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4857     
4858             responseLen = HTTP_GetResponseHeaders(request, TRUE);
4859             /* FIXME: We should know that connection is closed before sending
4860              * headers. Otherwise wrong callbacks are executed */
4861             if(!responseLen && reusing_connection) {
4862                 TRACE("Connection closed by server, reconnecting\n");
4863                 http_release_netconn(request, FALSE);
4864                 loop_next = TRUE;
4865                 continue;
4866             }
4867
4868             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4869                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4870                                 sizeof(DWORD));
4871
4872             http_process_keep_alive(request);
4873             HTTP_ProcessCookies(request);
4874             HTTP_ProcessExpires(request);
4875             HTTP_ProcessLastModified(request);
4876
4877             res = set_content_length(request);
4878             if(res != ERROR_SUCCESS)
4879                 goto lend;
4880             if(!request->contentLength)
4881                 http_release_netconn(request, TRUE);
4882
4883             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4884             {
4885                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4886                 dwBufferSize=sizeof(szNewLocation);
4887                 switch(request->status_code) {
4888                 case HTTP_STATUS_REDIRECT:
4889                 case HTTP_STATUS_MOVED:
4890                 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4891                 case HTTP_STATUS_REDIRECT_METHOD:
4892                     if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4893                         break;
4894
4895                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4896                         request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4897                     {
4898                         heap_free(request->verb);
4899                         request->verb = heap_strdupW(szGET);
4900                     }
4901                     drain_content(request);
4902                     if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4903                     {
4904                         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4905                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4906                         res = HTTP_HandleRedirect(request, new_url);
4907                         if (res == ERROR_SUCCESS)
4908                         {
4909                             heap_free(requestString);
4910                             loop_next = TRUE;
4911                         }
4912                         heap_free( new_url );
4913                     }
4914                     redirected = TRUE;
4915                 }
4916             }
4917             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4918             {
4919                 WCHAR szAuthValue[2048];
4920                 dwBufferSize=2048;
4921                 if (request->status_code == HTTP_STATUS_DENIED)
4922                 {
4923                     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4924                     DWORD dwIndex = 0;
4925                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4926                     {
4927                         if (HTTP_DoAuthorization(request, szAuthValue,
4928                                                  &request->authInfo,
4929                                                  request->session->userName,
4930                                                  request->session->password,
4931                                                  Host->lpszValue))
4932                         {
4933                             heap_free(requestString);
4934                             loop_next = TRUE;
4935                             break;
4936                         }
4937                     }
4938
4939                     if(!loop_next) {
4940                         TRACE("Cleaning wrong authorization data\n");
4941                         destroy_authinfo(request->authInfo);
4942                         request->authInfo = NULL;
4943                     }
4944                 }
4945                 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4946                 {
4947                     DWORD dwIndex = 0;
4948                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4949                     {
4950                         if (HTTP_DoAuthorization(request, szAuthValue,
4951                                                  &request->proxyAuthInfo,
4952                                                  request->session->appInfo->proxyUsername,
4953                                                  request->session->appInfo->proxyPassword,
4954                                                  NULL))
4955                         {
4956                             loop_next = TRUE;
4957                             break;
4958                         }
4959                     }
4960
4961                     if(!loop_next) {
4962                         TRACE("Cleaning wrong proxy authorization data\n");
4963                         destroy_authinfo(request->proxyAuthInfo);
4964                         request->proxyAuthInfo = NULL;
4965                     }
4966                 }
4967             }
4968         }
4969         else
4970             res = ERROR_SUCCESS;
4971     }
4972     while (loop_next);
4973
4974     if(res == ERROR_SUCCESS)
4975         HTTP_CacheRequest(request);
4976
4977 lend:
4978     heap_free(requestString);
4979
4980     /* TODO: send notification for P3P header */
4981
4982     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4983     {
4984         if (res == ERROR_SUCCESS) {
4985             if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4986                 HTTP_ReceiveRequestData(request, TRUE);
4987             else
4988                 send_request_complete(request,
4989                         request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4990         }else {
4991                 send_request_complete(request, 0, res);
4992         }
4993     }
4994
4995     TRACE("<--\n");
4996     return res;
4997 }
4998
4999 /***********************************************************************
5000  *
5001  * Helper functions for the HttpSendRequest(Ex) functions
5002  *
5003  */
5004 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5005 {
5006     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5007     http_request_t *request = (http_request_t*) workRequest->hdr;
5008
5009     TRACE("%p\n", request);
5010
5011     HTTP_HttpSendRequestW(request, req->lpszHeader,
5012             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5013             req->dwContentLength, req->bEndRequest);
5014
5015     heap_free(req->lpszHeader);
5016 }
5017
5018
5019 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5020 {
5021     DWORD dwBufferSize;
5022     INT responseLen;
5023     DWORD res = ERROR_SUCCESS;
5024
5025     if(!request->netconn) {
5026         WARN("Not connected\n");
5027         send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5028         return ERROR_INTERNET_OPERATION_CANCELLED;
5029     }
5030
5031     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5032                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5033
5034     responseLen = HTTP_GetResponseHeaders(request, TRUE);
5035     if (!responseLen)
5036         res = ERROR_HTTP_HEADER_NOT_FOUND;
5037
5038     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5039                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5040
5041     /* process cookies here. Is this right? */
5042     http_process_keep_alive(request);
5043     HTTP_ProcessCookies(request);
5044     HTTP_ProcessExpires(request);
5045     HTTP_ProcessLastModified(request);
5046
5047     if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5048         if(!request->contentLength)
5049             http_release_netconn(request, TRUE);
5050     }
5051
5052     if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5053     {
5054         switch(request->status_code) {
5055         case HTTP_STATUS_REDIRECT:
5056         case HTTP_STATUS_MOVED:
5057         case HTTP_STATUS_REDIRECT_METHOD:
5058         case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5059             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5060             dwBufferSize=sizeof(szNewLocation);
5061             if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5062                 break;
5063
5064             if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5065                 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5066             {
5067                 heap_free(request->verb);
5068                 request->verb = heap_strdupW(szGET);
5069             }
5070             drain_content(request);
5071             if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5072             {
5073                 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5074                                       new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5075                 res = HTTP_HandleRedirect(request, new_url);
5076                 if (res == ERROR_SUCCESS)
5077                     res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5078                 heap_free( new_url );
5079             }
5080         }
5081         }
5082     }
5083
5084     if (res == ERROR_SUCCESS && request->contentLength)
5085         HTTP_ReceiveRequestData(request, TRUE);
5086     else
5087         send_request_complete(request, res == ERROR_SUCCESS, res);
5088
5089     return res;
5090 }
5091
5092 /***********************************************************************
5093  *           HttpEndRequestA (WININET.@)
5094  *
5095  * Ends an HTTP request that was started by HttpSendRequestEx
5096  *
5097  * RETURNS
5098  *    TRUE      if successful
5099  *    FALSE     on failure
5100  *
5101  */
5102 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5103         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5104 {
5105     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5106
5107     if (lpBuffersOut)
5108     {
5109         SetLastError(ERROR_INVALID_PARAMETER);
5110         return FALSE;
5111     }
5112
5113     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5114 }
5115
5116 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5117 {
5118     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5119     http_request_t *request = (http_request_t*)work->hdr;
5120
5121     TRACE("%p\n", request);
5122
5123     HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5124 }
5125
5126 /***********************************************************************
5127  *           HttpEndRequestW (WININET.@)
5128  *
5129  * Ends an HTTP request that was started by HttpSendRequestEx
5130  *
5131  * RETURNS
5132  *    TRUE      if successful
5133  *    FALSE     on failure
5134  *
5135  */
5136 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5137         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5138 {
5139     http_request_t *request;
5140     DWORD res;
5141
5142     TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5143
5144     if (lpBuffersOut)
5145     {
5146         SetLastError(ERROR_INVALID_PARAMETER);
5147         return FALSE;
5148     }
5149
5150     request = (http_request_t*) get_handle_object( hRequest );
5151
5152     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5153     {
5154         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5155         if (request)
5156             WININET_Release( &request->hdr );
5157         return FALSE;
5158     }
5159     request->hdr.dwFlags |= dwFlags;
5160
5161     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5162     {
5163         WORKREQUEST work;
5164         struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5165
5166         work.asyncproc = AsyncHttpEndRequestProc;
5167         work.hdr = WININET_AddRef( &request->hdr );
5168
5169         work_endrequest = &work.u.HttpEndRequestW;
5170         work_endrequest->dwFlags = dwFlags;
5171         work_endrequest->dwContext = dwContext;
5172
5173         INTERNET_AsyncCall(&work);
5174         res = ERROR_IO_PENDING;
5175     }
5176     else
5177         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5178
5179     WININET_Release( &request->hdr );
5180     TRACE("%u <--\n", res);
5181     if(res != ERROR_SUCCESS)
5182         SetLastError(res);
5183     return res == ERROR_SUCCESS;
5184 }
5185
5186 /***********************************************************************
5187  *           HttpSendRequestExA (WININET.@)
5188  *
5189  * Sends the specified request to the HTTP server and allows chunked
5190  * transfers.
5191  *
5192  * RETURNS
5193  *  Success: TRUE
5194  *  Failure: FALSE, call GetLastError() for more information.
5195  */
5196 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5197                                LPINTERNET_BUFFERSA lpBuffersIn,
5198                                LPINTERNET_BUFFERSA lpBuffersOut,
5199                                DWORD dwFlags, DWORD_PTR dwContext)
5200 {
5201     INTERNET_BUFFERSW BuffersInW;
5202     BOOL rc = FALSE;
5203     DWORD headerlen;
5204     LPWSTR header = NULL;
5205
5206     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5207             lpBuffersOut, dwFlags, dwContext);
5208
5209     if (lpBuffersIn)
5210     {
5211         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5212         if (lpBuffersIn->lpcszHeader)
5213         {
5214             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5215                     lpBuffersIn->dwHeadersLength,0,0);
5216             header = heap_alloc(headerlen*sizeof(WCHAR));
5217             if (!(BuffersInW.lpcszHeader = header))
5218             {
5219                 SetLastError(ERROR_OUTOFMEMORY);
5220                 return FALSE;
5221             }
5222             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5223                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5224                     header, headerlen);
5225         }
5226         else
5227             BuffersInW.lpcszHeader = NULL;
5228         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5229         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5230         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5231         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5232         BuffersInW.Next = NULL;
5233     }
5234
5235     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5236
5237     heap_free(header);
5238     return rc;
5239 }
5240
5241 /***********************************************************************
5242  *           HttpSendRequestExW (WININET.@)
5243  *
5244  * Sends the specified request to the HTTP server and allows chunked
5245  * transfers
5246  *
5247  * RETURNS
5248  *  Success: TRUE
5249  *  Failure: FALSE, call GetLastError() for more information.
5250  */
5251 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5252                    LPINTERNET_BUFFERSW lpBuffersIn,
5253                    LPINTERNET_BUFFERSW lpBuffersOut,
5254                    DWORD dwFlags, DWORD_PTR dwContext)
5255 {
5256     http_request_t *request;
5257     http_session_t *session;
5258     appinfo_t *hIC;
5259     DWORD res;
5260
5261     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5262             lpBuffersOut, dwFlags, dwContext);
5263
5264     request = (http_request_t*) get_handle_object( hRequest );
5265
5266     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5267     {
5268         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5269         goto lend;
5270     }
5271
5272     session = request->session;
5273     assert(session->hdr.htype == WH_HHTTPSESSION);
5274     hIC = session->appInfo;
5275     assert(hIC->hdr.htype == WH_HINIT);
5276
5277     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5278     {
5279         WORKREQUEST workRequest;
5280         struct WORKREQ_HTTPSENDREQUESTW *req;
5281
5282         workRequest.asyncproc = AsyncHttpSendRequestProc;
5283         workRequest.hdr = WININET_AddRef( &request->hdr );
5284         req = &workRequest.u.HttpSendRequestW;
5285         if (lpBuffersIn)
5286         {
5287             DWORD size = 0;
5288
5289             if (lpBuffersIn->lpcszHeader)
5290             {
5291                 if (lpBuffersIn->dwHeadersLength == ~0u)
5292                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5293                 else
5294                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5295
5296                 req->lpszHeader = heap_alloc(size);
5297                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5298             }
5299             else req->lpszHeader = NULL;
5300
5301             req->dwHeaderLength = size / sizeof(WCHAR);
5302             req->lpOptional = lpBuffersIn->lpvBuffer;
5303             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5304             req->dwContentLength = lpBuffersIn->dwBufferTotal;
5305         }
5306         else
5307         {
5308             req->lpszHeader = NULL;
5309             req->dwHeaderLength = 0;
5310             req->lpOptional = NULL;
5311             req->dwOptionalLength = 0;
5312             req->dwContentLength = 0;
5313         }
5314
5315         req->bEndRequest = FALSE;
5316
5317         INTERNET_AsyncCall(&workRequest);
5318         /*
5319          * This is from windows.
5320          */
5321         res = ERROR_IO_PENDING;
5322     }
5323     else
5324     {
5325         if (lpBuffersIn)
5326             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5327                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5328                                         lpBuffersIn->dwBufferTotal, FALSE);
5329         else
5330             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5331     }
5332
5333 lend:
5334     if ( request )
5335         WININET_Release( &request->hdr );
5336
5337     TRACE("<---\n");
5338     SetLastError(res);
5339     return res == ERROR_SUCCESS;
5340 }
5341
5342 /***********************************************************************
5343  *           HttpSendRequestW (WININET.@)
5344  *
5345  * Sends the specified request to the HTTP server
5346  *
5347  * RETURNS
5348  *    TRUE  on success
5349  *    FALSE on failure
5350  *
5351  */
5352 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5353         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5354 {
5355     http_request_t *request;
5356     http_session_t *session = NULL;
5357     appinfo_t *hIC = NULL;
5358     DWORD res = ERROR_SUCCESS;
5359
5360     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5361             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5362
5363     request = (http_request_t*) get_handle_object( hHttpRequest );
5364     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5365     {
5366         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5367         goto lend;
5368     }
5369
5370     session = request->session;
5371     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
5372     {
5373         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5374         goto lend;
5375     }
5376
5377     hIC = session->appInfo;
5378     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
5379     {
5380         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5381         goto lend;
5382     }
5383
5384     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5385     {
5386         WORKREQUEST workRequest;
5387         struct WORKREQ_HTTPSENDREQUESTW *req;
5388
5389         workRequest.asyncproc = AsyncHttpSendRequestProc;
5390         workRequest.hdr = WININET_AddRef( &request->hdr );
5391         req = &workRequest.u.HttpSendRequestW;
5392         if (lpszHeaders)
5393         {
5394             DWORD size;
5395
5396             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5397             else size = dwHeaderLength * sizeof(WCHAR);
5398
5399             req->lpszHeader = heap_alloc(size);
5400             memcpy(req->lpszHeader, lpszHeaders, size);
5401         }
5402         else
5403             req->lpszHeader = 0;
5404         req->dwHeaderLength = dwHeaderLength;
5405         req->lpOptional = lpOptional;
5406         req->dwOptionalLength = dwOptionalLength;
5407         req->dwContentLength = dwOptionalLength;
5408         req->bEndRequest = TRUE;
5409
5410         INTERNET_AsyncCall(&workRequest);
5411         /*
5412          * This is from windows.
5413          */
5414         res = ERROR_IO_PENDING;
5415     }
5416     else
5417     {
5418         res = HTTP_HttpSendRequestW(request, lpszHeaders,
5419                 dwHeaderLength, lpOptional, dwOptionalLength,
5420                 dwOptionalLength, TRUE);
5421     }
5422 lend:
5423     if( request )
5424         WININET_Release( &request->hdr );
5425
5426     SetLastError(res);
5427     return res == ERROR_SUCCESS;
5428 }
5429
5430 /***********************************************************************
5431  *           HttpSendRequestA (WININET.@)
5432  *
5433  * Sends the specified request to the HTTP server
5434  *
5435  * RETURNS
5436  *    TRUE  on success
5437  *    FALSE on failure
5438  *
5439  */
5440 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5441         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5442 {
5443     BOOL result;
5444     LPWSTR szHeaders=NULL;
5445     DWORD nLen=dwHeaderLength;
5446     if(lpszHeaders!=NULL)
5447     {
5448         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5449         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5450         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5451     }
5452     result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5453     heap_free(szHeaders);
5454     return result;
5455 }
5456
5457 /***********************************************************************
5458  *           HTTPSESSION_Destroy (internal)
5459  *
5460  * Deallocate session handle
5461  *
5462  */
5463 static void HTTPSESSION_Destroy(object_header_t *hdr)
5464 {
5465     http_session_t *session = (http_session_t*) hdr;
5466
5467     TRACE("%p\n", session);
5468
5469     WININET_Release(&session->appInfo->hdr);
5470
5471     heap_free(session->hostName);
5472     heap_free(session->serverName);
5473     heap_free(session->password);
5474     heap_free(session->userName);
5475 }
5476
5477 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5478 {
5479     http_session_t *ses = (http_session_t *)hdr;
5480
5481     switch(option) {
5482     case INTERNET_OPTION_HANDLE_TYPE:
5483         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5484
5485         if (*size < sizeof(ULONG))
5486             return ERROR_INSUFFICIENT_BUFFER;
5487
5488         *size = sizeof(DWORD);
5489         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5490         return ERROR_SUCCESS;
5491     case INTERNET_OPTION_CONNECT_TIMEOUT:
5492         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5493
5494         if (*size < sizeof(DWORD))
5495             return ERROR_INSUFFICIENT_BUFFER;
5496
5497         *size = sizeof(DWORD);
5498         *(DWORD *)buffer = ses->connect_timeout;
5499         return ERROR_SUCCESS;
5500
5501     case INTERNET_OPTION_SEND_TIMEOUT:
5502         TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5503
5504         if (*size < sizeof(DWORD))
5505             return ERROR_INSUFFICIENT_BUFFER;
5506
5507         *size = sizeof(DWORD);
5508         *(DWORD *)buffer = ses->send_timeout;
5509         return ERROR_SUCCESS;
5510
5511     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5512         TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5513
5514         if (*size < sizeof(DWORD))
5515             return ERROR_INSUFFICIENT_BUFFER;
5516
5517         *size = sizeof(DWORD);
5518         *(DWORD *)buffer = ses->receive_timeout;
5519         return ERROR_SUCCESS;
5520     }
5521
5522     return INET_QueryOption(hdr, option, buffer, size, unicode);
5523 }
5524
5525 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5526 {
5527     http_session_t *ses = (http_session_t*)hdr;
5528
5529     switch(option) {
5530     case INTERNET_OPTION_USERNAME:
5531     {
5532         heap_free(ses->userName);
5533         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5534         return ERROR_SUCCESS;
5535     }
5536     case INTERNET_OPTION_PASSWORD:
5537     {
5538         heap_free(ses->password);
5539         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5540         return ERROR_SUCCESS;
5541     }
5542     case INTERNET_OPTION_CONNECT_TIMEOUT:
5543     {
5544         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5545         ses->connect_timeout = *(DWORD *)buffer;
5546         return ERROR_SUCCESS;
5547     }
5548     case INTERNET_OPTION_SEND_TIMEOUT:
5549     {
5550         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5551         ses->send_timeout = *(DWORD *)buffer;
5552         return ERROR_SUCCESS;
5553     }
5554     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5555     {
5556         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5557         ses->receive_timeout = *(DWORD *)buffer;
5558         return ERROR_SUCCESS;
5559     }
5560     default: break;
5561     }
5562
5563     return INET_SetOption(hdr, option, buffer, size);
5564 }
5565
5566 static const object_vtbl_t HTTPSESSIONVtbl = {
5567     HTTPSESSION_Destroy,
5568     NULL,
5569     HTTPSESSION_QueryOption,
5570     HTTPSESSION_SetOption,
5571     NULL,
5572     NULL,
5573     NULL,
5574     NULL,
5575     NULL
5576 };
5577
5578
5579 /***********************************************************************
5580  *           HTTP_Connect  (internal)
5581  *
5582  * Create http session handle
5583  *
5584  * RETURNS
5585  *   HINTERNET a session handle on success
5586  *   NULL on failure
5587  *
5588  */
5589 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5590         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5591         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5592         DWORD dwInternalFlags, HINTERNET *ret)
5593 {
5594     http_session_t *session = NULL;
5595
5596     TRACE("-->\n");
5597
5598     if (!lpszServerName || !lpszServerName[0])
5599         return ERROR_INVALID_PARAMETER;
5600
5601     assert( hIC->hdr.htype == WH_HINIT );
5602
5603     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5604     if (!session)
5605         return ERROR_OUTOFMEMORY;
5606
5607    /*
5608     * According to my tests. The name is not resolved until a request is sent
5609     */
5610
5611     session->hdr.htype = WH_HHTTPSESSION;
5612     session->hdr.dwFlags = dwFlags;
5613     session->hdr.dwContext = dwContext;
5614     session->hdr.dwInternalFlags |= dwInternalFlags;
5615
5616     WININET_AddRef( &hIC->hdr );
5617     session->appInfo = hIC;
5618     list_add_head( &hIC->hdr.children, &session->hdr.entry );
5619
5620     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5621         if(hIC->proxyBypass)
5622             FIXME("Proxy bypass is ignored.\n");
5623     }
5624     session->serverName = heap_strdupW(lpszServerName);
5625     session->hostName = heap_strdupW(lpszServerName);
5626     if (lpszUserName && lpszUserName[0])
5627         session->userName = heap_strdupW(lpszUserName);
5628     if (lpszPassword && lpszPassword[0])
5629         session->password = heap_strdupW(lpszPassword);
5630     session->serverPort = serverPort;
5631     session->hostPort = serverPort;
5632     session->connect_timeout = hIC->connect_timeout;
5633     session->send_timeout = INFINITE;
5634     session->receive_timeout = INFINITE;
5635
5636     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5637     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5638     {
5639         INTERNET_SendCallback(&hIC->hdr, dwContext,
5640                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5641                               sizeof(HINTERNET));
5642     }
5643
5644 /*
5645  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5646  * windows
5647  */
5648
5649     TRACE("%p --> %p\n", hIC, session);
5650
5651     *ret = session->hdr.hInternet;
5652     return ERROR_SUCCESS;
5653 }
5654
5655 /***********************************************************************
5656  *           HTTP_clear_response_headers (internal)
5657  *
5658  * clear out any old response headers
5659  */
5660 static void HTTP_clear_response_headers( http_request_t *request )
5661 {
5662     DWORD i;
5663
5664     for( i=0; i<request->nCustHeaders; i++)
5665     {
5666         if( !request->custHeaders[i].lpszField )
5667             continue;
5668         if( !request->custHeaders[i].lpszValue )
5669             continue;
5670         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5671             continue;
5672         HTTP_DeleteCustomHeader( request, i );
5673         i--;
5674     }
5675 }
5676
5677 /***********************************************************************
5678  *           HTTP_GetResponseHeaders (internal)
5679  *
5680  * Read server response
5681  *
5682  * RETURNS
5683  *
5684  *   TRUE  on success
5685  *   FALSE on error
5686  */
5687 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5688 {
5689     INT cbreaks = 0;
5690     WCHAR buffer[MAX_REPLY_LEN];
5691     DWORD buflen = MAX_REPLY_LEN;
5692     BOOL bSuccess = FALSE;
5693     INT  rc = 0;
5694     char bufferA[MAX_REPLY_LEN];
5695     LPWSTR status_code = NULL, status_text = NULL;
5696     DWORD cchMaxRawHeaders = 1024;
5697     LPWSTR lpszRawHeaders = NULL;
5698     LPWSTR temp;
5699     DWORD cchRawHeaders = 0;
5700     BOOL codeHundred = FALSE;
5701
5702     TRACE("-->\n");
5703
5704     if(!request->netconn)
5705         goto lend;
5706
5707     NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5708     do {
5709         static const WCHAR szHundred[] = {'1','0','0',0};
5710         /*
5711          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5712          */
5713         buflen = MAX_REPLY_LEN;
5714         if (!read_line(request, bufferA, &buflen))
5715             goto lend;
5716
5717         /* clear old response headers (eg. from a redirect response) */
5718         if (clear) {
5719             HTTP_clear_response_headers( request );
5720             clear = FALSE;
5721         }
5722
5723         rc += buflen;
5724         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5725         /* check is this a status code line? */
5726         if (!strncmpW(buffer, g_szHttp1_0, 4))
5727         {
5728             /* split the version from the status code */
5729             status_code = strchrW( buffer, ' ' );
5730             if( !status_code )
5731                 goto lend;
5732             *status_code++=0;
5733
5734             /* split the status code from the status text */
5735             status_text = strchrW( status_code, ' ' );
5736             if( !status_text )
5737                 goto lend;
5738             *status_text++=0;
5739
5740             request->status_code = atoiW(status_code);
5741
5742             TRACE("version [%s] status code [%s] status text [%s]\n",
5743                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5744
5745             codeHundred = (!strcmpW(status_code, szHundred));
5746         }
5747         else if (!codeHundred)
5748         {
5749             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5750
5751             heap_free(request->version);
5752             heap_free(request->statusText);
5753
5754             request->status_code = HTTP_STATUS_OK;
5755             request->version = heap_strdupW(g_szHttp1_0);
5756             request->statusText = heap_strdupW(szOK);
5757
5758             heap_free(request->rawHeaders);
5759             request->rawHeaders = heap_strdupW(szDefaultHeader);
5760
5761             bSuccess = TRUE;
5762             goto lend;
5763         }
5764     } while (codeHundred);
5765
5766     /* Add status code */
5767     HTTP_ProcessHeader(request, szStatus, status_code,
5768             HTTP_ADDHDR_FLAG_REPLACE);
5769
5770     heap_free(request->version);
5771     heap_free(request->statusText);
5772
5773     request->version = heap_strdupW(buffer);
5774     request->statusText = heap_strdupW(status_text);
5775
5776     /* Restore the spaces */
5777     *(status_code-1) = ' ';
5778     *(status_text-1) = ' ';
5779
5780     /* regenerate raw headers */
5781     lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5782     if (!lpszRawHeaders) goto lend;
5783
5784     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5785         cchMaxRawHeaders *= 2;
5786     temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5787     if (temp == NULL) goto lend;
5788     lpszRawHeaders = temp;
5789     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5790     cchRawHeaders += (buflen-1);
5791     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5792     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5793     lpszRawHeaders[cchRawHeaders] = '\0';
5794
5795     /* Parse each response line */
5796     do
5797     {
5798         buflen = MAX_REPLY_LEN;
5799         if (read_line(request, bufferA, &buflen))
5800         {
5801             LPWSTR * pFieldAndValue;
5802
5803             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5804
5805             if (!bufferA[0]) break;
5806             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5807
5808             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5809             if (pFieldAndValue)
5810             {
5811                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5812                     cchMaxRawHeaders *= 2;
5813                 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5814                 if (temp == NULL) goto lend;
5815                 lpszRawHeaders = temp;
5816                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5817                 cchRawHeaders += (buflen-1);
5818                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5819                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5820                 lpszRawHeaders[cchRawHeaders] = '\0';
5821
5822                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5823                                    HTTP_ADDREQ_FLAG_ADD );
5824
5825                 HTTP_FreeTokens(pFieldAndValue);
5826             }
5827         }
5828         else
5829         {
5830             cbreaks++;
5831             if (cbreaks >= 2)
5832                break;
5833         }
5834     }while(1);
5835
5836     /* make sure the response header is terminated with an empty line.  Some apps really
5837        truly care about that empty line being there for some reason.  Just add it to the
5838        header. */
5839     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5840     {
5841         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5842         temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5843         if (temp == NULL) goto lend;
5844         lpszRawHeaders = temp;
5845     }
5846
5847     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5848
5849     heap_free(request->rawHeaders);
5850     request->rawHeaders = lpszRawHeaders;
5851     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5852     bSuccess = TRUE;
5853
5854 lend:
5855
5856     TRACE("<--\n");
5857     if (bSuccess)
5858         return rc;
5859     else
5860     {
5861         heap_free(lpszRawHeaders);
5862         return 0;
5863     }
5864 }
5865
5866 /***********************************************************************
5867  *           HTTP_InterpretHttpHeader (internal)
5868  *
5869  * Parse server response
5870  *
5871  * RETURNS
5872  *
5873  *   Pointer to array of field, value, NULL on success.
5874  *   NULL on error.
5875  */
5876 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5877 {
5878     LPWSTR * pTokenPair;
5879     LPWSTR pszColon;
5880     INT len;
5881
5882     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5883
5884     pszColon = strchrW(buffer, ':');
5885     /* must have two tokens */
5886     if (!pszColon)
5887     {
5888         HTTP_FreeTokens(pTokenPair);
5889         if (buffer[0])
5890             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5891         return NULL;
5892     }
5893
5894     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5895     if (!pTokenPair[0])
5896     {
5897         HTTP_FreeTokens(pTokenPair);
5898         return NULL;
5899     }
5900     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5901     pTokenPair[0][pszColon - buffer] = '\0';
5902
5903     /* skip colon */
5904     pszColon++;
5905     len = strlenW(pszColon);
5906     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5907     if (!pTokenPair[1])
5908     {
5909         HTTP_FreeTokens(pTokenPair);
5910         return NULL;
5911     }
5912     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5913
5914     strip_spaces(pTokenPair[0]);
5915     strip_spaces(pTokenPair[1]);
5916
5917     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5918     return pTokenPair;
5919 }
5920
5921 /***********************************************************************
5922  *           HTTP_ProcessHeader (internal)
5923  *
5924  * Stuff header into header tables according to <dwModifier>
5925  *
5926  */
5927
5928 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5929
5930 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5931 {
5932     LPHTTPHEADERW lphttpHdr = NULL;
5933     INT index = -1;
5934     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5935     DWORD res = ERROR_HTTP_INVALID_HEADER;
5936
5937     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5938
5939     /* REPLACE wins out over ADD */
5940     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5941         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5942     
5943     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5944         index = -1;
5945     else
5946         index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5947
5948     if (index >= 0)
5949     {
5950         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5951             return ERROR_HTTP_INVALID_HEADER;
5952         lphttpHdr = &request->custHeaders[index];
5953     }
5954     else if (value)
5955     {
5956         HTTPHEADERW hdr;
5957
5958         hdr.lpszField = (LPWSTR)field;
5959         hdr.lpszValue = (LPWSTR)value;
5960         hdr.wFlags = hdr.wCount = 0;
5961
5962         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5963             hdr.wFlags |= HDR_ISREQUEST;
5964
5965         return HTTP_InsertCustomHeader(request, &hdr);
5966     }
5967     /* no value to delete */
5968     else return ERROR_SUCCESS;
5969
5970     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5971             lphttpHdr->wFlags |= HDR_ISREQUEST;
5972     else
5973         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5974
5975     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5976     {
5977         HTTP_DeleteCustomHeader( request, index );
5978
5979         if (value)
5980         {
5981             HTTPHEADERW hdr;
5982
5983             hdr.lpszField = (LPWSTR)field;
5984             hdr.lpszValue = (LPWSTR)value;
5985             hdr.wFlags = hdr.wCount = 0;
5986
5987             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5988                 hdr.wFlags |= HDR_ISREQUEST;
5989
5990             return HTTP_InsertCustomHeader(request, &hdr);
5991         }
5992
5993         return ERROR_SUCCESS;
5994     }
5995     else if (dwModifier & COALESCEFLAGS)
5996     {
5997         LPWSTR lpsztmp;
5998         WCHAR ch = 0;
5999         INT len = 0;
6000         INT origlen = strlenW(lphttpHdr->lpszValue);
6001         INT valuelen = strlenW(value);
6002
6003         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6004         {
6005             ch = ',';
6006             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6007         }
6008         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6009         {
6010             ch = ';';
6011             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6012         }
6013
6014         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6015
6016         lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6017         if (lpsztmp)
6018         {
6019             lphttpHdr->lpszValue = lpsztmp;
6020     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6021             if (ch > 0)
6022             {
6023                 lphttpHdr->lpszValue[origlen] = ch;
6024                 origlen++;
6025                 lphttpHdr->lpszValue[origlen] = ' ';
6026                 origlen++;
6027             }
6028
6029             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6030             lphttpHdr->lpszValue[len] = '\0';
6031             res = ERROR_SUCCESS;
6032         }
6033         else
6034         {
6035             WARN("heap_realloc (%d bytes) failed\n",len+1);
6036             res = ERROR_OUTOFMEMORY;
6037         }
6038     }
6039     TRACE("<-- %d\n", res);
6040     return res;
6041 }
6042
6043 /***********************************************************************
6044  *           HTTP_GetCustomHeaderIndex (internal)
6045  *
6046  * Return index of custom header from header array
6047  *
6048  */
6049 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6050                                      int requested_index, BOOL request_only)
6051 {
6052     DWORD index;
6053
6054     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6055
6056     for (index = 0; index < request->nCustHeaders; index++)
6057     {
6058         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6059             continue;
6060
6061         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6062             continue;
6063
6064         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6065             continue;
6066
6067         if (requested_index == 0)
6068             break;
6069         requested_index --;
6070     }
6071
6072     if (index >= request->nCustHeaders)
6073         index = -1;
6074
6075     TRACE("Return: %d\n", index);
6076     return index;
6077 }
6078
6079
6080 /***********************************************************************
6081  *           HTTP_InsertCustomHeader (internal)
6082  *
6083  * Insert header into array
6084  *
6085  */
6086 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6087 {
6088     INT count;
6089     LPHTTPHEADERW lph = NULL;
6090
6091     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6092     count = request->nCustHeaders + 1;
6093     if (count > 1)
6094         lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6095     else
6096         lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6097
6098     if (!lph)
6099         return ERROR_OUTOFMEMORY;
6100
6101     request->custHeaders = lph;
6102     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6103     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6104     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6105     request->custHeaders[count-1].wCount= lpHdr->wCount;
6106     request->nCustHeaders++;
6107
6108     return ERROR_SUCCESS;
6109 }
6110
6111
6112 /***********************************************************************
6113  *           HTTP_DeleteCustomHeader (internal)
6114  *
6115  * Delete header from array
6116  *  If this function is called, the indexs may change.
6117  */
6118 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6119 {
6120     if( request->nCustHeaders <= 0 )
6121         return FALSE;
6122     if( index >= request->nCustHeaders )
6123         return FALSE;
6124     request->nCustHeaders--;
6125
6126     heap_free(request->custHeaders[index].lpszField);
6127     heap_free(request->custHeaders[index].lpszValue);
6128
6129     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6130              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6131     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6132
6133     return TRUE;
6134 }
6135
6136
6137 /***********************************************************************
6138  *           HTTP_VerifyValidHeader (internal)
6139  *
6140  * Verify the given header is not invalid for the given http request
6141  *
6142  */
6143 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6144 {
6145     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6146     if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6147         return ERROR_HTTP_INVALID_HEADER;
6148
6149     return ERROR_SUCCESS;
6150 }
6151
6152 /***********************************************************************
6153  *          IsHostInProxyBypassList (@)
6154  *
6155  * Undocumented
6156  *
6157  */
6158 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6159 {
6160    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6161    return FALSE;
6162 }
6163
6164 /***********************************************************************
6165  *           InternetShowSecurityInfoByURLA (@)
6166  */
6167 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6168 {
6169    FIXME("stub: %s %p\n", url, window);
6170    return FALSE;
6171 }
6172
6173 /***********************************************************************
6174  *           InternetShowSecurityInfoByURLW (@)
6175  */
6176 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6177 {
6178    FIXME("stub: %s %p\n", debugstr_w(url), window);
6179    return FALSE;
6180 }
6181
6182 /***********************************************************************
6183  *           ShowX509EncodedCertificate (@)
6184  */
6185 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6186 {
6187     PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6188         cert, len);
6189     DWORD ret;
6190
6191     if (certContext)
6192     {
6193         CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6194
6195         memset(&view, 0, sizeof(view));
6196         view.hwndParent = parent;
6197         view.pCertContext = certContext;
6198         if (CryptUIDlgViewCertificateW(&view, NULL))
6199             ret = ERROR_SUCCESS;
6200         else
6201             ret = GetLastError();
6202         CertFreeCertificateContext(certContext);
6203     }
6204     else
6205         ret = GetLastError();
6206     return ret;
6207 }