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