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