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