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