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