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