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