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