~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/wininet/http.c

Version: ~ [ wine-1.5.4 ] ~ [ wine-1.5.3 ] ~ [ wine-1.5.2 ] ~ [ wine-1.5.1 ] ~ [ wine-1.5.0 ] ~ [ wine-1.4 ] ~ [ wine-1.4-rc6 ] ~ [ wine-1.4-rc5 ] ~ [ wine-1.4-rc4 ] ~ [ wine-1.4-rc3 ] ~ [ wine-1.4-rc2 ] ~ [ wine-1.4-rc1 ] ~ [ wine-1.3.37 ] ~ [ wine-1.3.36 ] ~ [ wine-1.3.35 ] ~ [ wine-1.3.34 ] ~ [ wine-1.3.33 ] ~ [ wine-1.3.32 ] ~ [ wine-1.3.31 ] ~ [ wine-1.3.30 ] ~ [ wine-1.3.29 ] ~ [ wine-1.3.28 ] ~ [ wine-1.3.27 ] ~ [ wine-1.3.26 ] ~ [ wine-1.3.25 ] ~ [ wine-1.3.24 ] ~ [ wine-1.3.23 ] ~ [ wine-1.3.22 ] ~ [ wine-1.3.21 ] ~ [ wine-1.3.20 ] ~ [ wine-1.3.19 ] ~ [ wine-1.3.18 ] ~ [ wine-1.2.3 ] ~ [ wine-1.3.17 ] ~ [ wine-1.3.16 ] ~ [ wine-1.3.15 ] ~ [ wine-1.3.14 ] ~ [ wine-1.3.13 ] ~ [ wine-1.3.12 ] ~ [ wine-1.3.11 ] ~ [ wine-1.3.10 ] ~ [ wine-1.3.9 ] ~ [ wine-1.2.2 ] ~ [ wine-1.3.8 ] ~ [ wine-1.3.7 ] ~ [ wine-1.3.6 ] ~ [ wine-1.3.5 ] ~ [ wine-1.2.1 ] ~ [ wine-1.3.4 ] ~ [ wine-1.3.3 ] ~ [ wine-1.3.2 ] ~ [ wine-1.3.1 ] ~ [ wine-1.3.0 ] ~ [ wine-1.2 ] ~ [ wine-1.2-rc7 ] ~ [ wine-1.2-rc6 ] ~ [ wine-1.2-rc5 ] ~ [ wine-1.2-rc4 ] ~ [ wine-1.2-rc3 ] ~ [ wine-1.2-rc2 ] ~ [ wine-1.2-rc1 ] ~ [ wine-1.1.44 ] ~ [ wine-1.1.43 ] ~ [ wine-1.1.42 ] ~ [ wine-1.1.41 ] ~ [ wine-1.1.40 ] ~ [ wine-1.1.39 ] ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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};
 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','.','',' ','2','','',' ','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) >= '' && (x) <= '9') ? (x) - '' + 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 INET_SetOption(hdr, option, buffer, size);
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 >= '' && ch <= '9') chunk_size = chunk_size * 16 + ch - '';
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)
2589 {
2590     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2591     WCHAR encoding[20];
2592     DWORD size;
2593 
2594     if(request->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     case HTTP_QUERY_STATUS_CODE: {
3425         DWORD res = ERROR_SUCCESS;
3426 
3427         if(request_only || requested_index)
3428             break;
3429 
3430         if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3431             if(*lpdwBufferLength >= sizeof(DWORD))
3432                 *(DWORD*)lpBuffer = request->status_code;
3433             else
3434                 res = ERROR_INSUFFICIENT_BUFFER;
3435             *lpdwBufferLength = sizeof(DWORD);
3436         }else {
3437             WCHAR buf[12];
3438             DWORD size;
3439             static const WCHAR formatW[] = {'%','u',0};
3440 
3441             size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3442 
3443             if(size <= *lpdwBufferLength)
3444                 memcpy(lpBuffer, buf, size);
3445             else
3446                 res = ERROR_INSUFFICIENT_BUFFER;
3447 
3448             *lpdwBufferLength = size;
3449         }
3450         return res;
3451     }
3452     default:
3453         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3454 
3455         if (level < LAST_TABLE_HEADER && header_lookup[level])
3456             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3457                                               requested_index,request_only);
3458     }
3459 
3460     if (index >= 0)
3461         lphttpHdr = &request->custHeaders[index];
3462 
3463     /* Ensure header satisfies requested attributes */
3464     if (!lphttpHdr ||
3465         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3466          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3467     {
3468         return ERROR_HTTP_HEADER_NOT_FOUND;
3469     }
3470 
3471     if (lpdwIndex) (*lpdwIndex)++;
3472 
3473     /* coalesce value to requested type */
3474     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3475     {
3476         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3477         TRACE(" returning number: %d\n", *(int *)lpBuffer);
3478      }
3479     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3480     {
3481         time_t tmpTime;
3482         struct tm tmpTM;
3483         SYSTEMTIME *STHook;
3484 
3485         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3486 
3487         tmpTM = *gmtime(&tmpTime);
3488         STHook = (SYSTEMTIME *)lpBuffer;
3489         STHook->wDay = tmpTM.tm_mday;
3490         STHook->wHour = tmpTM.tm_hour;
3491         STHook->wMilliseconds = 0;
3492         STHook->wMinute = tmpTM.tm_min;
3493         STHook->wDayOfWeek = tmpTM.tm_wday;
3494         STHook->wMonth = tmpTM.tm_mon + 1;
3495         STHook->wSecond = tmpTM.tm_sec;
3496         STHook->wYear = tmpTM.tm_year;
3497 
3498         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3499               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3500               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3501     }
3502     else if (lphttpHdr->lpszValue)
3503     {
3504         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3505 
3506         if (len > *lpdwBufferLength)
3507         {
3508             *lpdwBufferLength = len;
3509             return ERROR_INSUFFICIENT_BUFFER;
3510         }
3511         if (lpBuffer)
3512         {
3513             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3514             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3515         }
3516         *lpdwBufferLength = len - sizeof(WCHAR);
3517     }
3518     return ERROR_SUCCESS;
3519 }
3520 
3521 /***********************************************************************
3522  *           HttpQueryInfoW (WININET.@)
3523  *
3524  * Queries for information about an HTTP request
3525  *
3526  * RETURNS
3527  *    TRUE  on success
3528  *    FALSE on failure
3529  *
3530  */
3531 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3532         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3533 {
3534     http_request_t *request;
3535     DWORD res;
3536 
3537     if (TRACE_ON(wininet)) {
3538 #define FE(x) { x, #x }
3539         static const wininet_flag_info query_flags[] = {
3540             FE(HTTP_QUERY_MIME_VERSION),
3541             FE(HTTP_QUERY_CONTENT_TYPE),
3542             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3543             FE(HTTP_QUERY_CONTENT_ID),
3544             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3545             FE(HTTP_QUERY_CONTENT_LENGTH),
3546             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3547             FE(HTTP_QUERY_ALLOW),
3548             FE(HTTP_QUERY_PUBLIC),
3549             FE(HTTP_QUERY_DATE),
3550             FE(HTTP_QUERY_EXPIRES),
3551             FE(HTTP_QUERY_LAST_MODIFIED),
3552             FE(HTTP_QUERY_MESSAGE_ID),
3553             FE(HTTP_QUERY_URI),
3554             FE(HTTP_QUERY_DERIVED_FROM),
3555             FE(HTTP_QUERY_COST),
3556             FE(HTTP_QUERY_LINK),
3557             FE(HTTP_QUERY_PRAGMA),
3558             FE(HTTP_QUERY_VERSION),
3559             FE(HTTP_QUERY_STATUS_CODE),
3560             FE(HTTP_QUERY_STATUS_TEXT),
3561             FE(HTTP_QUERY_RAW_HEADERS),
3562             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3563             FE(HTTP_QUERY_CONNECTION),
3564             FE(HTTP_QUERY_ACCEPT),
3565             FE(HTTP_QUERY_ACCEPT_CHARSET),
3566             FE(HTTP_QUERY_ACCEPT_ENCODING),
3567             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3568             FE(HTTP_QUERY_AUTHORIZATION),
3569             FE(HTTP_QUERY_CONTENT_ENCODING),
3570             FE(HTTP_QUERY_FORWARDED),
3571             FE(HTTP_QUERY_FROM),
3572             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3573             FE(HTTP_QUERY_LOCATION),
3574             FE(HTTP_QUERY_ORIG_URI),
3575             FE(HTTP_QUERY_REFERER),
3576             FE(HTTP_QUERY_RETRY_AFTER),
3577             FE(HTTP_QUERY_SERVER),
3578             FE(HTTP_QUERY_TITLE),
3579             FE(HTTP_QUERY_USER_AGENT),
3580             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3581             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3582             FE(HTTP_QUERY_ACCEPT_RANGES),
3583         FE(HTTP_QUERY_SET_COOKIE),
3584         FE(HTTP_QUERY_COOKIE),
3585             FE(HTTP_QUERY_REQUEST_METHOD),
3586             FE(HTTP_QUERY_REFRESH),
3587             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3588             FE(HTTP_QUERY_AGE),
3589             FE(HTTP_QUERY_CACHE_CONTROL),
3590             FE(HTTP_QUERY_CONTENT_BASE),
3591             FE(HTTP_QUERY_CONTENT_LOCATION),
3592             FE(HTTP_QUERY_CONTENT_MD5),
3593             FE(HTTP_QUERY_CONTENT_RANGE),
3594             FE(HTTP_QUERY_ETAG),
3595             FE(HTTP_QUERY_HOST),
3596             FE(HTTP_QUERY_IF_MATCH),
3597             FE(HTTP_QUERY_IF_NONE_MATCH),
3598             FE(HTTP_QUERY_IF_RANGE),
3599             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3600             FE(HTTP_QUERY_MAX_FORWARDS),
3601             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3602             FE(HTTP_QUERY_RANGE),
3603             FE(HTTP_QUERY_TRANSFER_ENCODING),
3604             FE(HTTP_QUERY_UPGRADE),
3605             FE(HTTP_QUERY_VARY),
3606             FE(HTTP_QUERY_VIA),
3607             FE(HTTP_QUERY_WARNING),
3608             FE(HTTP_QUERY_CUSTOM)
3609         };
3610         static const wininet_flag_info modifier_flags[] = {
3611             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3612             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3613             FE(HTTP_QUERY_FLAG_NUMBER),
3614             FE(HTTP_QUERY_FLAG_COALESCE)
3615         };
3616 #undef FE
3617         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3618         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3619         DWORD i;
3620 
3621         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3622         TRACE("  Attribute:");
3623         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3624             if (query_flags[i].val == info) {
3625                 TRACE(" %s", query_flags[i].name);
3626                 break;
3627             }
3628         }
3629         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3630             TRACE(" Unknown (%08x)", info);
3631         }
3632 
3633         TRACE(" Modifier:");
3634         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3635             if (modifier_flags[i].val & info_mod) {
3636                 TRACE(" %s", modifier_flags[i].name);
3637                 info_mod &= ~ modifier_flags[i].val;
3638             }
3639         }
3640         
3641         if (info_mod) {
3642             TRACE(" Unknown (%08x)", info_mod);
3643         }
3644         TRACE("\n");
3645     }
3646     
3647     request = (http_request_t*) get_handle_object( hHttpRequest );
3648     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
3649     {
3650         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3651         goto lend;
3652     }
3653 
3654     if (lpBuffer == NULL)
3655         *lpdwBufferLength = 0;
3656     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3657                                lpBuffer, lpdwBufferLength, lpdwIndex);
3658 
3659 lend:
3660     if( request )
3661          WININET_Release( &request->hdr );
3662 
3663     TRACE("%u <--\n", res);
3664     if(res != ERROR_SUCCESS)
3665         SetLastError(res);
3666     return res == ERROR_SUCCESS;
3667 }
3668 
3669 /***********************************************************************
3670  *           HttpQueryInfoA (WININET.@)
3671  *
3672  * Queries for information about an HTTP request
3673  *
3674  * RETURNS
3675  *    TRUE  on success
3676  *    FALSE on failure
3677  *
3678  */
3679 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3680         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3681 {
3682     BOOL result;
3683     DWORD len;
3684     WCHAR* bufferW;
3685 
3686     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3687        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3688     {
3689         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3690                                lpdwBufferLength, lpdwIndex );
3691     }
3692 
3693     if (lpBuffer)
3694     {
3695         DWORD alloclen;
3696         len = (*lpdwBufferLength)*sizeof(WCHAR);
3697         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3698         {
3699             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3700             if (alloclen < len)
3701                 alloclen = len;
3702         }
3703         else
3704             alloclen = len;
3705         bufferW = heap_alloc(alloclen);
3706         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3707         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3708             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3709     } else
3710     {
3711         bufferW = NULL;
3712         len = 0;
3713     }
3714 
3715     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3716                            &len, lpdwIndex );
3717     if( result )
3718     {
3719         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3720                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3721         *lpdwBufferLength = len - 1;
3722 
3723         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3724     }
3725     else
3726         /* since the strings being returned from HttpQueryInfoW should be
3727          * only ASCII characters, it is reasonable to assume that all of
3728          * the Unicode characters can be reduced to a single byte */
3729         *lpdwBufferLength = len / sizeof(WCHAR);
3730 
3731     heap_free( bufferW );
3732     return result;
3733 }
3734 
3735 /***********************************************************************
3736  *           HTTP_GetRedirectURL (internal)
3737  */
3738 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3739 {
3740     static WCHAR szHttp[] = {'h','t','t','p',0};
3741     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3742     http_session_t *session = request->session;
3743     URL_COMPONENTSW urlComponents;
3744     DWORD url_length = 0;
3745     LPWSTR orig_url;
3746     LPWSTR combined_url;
3747 
3748     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3749     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3750     urlComponents.dwSchemeLength = 0;
3751     urlComponents.lpszHostName = session->hostName;
3752     urlComponents.dwHostNameLength = 0;
3753     urlComponents.nPort = session->hostPort;
3754     urlComponents.lpszUserName = session->userName;
3755     urlComponents.dwUserNameLength = 0;
3756     urlComponents.lpszPassword = NULL;
3757     urlComponents.dwPasswordLength = 0;
3758     urlComponents.lpszUrlPath = request->path;
3759     urlComponents.dwUrlPathLength = 0;
3760     urlComponents.lpszExtraInfo = NULL;
3761     urlComponents.dwExtraInfoLength = 0;
3762 
3763     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3764         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3765         return NULL;
3766 
3767     orig_url = heap_alloc(url_length);
3768 
3769     /* convert from bytes to characters */
3770     url_length = url_length / sizeof(WCHAR) - 1;
3771     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3772     {
3773         heap_free(orig_url);
3774         return NULL;
3775     }
3776 
3777     url_length = 0;
3778     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3779         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3780     {
3781         heap_free(orig_url);
3782         return NULL;
3783     }
3784     combined_url = heap_alloc(url_length * sizeof(WCHAR));
3785 
3786     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3787     {
3788         heap_free(orig_url);
3789         heap_free(combined_url);
3790         return NULL;
3791     }
3792     heap_free(orig_url);
3793     return combined_url;
3794 }
3795 
3796 
3797 /***********************************************************************
3798  *           HTTP_HandleRedirect (internal)
3799  */
3800 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3801 {
3802     http_session_t *session = request->session;
3803     appinfo_t *hIC = session->appInfo;
3804     BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3805     WCHAR path[INTERNET_MAX_PATH_LENGTH];
3806     int index;
3807 
3808     if(lpszUrl[0]=='/')
3809     {
3810         /* if it's an absolute path, keep the same session info */
3811         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3812     }
3813     else
3814     {
3815         URL_COMPONENTSW urlComponents;
3816         WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3817         WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3818         WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3819         BOOL custom_port = FALSE;
3820 
3821         static WCHAR httpW[] = {'h','t','t','p',0};
3822         static WCHAR httpsW[] = {'h','t','t','p','s',0};
3823 
3824         userName[0] = 0;
3825         hostName[0] = 0;
3826         protocol[0] = 0;
3827 
3828         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3829         urlComponents.lpszScheme = protocol;
3830         urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3831         urlComponents.lpszHostName = hostName;
3832         urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3833         urlComponents.lpszUserName = userName;
3834         urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3835         urlComponents.lpszPassword = NULL;
3836         urlComponents.dwPasswordLength = 0;
3837         urlComponents.lpszUrlPath = path;
3838         urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3839         urlComponents.lpszExtraInfo = NULL;
3840         urlComponents.dwExtraInfoLength = 0;
3841         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3842             return INTERNET_GetLastError();
3843 
3844         if(!strcmpiW(protocol, httpW)) {
3845             if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3846                 TRACE("redirect from secure page to non-secure page\n");
3847                 /* FIXME: warn about from secure redirect to non-secure page */
3848                 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3849             }
3850 
3851             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3852                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3853             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3854                 custom_port = TRUE;
3855         }else if(!strcmpiW(protocol, httpsW)) {
3856             if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3857                 TRACE("redirect from non-secure page to secure page\n");
3858                 /* FIXME: notify about redirect to secure page */
3859                 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3860             }
3861 
3862             if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3863                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3864             else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3865                 custom_port = TRUE;
3866         }
3867 
3868         heap_free(session->hostName);
3869 
3870         if(custom_port) {
3871             int len;
3872             static const WCHAR fmt[] = {'%','s',':','%','u',0};
3873             len = lstrlenW(hostName);
3874             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3875             session->hostName = heap_alloc(len*sizeof(WCHAR));
3876             sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3877         }
3878         else
3879             session->hostName = heap_strdupW(hostName);
3880 
3881         HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3882 
3883         heap_free(session->userName);
3884         session->userName = NULL;
3885         if (userName[0])
3886             session->userName = heap_strdupW(userName);
3887 
3888         reset_data_stream(request);
3889 
3890         if(!using_proxy) {
3891             if(strcmpiW(session->serverName, hostName)) {
3892                 heap_free(session->serverName);
3893                 session->serverName = heap_strdupW(hostName);
3894             }
3895             session->serverPort = urlComponents.nPort;
3896         }
3897     }
3898     heap_free(request->path);
3899     request->path=NULL;
3900     if (*path)
3901     {
3902         DWORD needed = 0;
3903         HRESULT rc;
3904 
3905         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3906         if (rc != E_POINTER)
3907             needed = strlenW(path)+1;
3908         request->path = heap_alloc(needed*sizeof(WCHAR));
3909         rc = UrlEscapeW(path, request->path, &needed,
3910                         URL_ESCAPE_SPACES_ONLY);
3911         if (rc != S_OK)
3912         {
3913             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3914             strcpyW(request->path,path);
3915         }
3916     }
3917 
3918     /* Remove custom content-type/length headers on redirects.  */
3919     index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3920     if (0 <= index)
3921         HTTP_DeleteCustomHeader(request, index);
3922     index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3923     if (0 <= index)
3924         HTTP_DeleteCustomHeader(request, index);
3925 
3926     return ERROR_SUCCESS;
3927 }
3928 
3929 /***********************************************************************
3930  *           HTTP_build_req (internal)
3931  *
3932  *  concatenate all the strings in the request together
3933  */
3934 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3935 {
3936     LPCWSTR *t;
3937     LPWSTR str;
3938 
3939     for( t = list; *t ; t++  )
3940         len += strlenW( *t );
3941     len++;
3942 
3943     str = heap_alloc(len*sizeof(WCHAR));
3944     *str = 0;
3945 
3946     for( t = list; *t ; t++ )
3947         strcatW( str, *t );
3948 
3949     return str;
3950 }
3951 
3952 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3953 {
3954     LPWSTR lpszPath;
3955     LPWSTR requestString;
3956     INT len;
3957     INT cnt;
3958     INT responseLen;
3959     char *ascii_req;
3960     DWORD res;
3961     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3962     static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3963     http_session_t *session = request->session;
3964 
3965     TRACE("\n");
3966 
3967     lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3968     sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3969     requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3970     heap_free( lpszPath );
3971 
3972     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3973                                 NULL, 0, NULL, NULL );
3974     len--; /* the nul terminator isn't needed */
3975     ascii_req = heap_alloc(len);
3976     WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3977     heap_free( requestString );
3978 
3979     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3980 
3981     NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3982     res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3983     heap_free( ascii_req );
3984     if (res != ERROR_SUCCESS)
3985         return res;
3986 
3987     responseLen = HTTP_GetResponseHeaders( request, TRUE );
3988     if (!responseLen)
3989         return ERROR_HTTP_INVALID_HEADER;
3990 
3991     return ERROR_SUCCESS;
3992 }
3993 
3994 static void HTTP_InsertCookies(http_request_t *request)
3995 {
3996     DWORD cookie_size, size, cnt = 0;
3997     HTTPHEADERW *host;
3998     WCHAR *cookies;
3999 
4000     static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4001 
4002     host = HTTP_GetHeader(request, hostW);
4003     if(!host)
4004         return;
4005 
4006     if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4007         return;
4008 
4009     size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4010     if(!(cookies = heap_alloc(size)))
4011         return;
4012 
4013     cnt += sprintfW(cookies, cookieW);
4014     get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4015     strcatW(cookies, szCrLf);
4016 
4017     HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4018 
4019     heap_free(cookies);
4020 }
4021 
4022 static WORD HTTP_ParseWkday(LPCWSTR day)
4023 {
4024     static const WCHAR days[7][4] = {{ 's','u','n',0 },
4025                                      { 'm','o','n',0 },
4026                                      { 't','u','e',0 },
4027                                      { 'w','e','d',0 },
4028                                      { 't','h','u',0 },
4029                                      { 'f','r','i',0 },
4030                                      { 's','a','t',0 }};
4031     int i;
4032     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4033         if (!strcmpiW(day, days[i]))
4034             return i;
4035 
4036     /* Invalid */
4037     return 7;
4038 }
4039 
4040 static WORD HTTP_ParseMonth(LPCWSTR month)
4041 {
4042     static const WCHAR jan[] = { 'j','a','n',0 };
4043     static const WCHAR feb[] = { 'f','e','b',0 };
4044     static const WCHAR mar[] = { 'm','a','r',0 };
4045     static const WCHAR apr[] = { 'a','p','r',0 };
4046     static const WCHAR may[] = { 'm','a','y',0 };
4047     static const WCHAR jun[] = { 'j','u','n',0 };
4048     static const WCHAR jul[] = { 'j','u','l',0 };
4049     static const WCHAR aug[] = { 'a','u','g',0 };
4050     static const WCHAR sep[] = { 's','e','p',0 };
4051     static const WCHAR oct[] = { 'o','c','t',0 };
4052     static const WCHAR nov[] = { 'n','o','v',0 };
4053     static const WCHAR dec[] = { 'd','e','c',0 };
4054 
4055     if (!strcmpiW(month, jan)) return 1;
4056     if (!strcmpiW(month, feb)) return 2;
4057     if (!strcmpiW(month, mar)) return 3;
4058     if (!strcmpiW(month, apr)) return 4;
4059     if (!strcmpiW(month, may)) return 5;
4060     if (!strcmpiW(month, jun)) return 6;
4061     if (!strcmpiW(month, jul)) return 7;
4062     if (!strcmpiW(month, aug)) return 8;
4063     if (!strcmpiW(month, sep)) return 9;
4064     if (!strcmpiW(month, oct)) return 10;
4065     if (!strcmpiW(month, nov)) return 11;
4066     if (!strcmpiW(month, dec)) return 12;
4067     /* Invalid */
4068     return 0;
4069 }
4070 
4071 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4072  * optionally preceded by whitespace.
4073  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4074  * st, and sets *str to the first character after the time format.
4075  */
4076 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4077 {
4078     LPCWSTR ptr = *str;
4079     WCHAR *nextPtr;
4080     unsigned long num;
4081 
4082     while (isspaceW(*ptr))
4083         ptr++;
4084 
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 > 23)
4092     {
4093         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4094         return FALSE;
4095     }
4096     ptr = nextPtr + 1;
4097     st->wHour = (WORD)num;
4098     num = strtoulW(ptr, &nextPtr, 10);
4099     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4100     {
4101         ERR("unexpected time format %s\n", debugstr_w(ptr));
4102         return FALSE;
4103     }
4104     if (num > 59)
4105     {
4106         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4107         return FALSE;
4108     }
4109     ptr = nextPtr + 1;
4110     st->wMinute = (WORD)num;
4111     num = strtoulW(ptr, &nextPtr, 10);
4112     if (!nextPtr || nextPtr <= ptr)
4113     {
4114         ERR("unexpected time format %s\n", debugstr_w(ptr));
4115         return FALSE;
4116     }
4117     if (num > 59)
4118     {
4119         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4120         return FALSE;
4121     }
4122     ptr = nextPtr + 1;
4123     *str = ptr;
4124     st->wSecond = (WORD)num;
4125     return TRUE;
4126 }
4127 
4128 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4129 {
4130     static const WCHAR gmt[]= { 'G','M','T',0 };
4131     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4132     LPCWSTR ptr;
4133     SYSTEMTIME st = { 0 };
4134     unsigned long num;
4135 
4136     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4137          dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4138         *dayPtr = *ptr;
4139     *dayPtr = 0;
4140     st.wDayOfWeek = HTTP_ParseWkday(day);
4141     if (st.wDayOfWeek >= 7)
4142     {
4143         ERR("unexpected weekday %s\n", debugstr_w(day));
4144         return FALSE;
4145     }
4146 
4147     while (isspaceW(*ptr))
4148         ptr++;
4149 
4150     for (monthPtr = month; !isspace(*ptr) &&
4151          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4152          monthPtr++, ptr++)
4153         *monthPtr = *ptr;
4154     *monthPtr = 0;
4155     st.wMonth = HTTP_ParseMonth(month);
4156     if (!st.wMonth || st.wMonth > 12)
4157     {
4158         ERR("unexpected month %s\n", debugstr_w(month));
4159         return FALSE;
4160     }
4161 
4162     while (isspaceW(*ptr))
4163         ptr++;
4164 
4165     num = strtoulW(ptr, &nextPtr, 10);
4166     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4167     {
4168         ERR("unexpected day %s\n", debugstr_w(ptr));
4169         return FALSE;
4170     }
4171     ptr = nextPtr;
4172     st.wDay = (WORD)num;
4173 
4174     while (isspaceW(*ptr))
4175         ptr++;
4176 
4177     if (!HTTP_ParseTime(&st, &ptr))
4178         return FALSE;
4179 
4180     while (isspaceW(*ptr))
4181         ptr++;
4182 
4183     num = strtoulW(ptr, &nextPtr, 10);
4184     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4185     {
4186         ERR("unexpected year %s\n", debugstr_w(ptr));
4187         return FALSE;
4188     }
4189     ptr = nextPtr;
4190     st.wYear = (WORD)num;
4191 
4192     while (isspaceW(*ptr))
4193         ptr++;
4194 
4195     /* asctime() doesn't report a timezone, but some web servers do, so accept
4196      * with or without GMT.
4197      */
4198     if (*ptr && strcmpW(ptr, gmt))
4199     {
4200         ERR("unexpected timezone %s\n", debugstr_w(ptr));
4201         return FALSE;
4202     }
4203     return SystemTimeToFileTime(&st, ft);
4204 }
4205 
4206 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4207 {
4208     static const WCHAR gmt[]= { 'G','M','T',0 };
4209     WCHAR *nextPtr, day[4], month[4], *monthPtr;
4210     LPCWSTR ptr;
4211     unsigned long num;
4212     SYSTEMTIME st = { 0 };
4213 
4214     ptr = strchrW(value, ',');
4215     if (!ptr)
4216         return FALSE;
4217     if (ptr - value != 3)
4218     {
4219         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4220         return FALSE;
4221     }
4222     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4223     day[3] = 0;
4224     st.wDayOfWeek = HTTP_ParseWkday(day);
4225     if (st.wDayOfWeek > 6)
4226     {
4227         WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4228         return FALSE;
4229     }
4230     ptr++;
4231 
4232     while (isspaceW(*ptr))
4233         ptr++;
4234 
4235     num = strtoulW(ptr, &nextPtr, 10);
4236     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4237     {
4238         WARN("unexpected day %s\n", debugstr_w(value));
4239         return FALSE;
4240     }
4241     ptr = nextPtr;
4242     st.wDay = (WORD)num;
4243 
4244     while (isspaceW(*ptr))
4245         ptr++;
4246 
4247     for (monthPtr = month; !isspace(*ptr) &&
4248          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4249          monthPtr++, ptr++)
4250         *monthPtr = *ptr;
4251     *monthPtr = 0;
4252     st.wMonth = HTTP_ParseMonth(month);
4253     if (!st.wMonth || st.wMonth > 12)
4254     {
4255         WARN("unexpected month %s\n", debugstr_w(month));
4256         return FALSE;
4257     }
4258 
4259     while (isspaceW(*ptr))
4260         ptr++;
4261 
4262     num = strtoulW(ptr, &nextPtr, 10);
4263     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4264     {
4265         ERR("unexpected year %s\n", debugstr_w(value));
4266         return FALSE;
4267     }
4268     ptr = nextPtr;
4269     st.wYear = (WORD)num;
4270 
4271     if (!HTTP_ParseTime(&st, &ptr))
4272         return FALSE;
4273 
4274     while (isspaceW(*ptr))
4275         ptr++;
4276 
4277     if (strcmpW(ptr, gmt))
4278     {
4279         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4280         return FALSE;
4281     }
4282     return SystemTimeToFileTime(&st, ft);
4283 }
4284 
4285 static WORD HTTP_ParseWeekday(LPCWSTR day)
4286 {
4287     static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4288                                      { 'm','o','n','d','a','y',0 },
4289                                      { 't','u','e','s','d','a','y',0 },
4290                                      { 'w','e','d','n','e','s','d','a','y',0 },
4291                                      { 't','h','u','r','s','d','a','y',0 },
4292                                      { 'f','r','i','d','a','y',0 },
4293                                      { 's','a','t','u','r','d','a','y',0 }};
4294     int i;
4295     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4296         if (!strcmpiW(day, days[i]))
4297             return i;
4298 
4299     /* Invalid */
4300     return 7;
4301 }
4302 
4303 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4304 {
4305     static const WCHAR gmt[]= { 'G','M','T',0 };
4306     WCHAR *nextPtr, day[10], month[4], *monthPtr;
4307     LPCWSTR ptr;
4308     unsigned long num;
4309     SYSTEMTIME st = { 0 };
4310 
4311     ptr = strchrW(value, ',');
4312     if (!ptr)
4313         return FALSE;
4314     if (ptr - value == 3)
4315     {
4316         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4317         day[3] = 0;
4318         st.wDayOfWeek = HTTP_ParseWkday(day);
4319         if (st.wDayOfWeek > 6)
4320         {
4321             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4322             return FALSE;
4323         }
4324     }
4325     else if (ptr - value < sizeof(day) / sizeof(day[0]))
4326     {
4327         memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4328         day[ptr - value + 1] = 0;
4329         st.wDayOfWeek = HTTP_ParseWeekday(day);
4330         if (st.wDayOfWeek > 6)
4331         {
4332             ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4333             return FALSE;
4334         }
4335     }
4336     else
4337     {
4338         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4339         return FALSE;
4340     }
4341     ptr++;
4342 
4343     while (isspaceW(*ptr))
4344         ptr++;
4345 
4346     num = strtoulW(ptr, &nextPtr, 10);
4347     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4348     {
4349         ERR("unexpected day %s\n", debugstr_w(value));
4350         return FALSE;
4351     }
4352     ptr = nextPtr;
4353     st.wDay = (WORD)num;
4354 
4355     if (*ptr != '-')
4356     {
4357         ERR("unexpected month format %s\n", debugstr_w(ptr));
4358         return FALSE;
4359     }
4360     ptr++;
4361 
4362     for (monthPtr = month; *ptr != '-' &&
4363          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4364          monthPtr++, ptr++)
4365         *monthPtr = *ptr;
4366     *monthPtr = 0;
4367     st.wMonth = HTTP_ParseMonth(month);
4368     if (!st.wMonth || st.wMonth > 12)
4369     {
4370         ERR("unexpected month %s\n", debugstr_w(month));
4371         return FALSE;
4372     }
4373 
4374     if (*ptr != '-')
4375     {
4376         ERR("unexpected year format %s\n", debugstr_w(ptr));
4377         return FALSE;
4378     }
4379     ptr++;
4380 
4381     num = strtoulW(ptr, &nextPtr, 10);
4382     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4383     {
4384         ERR("unexpected year %s\n", debugstr_w(value));
4385         return FALSE;
4386     }
4387     ptr = nextPtr;
4388     st.wYear = (WORD)num;
4389 
4390     if (!HTTP_ParseTime(&st, &ptr))
4391         return FALSE;
4392 
4393     while (isspaceW(*ptr))
4394         ptr++;
4395 
4396     if (strcmpW(ptr, gmt))
4397     {
4398         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4399         return FALSE;
4400     }
4401     return SystemTimeToFileTime(&st, ft);
4402 }
4403 
4404 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4405 {
4406     static const WCHAR zero[] = { '',0 };
4407     BOOL ret;
4408 
4409     if (!strcmpW(value, zero))
4410     {
4411         ft->dwLowDateTime = ft->dwHighDateTime = 0;
4412         ret = TRUE;
4413     }
4414     else if (strchrW(value, ','))
4415     {
4416         ret = HTTP_ParseRfc1123Date(value, ft);
4417         if (!ret)
4418         {
4419             ret = HTTP_ParseRfc850Date(value, ft);
4420             if (!ret)
4421                 ERR("unexpected date format %s\n", debugstr_w(value));
4422         }
4423     }
4424     else
4425     {
4426         ret = HTTP_ParseDateAsAsctime(value, ft);
4427         if (!ret)
4428             ERR("unexpected date format %s\n", debugstr_w(value));
4429     }
4430     return ret;
4431 }
4432 
4433 static void HTTP_ProcessExpires(http_request_t *request)
4434 {
4435     BOOL expirationFound = FALSE;
4436     int headerIndex;
4437 
4438     /* Look for a Cache-Control header with a max-age directive, as it takes
4439      * precedence over the Expires header.
4440      */
4441     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4442     if (headerIndex != -1)
4443     {
4444         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4445         LPWSTR ptr;
4446 
4447         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4448         {
4449             LPWSTR comma = strchrW(ptr, ','), end, equal;
4450 
4451             if (comma)
4452                 end = comma;
4453             else
4454                 end = ptr + strlenW(ptr);
4455             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4456                 ;
4457             if (*equal == '=')
4458             {
4459                 static const WCHAR max_age[] = {
4460                     'm','a','x','-','a','g','e',0 };
4461 
4462                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4463                 {
4464                     LPWSTR nextPtr;
4465                     unsigned long age;
4466 
4467                     age = strtoulW(equal + 1, &nextPtr, 10);
4468                     if (nextPtr > equal + 1)
4469                     {
4470                         LARGE_INTEGER ft;
4471 
4472                         NtQuerySystemTime( &ft );
4473                         /* Age is in seconds, FILETIME resolution is in
4474                          * 100 nanosecond intervals.
4475                          */
4476                         ft.QuadPart += age * (ULONGLONG)1000000;
4477                         request->expires.dwLowDateTime = ft.u.LowPart;
4478                         request->expires.dwHighDateTime = ft.u.HighPart;
4479                         expirationFound = TRUE;
4480                     }
4481                 }
4482             }
4483             if (comma)
4484             {
4485                 ptr = comma + 1;
4486                 while (isspaceW(*ptr))
4487                     ptr++;
4488             }
4489             else
4490                 ptr = NULL;
4491         }
4492     }
4493     if (!expirationFound)
4494     {
4495         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4496         if (headerIndex != -1)
4497         {
4498             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4499             FILETIME ft;
4500 
4501             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4502             {
4503                 expirationFound = TRUE;
4504                 request->expires = ft;
4505             }
4506         }
4507     }
4508     if (!expirationFound)
4509     {
4510         LARGE_INTEGER t;
4511 
4512         /* With no known age, default to 10 minutes until expiration. */
4513         NtQuerySystemTime( &t );
4514         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4515         request->expires.dwLowDateTime = t.u.LowPart;
4516         request->expires.dwHighDateTime = t.u.HighPart;
4517     }
4518 }
4519 
4520 static void HTTP_ProcessLastModified(http_request_t *request)
4521 {
4522     int headerIndex;
4523 
4524     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4525     if (headerIndex != -1)
4526     {
4527         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4528         FILETIME ft;
4529 
4530         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4531             request->last_modified = ft;
4532     }
4533 }
4534 
4535 static void http_process_keep_alive(http_request_t *req)
4536 {
4537     int index;
4538 
4539     index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4540     if(index != -1)
4541         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4542     else
4543         req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4544 }
4545 
4546 static void HTTP_CacheRequest(http_request_t *request)
4547 {
4548     WCHAR url[INTERNET_MAX_URL_LENGTH];
4549     WCHAR cacheFileName[MAX_PATH+1];
4550     BOOL b;
4551 
4552     b = HTTP_GetRequestURL(request, url);
4553     if(!b) {
4554         WARN("Could not get URL\n");
4555         return;
4556     }
4557 
4558     b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4559     if(b) {
4560         heap_free(request->cacheFile);
4561         CloseHandle(request->hCacheFile);
4562 
4563         request->cacheFile = heap_strdupW(cacheFileName);
4564         request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4565                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4566         if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4567             WARN("Could not create file: %u\n", GetLastError());
4568             request->hCacheFile = NULL;
4569         }
4570     }else {
4571         WARN("Could not create cache entry: %08x\n", GetLastError());
4572     }
4573 }
4574 
4575 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4576 {
4577     const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4578     http_session_t *session = request->session;
4579     netconn_t *netconn = NULL;
4580     server_t *server;
4581     DWORD res;
4582 
4583     assert(!request->netconn);
4584     reset_data_stream(request);
4585 
4586     server = get_server(session->serverName, session->serverPort);
4587     if(!server)
4588         return ERROR_OUTOFMEMORY;
4589 
4590     res = HTTP_ResolveName(request, server);
4591     if(res != ERROR_SUCCESS) {
4592         server_release(server);
4593         return res;
4594     }
4595 
4596     EnterCriticalSection(&connection_pool_cs);
4597 
4598     while(!list_empty(&server->conn_pool)) {
4599         netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4600         list_remove(&netconn->pool_entry);
4601 
4602         if(NETCON_is_alive(netconn))
4603             break;
4604 
4605         TRACE("connection %p closed during idle\n", netconn);
4606         free_netconn(netconn);
4607         netconn = NULL;
4608     }
4609 
4610     LeaveCriticalSection(&connection_pool_cs);
4611 
4612     if(netconn) {
4613         TRACE("<-- reusing %p netconn\n", netconn);
4614         request->netconn = netconn;
4615         *reusing = TRUE;
4616         return ERROR_SUCCESS;
4617     }
4618 
4619     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4620                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4621                           server->addr_str,
4622                           strlen(server->addr_str)+1);
4623 
4624     res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4625     server_release(server);
4626     if(res != ERROR_SUCCESS) {
4627         ERR("create_netconn failed: %u\n", res);
4628         return res;
4629     }
4630 
4631     request->netconn = netconn;
4632 
4633     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4634             INTERNET_STATUS_CONNECTED_TO_SERVER,
4635             server->addr_str, strlen(server->addr_str)+1);
4636 
4637     if(is_https) {
4638         /* Note: we differ from Microsoft's WinINet here. they seem to have
4639          * a bug that causes no status callbacks to be sent when starting
4640          * a tunnel to a proxy server using the CONNECT verb. i believe our
4641          * behaviour to be more correct and to not cause any incompatibilities
4642          * because using a secure connection through a proxy server is a rare
4643          * case that would be hard for anyone to depend on */
4644         if(session->appInfo->proxy)
4645             res = HTTP_SecureProxyConnect(request);
4646         if(res == ERROR_SUCCESS)
4647             res = NETCON_secure_connect(request->netconn);
4648         if(res != ERROR_SUCCESS)
4649         {
4650             WARN("Couldn't connect securely to host\n");
4651 
4652             if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4653                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4654                     || res == ERROR_INTERNET_INVALID_CA
4655                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
4656                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4657                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
4658                     || res == ERROR_INTERNET_SEC_INVALID_CERT
4659                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4660                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4661         }
4662     }
4663 
4664     if(res != ERROR_SUCCESS) {
4665         http_release_netconn(request, FALSE);
4666         return res;
4667     }
4668 
4669     *reusing = FALSE;
4670     TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4671     return ERROR_SUCCESS;
4672 }
4673 
4674 /***********************************************************************
4675  *           HTTP_HttpSendRequestW (internal)
4676  *
4677  * Sends the specified request to the HTTP server
4678  *
4679  * RETURNS
4680  *    ERROR_SUCCESS on success
4681  *    win32 error code on failure
4682  *
4683  */
4684 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4685         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4686         DWORD dwContentLength, BOOL bEndRequest)
4687 {
4688     INT cnt;
4689     BOOL redirected = FALSE;
4690     LPWSTR requestString = NULL;
4691     INT responseLen;
4692     BOOL loop_next;
4693     static const WCHAR szPost[] = { 'P','O','S','T',0 };
4694     static const WCHAR szContentLength[] =
4695         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4696     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4697     DWORD res;
4698 
4699     TRACE("--> %p\n", request);
4700 
4701     assert(request->hdr.htype == WH_HHTTPREQ);
4702 
4703     /* if the verb is NULL default to GET */
4704     if (!request->verb)
4705         request->verb = heap_strdupW(szGET);
4706 
4707     if (dwContentLength || strcmpW(request->verb, szGET))
4708     {
4709         sprintfW(contentLengthStr, szContentLength, dwContentLength);
4710         HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4711         request->bytesToWrite = dwContentLength;
4712     }
4713     if (request->session->appInfo->agent)
4714     {
4715         WCHAR *agent_header;
4716         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4717         int len;
4718 
4719         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4720         agent_header = heap_alloc(len * sizeof(WCHAR));
4721         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4722 
4723         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4724         heap_free(agent_header);
4725     }
4726     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4727     {
4728         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4729         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4730     }
4731     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4732     {
4733         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4734                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4735         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4736     }
4737 
4738     /* add the headers the caller supplied */
4739     if( lpszHeaders && dwHeaderLength )
4740         HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4741 
4742     do
4743     {
4744         DWORD len;
4745         BOOL reusing_connection;
4746         char *ascii_req;
4747 
4748         loop_next = FALSE;
4749 
4750         /* like native, just in case the caller forgot to call InternetReadFile
4751          * for all the data */
4752         drain_content(request);
4753         if(redirected) {
4754             request->contentLength = ~0u;
4755             request->bytesToWrite = 0;
4756         }
4757 
4758         if (TRACE_ON(wininet))
4759         {
4760             LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4761             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4762         }
4763 
4764         HTTP_FixURL(request);
4765         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4766         {
4767             HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4768         }
4769         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4770         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4771 
4772         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4773             HTTP_InsertCookies(request);
4774 
4775         if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4776         {
4777             WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4778             requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4779             heap_free(url);
4780         }
4781         else
4782             requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4783 
4784  
4785         TRACE("Request header -> %s\n", debugstr_w(requestString) );
4786 
4787         if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4788             break;
4789 
4790         /* send the request as ASCII, tack on the optional data */
4791         if (!lpOptional || redirected)
4792             dwOptionalLength = 0;
4793         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4794                                    NULL, 0, NULL, NULL );
4795         ascii_req = heap_alloc(len + dwOptionalLength);
4796         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4797                              ascii_req, len, NULL, NULL );
4798         if( lpOptional )
4799             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4800         len = (len + dwOptionalLength - 1);
4801         ascii_req[len] = 0;
4802         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4803 
4804         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4805                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4806 
4807         NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4808         res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4809         heap_free( ascii_req );
4810         if(res != ERROR_SUCCESS) {
4811             TRACE("send failed: %u\n", res);
4812             if(!reusing_connection)
4813                 break;
4814             http_release_netconn(request, FALSE);
4815             loop_next = TRUE;
4816             continue;
4817         }
4818 
4819         request->bytesWritten = dwOptionalLength;
4820 
4821         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4822                               INTERNET_STATUS_REQUEST_SENT,
4823                               &len, sizeof(DWORD));
4824 
4825         if (bEndRequest)
4826         {
4827             DWORD dwBufferSize;
4828 
4829             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4830                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4831     
4832             responseLen = HTTP_GetResponseHeaders(request, TRUE);
4833             /* FIXME: We should know that connection is closed before sending
4834              * headers. Otherwise wrong callbacks are executed */
4835             if(!responseLen && reusing_connection) {
4836                 TRACE("Connection closed by server, reconnecting\n");
4837                 http_release_netconn(request, FALSE);
4838                 loop_next = TRUE;
4839                 continue;
4840             }
4841 
4842             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4843                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4844                                 sizeof(DWORD));
4845 
4846             http_process_keep_alive(request);
4847             HTTP_ProcessCookies(request);
4848             HTTP_ProcessExpires(request);
4849             HTTP_ProcessLastModified(request);
4850 
4851             res = set_content_length(request);
4852             if(res != ERROR_SUCCESS)
4853                 goto lend;
4854             if(!request->contentLength)
4855                 http_release_netconn(request, TRUE);
4856 
4857             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4858             {
4859                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4860                 dwBufferSize=sizeof(szNewLocation);
4861                 switch(request->status_code) {
4862                 case HTTP_STATUS_REDIRECT:
4863                 case HTTP_STATUS_MOVED:
4864                 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4865                 case HTTP_STATUS_REDIRECT_METHOD:
4866                     if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4867                         break;
4868 
4869                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4870                         request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4871                     {
4872                         heap_free(request->verb);
4873                         request->verb = heap_strdupW(szGET);
4874                     }
4875                     drain_content(request);
4876                     if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4877                     {
4878                         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4879                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4880                         res = HTTP_HandleRedirect(request, new_url);
4881                         if (res == ERROR_SUCCESS)
4882                         {
4883                             heap_free(requestString);
4884                             loop_next = TRUE;
4885                         }
4886                         heap_free( new_url );
4887                     }
4888                     redirected = TRUE;
4889                 }
4890             }
4891             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4892             {
4893                 WCHAR szAuthValue[2048];
4894                 dwBufferSize=2048;
4895                 if (request->status_code == HTTP_STATUS_DENIED)
4896                 {
4897                     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4898                     DWORD dwIndex = 0;
4899                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4900                     {
4901                         if (HTTP_DoAuthorization(request, szAuthValue,
4902                                                  &request->authInfo,
4903                                                  request->session->userName,
4904                                                  request->session->password,
4905                                                  Host->lpszValue))
4906                         {
4907                             heap_free(requestString);
4908                             loop_next = TRUE;
4909                             break;
4910                         }
4911                     }
4912 
4913                     if(!loop_next) {
4914                         TRACE("Cleaning wrong authorization data\n");
4915                         destroy_authinfo(request->authInfo);
4916                         request->authInfo = NULL;
4917                     }
4918                 }
4919                 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4920                 {
4921                     DWORD dwIndex = 0;
4922                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4923                     {
4924                         if (HTTP_DoAuthorization(request, szAuthValue,
4925                                                  &request->proxyAuthInfo,
4926                                                  request->session->appInfo->proxyUsername,
4927                                                  request->session->appInfo->proxyPassword,
4928                                                  NULL))
4929                         {
4930                             loop_next = TRUE;
4931                             break;
4932                         }
4933                     }
4934 
4935                     if(!loop_next) {
4936                         TRACE("Cleaning wrong proxy authorization data\n");
4937                         destroy_authinfo(request->proxyAuthInfo);
4938                         request->proxyAuthInfo = NULL;
4939                     }
4940                 }
4941             }
4942         }
4943         else
4944             res = ERROR_SUCCESS;
4945     }
4946     while (loop_next);
4947 
4948     if(res == ERROR_SUCCESS)
4949         HTTP_CacheRequest(request);
4950 
4951 lend:
4952     heap_free(requestString);
4953 
4954     /* TODO: send notification for P3P header */
4955 
4956     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4957     {
4958         if (res == ERROR_SUCCESS) {
4959             if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4960                 HTTP_ReceiveRequestData(request, TRUE);
4961             else
4962                 send_request_complete(request,
4963                         request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4964         }else {
4965                 send_request_complete(request, 0, res);
4966         }
4967     }
4968 
4969     TRACE("<--\n");
4970     return res;
4971 }
4972 
4973 /***********************************************************************
4974  *
4975  * Helper functions for the HttpSendRequest(Ex) functions
4976  *
4977  */
4978 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4979 {
4980     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4981     http_request_t *request = (http_request_t*) workRequest->hdr;
4982 
4983     TRACE("%p\n", request);
4984 
4985     HTTP_HttpSendRequestW(request, req->lpszHeader,
4986             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4987             req->dwContentLength, req->bEndRequest);
4988 
4989     heap_free(req->lpszHeader);
4990 }
4991 
4992 
4993 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4994 {
4995     DWORD dwBufferSize;
4996     INT responseLen;
4997     DWORD res = ERROR_SUCCESS;
4998 
4999     if(!request->netconn) {
5000         WARN("Not connected\n");
5001         send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5002         return ERROR_INTERNET_OPERATION_CANCELLED;
5003     }
5004 
5005     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5006                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5007 
5008     responseLen = HTTP_GetResponseHeaders(request, TRUE);
5009     if (!responseLen)
5010         res = ERROR_HTTP_HEADER_NOT_FOUND;
5011 
5012     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5013                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5014 
5015     /* process cookies here. Is this right? */
5016     http_process_keep_alive(request);
5017     HTTP_ProcessCookies(request);
5018     HTTP_ProcessExpires(request);
5019     HTTP_ProcessLastModified(request);
5020 
5021     if ((res = set_content_length(request)) == 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         switch(request->status_code) {
5029         case HTTP_STATUS_REDIRECT:
5030         case HTTP_STATUS_MOVED:
5031         case HTTP_STATUS_REDIRECT_METHOD:
5032         case HTTP_STATUS_REDIRECT_KEEP_VERB: {
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                 break;
5037 
5038             if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5039                 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5040             {
5041                 heap_free(request->verb);
5042                 request->verb = heap_strdupW(szGET);
5043             }
5044             drain_content(request);
5045             if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5046             {
5047                 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5048                                       new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5049                 res = HTTP_HandleRedirect(request, new_url);
5050                 if (res == ERROR_SUCCESS)
5051                     res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5052                 heap_free( new_url );
5053             }
5054         }
5055         }
5056     }
5057 
5058     if (res == ERROR_SUCCESS && request->contentLength)
5059         HTTP_ReceiveRequestData(request, TRUE);
5060     else
5061         send_request_complete(request, res == ERROR_SUCCESS, res);
5062 
5063     return res;
5064 }
5065 
5066 /***********************************************************************
5067  *           HttpEndRequestA (WININET.@)
5068  *
5069  * Ends an HTTP request that was started by HttpSendRequestEx
5070  *
5071  * RETURNS
5072  *    TRUE      if successful
5073  *    FALSE     on failure
5074  *
5075  */
5076 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5077         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5078 {
5079     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5080 
5081     if (lpBuffersOut)
5082     {
5083         SetLastError(ERROR_INVALID_PARAMETER);
5084         return FALSE;
5085     }
5086 
5087     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5088 }
5089 
5090 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5091 {
5092     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5093     http_request_t *request = (http_request_t*)work->hdr;
5094 
5095     TRACE("%p\n", request);
5096 
5097     HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5098 }
5099 
5100 /***********************************************************************
5101  *           HttpEndRequestW (WININET.@)
5102  *
5103  * Ends an HTTP request that was started by HttpSendRequestEx
5104  *
5105  * RETURNS
5106  *    TRUE      if successful
5107  *    FALSE     on failure
5108  *
5109  */
5110 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5111         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5112 {
5113     http_request_t *request;
5114     DWORD res;
5115 
5116     TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5117 
5118     if (lpBuffersOut)
5119     {
5120         SetLastError(ERROR_INVALID_PARAMETER);
5121         return FALSE;
5122     }
5123 
5124     request = (http_request_t*) get_handle_object( hRequest );
5125 
5126     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5127     {
5128         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5129         if (request)
5130             WININET_Release( &request->hdr );
5131         return FALSE;
5132     }
5133     request->hdr.dwFlags |= dwFlags;
5134 
5135     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5136     {
5137         WORKREQUEST work;
5138         struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5139 
5140         work.asyncproc = AsyncHttpEndRequestProc;
5141         work.hdr = WININET_AddRef( &request->hdr );
5142 
5143         work_endrequest = &work.u.HttpEndRequestW;
5144         work_endrequest->dwFlags = dwFlags;
5145         work_endrequest->dwContext = dwContext;
5146 
5147         INTERNET_AsyncCall(&work);
5148         res = ERROR_IO_PENDING;
5149     }
5150     else
5151         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5152 
5153     WININET_Release( &request->hdr );
5154     TRACE("%u <--\n", res);
5155     if(res != ERROR_SUCCESS)
5156         SetLastError(res);
5157     return res == ERROR_SUCCESS;
5158 }
5159 
5160 /***********************************************************************
5161  *           HttpSendRequestExA (WININET.@)
5162  *
5163  * Sends the specified request to the HTTP server and allows chunked
5164  * transfers.
5165  *
5166  * RETURNS
5167  *  Success: TRUE
5168  *  Failure: FALSE, call GetLastError() for more information.
5169  */
5170 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5171                                LPINTERNET_BUFFERSA lpBuffersIn,
5172                                LPINTERNET_BUFFERSA lpBuffersOut,
5173                                DWORD dwFlags, DWORD_PTR dwContext)
5174 {
5175     INTERNET_BUFFERSW BuffersInW;
5176     BOOL rc = FALSE;
5177     DWORD headerlen;
5178     LPWSTR header = NULL;
5179 
5180     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5181             lpBuffersOut, dwFlags, dwContext);
5182 
5183     if (lpBuffersIn)
5184     {
5185         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5186         if (lpBuffersIn->lpcszHeader)
5187         {
5188             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5189                     lpBuffersIn->dwHeadersLength,0,0);
5190             header = heap_alloc(headerlen*sizeof(WCHAR));
5191             if (!(BuffersInW.lpcszHeader = header))
5192             {
5193                 SetLastError(ERROR_OUTOFMEMORY);
5194                 return FALSE;
5195             }
5196             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5197                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5198                     header, headerlen);
5199         }
5200         else
5201             BuffersInW.lpcszHeader = NULL;
5202         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5203         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5204         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5205         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5206         BuffersInW.Next = NULL;
5207     }
5208 
5209     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5210 
5211     heap_free(header);
5212     return rc;
5213 }
5214 
5215 /***********************************************************************
5216  *           HttpSendRequestExW (WININET.@)
5217  *
5218  * Sends the specified request to the HTTP server and allows chunked
5219  * transfers
5220  *
5221  * RETURNS
5222  *  Success: TRUE
5223  *  Failure: FALSE, call GetLastError() for more information.
5224  */
5225 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5226                    LPINTERNET_BUFFERSW lpBuffersIn,
5227                    LPINTERNET_BUFFERSW lpBuffersOut,
5228                    DWORD dwFlags, DWORD_PTR dwContext)
5229 {
5230     http_request_t *request;
5231     http_session_t *session;
5232     appinfo_t *hIC;
5233     DWORD res;
5234 
5235     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5236             lpBuffersOut, dwFlags, dwContext);
5237 
5238     request = (http_request_t*) get_handle_object( hRequest );
5239 
5240     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5241     {
5242         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5243         goto lend;
5244     }
5245 
5246     session = request->session;
5247     assert(session->hdr.htype == WH_HHTTPSESSION);
5248     hIC = session->appInfo;
5249     assert(hIC->hdr.htype == WH_HINIT);
5250 
5251     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5252     {
5253         WORKREQUEST workRequest;
5254         struct WORKREQ_HTTPSENDREQUESTW *req;
5255 
5256         workRequest.asyncproc = AsyncHttpSendRequestProc;
5257         workRequest.hdr = WININET_AddRef( &request->hdr );
5258         req = &workRequest.u.HttpSendRequestW;
5259         if (lpBuffersIn)
5260         {
5261             DWORD size = 0;
5262 
5263             if (lpBuffersIn->lpcszHeader)
5264             {
5265                 if (lpBuffersIn->dwHeadersLength == ~0u)
5266                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5267                 else
5268                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5269 
5270                 req->lpszHeader = heap_alloc(size);
5271                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5272             }
5273             else req->lpszHeader = NULL;
5274 
5275             req->dwHeaderLength = size / sizeof(WCHAR);
5276             req->lpOptional = lpBuffersIn->lpvBuffer;
5277             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5278             req->dwContentLength = lpBuffersIn->dwBufferTotal;
5279         }
5280         else
5281         {
5282             req->lpszHeader = NULL;
5283             req->dwHeaderLength = 0;
5284             req->lpOptional = NULL;
5285             req->dwOptionalLength = 0;
5286             req->dwContentLength = 0;
5287         }
5288 
5289         req->bEndRequest = FALSE;
5290 
5291         INTERNET_AsyncCall(&workRequest);
5292         /*
5293          * This is from windows.
5294          */
5295         res = ERROR_IO_PENDING;
5296     }
5297     else
5298     {
5299         if (lpBuffersIn)
5300             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5301                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5302                                         lpBuffersIn->dwBufferTotal, FALSE);
5303         else
5304             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5305     }
5306 
5307 lend:
5308     if ( request )
5309         WININET_Release( &request->hdr );
5310 
5311     TRACE("<---\n");
5312     SetLastError(res);
5313     return res == ERROR_SUCCESS;
5314 }
5315 
5316 /***********************************************************************
5317  *           HttpSendRequestW (WININET.@)
5318  *
5319  * Sends the specified request to the HTTP server
5320  *
5321  * RETURNS
5322  *    TRUE  on success
5323  *    FALSE on failure
5324  *
5325  */
5326 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5327         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5328 {
5329     http_request_t *request;
5330     http_session_t *session = NULL;
5331     appinfo_t *hIC = NULL;
5332     DWORD res = ERROR_SUCCESS;
5333 
5334     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5335             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5336 
5337     request = (http_request_t*) get_handle_object( hHttpRequest );
5338     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5339     {
5340         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5341         goto lend;
5342     }
5343 
5344     session = request->session;
5345     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
5346     {
5347         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5348         goto lend;
5349     }
5350 
5351     hIC = session->appInfo;
5352     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
5353     {
5354         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5355         goto lend;
5356     }
5357 
5358     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5359     {
5360         WORKREQUEST workRequest;
5361         struct WORKREQ_HTTPSENDREQUESTW *req;
5362 
5363         workRequest.asyncproc = AsyncHttpSendRequestProc;
5364         workRequest.hdr = WININET_AddRef( &request->hdr );
5365         req = &workRequest.u.HttpSendRequestW;
5366         if (lpszHeaders)
5367         {
5368             DWORD size;
5369 
5370             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5371             else size = dwHeaderLength * sizeof(WCHAR);
5372 
5373             req->lpszHeader = heap_alloc(size);
5374             memcpy(req->lpszHeader, lpszHeaders, size);
5375         }
5376         else
5377             req->lpszHeader = 0;
5378         req->dwHeaderLength = dwHeaderLength;
5379         req->lpOptional = lpOptional;
5380         req->dwOptionalLength = dwOptionalLength;
5381         req->dwContentLength = dwOptionalLength;
5382         req->bEndRequest = TRUE;
5383 
5384         INTERNET_AsyncCall(&workRequest);
5385         /*
5386          * This is from windows.
5387          */
5388         res = ERROR_IO_PENDING;
5389     }
5390     else
5391     {
5392         res = HTTP_HttpSendRequestW(request, lpszHeaders,
5393                 dwHeaderLength, lpOptional, dwOptionalLength,
5394                 dwOptionalLength, TRUE);
5395     }
5396 lend:
5397     if( request )
5398         WININET_Release( &request->hdr );
5399 
5400     SetLastError(res);
5401     return res == ERROR_SUCCESS;
5402 }
5403 
5404 /***********************************************************************
5405  *           HttpSendRequestA (WININET.@)
5406  *
5407  * Sends the specified request to the HTTP server
5408  *
5409  * RETURNS
5410  *    TRUE  on success
5411  *    FALSE on failure
5412  *
5413  */
5414 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5415         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5416 {
5417     BOOL result;
5418     LPWSTR szHeaders=NULL;
5419     DWORD nLen=dwHeaderLength;
5420     if(lpszHeaders!=NULL)
5421     {
5422         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5423         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5424         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5425     }
5426     result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5427     heap_free(szHeaders);
5428     return result;
5429 }
5430 
5431 /***********************************************************************
5432  *           HTTPSESSION_Destroy (internal)
5433  *
5434  * Deallocate session handle
5435  *
5436  */
5437 static void HTTPSESSION_Destroy(object_header_t *hdr)
5438 {
5439     http_session_t *session = (http_session_t*) hdr;
5440 
5441     TRACE("%p\n", session);
5442 
5443     WININET_Release(&session->appInfo->hdr);
5444 
5445     heap_free(session->hostName);
5446     heap_free(session->serverName);
5447     heap_free(session->password);
5448     heap_free(session->userName);
5449 }
5450 
5451 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5452 {
5453     http_session_t *ses = (http_session_t *)hdr;
5454 
5455     switch(option) {
5456     case INTERNET_OPTION_HANDLE_TYPE:
5457         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5458 
5459         if (*size < sizeof(ULONG))
5460             return ERROR_INSUFFICIENT_BUFFER;
5461 
5462         *size = sizeof(DWORD);
5463         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5464         return ERROR_SUCCESS;
5465     case INTERNET_OPTION_CONNECT_TIMEOUT:
5466         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5467 
5468         if (*size < sizeof(DWORD))
5469             return ERROR_INSUFFICIENT_BUFFER;
5470 
5471         *size = sizeof(DWORD);
5472         *(DWORD *)buffer = ses->connect_timeout;
5473         return ERROR_SUCCESS;
5474 
5475     case INTERNET_OPTION_SEND_TIMEOUT:
5476         TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5477 
5478         if (*size < sizeof(DWORD))
5479             return ERROR_INSUFFICIENT_BUFFER;
5480 
5481         *size = sizeof(DWORD);
5482         *(DWORD *)buffer = ses->send_timeout;
5483         return ERROR_SUCCESS;
5484 
5485     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5486         TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5487 
5488         if (*size < sizeof(DWORD))
5489             return ERROR_INSUFFICIENT_BUFFER;
5490 
5491         *size = sizeof(DWORD);
5492         *(DWORD *)buffer = ses->receive_timeout;
5493         return ERROR_SUCCESS;
5494     }
5495 
5496     return INET_QueryOption(hdr, option, buffer, size, unicode);
5497 }
5498 
5499 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5500 {
5501     http_session_t *ses = (http_session_t*)hdr;
5502 
5503     switch(option) {
5504     case INTERNET_OPTION_USERNAME:
5505     {
5506         heap_free(ses->userName);
5507         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5508         return ERROR_SUCCESS;
5509     }
5510     case INTERNET_OPTION_PASSWORD:
5511     {
5512         heap_free(ses->password);
5513         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5514         return ERROR_SUCCESS;
5515     }
5516     case INTERNET_OPTION_CONNECT_TIMEOUT:
5517     {
5518         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5519         ses->connect_timeout = *(DWORD *)buffer;
5520         return ERROR_SUCCESS;
5521     }
5522     case INTERNET_OPTION_SEND_TIMEOUT:
5523     {
5524         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5525         ses->send_timeout = *(DWORD *)buffer;
5526         return ERROR_SUCCESS;
5527     }
5528     case INTERNET_OPTION_RECEIVE_TIMEOUT:
5529     {
5530         if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5531         ses->receive_timeout = *(DWORD *)buffer;
5532         return ERROR_SUCCESS;
5533     }
5534     default: break;
5535     }
5536 
5537     return INET_SetOption(hdr, option, buffer, size);
5538 }
5539 
5540 static const object_vtbl_t HTTPSESSIONVtbl = {
5541     HTTPSESSION_Destroy,
5542     NULL,
5543     HTTPSESSION_QueryOption,
5544     HTTPSESSION_SetOption,
5545     NULL,
5546     NULL,
5547     NULL,
5548     NULL,
5549     NULL
5550 };
5551 
5552 
5553 /***********************************************************************
5554  *           HTTP_Connect  (internal)
5555  *
5556  * Create http session handle
5557  *
5558  * RETURNS
5559  *   HINTERNET a session handle on success
5560  *   NULL on failure
5561  *
5562  */
5563 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5564         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5565         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5566         DWORD dwInternalFlags, HINTERNET *ret)
5567 {
5568     http_session_t *session = NULL;
5569 
5570     TRACE("-->\n");
5571 
5572     if (!lpszServerName || !lpszServerName[0])
5573         return ERROR_INVALID_PARAMETER;
5574 
5575     assert( hIC->hdr.htype == WH_HINIT );
5576 
5577     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5578     if (!session)
5579         return ERROR_OUTOFMEMORY;
5580 
5581    /*
5582     * According to my tests. The name is not resolved until a request is sent
5583     */
5584 
5585     session->hdr.htype = WH_HHTTPSESSION;
5586     session->hdr.dwFlags = dwFlags;
5587     session->hdr.dwContext = dwContext;
5588     session->hdr.dwInternalFlags |= dwInternalFlags;
5589 
5590     WININET_AddRef( &hIC->hdr );
5591     session->appInfo = hIC;
5592     list_add_head( &hIC->hdr.children, &session->hdr.entry );
5593 
5594     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5595         if(hIC->proxyBypass)
5596             FIXME("Proxy bypass is ignored.\n");
5597     }
5598     session->serverName = heap_strdupW(lpszServerName);
5599     session->hostName = heap_strdupW(lpszServerName);
5600     if (lpszUserName && lpszUserName[0])
5601         session->userName = heap_strdupW(lpszUserName);
5602     if (lpszPassword && lpszPassword[0])
5603         session->password = heap_strdupW(lpszPassword);
5604     session->serverPort = serverPort;
5605     session->hostPort = serverPort;
5606     session->connect_timeout = INFINITE;
5607     session->send_timeout = INFINITE;
5608     session->receive_timeout = INFINITE;
5609 
5610     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5611     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5612     {
5613         INTERNET_SendCallback(&hIC->hdr, dwContext,
5614                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5615                               sizeof(HINTERNET));
5616     }
5617 
5618 /*
5619  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5620  * windows
5621  */
5622 
5623     TRACE("%p --> %p\n", hIC, session);
5624 
5625     *ret = session->hdr.hInternet;
5626     return ERROR_SUCCESS;
5627 }
5628 
5629 /***********************************************************************
5630  *           HTTP_clear_response_headers (internal)
5631  *
5632  * clear out any old response headers
5633  */
5634 static void HTTP_clear_response_headers( http_request_t *request )
5635 {
5636     DWORD i;
5637 
5638     for( i=0; i<request->nCustHeaders; i++)
5639     {
5640         if( !request->custHeaders[i].lpszField )
5641             continue;
5642         if( !request->custHeaders[i].lpszValue )
5643             continue;
5644         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5645             continue;
5646         HTTP_DeleteCustomHeader( request, i );
5647         i--;
5648     }
5649 }
5650 
5651 /***********************************************************************
5652  *           HTTP_GetResponseHeaders (internal)
5653  *
5654  * Read server response
5655  *
5656  * RETURNS
5657  *
5658  *   TRUE  on success
5659  *   FALSE on error
5660  */
5661 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5662 {
5663     INT cbreaks = 0;
5664     WCHAR buffer[MAX_REPLY_LEN];
5665     DWORD buflen = MAX_REPLY_LEN;
5666     BOOL bSuccess = FALSE;
5667     INT  rc = 0;
5668     char bufferA[MAX_REPLY_LEN];
5669     LPWSTR status_code = NULL, status_text = NULL;
5670     DWORD cchMaxRawHeaders = 1024;
5671     LPWSTR lpszRawHeaders = NULL;
5672     LPWSTR temp;
5673     DWORD cchRawHeaders = 0;
5674     BOOL codeHundred = FALSE;
5675 
5676     TRACE("-->\n");
5677 
5678     if(!request->netconn)
5679         goto lend;
5680 
5681     NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5682     do {
5683         static const WCHAR szHundred[] = {'1','','',0};
5684         /*
5685          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5686          */
5687         buflen = MAX_REPLY_LEN;
5688         if (!read_line(request, bufferA, &buflen))
5689             goto lend;
5690 
5691         /* clear old response headers (eg. from a redirect response) */
5692         if (clear) {
5693             HTTP_clear_response_headers( request );
5694             clear = FALSE;
5695         }
5696 
5697         rc += buflen;
5698         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5699         /* check is this a status code line? */
5700         if (!strncmpW(buffer, g_szHttp1_0, 4))
5701         {
5702             /* split the version from the status code */
5703             status_code = strchrW( buffer, ' ' );
5704             if( !status_code )
5705                 goto lend;
5706             *status_code++=0;
5707 
5708             /* split the status code from the status text */
5709             status_text = strchrW( status_code, ' ' );
5710             if( !status_text )
5711                 goto lend;
5712             *status_text++=0;
5713 
5714             request->status_code = atoiW(status_code);
5715 
5716             TRACE("version [%s] status code [%s] status text [%s]\n",
5717                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5718 
5719             codeHundred = (!strcmpW(status_code, szHundred));
5720         }
5721         else if (!codeHundred)
5722         {
5723             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5724 
5725             heap_free(request->version);
5726             heap_free(request->statusText);
5727 
5728             request->status_code = HTTP_STATUS_OK;
5729             request->version = heap_strdupW(g_szHttp1_0);
5730             request->statusText = heap_strdupW(szOK);
5731 
5732             heap_free(request->rawHeaders);
5733             request->rawHeaders = heap_strdupW(szDefaultHeader);
5734 
5735             bSuccess = TRUE;
5736             goto lend;
5737         }
5738     } while (codeHundred);
5739 
5740     /* Add status code */
5741     HTTP_ProcessHeader(request, szStatus, status_code,
5742             HTTP_ADDHDR_FLAG_REPLACE);
5743 
5744     heap_free(request->version);
5745     heap_free(request->statusText);
5746 
5747     request->version = heap_strdupW(buffer);
5748     request->statusText = heap_strdupW(status_text);
5749 
5750     /* Restore the spaces */
5751     *(status_code-1) = ' ';
5752     *(status_text-1) = ' ';
5753 
5754     /* regenerate raw headers */
5755     lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5756     if (!lpszRawHeaders) goto lend;
5757 
5758     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5759         cchMaxRawHeaders *= 2;
5760     temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5761     if (temp == NULL) goto lend;
5762     lpszRawHeaders = temp;
5763     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5764     cchRawHeaders += (buflen-1);
5765     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5766     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5767     lpszRawHeaders[cchRawHeaders] = '\0';
5768 
5769     /* Parse each response line */
5770     do
5771     {
5772         buflen = MAX_REPLY_LEN;
5773         if (read_line(request, bufferA, &buflen))
5774         {
5775             LPWSTR * pFieldAndValue;
5776 
5777             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5778 
5779             if (!bufferA[0]) break;
5780             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5781 
5782             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5783             if (pFieldAndValue)
5784             {
5785                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5786                     cchMaxRawHeaders *= 2;
5787                 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5788                 if (temp == NULL) goto lend;
5789                 lpszRawHeaders = temp;
5790                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5791                 cchRawHeaders += (buflen-1);
5792                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5793                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5794                 lpszRawHeaders[cchRawHeaders] = '\0';
5795 
5796                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5797                                    HTTP_ADDREQ_FLAG_ADD );
5798 
5799                 HTTP_FreeTokens(pFieldAndValue);
5800             }
5801         }
5802         else
5803         {
5804             cbreaks++;
5805             if (cbreaks >= 2)
5806                break;
5807         }
5808     }while(1);
5809 
5810     /* make sure the response header is terminated with an empty line.  Some apps really
5811        truly care about that empty line being there for some reason.  Just add it to the
5812        header. */
5813     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5814     {
5815         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5816         temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5817         if (temp == NULL) goto lend;
5818         lpszRawHeaders = temp;
5819     }
5820 
5821     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5822 
5823     heap_free(request->rawHeaders);
5824     request->rawHeaders = lpszRawHeaders;
5825     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5826     bSuccess = TRUE;
5827 
5828 lend:
5829 
5830     TRACE("<--\n");
5831     if (bSuccess)
5832         return rc;
5833     else
5834     {
5835         heap_free(lpszRawHeaders);
5836         return 0;
5837     }
5838 }
5839 
5840 /***********************************************************************
5841  *           HTTP_InterpretHttpHeader (internal)
5842  *
5843  * Parse server response
5844  *
5845  * RETURNS
5846  *
5847  *   Pointer to array of field, value, NULL on success.
5848  *   NULL on error.
5849  */
5850 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5851 {
5852     LPWSTR * pTokenPair;
5853     LPWSTR pszColon;
5854     INT len;
5855 
5856     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5857 
5858     pszColon = strchrW(buffer, ':');
5859     /* must have two tokens */
5860     if (!pszColon)
5861     {
5862         HTTP_FreeTokens(pTokenPair);
5863         if (buffer[0])
5864             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5865         return NULL;
5866     }
5867 
5868     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5869     if (!pTokenPair[0])
5870     {
5871         HTTP_FreeTokens(pTokenPair);
5872         return NULL;
5873     }
5874     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5875     pTokenPair[0][pszColon - buffer] = '\0';
5876 
5877     /* skip colon */
5878     pszColon++;
5879     len = strlenW(pszColon);
5880     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5881     if (!pTokenPair[1])
5882     {
5883         HTTP_FreeTokens(pTokenPair);
5884         return NULL;
5885     }
5886     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5887 
5888     strip_spaces(pTokenPair[0]);
5889     strip_spaces(pTokenPair[1]);
5890 
5891     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5892     return pTokenPair;
5893 }
5894 
5895 /***********************************************************************
5896  *           HTTP_ProcessHeader (internal)
5897  *
5898  * Stuff header into header tables according to <dwModifier>
5899  *
5900  */
5901 
5902 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5903 
5904 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5905 {
5906     LPHTTPHEADERW lphttpHdr = NULL;
5907     INT index = -1;
5908     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5909     DWORD res = ERROR_HTTP_INVALID_HEADER;
5910 
5911     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5912 
5913     /* REPLACE wins out over ADD */
5914     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5915         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5916     
5917     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5918         index = -1;
5919     else
5920         index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5921 
5922     if (index >= 0)
5923     {
5924         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5925             return ERROR_HTTP_INVALID_HEADER;
5926         lphttpHdr = &request->custHeaders[index];
5927     }
5928     else if (value)
5929     {
5930         HTTPHEADERW hdr;
5931 
5932         hdr.lpszField = (LPWSTR)field;
5933         hdr.lpszValue = (LPWSTR)value;
5934         hdr.wFlags = hdr.wCount = 0;
5935 
5936         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5937             hdr.wFlags |= HDR_ISREQUEST;
5938 
5939         return HTTP_InsertCustomHeader(request, &hdr);
5940     }
5941     /* no value to delete */
5942     else return ERROR_SUCCESS;
5943 
5944     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5945             lphttpHdr->wFlags |= HDR_ISREQUEST;
5946     else
5947         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5948 
5949     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5950     {
5951         HTTP_DeleteCustomHeader( request, index );
5952 
5953         if (value)
5954         {
5955             HTTPHEADERW hdr;
5956 
5957             hdr.lpszField = (LPWSTR)field;
5958             hdr.lpszValue = (LPWSTR)value;
5959             hdr.wFlags = hdr.wCount = 0;
5960 
5961             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5962                 hdr.wFlags |= HDR_ISREQUEST;
5963 
5964             return HTTP_InsertCustomHeader(request, &hdr);
5965         }
5966 
5967         return ERROR_SUCCESS;
5968     }
5969     else if (dwModifier & COALESCEFLAGS)
5970     {
5971         LPWSTR lpsztmp;
5972         WCHAR ch = 0;
5973         INT len = 0;
5974         INT origlen = strlenW(lphttpHdr->lpszValue);
5975         INT valuelen = strlenW(value);
5976 
5977         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5978         {
5979             ch = ',';
5980             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5981         }
5982         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5983         {
5984             ch = ';';
5985             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5986         }
5987 
5988         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5989 
5990         lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5991         if (lpsztmp)
5992         {
5993             lphttpHdr->lpszValue = lpsztmp;
5994     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5995             if (ch > 0)
5996             {
5997                 lphttpHdr->lpszValue[origlen] = ch;
5998                 origlen++;
5999                 lphttpHdr->lpszValue[origlen] = ' ';
6000                 origlen++;
6001             }
6002 
6003             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6004             lphttpHdr->lpszValue[len] = '\0';
6005             res = ERROR_SUCCESS;
6006         }
6007         else
6008         {
6009             WARN("heap_realloc (%d bytes) failed\n",len+1);
6010             res = ERROR_OUTOFMEMORY;
6011         }
6012     }
6013     TRACE("<-- %d\n", res);
6014     return res;
6015 }
6016 
6017 /***********************************************************************
6018  *           HTTP_GetCustomHeaderIndex (internal)
6019  *
6020  * Return index of custom header from header array
6021  *
6022  */
6023 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6024                                      int requested_index, BOOL request_only)
6025 {
6026     DWORD index;
6027 
6028     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6029 
6030     for (index = 0; index < request->nCustHeaders; index++)
6031     {
6032         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6033             continue;
6034 
6035         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6036             continue;
6037 
6038         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6039             continue;
6040 
6041         if (requested_index == 0)
6042             break;
6043         requested_index --;
6044     }
6045 
6046     if (index >= request->nCustHeaders)
6047         index = -1;
6048 
6049     TRACE("Return: %d\n", index);
6050     return index;
6051 }
6052 
6053 
6054 /***********************************************************************
6055  *           HTTP_InsertCustomHeader (internal)
6056  *
6057  * Insert header into array
6058  *
6059  */
6060 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6061 {
6062     INT count;
6063     LPHTTPHEADERW lph = NULL;
6064 
6065     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6066     count = request->nCustHeaders + 1;
6067     if (count > 1)
6068         lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6069     else
6070         lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6071 
6072     if (!lph)
6073         return ERROR_OUTOFMEMORY;
6074 
6075     request->custHeaders = lph;
6076     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6077     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6078     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6079     request->custHeaders[count-1].wCount= lpHdr->wCount;
6080     request->nCustHeaders++;
6081 
6082     return ERROR_SUCCESS;
6083 }
6084 
6085 
6086 /***********************************************************************
6087  *           HTTP_DeleteCustomHeader (internal)
6088  *
6089  * Delete header from array
6090  *  If this function is called, the indexs may change.
6091  */
6092 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6093 {
6094     if( request->nCustHeaders <= 0 )
6095         return FALSE;
6096     if( index >= request->nCustHeaders )
6097         return FALSE;
6098     request->nCustHeaders--;
6099 
6100     heap_free(request->custHeaders[index].lpszField);
6101     heap_free(request->custHeaders[index].lpszValue);
6102 
6103     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6104              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6105     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6106 
6107     return TRUE;
6108 }
6109 
6110 
6111 /***********************************************************************
6112  *           HTTP_VerifyValidHeader (internal)
6113  *
6114  * Verify the given header is not invalid for the given http request
6115  *
6116  */
6117 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6118 {
6119     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6120     if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6121         return ERROR_HTTP_INVALID_HEADER;
6122 
6123     return ERROR_SUCCESS;
6124 }
6125 
6126 /***********************************************************************
6127  *          IsHostInProxyBypassList (@)
6128  *
6129  * Undocumented
6130  *
6131  */
6132 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6133 {
6134    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6135    return FALSE;
6136 }
6137 
6138 /***********************************************************************
6139  *           InternetShowSecurityInfoByURLA (@)
6140  */
6141 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6142 {
6143    FIXME("stub: %s %p\n", url, window);
6144    return FALSE;
6145 }
6146 
6147 /***********************************************************************
6148  *           InternetShowSecurityInfoByURLW (@)
6149  */
6150 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6151 {
6152    FIXME("stub: %s %p\n", debugstr_w(url), window);
6153    return FALSE;
6154 }
6155 
6156 /***********************************************************************
6157  *           ShowX509EncodedCertificate (@)
6158  */
6159 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6160 {
6161     PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6162         cert, len);
6163     DWORD ret;
6164 
6165     if (certContext)
6166     {
6167         CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6168 
6169         memset(&view, 0, sizeof(view));
6170         view.hwndParent = parent;
6171         view.pCertContext = certContext;
6172         if (CryptUIDlgViewCertificateW(&view, NULL))
6173             ret = ERROR_SUCCESS;
6174         else
6175             ret = GetLastError();
6176         CertFreeCertificateContext(certContext);
6177     }
6178     else
6179         ret = GetLastError();
6180     return ret;
6181 }
6182 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.