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