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