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