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