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