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