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