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