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

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

Version: ~ [ 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  *
 11  * Ulrich Czekalla
 12  * David Hammerton
 13  *
 14  * This library is free software; you can redistribute it and/or
 15  * modify it under the terms of the GNU Lesser General Public
 16  * License as published by the Free Software Foundation; either
 17  * version 2.1 of the License, or (at your option) any later version.
 18  *
 19  * This library is distributed in the hope that it will be useful,
 20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 22  * Lesser General Public License for more details.
 23  *
 24  * You should have received a copy of the GNU Lesser General Public
 25  * License along with this library; if not, write to the Free Software
 26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 27  */
 28 
 29 #include "config.h"
 30 #include "wine/port.h"
 31 
 32 #if defined(__MINGW32__) || defined (_MSC_VER)
 33 #include <ws2tcpip.h>
 34 #endif
 35 
 36 #include <sys/types.h>
 37 #ifdef HAVE_SYS_SOCKET_H
 38 # include <sys/socket.h>
 39 #endif
 40 #ifdef HAVE_ARPA_INET_H
 41 # include <arpa/inet.h>
 42 #endif
 43 #include <stdarg.h>
 44 #include <stdio.h>
 45 #include <stdlib.h>
 46 #ifdef HAVE_UNISTD_H
 47 # include <unistd.h>
 48 #endif
 49 #include <time.h>
 50 #include <assert.h>
 51 #ifdef HAVE_ZLIB
 52 #  include <zlib.h>
 53 #endif
 54 
 55 #include "windef.h"
 56 #include "winbase.h"
 57 #include "wininet.h"
 58 #include "winerror.h"
 59 #define NO_SHLWAPI_STREAM
 60 #define NO_SHLWAPI_REG
 61 #define NO_SHLWAPI_STRFCNS
 62 #define NO_SHLWAPI_GDI
 63 #include "shlwapi.h"
 64 #include "sspi.h"
 65 #include "wincrypt.h"
 66 
 67 #include "internet.h"
 68 #include "wine/debug.h"
 69 #include "wine/exception.h"
 70 #include "wine/unicode.h"
 71 
 72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 73 
 74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','',0};
 75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
 76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
 77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
 80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
 81 static const WCHAR szGET[] = { 'G','E','T', 0 };
 82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
 83 static const WCHAR szCrLf[] = {'\r','\n', 0};
 84 
 85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
 86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
 87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
 88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
 89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
 90 static const WCHAR szAge[] = { 'A','g','e',0 };
 91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
 92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
 93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
 94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
 95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
 96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
 97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
 98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
 99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 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 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 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 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
137 
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
141 
142 #define HTTP_REFERER    szReferer
143 #define HTTP_ACCEPT     szAccept
144 #define HTTP_USERAGENT  szUser_Agent
145 
146 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
153 
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
155 
156 struct HttpAuthInfo
157 {
158     LPWSTR scheme;
159     CredHandle cred;
160     CtxtHandle ctx;
161     TimeStamp exp;
162     ULONG attr;
163     ULONG max_token;
164     void *auth_data;
165     unsigned int auth_data_len;
166     BOOL finished; /* finished authenticating */
167 };
168 
169 
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172     z_stream zstream;
173 #endif
174     BYTE buf[8192];
175     DWORD buf_size;
176     DWORD buf_pos;
177     BOOL end_of_data;
178 };
179 
180 typedef struct _authorizationData
181 {
182     struct list entry;
183 
184     LPWSTR lpszwHost;
185     LPWSTR lpszwRealm;
186     LPSTR  lpszAuthorization;
187     UINT   AuthorizationLen;
188 } authorizationData;
189 
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
191 
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
194 {
195     0, 0, &authcache_cs,
196     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197       0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
198 };
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
200 
201 static BOOL HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
210 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
211 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
212 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
213 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
214 static void HTTP_DrainContent(http_request_t *req);
215 static BOOL HTTP_FinishedReading(http_request_t *req);
216 
217 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
218 {
219     int HeaderIndex = 0;
220     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
221     if (HeaderIndex == -1)
222         return NULL;
223     else
224         return &req->pCustHeaders[HeaderIndex];
225 }
226 
227 #ifdef HAVE_ZLIB
228 
229 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
230 {
231     return HeapAlloc(GetProcessHeap(), 0, items*size);
232 }
233 
234 static void wininet_zfree(voidpf opaque, voidpf address)
235 {
236     HeapFree(GetProcessHeap(), 0, address);
237 }
238 
239 static void init_gzip_stream(http_request_t *req)
240 {
241     gzip_stream_t *gzip_stream;
242     int index, zres;
243 
244     gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
245     gzip_stream->zstream.zalloc = wininet_zalloc;
246     gzip_stream->zstream.zfree = wininet_zfree;
247     gzip_stream->zstream.opaque = NULL;
248     gzip_stream->zstream.next_in = NULL;
249     gzip_stream->zstream.avail_in = 0;
250     gzip_stream->zstream.next_out = NULL;
251     gzip_stream->zstream.avail_out = 0;
252     gzip_stream->buf_pos = 0;
253     gzip_stream->buf_size = 0;
254     gzip_stream->end_of_data = FALSE;
255 
256     zres = inflateInit2(&gzip_stream->zstream, 0x1f);
257     if(zres != Z_OK) {
258         ERR("inflateInit failed: %d\n", zres);
259         HeapFree(GetProcessHeap(), 0, gzip_stream);
260         return;
261     }
262 
263     req->gzip_stream = gzip_stream;
264 
265     index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
266     if(index != -1)
267         HTTP_DeleteCustomHeader(req, index);
268 }
269 
270 #else
271 
272 static void init_gzip_stream(http_request_t *req)
273 {
274     ERR("gzip stream not supported, missing zlib.\n");
275 }
276 
277 #endif
278 
279 /* set the request content length based on the headers */
280 static DWORD set_content_length( http_request_t *lpwhr )
281 {
282     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
283     WCHAR encoding[20];
284     DWORD size;
285 
286     size = sizeof(lpwhr->dwContentLength);
287     if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
288                              &lpwhr->dwContentLength, &size, NULL))
289         lpwhr->dwContentLength = ~0u;
290 
291     size = sizeof(encoding);
292     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
293         !strcmpiW(encoding, szChunked))
294     {
295         lpwhr->dwContentLength = ~0u;
296         lpwhr->read_chunked = TRUE;
297     }
298 
299     if(lpwhr->decoding) {
300         int encoding_idx;
301 
302         static const WCHAR gzipW[] = {'g','z','i','p',0};
303 
304         encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
305         if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
306             init_gzip_stream(lpwhr);
307     }
308 
309     return lpwhr->dwContentLength;
310 }
311 
312 /***********************************************************************
313  *           HTTP_Tokenize (internal)
314  *
315  *  Tokenize a string, allocating memory for the tokens.
316  */
317 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
318 {
319     LPWSTR * token_array;
320     int tokens = 0;
321     int i;
322     LPCWSTR next_token;
323 
324     if (string)
325     {
326         /* empty string has no tokens */
327         if (*string)
328             tokens++;
329         /* count tokens */
330         for (i = 0; string[i]; i++)
331         {
332             if (!strncmpW(string+i, token_string, strlenW(token_string)))
333             {
334                 DWORD j;
335                 tokens++;
336                 /* we want to skip over separators, but not the null terminator */
337                 for (j = 0; j < strlenW(token_string) - 1; j++)
338                     if (!string[i+j])
339                         break;
340                 i += j;
341             }
342         }
343     }
344 
345     /* add 1 for terminating NULL */
346     token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
347     token_array[tokens] = NULL;
348     if (!tokens)
349         return token_array;
350     for (i = 0; i < tokens; i++)
351     {
352         int len;
353         next_token = strstrW(string, token_string);
354         if (!next_token) next_token = string+strlenW(string);
355         len = next_token - string;
356         token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
357         memcpy(token_array[i], string, len*sizeof(WCHAR));
358         token_array[i][len] = '\0';
359         string = next_token+strlenW(token_string);
360     }
361     return token_array;
362 }
363 
364 /***********************************************************************
365  *           HTTP_FreeTokens (internal)
366  *
367  *  Frees memory returned from HTTP_Tokenize.
368  */
369 static void HTTP_FreeTokens(LPWSTR * token_array)
370 {
371     int i;
372     for (i = 0; token_array[i]; i++)
373         HeapFree(GetProcessHeap(), 0, token_array[i]);
374     HeapFree(GetProcessHeap(), 0, token_array);
375 }
376 
377 /* **********************************************************************
378  * 
379  * Helper functions for the HttpSendRequest(Ex) functions
380  * 
381  */
382 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
383 {
384     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
385     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
386 
387     TRACE("%p\n", lpwhr);
388 
389     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
390             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
391             req->dwContentLength, req->bEndRequest);
392 
393     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
394 }
395 
396 static void HTTP_FixURL(http_request_t *lpwhr)
397 {
398     static const WCHAR szSlash[] = { '/',0 };
399     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
400 
401     /* If we don't have a path we set it to root */
402     if (NULL == lpwhr->lpszPath)
403         lpwhr->lpszPath = heap_strdupW(szSlash);
404     else /* remove \r and \n*/
405     {
406         int nLen = strlenW(lpwhr->lpszPath);
407         while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
408         {
409             nLen--;
410             lpwhr->lpszPath[nLen]='\0';
411         }
412         /* Replace '\' with '/' */
413         while (nLen>0) {
414             nLen--;
415             if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
416         }
417     }
418 
419     if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
420                        lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
421        && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
422     {
423         WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0, 
424                              (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
425         *fixurl = '/';
426         strcpyW(fixurl + 1, lpwhr->lpszPath);
427         HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
428         lpwhr->lpszPath = fixurl;
429     }
430 }
431 
432 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
433 {
434     LPWSTR requestString;
435     DWORD len, n;
436     LPCWSTR *req;
437     UINT i;
438     LPWSTR p;
439 
440     static const WCHAR szSpace[] = { ' ',0 };
441     static const WCHAR szColon[] = { ':',' ',0 };
442     static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
443 
444     /* allocate space for an array of all the string pointers to be added */
445     len = (lpwhr->nCustHeaders)*4 + 10;
446     req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
447 
448     /* add the verb, path and HTTP version string */
449     n = 0;
450     req[n++] = verb;
451     req[n++] = szSpace;
452     req[n++] = path;
453     req[n++] = szSpace;
454     req[n++] = version;
455 
456     /* Append custom request headers */
457     for (i = 0; i < lpwhr->nCustHeaders; i++)
458     {
459         if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
460         {
461             req[n++] = szCrLf;
462             req[n++] = lpwhr->pCustHeaders[i].lpszField;
463             req[n++] = szColon;
464             req[n++] = lpwhr->pCustHeaders[i].lpszValue;
465 
466             TRACE("Adding custom header %s (%s)\n",
467                    debugstr_w(lpwhr->pCustHeaders[i].lpszField),
468                    debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
469         }
470     }
471 
472     if( n >= len )
473         ERR("oops. buffer overrun\n");
474 
475     req[n] = NULL;
476     requestString = HTTP_build_req( req, 4 );
477     HeapFree( GetProcessHeap(), 0, req );
478 
479     /*
480      * Set (header) termination string for request
481      * Make sure there's exactly two new lines at the end of the request
482      */
483     p = &requestString[strlenW(requestString)-1];
484     while ( (*p == '\n') || (*p == '\r') )
485        p--;
486     strcpyW( p+1, sztwocrlf );
487     
488     return requestString;
489 }
490 
491 static void HTTP_ProcessCookies( http_request_t *lpwhr )
492 {
493     int HeaderIndex;
494     int numCookies = 0;
495     LPHTTPHEADERW setCookieHeader;
496 
497     while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
498     {
499         setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
500 
501         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
502         {
503             int len;
504             static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
505             LPWSTR buf_url;
506             LPHTTPHEADERW Host;
507 
508             Host = HTTP_GetHeader(lpwhr, hostW);
509             len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
510             buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
511             sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
512             InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
513 
514             HeapFree(GetProcessHeap(), 0, buf_url);
515         }
516         numCookies++;
517     }
518 }
519 
520 static void strip_spaces(LPWSTR start)
521 {
522     LPWSTR str = start;
523     LPWSTR end;
524 
525     while (*str == ' ' && *str != '\0')
526         str++;
527 
528     if (str != start)
529         memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
530 
531     end = start + strlenW(start) - 1;
532     while (end >= start && *end == ' ')
533     {
534         *end = '\0';
535         end--;
536     }
537 }
538 
539 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
540 {
541     static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
542     static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
543     BOOL is_basic;
544     is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
545         ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
546     if (is_basic && pszRealm)
547     {
548         LPCWSTR token;
549         LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
550         LPCWSTR realm;
551         ptr++;
552         *pszRealm=NULL;
553         token = strchrW(ptr,'=');
554         if (!token)
555             return TRUE;
556         realm = ptr;
557         while (*realm == ' ' && *realm != '\0')
558             realm++;
559         if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
560             (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
561         {
562             token++;
563             while (*token == ' ' && *token != '\0')
564                 token++;
565             if (*token == '\0')
566                 return TRUE;
567             *pszRealm = heap_strdupW(token);
568             strip_spaces(*pszRealm);
569         }
570     }
571 
572     return is_basic;
573 }
574 
575 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
576 {
577     if (!authinfo) return;
578 
579     if (SecIsValidHandle(&authinfo->ctx))
580         DeleteSecurityContext(&authinfo->ctx);
581     if (SecIsValidHandle(&authinfo->cred))
582         FreeCredentialsHandle(&authinfo->cred);
583 
584     HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
585     HeapFree(GetProcessHeap(), 0, authinfo->scheme);
586     HeapFree(GetProcessHeap(), 0, authinfo);
587 }
588 
589 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
590 {
591     authorizationData *ad;
592     UINT rc = 0;
593 
594     TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
595 
596     EnterCriticalSection(&authcache_cs);
597     LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
598     {
599         if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
600         {
601             TRACE("Authorization found in cache\n");
602             *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
603             memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
604             rc = ad->AuthorizationLen;
605             break;
606         }
607     }
608     LeaveCriticalSection(&authcache_cs);
609     return rc;
610 }
611 
612 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
613 {
614     struct list *cursor;
615     authorizationData* ad = NULL;
616 
617     TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
618 
619     EnterCriticalSection(&authcache_cs);
620     LIST_FOR_EACH(cursor, &basicAuthorizationCache)
621     {
622         authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
623         if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
624         {
625             ad = check;
626             break;
627         }
628     }
629 
630     if (ad)
631     {
632         TRACE("Found match in cache, replacing\n");
633         HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
634         ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
635         memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
636         ad->AuthorizationLen = auth_data_len;
637     }
638     else
639     {
640         ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
641         ad->lpszwHost = heap_strdupW(host);
642         ad->lpszwRealm = heap_strdupW(realm);
643         ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
644         memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
645         ad->AuthorizationLen = auth_data_len;
646         list_add_head(&basicAuthorizationCache,&ad->entry);
647         TRACE("authorization cached\n");
648     }
649     LeaveCriticalSection(&authcache_cs);
650 }
651 
652 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
653                                   struct HttpAuthInfo **ppAuthInfo,
654                                   LPWSTR domain_and_username, LPWSTR password,
655                                   LPWSTR host )
656 {
657     SECURITY_STATUS sec_status;
658     struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
659     BOOL first = FALSE;
660     LPWSTR szRealm = NULL;
661 
662     TRACE("%s\n", debugstr_w(pszAuthValue));
663 
664     if (!pAuthInfo)
665     {
666         TimeStamp exp;
667 
668         first = TRUE;
669         pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
670         if (!pAuthInfo)
671             return FALSE;
672 
673         SecInvalidateHandle(&pAuthInfo->cred);
674         SecInvalidateHandle(&pAuthInfo->ctx);
675         memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
676         pAuthInfo->attr = 0;
677         pAuthInfo->auth_data = NULL;
678         pAuthInfo->auth_data_len = 0;
679         pAuthInfo->finished = FALSE;
680 
681         if (is_basic_auth_value(pszAuthValue,NULL))
682         {
683             static const WCHAR szBasic[] = {'B','a','s','i','c',0};
684             pAuthInfo->scheme = heap_strdupW(szBasic);
685             if (!pAuthInfo->scheme)
686             {
687                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
688                 return FALSE;
689             }
690         }
691         else
692         {
693             PVOID pAuthData;
694             SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
695 
696             pAuthInfo->scheme = heap_strdupW(pszAuthValue);
697             if (!pAuthInfo->scheme)
698             {
699                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
700                 return FALSE;
701             }
702 
703             if (domain_and_username)
704             {
705                 WCHAR *user = strchrW(domain_and_username, '\\');
706                 WCHAR *domain = domain_and_username;
707 
708                 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
709 
710                 pAuthData = &nt_auth_identity;
711 
712                 if (user) user++;
713                 else
714                 {
715                     user = domain_and_username;
716                     domain = NULL;
717                 }
718 
719                 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
720                 nt_auth_identity.User = user;
721                 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
722                 nt_auth_identity.Domain = domain;
723                 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
724                 nt_auth_identity.Password = password;
725                 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
726             }
727             else
728                 /* use default credentials */
729                 pAuthData = NULL;
730 
731             sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
732                                                    SECPKG_CRED_OUTBOUND, NULL,
733                                                    pAuthData, NULL,
734                                                    NULL, &pAuthInfo->cred,
735                                                    &exp);
736             if (sec_status == SEC_E_OK)
737             {
738                 PSecPkgInfoW sec_pkg_info;
739                 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
740                 if (sec_status == SEC_E_OK)
741                 {
742                     pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
743                     FreeContextBuffer(sec_pkg_info);
744                 }
745             }
746             if (sec_status != SEC_E_OK)
747             {
748                 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
749                      debugstr_w(pAuthInfo->scheme), sec_status);
750                 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
751                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
752                 return FALSE;
753             }
754         }
755         *ppAuthInfo = pAuthInfo;
756     }
757     else if (pAuthInfo->finished)
758         return FALSE;
759 
760     if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
761         strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
762     {
763         ERR("authentication scheme changed from %s to %s\n",
764             debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
765         return FALSE;
766     }
767 
768     if (is_basic_auth_value(pszAuthValue,&szRealm))
769     {
770         int userlen;
771         int passlen;
772         char *auth_data = NULL;
773         UINT auth_data_len = 0;
774 
775         TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
776 
777         if (!domain_and_username)
778         {
779             if (host && szRealm)
780                 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
781             if (auth_data_len == 0)
782             {
783                 HeapFree(GetProcessHeap(),0,szRealm);
784                 return FALSE;
785             }
786         }
787         else
788         {
789             userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
790             passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
791 
792             /* length includes a nul terminator, which will be re-used for the ':' */
793             auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
794             if (!auth_data)
795             {
796                 HeapFree(GetProcessHeap(),0,szRealm);
797                 return FALSE;
798             }
799 
800             WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
801             auth_data[userlen] = ':';
802             WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
803             auth_data_len = userlen + 1 + passlen;
804             if (host && szRealm)
805                 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
806         }
807 
808         pAuthInfo->auth_data = auth_data;
809         pAuthInfo->auth_data_len = auth_data_len;
810         pAuthInfo->finished = TRUE;
811         HeapFree(GetProcessHeap(),0,szRealm);
812 
813         return TRUE;
814     }
815     else
816     {
817         LPCWSTR pszAuthData;
818         SecBufferDesc out_desc, in_desc;
819         SecBuffer out, in;
820         unsigned char *buffer;
821         ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
822             ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
823 
824         in.BufferType = SECBUFFER_TOKEN;
825         in.cbBuffer = 0;
826         in.pvBuffer = NULL;
827 
828         in_desc.ulVersion = 0;
829         in_desc.cBuffers = 1;
830         in_desc.pBuffers = &in;
831 
832         pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
833         if (*pszAuthData == ' ')
834         {
835             pszAuthData++;
836             in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
837             in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
838             HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
839         }
840 
841         buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
842 
843         out.BufferType = SECBUFFER_TOKEN;
844         out.cbBuffer = pAuthInfo->max_token;
845         out.pvBuffer = buffer;
846 
847         out_desc.ulVersion = 0;
848         out_desc.cBuffers = 1;
849         out_desc.pBuffers = &out;
850 
851         sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
852                                                 first ? NULL : &pAuthInfo->ctx,
853                                                 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
854                                                 context_req, 0, SECURITY_NETWORK_DREP,
855                                                 in.pvBuffer ? &in_desc : NULL,
856                                                 0, &pAuthInfo->ctx, &out_desc,
857                                                 &pAuthInfo->attr, &pAuthInfo->exp);
858         if (sec_status == SEC_E_OK)
859         {
860             pAuthInfo->finished = TRUE;
861             pAuthInfo->auth_data = out.pvBuffer;
862             pAuthInfo->auth_data_len = out.cbBuffer;
863             TRACE("sending last auth packet\n");
864         }
865         else if (sec_status == SEC_I_CONTINUE_NEEDED)
866         {
867             pAuthInfo->auth_data = out.pvBuffer;
868             pAuthInfo->auth_data_len = out.cbBuffer;
869             TRACE("sending next auth packet\n");
870         }
871         else
872         {
873             ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
874             HeapFree(GetProcessHeap(), 0, out.pvBuffer);
875             destroy_authinfo(pAuthInfo);
876             *ppAuthInfo = NULL;
877             return FALSE;
878         }
879     }
880 
881     return TRUE;
882 }
883 
884 /***********************************************************************
885  *           HTTP_HttpAddRequestHeadersW (internal)
886  */
887 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
888         LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
889 {
890     LPWSTR lpszStart;
891     LPWSTR lpszEnd;
892     LPWSTR buffer;
893     BOOL bSuccess = FALSE;
894     DWORD len;
895 
896     TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
897 
898     if( dwHeaderLength == ~0U )
899         len = strlenW(lpszHeader);
900     else
901         len = dwHeaderLength;
902     buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
903     lstrcpynW( buffer, lpszHeader, len + 1);
904 
905     lpszStart = buffer;
906 
907     do
908     {
909         LPWSTR * pFieldAndValue;
910 
911         lpszEnd = lpszStart;
912 
913         while (*lpszEnd != '\0')
914         {
915             if (*lpszEnd == '\r' || *lpszEnd == '\n')
916                  break;
917             lpszEnd++;
918         }
919 
920         if (*lpszStart == '\0')
921             break;
922 
923         if (*lpszEnd == '\r' || *lpszEnd == '\n')
924         {
925             *lpszEnd = '\0';
926             lpszEnd++; /* Jump over newline */
927         }
928         TRACE("interpreting header %s\n", debugstr_w(lpszStart));
929         if (*lpszStart == '\0')
930         {
931             /* Skip 0-length headers */
932             lpszStart = lpszEnd;
933             bSuccess = TRUE;
934             continue;
935         }
936         pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
937         if (pFieldAndValue)
938         {
939             bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
940             if (bSuccess)
941                 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
942                     pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
943             HTTP_FreeTokens(pFieldAndValue);
944         }
945 
946         lpszStart = lpszEnd;
947     } while (bSuccess);
948 
949     HeapFree(GetProcessHeap(), 0, buffer);
950 
951     return bSuccess;
952 }
953 
954 /***********************************************************************
955  *           HttpAddRequestHeadersW (WININET.@)
956  *
957  * Adds one or more HTTP header to the request handler
958  *
959  * NOTE
960  * On Windows if dwHeaderLength includes the trailing '\0', then
961  * HttpAddRequestHeadersW() adds it too. However this results in an
962  * invalid Http header which is rejected by some servers so we probably
963  * don't need to match Windows on that point.
964  *
965  * RETURNS
966  *    TRUE  on success
967  *    FALSE on failure
968  *
969  */
970 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
971         LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
972 {
973     BOOL bSuccess = FALSE;
974     http_request_t *lpwhr;
975 
976     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
977 
978     if (!lpszHeader) 
979       return TRUE;
980 
981     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
982     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
983     {
984         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
985         goto lend;
986     }
987     bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
988 lend:
989     if( lpwhr )
990         WININET_Release( &lpwhr->hdr );
991 
992     return bSuccess;
993 }
994 
995 /***********************************************************************
996  *           HttpAddRequestHeadersA (WININET.@)
997  *
998  * Adds one or more HTTP header to the request handler
999  *
1000  * RETURNS
1001  *    TRUE  on success
1002  *    FALSE on failure
1003  *
1004  */
1005 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1006         LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1007 {
1008     DWORD len;
1009     LPWSTR hdr;
1010     BOOL r;
1011 
1012     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1013 
1014     len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1015     hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1016     MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1017     if( dwHeaderLength != ~0U )
1018         dwHeaderLength = len;
1019 
1020     r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1021 
1022     HeapFree( GetProcessHeap(), 0, hdr );
1023 
1024     return r;
1025 }
1026 
1027 /***********************************************************************
1028  *           HttpEndRequestA (WININET.@)
1029  *
1030  * Ends an HTTP request that was started by HttpSendRequestEx
1031  *
1032  * RETURNS
1033  *    TRUE      if successful
1034  *    FALSE     on failure
1035  *
1036  */
1037 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest, 
1038         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
1039 {
1040     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
1041 
1042     if (lpBuffersOut)
1043     {
1044         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1045         return FALSE;
1046     }
1047 
1048     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
1049 }
1050 
1051 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
1052 {
1053     BOOL rc = FALSE;
1054     INT responseLen;
1055     DWORD dwBufferSize;
1056     INTERNET_ASYNC_RESULT iar;
1057 
1058     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1059                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1060 
1061     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
1062     if (responseLen)
1063         rc = TRUE;
1064 
1065     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1066                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
1067 
1068     /* process cookies here. Is this right? */
1069     HTTP_ProcessCookies(lpwhr);
1070 
1071     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
1072 
1073     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
1074     {
1075         DWORD dwCode,dwCodeLength = sizeof(DWORD);
1076         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
1077             (dwCode == 302 || dwCode == 301 || dwCode == 303))
1078         {
1079             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
1080             dwBufferSize=sizeof(szNewLocation);
1081             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
1082             {
1083                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
1084                 {
1085                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1086                     lpwhr->lpszVerb = heap_strdupW(szGET);
1087                 }
1088                 HTTP_DrainContent(lpwhr);
1089                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
1090                 {
1091                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
1092                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
1093                     rc = HTTP_HandleRedirect(lpwhr, new_url);
1094                     if (rc)
1095                         rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
1096                     HeapFree( GetProcessHeap(), 0, new_url );
1097                 }
1098             }
1099         }
1100     }
1101 
1102     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
1103     iar.dwError = rc ? 0 : INTERNET_GetLastError();
1104 
1105     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1106                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1107                           sizeof(INTERNET_ASYNC_RESULT));
1108     return rc;
1109 }
1110 
1111 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
1112 {
1113     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
1114     http_request_t *lpwhr = (http_request_t*)work->hdr;
1115 
1116     TRACE("%p\n", lpwhr);
1117 
1118     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
1119 }
1120 
1121 /***********************************************************************
1122  *           HttpEndRequestW (WININET.@)
1123  *
1124  * Ends an HTTP request that was started by HttpSendRequestEx
1125  *
1126  * RETURNS
1127  *    TRUE      if successful
1128  *    FALSE     on failure
1129  *
1130  */
1131 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest, 
1132         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
1133 {
1134     BOOL rc = FALSE;
1135     http_request_t *lpwhr;
1136 
1137     TRACE("-->\n");
1138 
1139     if (lpBuffersOut)
1140     {
1141         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1142         return FALSE;
1143     }
1144 
1145     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
1146 
1147     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
1148     {
1149         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1150         if (lpwhr)
1151             WININET_Release( &lpwhr->hdr );
1152         return FALSE;
1153     }
1154     lpwhr->hdr.dwFlags |= dwFlags;
1155 
1156     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1157     {
1158         WORKREQUEST work;
1159         struct WORKREQ_HTTPENDREQUESTW *request;
1160 
1161         work.asyncproc = AsyncHttpEndRequestProc;
1162         work.hdr = WININET_AddRef( &lpwhr->hdr );
1163 
1164         request = &work.u.HttpEndRequestW;
1165         request->dwFlags = dwFlags;
1166         request->dwContext = dwContext;
1167 
1168         INTERNET_AsyncCall(&work);
1169         INTERNET_SetLastError(ERROR_IO_PENDING);
1170     }
1171     else
1172         rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1173 
1174     WININET_Release( &lpwhr->hdr );
1175     TRACE("%i <--\n",rc);
1176     return rc;
1177 }
1178 
1179 /***********************************************************************
1180  *           HttpOpenRequestW (WININET.@)
1181  *
1182  * Open a HTTP request handle
1183  *
1184  * RETURNS
1185  *    HINTERNET  a HTTP request handle on success
1186  *    NULL       on failure
1187  *
1188  */
1189 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1190         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1191         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1192         DWORD dwFlags, DWORD_PTR dwContext)
1193 {
1194     http_session_t *lpwhs;
1195     HINTERNET handle = NULL;
1196 
1197     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1198           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1199           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1200           dwFlags, dwContext);
1201     if(lpszAcceptTypes!=NULL)
1202     {
1203         int i;
1204         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1205             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1206     }    
1207 
1208     lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1209     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
1210     {
1211         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1212         goto lend;
1213     }
1214 
1215     /*
1216      * My tests seem to show that the windows version does not
1217      * become asynchronous until after this point. And anyhow
1218      * if this call was asynchronous then how would you get the
1219      * necessary HINTERNET pointer returned by this function.
1220      *
1221      */
1222     handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1223                                    lpszVersion, lpszReferrer, lpszAcceptTypes,
1224                                    dwFlags, dwContext);
1225 lend:
1226     if( lpwhs )
1227         WININET_Release( &lpwhs->hdr );
1228     TRACE("returning %p\n", handle);
1229     return handle;
1230 }
1231 
1232 
1233 /***********************************************************************
1234  *           HttpOpenRequestA (WININET.@)
1235  *
1236  * Open a HTTP request handle
1237  *
1238  * RETURNS
1239  *    HINTERNET  a HTTP request handle on success
1240  *    NULL       on failure
1241  *
1242  */
1243 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1244         LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1245         LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1246         DWORD dwFlags, DWORD_PTR dwContext)
1247 {
1248     LPWSTR szVerb = NULL, szObjectName = NULL;
1249     LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1250     INT acceptTypesCount;
1251     HINTERNET rc = FALSE;
1252     LPCSTR *types;
1253 
1254     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1255           debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1256           debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1257           dwFlags, dwContext);
1258 
1259     if (lpszVerb)
1260     {
1261         szVerb = heap_strdupAtoW(lpszVerb);
1262         if ( !szVerb )
1263             goto end;
1264     }
1265 
1266     if (lpszObjectName)
1267     {
1268         szObjectName = heap_strdupAtoW(lpszObjectName);
1269         if ( !szObjectName )
1270             goto end;
1271     }
1272 
1273     if (lpszVersion)
1274     {
1275         szVersion = heap_strdupAtoW(lpszVersion);
1276         if ( !szVersion )
1277             goto end;
1278     }
1279 
1280     if (lpszReferrer)
1281     {
1282         szReferrer = heap_strdupAtoW(lpszReferrer);
1283         if ( !szReferrer )
1284             goto end;
1285     }
1286 
1287     if (lpszAcceptTypes)
1288     {
1289         acceptTypesCount = 0;
1290         types = lpszAcceptTypes;
1291         while (*types)
1292         {
1293             __TRY
1294             {
1295                 /* find out how many there are */
1296                 if (*types && **types)
1297                 {
1298                     TRACE("accept type: %s\n", debugstr_a(*types));
1299                     acceptTypesCount++;
1300                 }
1301             }
1302             __EXCEPT_PAGE_FAULT
1303             {
1304                 WARN("invalid accept type pointer\n");
1305             }
1306             __ENDTRY;
1307             types++;
1308         }
1309         szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1310         if (!szAcceptTypes) goto end;
1311 
1312         acceptTypesCount = 0;
1313         types = lpszAcceptTypes;
1314         while (*types)
1315         {
1316             __TRY
1317             {
1318                 if (*types && **types)
1319                     szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1320             }
1321             __EXCEPT_PAGE_FAULT
1322             {
1323                 /* ignore invalid pointer */
1324             }
1325             __ENDTRY;
1326             types++;
1327         }
1328         szAcceptTypes[acceptTypesCount] = NULL;
1329     }
1330 
1331     rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1332                           szVersion, szReferrer,
1333                           (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1334 
1335 end:
1336     if (szAcceptTypes)
1337     {
1338         acceptTypesCount = 0;
1339         while (szAcceptTypes[acceptTypesCount])
1340         {
1341             HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1342             acceptTypesCount++;
1343         }
1344         HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1345     }
1346     HeapFree(GetProcessHeap(), 0, szReferrer);
1347     HeapFree(GetProcessHeap(), 0, szVersion);
1348     HeapFree(GetProcessHeap(), 0, szObjectName);
1349     HeapFree(GetProcessHeap(), 0, szVerb);
1350 
1351     return rc;
1352 }
1353 
1354 /***********************************************************************
1355  *  HTTP_EncodeBase64
1356  */
1357 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1358 {
1359     UINT n = 0, x;
1360     static const CHAR HTTP_Base64Enc[] =
1361         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1362 
1363     while( len > 0 )
1364     {
1365         /* first 6 bits, all from bin[0] */
1366         base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1367         x = (bin[0] & 3) << 4;
1368 
1369         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1370         if( len == 1 )
1371         {
1372             base64[n++] = HTTP_Base64Enc[x];
1373             base64[n++] = '=';
1374             base64[n++] = '=';
1375             break;
1376         }
1377         base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1378         x = ( bin[1] & 0x0f ) << 2;
1379 
1380         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1381         if( len == 2 )
1382         {
1383             base64[n++] = HTTP_Base64Enc[x];
1384             base64[n++] = '=';
1385             break;
1386         }
1387         base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1388 
1389         /* last 6 bits, all from bin [2] */
1390         base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1391         bin += 3;
1392         len -= 3;
1393     }
1394     base64[n] = 0;
1395     return n;
1396 }
1397 
1398 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1399                ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1400                ((x) >= '' && (x) <= '9') ? (x) - '' + 52 : \
1401                ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1402 static const signed char HTTP_Base64Dec[256] =
1403 {
1404     CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1405     CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1406     CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1407     CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1408     CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1409     CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1410     CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1411     CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1412     CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1413     CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1414     CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1415     CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1416     CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1417     CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1418     CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1419     CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1420     CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1421     CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1422     CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1423     CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1424     CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1425     CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1426     CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1427     CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1428     CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1429     CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1430 };
1431 #undef CH
1432 
1433 /***********************************************************************
1434  *  HTTP_DecodeBase64
1435  */
1436 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1437 {
1438     unsigned int n = 0;
1439 
1440     while(*base64)
1441     {
1442         signed char in[4];
1443 
1444         if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1445             ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1446             base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1447             ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1448         {
1449             WARN("invalid base64: %s\n", debugstr_w(base64));
1450             return 0;
1451         }
1452         if (bin)
1453             bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1454         n++;
1455 
1456         if ((base64[2] == '=') && (base64[3] == '='))
1457             break;
1458         if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1459             ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1460         {
1461             WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1462             return 0;
1463         }
1464         if (bin)
1465             bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1466         n++;
1467 
1468         if (base64[3] == '=')
1469             break;
1470         if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1471             ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1472         {
1473             WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1474             return 0;
1475         }
1476         if (bin)
1477             bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1478         n++;
1479 
1480         base64 += 4;
1481     }
1482 
1483     return n;
1484 }
1485 
1486 /***********************************************************************
1487  *  HTTP_InsertAuthorization
1488  *
1489  *   Insert or delete the authorization field in the request header.
1490  */
1491 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1492 {
1493     if (pAuthInfo)
1494     {
1495         static const WCHAR wszSpace[] = {' ',0};
1496         static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1497         unsigned int len;
1498         WCHAR *authorization = NULL;
1499 
1500         if (pAuthInfo->auth_data_len)
1501         {
1502             /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1503             len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1504             authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1505             if (!authorization)
1506                 return FALSE;
1507 
1508             strcpyW(authorization, pAuthInfo->scheme);
1509             strcatW(authorization, wszSpace);
1510             HTTP_EncodeBase64(pAuthInfo->auth_data,
1511                               pAuthInfo->auth_data_len,
1512                               authorization+strlenW(authorization));
1513 
1514             /* clear the data as it isn't valid now that it has been sent to the
1515              * server, unless it's Basic authentication which doesn't do
1516              * connection tracking */
1517             if (strcmpiW(pAuthInfo->scheme, wszBasic))
1518             {
1519                 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1520                 pAuthInfo->auth_data = NULL;
1521                 pAuthInfo->auth_data_len = 0;
1522             }
1523         }
1524 
1525         TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1526 
1527         HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1528 
1529         HeapFree(GetProcessHeap(), 0, authorization);
1530     }
1531     return TRUE;
1532 }
1533 
1534 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1535 {
1536     WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1537     DWORD size;
1538 
1539     size = sizeof(new_location);
1540     if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1541     {
1542         if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1543         strcpyW( url, new_location );
1544     }
1545     else
1546     {
1547         static const WCHAR slash[] = { '/',0 };
1548         static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1549         static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1550         http_session_t *session = req->lpHttpSession;
1551 
1552         size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1553         size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1554 
1555         if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1556 
1557         if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1558             sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1559         else
1560             sprintfW( url, format, session->lpszHostName, session->nHostPort );
1561         if (req->lpszPath[0] != '/') strcatW( url, slash );
1562         strcatW( url, req->lpszPath );
1563     }
1564     TRACE("url=%s\n", debugstr_w(url));
1565     return url;
1566 }
1567 
1568 /***********************************************************************
1569  *           HTTP_DealWithProxy
1570  */
1571 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1572 {
1573     WCHAR buf[MAXHOSTNAME];
1574     WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1575     static WCHAR szNul[] = { 0 };
1576     URL_COMPONENTSW UrlComponents;
1577     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1578     static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1579 
1580     memset( &UrlComponents, 0, sizeof UrlComponents );
1581     UrlComponents.dwStructSize = sizeof UrlComponents;
1582     UrlComponents.lpszHostName = buf;
1583     UrlComponents.dwHostNameLength = MAXHOSTNAME;
1584 
1585     if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1586                                  hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1587         sprintfW(proxy, szFormat, hIC->lpszProxy);
1588     else
1589         strcpyW(proxy, hIC->lpszProxy);
1590     if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1591         return FALSE;
1592     if( UrlComponents.dwHostNameLength == 0 )
1593         return FALSE;
1594 
1595     if( !lpwhr->lpszPath )
1596         lpwhr->lpszPath = szNul;
1597 
1598     if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1599         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1600 
1601     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1602     lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1603     lpwhs->nServerPort = UrlComponents.nPort;
1604 
1605     TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1606     return TRUE;
1607 }
1608 
1609 #ifndef INET6_ADDRSTRLEN
1610 #define INET6_ADDRSTRLEN 46
1611 #endif
1612 
1613 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1614 {
1615     char szaddr[INET6_ADDRSTRLEN];
1616     http_session_t *lpwhs = lpwhr->lpHttpSession;
1617     const void *addr;
1618 
1619     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1620                           INTERNET_STATUS_RESOLVING_NAME,
1621                           lpwhs->lpszServerName,
1622                           strlenW(lpwhs->lpszServerName)+1);
1623 
1624     lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1625     if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1626                     (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1627     {
1628         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1629         return FALSE;
1630     }
1631 
1632     switch (lpwhs->socketAddress.ss_family)
1633     {
1634     case AF_INET:
1635         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1636         break;
1637     case AF_INET6:
1638         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1639         break;
1640     default:
1641         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1642         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1643         return FALSE;
1644     }
1645     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1646     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1647                           INTERNET_STATUS_NAME_RESOLVED,
1648                           szaddr, strlen(szaddr)+1);
1649 
1650     TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1651     return TRUE;
1652 }
1653 
1654 
1655 /***********************************************************************
1656  *           HTTPREQ_Destroy (internal)
1657  *
1658  * Deallocate request handle
1659  *
1660  */
1661 static void HTTPREQ_Destroy(object_header_t *hdr)
1662 {
1663     http_request_t *lpwhr = (http_request_t*) hdr;
1664     DWORD i;
1665 
1666     TRACE("\n");
1667 
1668     if(lpwhr->hCacheFile)
1669         CloseHandle(lpwhr->hCacheFile);
1670 
1671     HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1672 
1673     DeleteCriticalSection( &lpwhr->read_section );
1674     WININET_Release(&lpwhr->lpHttpSession->hdr);
1675 
1676     destroy_authinfo(lpwhr->pAuthInfo);
1677     destroy_authinfo(lpwhr->pProxyAuthInfo);
1678 
1679     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1680     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1681     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1682     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1683     HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1684 
1685     for (i = 0; i < lpwhr->nCustHeaders; i++)
1686     {
1687         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1688         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1689     }
1690 
1691 #ifdef HAVE_ZLIB
1692     if(lpwhr->gzip_stream) {
1693         if(!lpwhr->gzip_stream->end_of_data)
1694             inflateEnd(&lpwhr->gzip_stream->zstream);
1695         HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1696     }
1697 #endif
1698 
1699     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1700     HeapFree(GetProcessHeap(), 0, lpwhr);
1701 }
1702 
1703 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1704 {
1705     http_request_t *lpwhr = (http_request_t*) hdr;
1706 
1707     TRACE("%p\n",lpwhr);
1708 
1709     if (!NETCON_connected(&lpwhr->netConnection))
1710         return;
1711 
1712     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1713                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1714 
1715     NETCON_close(&lpwhr->netConnection);
1716 
1717     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1718                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1719 }
1720 
1721 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1722 {
1723     LPHTTPHEADERW host_header;
1724 
1725     static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1726 
1727     host_header = HTTP_GetHeader(req, hostW);
1728     if(!host_header)
1729         return FALSE;
1730 
1731     sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1732     return TRUE;
1733 }
1734 
1735 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1736 {
1737     WCHAR szVersion[10];
1738     WCHAR szConnectionResponse[20];
1739     DWORD dwBufferSize = sizeof(szVersion);
1740     BOOL keepalive = FALSE;
1741 
1742     /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1743      * the connection is keep-alive by default */
1744     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1745                              &dwBufferSize, NULL) &&
1746         !strcmpiW(szVersion, g_szHttp1_1))
1747     {
1748         keepalive = TRUE;
1749     }
1750 
1751     dwBufferSize = sizeof(szConnectionResponse);
1752     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1753         HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1754     {
1755         keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1756     }
1757 
1758     return keepalive;
1759 }
1760 
1761 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1762 {
1763     http_request_t *req = (http_request_t*)hdr;
1764 
1765     switch(option) {
1766     case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1767     {
1768         http_session_t *lpwhs = req->lpHttpSession;
1769         INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1770 
1771         FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1772 
1773         if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1774             return ERROR_INSUFFICIENT_BUFFER;
1775         *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1776         /* FIXME: can't get a SOCKET from our connection since we don't use
1777          * winsock
1778          */
1779         info->Socket = 0;
1780         /* FIXME: get source port from req->netConnection */
1781         info->SourcePort = 0;
1782         info->DestPort = lpwhs->nHostPort;
1783         info->Flags = 0;
1784         if (HTTP_KeepAlive(req))
1785             info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1786         if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1787             info->Flags |= IDSI_FLAG_PROXY;
1788         if (req->netConnection.useSSL)
1789             info->Flags |= IDSI_FLAG_SECURE;
1790 
1791         return ERROR_SUCCESS;
1792     }
1793 
1794     case INTERNET_OPTION_SECURITY_FLAGS:
1795     {
1796         http_session_t *lpwhs;
1797         lpwhs = req->lpHttpSession;
1798 
1799         if (*size < sizeof(ULONG))
1800             return ERROR_INSUFFICIENT_BUFFER;
1801 
1802         *size = sizeof(DWORD);
1803         if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1804             *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1805         else
1806             *(DWORD*)buffer = 0;
1807         FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1808         return ERROR_SUCCESS;
1809     }
1810 
1811     case INTERNET_OPTION_HANDLE_TYPE:
1812         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1813 
1814         if (*size < sizeof(ULONG))
1815             return ERROR_INSUFFICIENT_BUFFER;
1816 
1817         *size = sizeof(DWORD);
1818         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1819         return ERROR_SUCCESS;
1820 
1821     case INTERNET_OPTION_URL: {
1822         WCHAR url[INTERNET_MAX_URL_LENGTH];
1823         HTTPHEADERW *host;
1824         DWORD len;
1825         WCHAR *pch;
1826 
1827         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1828 
1829         TRACE("INTERNET_OPTION_URL\n");
1830 
1831         host = HTTP_GetHeader(req, hostW);
1832         strcpyW(url, httpW);
1833         strcatW(url, host->lpszValue);
1834         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1835             *pch = 0;
1836         strcatW(url, req->lpszPath);
1837 
1838         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1839 
1840         if(unicode) {
1841             len = (strlenW(url)+1) * sizeof(WCHAR);
1842             if(*size < len)
1843                 return ERROR_INSUFFICIENT_BUFFER;
1844 
1845             *size = len;
1846             strcpyW(buffer, url);
1847             return ERROR_SUCCESS;
1848         }else {
1849             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1850             if(len > *size)
1851                 return ERROR_INSUFFICIENT_BUFFER;
1852 
1853             *size = len;
1854             return ERROR_SUCCESS;
1855         }
1856     }
1857 
1858     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1859         INTERNET_CACHE_ENTRY_INFOW *info;
1860         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1861         WCHAR url[INTERNET_MAX_URL_LENGTH];
1862         DWORD nbytes, error;
1863         BOOL ret;
1864 
1865         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1866 
1867         if (*size < sizeof(*ts))
1868         {
1869             *size = sizeof(*ts);
1870             return ERROR_INSUFFICIENT_BUFFER;
1871         }
1872         nbytes = 0;
1873         HTTP_GetRequestURL(req, url);
1874         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1875         error = GetLastError();
1876         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1877         {
1878             if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1879                 return ERROR_OUTOFMEMORY;
1880 
1881             GetUrlCacheEntryInfoW(url, info, &nbytes);
1882 
1883             ts->ftExpires = info->ExpireTime;
1884             ts->ftLastModified = info->LastModifiedTime;
1885 
1886             HeapFree(GetProcessHeap(), 0, info);
1887             *size = sizeof(*ts);
1888             return ERROR_SUCCESS;
1889         }
1890         return error;
1891     }
1892 
1893     case INTERNET_OPTION_DATAFILE_NAME: {
1894         DWORD req_size;
1895 
1896         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1897 
1898         if(!req->lpszCacheFile) {
1899             *size = 0;
1900             return ERROR_INTERNET_ITEM_NOT_FOUND;
1901         }
1902 
1903         if(unicode) {
1904             req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1905             if(*size < req_size)
1906                 return ERROR_INSUFFICIENT_BUFFER;
1907 
1908             *size = req_size;
1909             memcpy(buffer, req->lpszCacheFile, *size);
1910             return ERROR_SUCCESS;
1911         }else {
1912             req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1913             if (req_size > *size)
1914                 return ERROR_INSUFFICIENT_BUFFER;
1915 
1916             *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1917                     -1, buffer, *size, NULL, NULL);
1918             return ERROR_SUCCESS;
1919         }
1920     }
1921 
1922     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1923         PCCERT_CONTEXT context;
1924 
1925         if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1926             *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1927             return ERROR_INSUFFICIENT_BUFFER;
1928         }
1929 
1930         context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1931         if(context) {
1932             INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1933             DWORD len;
1934 
1935             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1936             info->ftExpiry = context->pCertInfo->NotAfter;
1937             info->ftStart = context->pCertInfo->NotBefore;
1938             if(unicode) {
1939                 len = CertNameToStrW(context->dwCertEncodingType,
1940                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1941                 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1942                 if(info->lpszSubjectInfo)
1943                     CertNameToStrW(context->dwCertEncodingType,
1944                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1945                              info->lpszSubjectInfo, len);
1946                 len = CertNameToStrW(context->dwCertEncodingType,
1947                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1948                 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1949                 if (info->lpszIssuerInfo)
1950                     CertNameToStrW(context->dwCertEncodingType,
1951                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1952                              info->lpszIssuerInfo, len);
1953             }else {
1954                 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1955 
1956                 len = CertNameToStrA(context->dwCertEncodingType,
1957                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1958                 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1959                 if(infoA->lpszSubjectInfo)
1960                     CertNameToStrA(context->dwCertEncodingType,
1961                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1962                              infoA->lpszSubjectInfo, len);
1963                 len = CertNameToStrA(context->dwCertEncodingType,
1964                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1965                 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1966                 if(infoA->lpszIssuerInfo)
1967                     CertNameToStrA(context->dwCertEncodingType,
1968                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1969                              infoA->lpszIssuerInfo, len);
1970             }
1971 
1972             /*
1973              * Contrary to MSDN, these do not appear to be set.
1974              * lpszProtocolName
1975              * lpszSignatureAlgName
1976              * lpszEncryptionAlgName
1977              * dwKeySize
1978              */
1979             CertFreeCertificateContext(context);
1980             return ERROR_SUCCESS;
1981         }
1982     }
1983     }
1984 
1985     return INET_QueryOption(option, buffer, size, unicode);
1986 }
1987 
1988 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1989 {
1990     http_request_t *req = (http_request_t*)hdr;
1991 
1992     switch(option) {
1993     case INTERNET_OPTION_SEND_TIMEOUT:
1994     case INTERNET_OPTION_RECEIVE_TIMEOUT:
1995         TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1996 
1997         if (size != sizeof(DWORD))
1998             return ERROR_INVALID_PARAMETER;
1999 
2000         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
2001                     *(DWORD*)buffer);
2002 
2003     case INTERNET_OPTION_USERNAME:
2004         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
2005         if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2006         return ERROR_SUCCESS;
2007 
2008     case INTERNET_OPTION_PASSWORD:
2009         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
2010         if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2011         return ERROR_SUCCESS;
2012     case INTERNET_OPTION_HTTP_DECODING:
2013         if(size != sizeof(BOOL))
2014             return ERROR_INVALID_PARAMETER;
2015         req->decoding = *(BOOL*)buffer;
2016         return ERROR_SUCCESS;
2017     }
2018 
2019     return ERROR_INTERNET_INVALID_OPTION;
2020 }
2021 
2022 /* read some more data into the read buffer (the read section must be held) */
2023 static BOOL read_more_data( http_request_t *req, int maxlen )
2024 {
2025     int len;
2026 
2027     if (req->read_pos)
2028     {
2029         /* move existing data to the start of the buffer */
2030         if(req->read_size)
2031             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2032         req->read_pos = 0;
2033     }
2034 
2035     if (maxlen == -1) maxlen = sizeof(req->read_buf);
2036 
2037     if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2038                      maxlen - req->read_size, 0, &len ))
2039         return FALSE;
2040 
2041     req->read_size += len;
2042     return TRUE;
2043 }
2044 
2045 /* remove some amount of data from the read buffer (the read section must be held) */
2046 static void remove_data( http_request_t *req, int count )
2047 {
2048     if (!(req->read_size -= count)) req->read_pos = 0;
2049     else req->read_pos += count;
2050 }
2051 
2052 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2053 {
2054     int count, bytes_read, pos = 0;
2055 
2056     EnterCriticalSection( &req->read_section );
2057     for (;;)
2058     {
2059         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2060 
2061         if (eol)
2062         {
2063             count = eol - (req->read_buf + req->read_pos);
2064             bytes_read = count + 1;
2065         }
2066         else count = bytes_read = req->read_size;
2067 
2068         count = min( count, *len - pos );
2069         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2070         pos += count;
2071         remove_data( req, bytes_read );
2072         if (eol) break;
2073 
2074         if (!read_more_data( req, -1 ) || !req->read_size)
2075         {
2076             *len = 0;
2077             TRACE( "returning empty string\n" );
2078             LeaveCriticalSection( &req->read_section );
2079             return FALSE;
2080         }
2081     }
2082     LeaveCriticalSection( &req->read_section );
2083 
2084     if (pos < *len)
2085     {
2086         if (pos && buffer[pos - 1] == '\r') pos--;
2087         *len = pos + 1;
2088     }
2089     buffer[*len - 1] = 0;
2090     TRACE( "returning %s\n", debugstr_a(buffer));
2091     return TRUE;
2092 }
2093 
2094 /* discard data contents until we reach end of line (the read section must be held) */
2095 static BOOL discard_eol( http_request_t *req )
2096 {
2097     do
2098     {
2099         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2100         if (eol)
2101         {
2102             remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2103             break;
2104         }
2105         req->read_pos = req->read_size = 0;  /* discard everything */
2106         if (!read_more_data( req, -1 )) return FALSE;
2107     } while (req->read_size);
2108     return TRUE;
2109 }
2110 
2111 /* read the size of the next chunk (the read section must be held) */
2112 static BOOL start_next_chunk( http_request_t *req )
2113 {
2114     DWORD chunk_size = 0;
2115 
2116     if (!req->dwContentLength) return TRUE;
2117     if (req->dwContentLength == req->dwContentRead)
2118     {
2119         /* read terminator for the previous chunk */
2120         if (!discard_eol( req )) return FALSE;
2121         req->dwContentLength = ~0u;
2122         req->dwContentRead = 0;
2123     }
2124     for (;;)
2125     {
2126         while (req->read_size)
2127         {
2128             char ch = req->read_buf[req->read_pos];
2129             if (ch >= '' && ch <= '9') chunk_size = chunk_size * 16 + ch - '';
2130             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2131             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2132             else if (ch == ';' || ch == '\r' || ch == '\n')
2133             {
2134                 TRACE( "reading %u byte chunk\n", chunk_size );
2135                 req->dwContentLength = chunk_size;
2136                 req->dwContentRead = 0;
2137                 if (!discard_eol( req )) return FALSE;
2138                 return TRUE;
2139             }
2140             remove_data( req, 1 );
2141         }
2142         if (!read_more_data( req, -1 )) return FALSE;
2143         if (!req->read_size)
2144         {
2145             req->dwContentLength = req->dwContentRead = 0;
2146             return TRUE;
2147         }
2148     }
2149 }
2150 
2151 /* check if we have reached the end of the data to read (the read section must be held) */
2152 static BOOL end_of_read_data( http_request_t *req )
2153 {
2154     if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2155     if (req->read_chunked) return (req->dwContentLength == 0);
2156     if (req->dwContentLength == ~0u) return FALSE;
2157     return (req->dwContentLength == req->dwContentRead);
2158 }
2159 
2160 /* fetch some more data into the read buffer (the read section must be held) */
2161 static BOOL refill_buffer( http_request_t *req )
2162 {
2163     int len = sizeof(req->read_buf);
2164 
2165     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2166     {
2167         if (!start_next_chunk( req )) return FALSE;
2168     }
2169 
2170     if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2171     if (len <= req->read_size) return TRUE;
2172 
2173     if (!read_more_data( req, len )) return FALSE;
2174     if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2175     return TRUE;
2176 }
2177 
2178 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2179 {
2180     DWORD ret = ERROR_SUCCESS;
2181     int read = 0;
2182 
2183 #ifdef HAVE_ZLIB
2184     z_stream *zstream = &req->gzip_stream->zstream;
2185     int zres;
2186 
2187     while(read < size && !req->gzip_stream->end_of_data) {
2188         if(!req->read_size) {
2189             if(!sync || !refill_buffer(req))
2190                 break;
2191         }
2192 
2193         zstream->next_in = req->read_buf+req->read_pos;
2194         zstream->avail_in = req->read_size;
2195         zstream->next_out = buf+read;
2196         zstream->avail_out = size-read;
2197         zres = inflate(zstream, Z_FULL_FLUSH);
2198         read = size - zstream->avail_out;
2199         remove_data(req, req->read_size-zstream->avail_in);
2200         if(zres == Z_STREAM_END) {
2201             TRACE("end of data\n");
2202             req->gzip_stream->end_of_data = TRUE;
2203             inflateEnd(&req->gzip_stream->zstream);
2204         }else if(zres != Z_OK) {
2205             WARN("inflate failed %d\n", zres);
2206             if(!read)
2207                 ret = ERROR_INTERNET_DECODING_FAILED;
2208             break;
2209         }
2210     }
2211 #endif
2212 
2213     *read_ret = read;
2214     return ret;
2215 }
2216 
2217 static void refill_gzip_buffer(http_request_t *req)
2218 {
2219     DWORD res;
2220     int len;
2221 
2222     if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2223         return;
2224 
2225     if(req->gzip_stream->buf_pos) {
2226         if(req->gzip_stream->buf_size)
2227             memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2228         req->gzip_stream->buf_pos = 0;
2229     }
2230 
2231     res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2232             sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2233     if(res == ERROR_SUCCESS)
2234         req->gzip_stream->buf_size += len;
2235 }
2236 
2237 /* return the size of data available to be read immediately (the read section must be held) */
2238 static DWORD get_avail_data( http_request_t *req )
2239 {
2240     if (req->gzip_stream) {
2241         refill_gzip_buffer(req);
2242         return req->gzip_stream->buf_size;
2243     }
2244     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2245         return 0;
2246     return min( req->read_size, req->dwContentLength - req->dwContentRead );
2247 }
2248 
2249 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2250 {
2251     INTERNET_ASYNC_RESULT iar;
2252 
2253     TRACE("%p\n", req);
2254 
2255     EnterCriticalSection( &req->read_section );
2256     if (refill_buffer( req )) {
2257         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2258         iar.dwError = first_notif ? 0 : get_avail_data(req);
2259     }else {
2260         iar.dwResult = 0;
2261         iar.dwError = INTERNET_GetLastError();
2262     }
2263     LeaveCriticalSection( &req->read_section );
2264 
2265     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2266                           sizeof(INTERNET_ASYNC_RESULT));
2267 }
2268 
2269 /* read data from the http connection (the read section must be held) */
2270 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2271 {
2272     BOOL finished_reading = FALSE;
2273     int len, bytes_read = 0;
2274     DWORD ret = ERROR_SUCCESS;
2275 
2276     EnterCriticalSection( &req->read_section );
2277 
2278     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2279     {
2280         if (!start_next_chunk( req )) goto done;
2281     }
2282 
2283     if(req->gzip_stream) {
2284         if(req->gzip_stream->buf_size) {
2285             bytes_read = min(req->gzip_stream->buf_size, size);
2286             memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2287             req->gzip_stream->buf_pos += bytes_read;
2288             req->gzip_stream->buf_size -= bytes_read;
2289         }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2290             refill_buffer(req);
2291         }
2292 
2293         if(size > bytes_read) {
2294             ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2295             if(ret == ERROR_SUCCESS)
2296                 bytes_read += len;
2297         }
2298 
2299         finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2300     }else {
2301         if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2302 
2303         if (req->read_size) {
2304             bytes_read = min( req->read_size, size );
2305             memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2306             remove_data( req, bytes_read );
2307         }
2308 
2309         if (size > bytes_read && (!bytes_read || sync)) {
2310             if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2311                              sync ? MSG_WAITALL : 0, &len))
2312                 bytes_read += len;
2313             /* always return success, even if the network layer returns an error */
2314         }
2315 
2316         finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2317     }
2318 done:
2319     req->dwContentRead += bytes_read;
2320     *read = bytes_read;
2321 
2322     TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2323     LeaveCriticalSection( &req->read_section );
2324 
2325     if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2326         BOOL res;
2327         DWORD dwBytesWritten;
2328 
2329         res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2330         if(!res)
2331             WARN("WriteFile failed: %u\n", GetLastError());
2332     }
2333 
2334     if(finished_reading)
2335         HTTP_FinishedReading(req);
2336 
2337     return ret;
2338 }
2339 
2340 
2341 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2342 {
2343     http_request_t *req = (http_request_t*)hdr;
2344     return HTTPREQ_Read(req, buffer, size, read, TRUE);
2345 }
2346 
2347 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2348 {
2349     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2350     http_request_t *req = (http_request_t*)workRequest->hdr;
2351     INTERNET_ASYNC_RESULT iar;
2352     DWORD res;
2353 
2354     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2355 
2356     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2357             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2358 
2359     iar.dwResult = res == ERROR_SUCCESS;
2360     iar.dwError = res;
2361 
2362     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2363                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2364                           sizeof(INTERNET_ASYNC_RESULT));
2365 }
2366 
2367 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2368         DWORD flags, DWORD_PTR context)
2369 {
2370     http_request_t *req = (http_request_t*)hdr;
2371     DWORD res;
2372 
2373     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2374         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2375 
2376     if (buffers->dwStructSize != sizeof(*buffers))
2377         return ERROR_INVALID_PARAMETER;
2378 
2379     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2380 
2381     if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2382     {
2383         WORKREQUEST workRequest;
2384 
2385         if (TryEnterCriticalSection( &req->read_section ))
2386         {
2387             if (get_avail_data(req))
2388             {
2389                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2390                                    &buffers->dwBufferLength, FALSE);
2391                 LeaveCriticalSection( &req->read_section );
2392                 goto done;
2393             }
2394             LeaveCriticalSection( &req->read_section );
2395         }
2396 
2397         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2398         workRequest.hdr = WININET_AddRef(&req->hdr);
2399         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2400 
2401         INTERNET_AsyncCall(&workRequest);
2402 
2403         return ERROR_IO_PENDING;
2404     }
2405 
2406     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2407             !(flags & IRF_NO_WAIT));
2408 
2409 done:
2410     if (res == ERROR_SUCCESS) {
2411         DWORD size = buffers->dwBufferLength;
2412         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2413                 &size, sizeof(size));
2414     }
2415 
2416     return res;
2417 }
2418 
2419 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2420 {
2421     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2422     http_request_t *req = (http_request_t*)workRequest->hdr;
2423     INTERNET_ASYNC_RESULT iar;
2424     DWORD res;
2425 
2426     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2427 
2428     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2429             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2430 
2431     iar.dwResult = res == ERROR_SUCCESS;
2432     iar.dwError = res;
2433 
2434     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2435                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2436                           sizeof(INTERNET_ASYNC_RESULT));
2437 }
2438 
2439 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2440         DWORD flags, DWORD_PTR context)
2441 {
2442 
2443     http_request_t *req = (http_request_t*)hdr;
2444     DWORD res;
2445 
2446     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2447         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2448 
2449     if (buffers->dwStructSize != sizeof(*buffers))
2450         return ERROR_INVALID_PARAMETER;
2451 
2452     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2453 
2454     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2455     {
2456         WORKREQUEST workRequest;
2457 
2458         if (TryEnterCriticalSection( &req->read_section ))
2459         {
2460             if (get_avail_data(req))
2461             {
2462                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2463                                    &buffers->dwBufferLength, FALSE);
2464                 LeaveCriticalSection( &req->read_section );
2465                 goto done;
2466             }
2467             LeaveCriticalSection( &req->read_section );
2468         }
2469 
2470         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2471         workRequest.hdr = WININET_AddRef(&req->hdr);
2472         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2473 
2474         INTERNET_AsyncCall(&workRequest);
2475 
2476         return ERROR_IO_PENDING;
2477     }
2478 
2479     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2480             !(flags & IRF_NO_WAIT));
2481 
2482 done:
2483     if (res == ERROR_SUCCESS) {
2484         DWORD size = buffers->dwBufferLength;
2485         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2486                 &size, sizeof(size));
2487     }
2488 
2489     return res;
2490 }
2491 
2492 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2493 {
2494     BOOL ret;
2495     http_request_t *lpwhr = (http_request_t*)hdr;
2496 
2497     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2498 
2499     *written = 0;
2500     if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2501         lpwhr->dwBytesWritten += *written;
2502 
2503     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2504     return ret;
2505 }
2506 
2507 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2508 {
2509     http_request_t *req = (http_request_t*)workRequest->hdr;
2510 
2511     HTTP_ReceiveRequestData(req, FALSE);
2512 }
2513 
2514 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2515 {
2516     http_request_t *req = (http_request_t*)hdr;
2517 
2518     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2519 
2520     if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2521     {
2522         WORKREQUEST workRequest;
2523 
2524         /* never wait, if we can't enter the section we queue an async request right away */
2525         if (TryEnterCriticalSection( &req->read_section ))
2526         {
2527             if ((*available = get_avail_data( req ))) goto done;
2528             if (end_of_read_data( req )) goto done;
2529             LeaveCriticalSection( &req->read_section );
2530         }
2531 
2532         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2533         workRequest.hdr = WININET_AddRef( &req->hdr );
2534 
2535         INTERNET_AsyncCall(&workRequest);
2536 
2537         return ERROR_IO_PENDING;
2538     }
2539 
2540     EnterCriticalSection( &req->read_section );
2541 
2542     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2543     {
2544         refill_buffer( req );
2545         *available = get_avail_data( req );
2546     }
2547 
2548 done:
2549     if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
2550     {
2551         DWORD extra;
2552         if (NETCON_query_data_available(&req->netConnection, &extra))
2553             *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2554     }
2555     LeaveCriticalSection( &req->read_section );
2556 
2557     TRACE( "returning %u\n", *available );
2558     return ERROR_SUCCESS;
2559 }
2560 
2561 static const object_vtbl_t HTTPREQVtbl = {
2562     HTTPREQ_Destroy,
2563     HTTPREQ_CloseConnection,
2564     HTTPREQ_QueryOption,
2565     HTTPREQ_SetOption,
2566     HTTPREQ_ReadFile,
2567     HTTPREQ_ReadFileExA,
2568     HTTPREQ_ReadFileExW,
2569     HTTPREQ_WriteFile,
2570     HTTPREQ_QueryDataAvailable,
2571     NULL
2572 };
2573 
2574 /***********************************************************************
2575  *           HTTP_HttpOpenRequestW (internal)
2576  *
2577  * Open a HTTP request handle
2578  *
2579  * RETURNS
2580  *    HINTERNET  a HTTP request handle on success
2581  *    NULL       on failure
2582  *
2583  */
2584 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2585         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2586         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2587         DWORD dwFlags, DWORD_PTR dwContext)
2588 {
2589     appinfo_t *hIC = NULL;
2590     http_request_t *lpwhr;
2591     LPWSTR lpszHostName = NULL;
2592     HINTERNET handle = NULL;
2593     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2594     DWORD len;
2595 
2596     TRACE("-->\n");
2597 
2598     assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2599     hIC = lpwhs->lpAppInfo;
2600 
2601     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2602     if (NULL == lpwhr)
2603     {
2604         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2605         goto lend;
2606     }
2607     lpwhr->hdr.htype = WH_HHTTPREQ;
2608     lpwhr->hdr.vtbl = &HTTPREQVtbl;
2609     lpwhr->hdr.dwFlags = dwFlags;
2610     lpwhr->hdr.dwContext = dwContext;
2611     lpwhr->hdr.refs = 1;
2612     lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2613     lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2614     lpwhr->dwContentLength = ~0u;
2615     InitializeCriticalSection( &lpwhr->read_section );
2616 
2617     WININET_AddRef( &lpwhs->hdr );
2618     lpwhr->lpHttpSession = lpwhs;
2619     list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2620 
2621     lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2622             (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2623     if (NULL == lpszHostName)
2624     {
2625         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2626         goto lend;
2627     }
2628 
2629     handle = WININET_AllocHandle( &lpwhr->hdr );
2630     if (NULL == handle)
2631     {
2632         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2633         goto lend;
2634     }
2635 
2636     if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2637     {
2638         InternetCloseHandle( handle );
2639         handle = NULL;
2640         goto lend;
2641     }
2642 
2643     if (lpszObjectName && *lpszObjectName) {
2644         HRESULT rc;
2645 
2646         len = 0;
2647         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2648         if (rc != E_POINTER)
2649             len = strlenW(lpszObjectName)+1;
2650         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2651         rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2652                    URL_ESCAPE_SPACES_ONLY);
2653         if (rc != S_OK)
2654         {
2655             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2656             strcpyW(lpwhr->lpszPath,lpszObjectName);
2657         }
2658     }else {
2659         static const WCHAR slashW[] = {'/',0};
2660 
2661         lpwhr->lpszPath = heap_strdupW(slashW);
2662     }
2663 
2664     if (lpszReferrer && *lpszReferrer)
2665         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2666 
2667     if (lpszAcceptTypes)
2668     {
2669         int i;
2670         for (i = 0; lpszAcceptTypes[i]; i++)
2671         {
2672             if (!*lpszAcceptTypes[i]) continue;
2673             HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2674                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2675                                HTTP_ADDHDR_FLAG_REQ |
2676                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2677         }
2678     }
2679 
2680     lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2681     lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2682 
2683     if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2684         lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2685         lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2686     {
2687         sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2688         HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2689                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2690     }
2691     else
2692         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2693                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2694 
2695     if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2696         lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2697                         INTERNET_DEFAULT_HTTPS_PORT :
2698                         INTERNET_DEFAULT_HTTP_PORT);
2699 
2700     if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2701         lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2702                         INTERNET_DEFAULT_HTTPS_PORT :
2703                         INTERNET_DEFAULT_HTTP_PORT);
2704 
2705     if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2706         HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2707 
2708     INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2709                           INTERNET_STATUS_HANDLE_CREATED, &handle,
2710                           sizeof(handle));
2711 
2712 lend:
2713     HeapFree(GetProcessHeap(), 0, lpszHostName);
2714     if( lpwhr )
2715         WININET_Release( &lpwhr->hdr );
2716 
2717     TRACE("<-- %p (%p)\n", handle, lpwhr);
2718     return handle;
2719 }
2720 
2721 /* read any content returned by the server so that the connection can be
2722  * reused */
2723 static void HTTP_DrainContent(http_request_t *req)
2724 {
2725     DWORD bytes_read;
2726 
2727     if (!NETCON_connected(&req->netConnection)) return;
2728 
2729     if (req->dwContentLength == -1)
2730     {
2731         NETCON_close(&req->netConnection);
2732         return;
2733     }
2734     if (!strcmpW(req->lpszVerb, szHEAD)) return;
2735 
2736     do
2737     {
2738         char buffer[2048];
2739         if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2740             return;
2741     } while (bytes_read);
2742 }
2743 
2744 static const LPCWSTR header_lookup[] = {
2745     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
2746     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
2747     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2748     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
2749     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2750     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
2751     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
2752     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
2753     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
2754     szDate,                     /* HTTP_QUERY_DATE = 9 */
2755     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
2756     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
2757     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
2758     szURI,                      /* HTTP_QUERY_URI = 13 */
2759     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
2760     NULL,                       /* HTTP_QUERY_COST = 15 */
2761     NULL,                       /* HTTP_QUERY_LINK = 16 */
2762     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
2763     NULL,                       /* HTTP_QUERY_VERSION = 18 */
2764     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
2765     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
2766     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
2767     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2768     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
2769     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
2770     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2771     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2772     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2773     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
2774     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2775     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
2776     NULL,                       /* HTTP_QUERY_FROM = 31 */
2777     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2778     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
2779     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
2780     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
2781     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
2782     szServer,                   /* HTTP_QUERY_SERVER = 37 */
2783     NULL,                       /* HTTP_TITLE = 38 */
2784     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
2785     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2786     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2787     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2788     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
2789     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
2790     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
2791     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
2792     NULL,                       /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2793     szAge,                      /* HTTP_QUERY_AGE = 48 */
2794     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
2795     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
2796     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2797     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
2798     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
2799     szETag,                     /* HTTP_QUERY_ETAG = 54 */
2800     hostW,                      /* HTTP_QUERY_HOST = 55 */
2801     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
2802     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2803     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
2804     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2805     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
2806     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2807     szRange,                    /* HTTP_QUERY_RANGE = 62 */
2808     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2809     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
2810     szVary,                     /* HTTP_QUERY_VARY = 65 */
2811     szVia,                      /* HTTP_QUERY_VIA = 66 */
2812     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
2813     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
2814     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2815     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2816 };
2817 
2818 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2819 
2820 /***********************************************************************
2821  *           HTTP_HttpQueryInfoW (internal)
2822  */
2823 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2824         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2825 {
2826     LPHTTPHEADERW lphttpHdr = NULL;
2827     BOOL bSuccess = FALSE;
2828     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2829     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2830     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2831     INT index = -1;
2832 
2833     /* Find requested header structure */
2834     switch (level)
2835     {
2836     case HTTP_QUERY_CUSTOM:
2837         if (!lpBuffer) return FALSE;
2838         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2839         break;
2840     case HTTP_QUERY_RAW_HEADERS_CRLF:
2841         {
2842             LPWSTR headers;
2843             DWORD len = 0;
2844             BOOL ret = FALSE;
2845 
2846             if (request_only)
2847                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2848             else
2849                 headers = lpwhr->lpszRawHeaders;
2850 
2851             if (headers)
2852                 len = strlenW(headers) * sizeof(WCHAR);
2853 
2854             if (len + sizeof(WCHAR) > *lpdwBufferLength)
2855             {
2856                 len += sizeof(WCHAR);
2857                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2858                 ret = FALSE;
2859             }
2860             else if (lpBuffer)
2861             {
2862                 if (headers)
2863                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2864                 else
2865                 {
2866                     len = strlenW(szCrLf) * sizeof(WCHAR);
2867                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2868                 }
2869                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2870                 ret = TRUE;
2871             }
2872             *lpdwBufferLength = len;
2873 
2874             if (request_only)
2875                 HeapFree(GetProcessHeap(), 0, headers);
2876             return ret;
2877         }
2878     case HTTP_QUERY_RAW_HEADERS:
2879         {
2880             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2881             DWORD i, size = 0;
2882             LPWSTR pszString = lpBuffer;
2883 
2884             for (i = 0; ppszRawHeaderLines[i]; i++)
2885                 size += strlenW(ppszRawHeaderLines[i]) + 1;
2886 
2887             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2888             {
2889                 HTTP_FreeTokens(ppszRawHeaderLines);
2890                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2891                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2892                 return FALSE;
2893             }
2894             if (pszString)
2895             {
2896                 for (i = 0; ppszRawHeaderLines[i]; i++)
2897                 {
2898                     DWORD len = strlenW(ppszRawHeaderLines[i]);
2899                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2900                     pszString += len+1;
2901                 }
2902                 *pszString = '\0';
2903                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2904             }
2905             *lpdwBufferLength = size * sizeof(WCHAR);
2906             HTTP_FreeTokens(ppszRawHeaderLines);
2907 
2908             return TRUE;
2909         }
2910     case HTTP_QUERY_STATUS_TEXT:
2911         if (lpwhr->lpszStatusText)
2912         {
2913             DWORD len = strlenW(lpwhr->lpszStatusText);
2914             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2915             {
2916                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2917                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2918                 return FALSE;
2919             }
2920             if (lpBuffer)
2921             {
2922                 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2923                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2924             }
2925             *lpdwBufferLength = len * sizeof(WCHAR);
2926             return TRUE;
2927         }
2928         break;
2929     case HTTP_QUERY_VERSION:
2930         if (lpwhr->lpszVersion)
2931         {
2932             DWORD len = strlenW(lpwhr->lpszVersion);
2933             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2934             {
2935                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2936                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2937                 return FALSE;
2938             }
2939             if (lpBuffer)
2940             {
2941                 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2942                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2943             }
2944             *lpdwBufferLength = len * sizeof(WCHAR);
2945             return TRUE;
2946         }
2947         break;
2948     case HTTP_QUERY_CONTENT_ENCODING:
2949         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2950                 requested_index,request_only);
2951         break;
2952     default:
2953         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2954 
2955         if (level < LAST_TABLE_HEADER && header_lookup[level])
2956             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2957                                               requested_index,request_only);
2958     }
2959 
2960     if (index >= 0)
2961         lphttpHdr = &lpwhr->pCustHeaders[index];
2962 
2963     /* Ensure header satisfies requested attributes */
2964     if (!lphttpHdr ||
2965         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2966          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2967     {
2968         INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2969         return bSuccess;
2970     }
2971 
2972     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2973 
2974     /* coalesce value to requested type */
2975     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2976     {
2977         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2978         TRACE(" returning number: %d\n", *(int *)lpBuffer);
2979         bSuccess = TRUE;
2980     }
2981     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2982     {
2983         time_t tmpTime;
2984         struct tm tmpTM;
2985         SYSTEMTIME *STHook;
2986 
2987         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2988 
2989         tmpTM = *gmtime(&tmpTime);
2990         STHook = (SYSTEMTIME *)lpBuffer;
2991         STHook->wDay = tmpTM.tm_mday;
2992         STHook->wHour = tmpTM.tm_hour;
2993         STHook->wMilliseconds = 0;
2994         STHook->wMinute = tmpTM.tm_min;
2995         STHook->wDayOfWeek = tmpTM.tm_wday;
2996         STHook->wMonth = tmpTM.tm_mon + 1;
2997         STHook->wSecond = tmpTM.tm_sec;
2998         STHook->wYear = tmpTM.tm_year;
2999         bSuccess = TRUE;
3000         
3001         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3002               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3003               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3004     }
3005     else if (lphttpHdr->lpszValue)
3006     {
3007         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3008 
3009         if (len > *lpdwBufferLength)
3010         {
3011             *lpdwBufferLength = len;
3012             INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
3013             return bSuccess;
3014         }
3015         if (lpBuffer)
3016         {
3017             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3018             TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
3019         }
3020         *lpdwBufferLength = len - sizeof(WCHAR);
3021         bSuccess = TRUE;
3022     }
3023     return bSuccess;
3024 }
3025 
3026 /***********************************************************************
3027  *           HttpQueryInfoW (WININET.@)
3028  *
3029  * Queries for information about an HTTP request
3030  *
3031  * RETURNS
3032  *    TRUE  on success
3033  *    FALSE on failure
3034  *
3035  */
3036 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3037         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3038 {
3039     BOOL bSuccess = FALSE;
3040     http_request_t *lpwhr;
3041 
3042     if (TRACE_ON(wininet)) {
3043 #define FE(x) { x, #x }
3044         static const wininet_flag_info query_flags[] = {
3045             FE(HTTP_QUERY_MIME_VERSION),
3046             FE(HTTP_QUERY_CONTENT_TYPE),
3047             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3048             FE(HTTP_QUERY_CONTENT_ID),
3049             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3050             FE(HTTP_QUERY_CONTENT_LENGTH),
3051             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3052             FE(HTTP_QUERY_ALLOW),
3053             FE(HTTP_QUERY_PUBLIC),
3054             FE(HTTP_QUERY_DATE),
3055             FE(HTTP_QUERY_EXPIRES),
3056             FE(HTTP_QUERY_LAST_MODIFIED),
3057             FE(HTTP_QUERY_MESSAGE_ID),
3058             FE(HTTP_QUERY_URI),
3059             FE(HTTP_QUERY_DERIVED_FROM),
3060             FE(HTTP_QUERY_COST),
3061             FE(HTTP_QUERY_LINK),
3062             FE(HTTP_QUERY_PRAGMA),
3063             FE(HTTP_QUERY_VERSION),
3064             FE(HTTP_QUERY_STATUS_CODE),
3065             FE(HTTP_QUERY_STATUS_TEXT),
3066             FE(HTTP_QUERY_RAW_HEADERS),
3067             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3068             FE(HTTP_QUERY_CONNECTION),
3069             FE(HTTP_QUERY_ACCEPT),
3070             FE(HTTP_QUERY_ACCEPT_CHARSET),
3071             FE(HTTP_QUERY_ACCEPT_ENCODING),
3072             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3073             FE(HTTP_QUERY_AUTHORIZATION),
3074             FE(HTTP_QUERY_CONTENT_ENCODING),
3075             FE(HTTP_QUERY_FORWARDED),
3076             FE(HTTP_QUERY_FROM),
3077             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3078             FE(HTTP_QUERY_LOCATION),
3079             FE(HTTP_QUERY_ORIG_URI),
3080             FE(HTTP_QUERY_REFERER),
3081             FE(HTTP_QUERY_RETRY_AFTER),
3082             FE(HTTP_QUERY_SERVER),
3083             FE(HTTP_QUERY_TITLE),
3084             FE(HTTP_QUERY_USER_AGENT),
3085             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3086             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3087             FE(HTTP_QUERY_ACCEPT_RANGES),
3088         FE(HTTP_QUERY_SET_COOKIE),
3089         FE(HTTP_QUERY_COOKIE),
3090             FE(HTTP_QUERY_REQUEST_METHOD),
3091             FE(HTTP_QUERY_REFRESH),
3092             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3093             FE(HTTP_QUERY_AGE),
3094             FE(HTTP_QUERY_CACHE_CONTROL),
3095             FE(HTTP_QUERY_CONTENT_BASE),
3096             FE(HTTP_QUERY_CONTENT_LOCATION),
3097             FE(HTTP_QUERY_CONTENT_MD5),
3098             FE(HTTP_QUERY_CONTENT_RANGE),
3099             FE(HTTP_QUERY_ETAG),
3100             FE(HTTP_QUERY_HOST),
3101             FE(HTTP_QUERY_IF_MATCH),
3102             FE(HTTP_QUERY_IF_NONE_MATCH),
3103             FE(HTTP_QUERY_IF_RANGE),
3104             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3105             FE(HTTP_QUERY_MAX_FORWARDS),
3106             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3107             FE(HTTP_QUERY_RANGE),
3108             FE(HTTP_QUERY_TRANSFER_ENCODING),
3109             FE(HTTP_QUERY_UPGRADE),
3110             FE(HTTP_QUERY_VARY),
3111             FE(HTTP_QUERY_VIA),
3112             FE(HTTP_QUERY_WARNING),
3113             FE(HTTP_QUERY_CUSTOM)
3114         };
3115         static const wininet_flag_info modifier_flags[] = {
3116             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3117             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3118             FE(HTTP_QUERY_FLAG_NUMBER),
3119             FE(HTTP_QUERY_FLAG_COALESCE)
3120         };
3121 #undef FE
3122         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3123         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3124         DWORD i;
3125 
3126         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
3127         TRACE("  Attribute:");
3128         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3129             if (query_flags[i].val == info) {
3130                 TRACE(" %s", query_flags[i].name);
3131                 break;
3132             }
3133         }
3134         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3135             TRACE(" Unknown (%08x)", info);
3136         }
3137 
3138         TRACE(" Modifier:");
3139         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3140             if (modifier_flags[i].val & info_mod) {
3141                 TRACE(" %s", modifier_flags[i].name);
3142                 info_mod &= ~ modifier_flags[i].val;
3143             }
3144         }
3145         
3146         if (info_mod) {
3147             TRACE(" Unknown (%08x)", info_mod);
3148         }
3149         TRACE("\n");
3150     }
3151     
3152     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3153     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
3154     {
3155         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3156         goto lend;
3157     }
3158 
3159     if (lpBuffer == NULL)
3160         *lpdwBufferLength = 0;
3161     bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3162                                     lpBuffer, lpdwBufferLength, lpdwIndex);
3163 
3164 lend:
3165     if( lpwhr )
3166          WININET_Release( &lpwhr->hdr );
3167 
3168     TRACE("%d <--\n", bSuccess);
3169     return bSuccess;
3170 }
3171 
3172 /***********************************************************************
3173  *           HttpQueryInfoA (WININET.@)
3174  *
3175  * Queries for information about an HTTP request
3176  *
3177  * RETURNS
3178  *    TRUE  on success
3179  *    FALSE on failure
3180  *
3181  */
3182 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3183         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3184 {
3185     BOOL result;
3186     DWORD len;
3187     WCHAR* bufferW;
3188 
3189     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3190        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3191     {
3192         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3193                                lpdwBufferLength, lpdwIndex );
3194     }
3195 
3196     if (lpBuffer)
3197     {
3198         DWORD alloclen;
3199         len = (*lpdwBufferLength)*sizeof(WCHAR);
3200         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3201         {
3202             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3203             if (alloclen < len)
3204                 alloclen = len;
3205         }
3206         else
3207             alloclen = len;
3208         bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3209         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3210         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3211             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3212     } else
3213     {
3214         bufferW = NULL;
3215         len = 0;
3216     }
3217 
3218     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3219                            &len, lpdwIndex );
3220     if( result )
3221     {
3222         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3223                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3224         *lpdwBufferLength = len - 1;
3225 
3226         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3227     }
3228     else
3229         /* since the strings being returned from HttpQueryInfoW should be
3230          * only ASCII characters, it is reasonable to assume that all of
3231          * the Unicode characters can be reduced to a single byte */
3232         *lpdwBufferLength = len / sizeof(WCHAR);
3233 
3234     HeapFree(GetProcessHeap(), 0, bufferW );
3235 
3236     return result;
3237 }
3238 
3239 /***********************************************************************
3240  *           HttpSendRequestExA (WININET.@)
3241  *
3242  * Sends the specified request to the HTTP server and allows chunked
3243  * transfers.
3244  *
3245  * RETURNS
3246  *  Success: TRUE
3247  *  Failure: FALSE, call GetLastError() for more information.
3248  */
3249 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3250                                LPINTERNET_BUFFERSA lpBuffersIn,
3251                                LPINTERNET_BUFFERSA lpBuffersOut,
3252                                DWORD dwFlags, DWORD_PTR dwContext)
3253 {
3254     INTERNET_BUFFERSW BuffersInW;
3255     BOOL rc = FALSE;
3256     DWORD headerlen;
3257     LPWSTR header = NULL;
3258 
3259     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3260             lpBuffersOut, dwFlags, dwContext);
3261 
3262     if (lpBuffersIn)
3263     {
3264         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3265         if (lpBuffersIn->lpcszHeader)
3266         {
3267             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3268                     lpBuffersIn->dwHeadersLength,0,0);
3269             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3270             if (!(BuffersInW.lpcszHeader = header))
3271             {
3272                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3273                 return FALSE;
3274             }
3275             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3276                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3277                     header, headerlen);
3278         }
3279         else
3280             BuffersInW.lpcszHeader = NULL;
3281         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3282         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3283         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3284         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3285         BuffersInW.Next = NULL;
3286     }
3287 
3288     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3289 
3290     HeapFree(GetProcessHeap(),0,header);
3291 
3292     return rc;
3293 }
3294 
3295 /***********************************************************************
3296  *           HttpSendRequestExW (WININET.@)
3297  *
3298  * Sends the specified request to the HTTP server and allows chunked
3299  * transfers
3300  *
3301  * RETURNS
3302  *  Success: TRUE
3303  *  Failure: FALSE, call GetLastError() for more information.
3304  */
3305 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3306                    LPINTERNET_BUFFERSW lpBuffersIn,
3307                    LPINTERNET_BUFFERSW lpBuffersOut,
3308                    DWORD dwFlags, DWORD_PTR dwContext)
3309 {
3310     BOOL ret = FALSE;
3311     http_request_t *lpwhr;
3312     http_session_t *lpwhs;
3313     appinfo_t *hIC;
3314 
3315     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3316             lpBuffersOut, dwFlags, dwContext);
3317 
3318     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3319 
3320     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3321     {
3322         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3323         goto lend;
3324     }
3325 
3326     lpwhs = lpwhr->lpHttpSession;
3327     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3328     hIC = lpwhs->lpAppInfo;
3329     assert(hIC->hdr.htype == WH_HINIT);
3330 
3331     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3332     {
3333         WORKREQUEST workRequest;
3334         struct WORKREQ_HTTPSENDREQUESTW *req;
3335 
3336         workRequest.asyncproc = AsyncHttpSendRequestProc;
3337         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3338         req = &workRequest.u.HttpSendRequestW;
3339         if (lpBuffersIn)
3340         {
3341             DWORD size;
3342 
3343             if (lpBuffersIn->dwHeadersLength == ~0u)
3344                 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3345             else
3346                 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3347 
3348             req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3349             memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3350 
3351             req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3352             req->lpOptional = lpBuffersIn->lpvBuffer;
3353             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3354             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3355         }
3356         else
3357         {
3358             req->lpszHeader = NULL;
3359             req->dwHeaderLength = 0;
3360             req->lpOptional = NULL;
3361             req->dwOptionalLength = 0;
3362             req->dwContentLength = 0;
3363         }
3364 
3365         req->bEndRequest = FALSE;
3366 
3367         INTERNET_AsyncCall(&workRequest);
3368         /*
3369          * This is from windows.
3370          */
3371         INTERNET_SetLastError(ERROR_IO_PENDING);
3372     }
3373     else
3374     {
3375         if (lpBuffersIn)
3376             ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3377                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3378                                         lpBuffersIn->dwBufferTotal, FALSE);
3379         else
3380             ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3381     }
3382 
3383 lend:
3384     if ( lpwhr )
3385         WININET_Release( &lpwhr->hdr );
3386 
3387     TRACE("<---\n");
3388     return ret;
3389 }
3390 
3391 /***********************************************************************
3392  *           HttpSendRequestW (WININET.@)
3393  *
3394  * Sends the specified request to the HTTP server
3395  *
3396  * RETURNS
3397  *    TRUE  on success
3398  *    FALSE on failure
3399  *
3400  */
3401 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3402         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3403 {
3404     http_request_t *lpwhr;
3405     http_session_t *lpwhs = NULL;
3406     appinfo_t *hIC = NULL;
3407     BOOL r;
3408 
3409     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3410             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3411 
3412     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3413     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3414     {
3415         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3416         r = FALSE;
3417         goto lend;
3418     }
3419 
3420     lpwhs = lpwhr->lpHttpSession;
3421     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
3422     {
3423         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3424         r = FALSE;
3425         goto lend;
3426     }
3427 
3428     hIC = lpwhs->lpAppInfo;
3429     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
3430     {
3431         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3432         r = FALSE;
3433         goto lend;
3434     }
3435 
3436     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3437     {
3438         WORKREQUEST workRequest;
3439         struct WORKREQ_HTTPSENDREQUESTW *req;
3440 
3441         workRequest.asyncproc = AsyncHttpSendRequestProc;
3442         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3443         req = &workRequest.u.HttpSendRequestW;
3444         if (lpszHeaders)
3445         {
3446             DWORD size;
3447 
3448             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3449             else size = dwHeaderLength * sizeof(WCHAR);
3450 
3451             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3452             memcpy(req->lpszHeader, lpszHeaders, size);
3453         }
3454         else
3455             req->lpszHeader = 0;
3456         req->dwHeaderLength = dwHeaderLength;
3457         req->lpOptional = lpOptional;
3458         req->dwOptionalLength = dwOptionalLength;
3459         req->dwContentLength = dwOptionalLength;
3460         req->bEndRequest = TRUE;
3461 
3462         INTERNET_AsyncCall(&workRequest);
3463         /*
3464          * This is from windows.
3465          */
3466         INTERNET_SetLastError(ERROR_IO_PENDING);
3467         r = FALSE;
3468     }
3469     else
3470     {
3471         r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3472                 dwHeaderLength, lpOptional, dwOptionalLength,
3473                 dwOptionalLength, TRUE);
3474     }
3475 lend:
3476     if( lpwhr )
3477         WININET_Release( &lpwhr->hdr );
3478     return r;
3479 }
3480 
3481 /***********************************************************************
3482  *           HttpSendRequestA (WININET.@)
3483  *
3484  * Sends the specified request to the HTTP server
3485  *
3486  * RETURNS
3487  *    TRUE  on success
3488  *    FALSE on failure
3489  *
3490  */
3491 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3492         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3493 {
3494     BOOL result;
3495     LPWSTR szHeaders=NULL;
3496     DWORD nLen=dwHeaderLength;
3497     if(lpszHeaders!=NULL)
3498     {
3499         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3500         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3501         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3502     }
3503     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3504     HeapFree(GetProcessHeap(),0,szHeaders);
3505     return result;
3506 }
3507 
3508 /***********************************************************************
3509  *           HTTP_GetRedirectURL (internal)
3510  */
3511 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3512 {
3513     static WCHAR szHttp[] = {'h','t','t','p',0};
3514     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3515     http_session_t *lpwhs = lpwhr->lpHttpSession;
3516     URL_COMPONENTSW urlComponents;
3517     DWORD url_length = 0;
3518     LPWSTR orig_url;
3519     LPWSTR combined_url;
3520 
3521     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3522     urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3523     urlComponents.dwSchemeLength = 0;
3524     urlComponents.lpszHostName = lpwhs->lpszHostName;
3525     urlComponents.dwHostNameLength = 0;
3526     urlComponents.nPort = lpwhs->nHostPort;
3527     urlComponents.lpszUserName = lpwhs->lpszUserName;
3528     urlComponents.dwUserNameLength = 0;
3529     urlComponents.lpszPassword = NULL;
3530     urlComponents.dwPasswordLength = 0;
3531     urlComponents.lpszUrlPath = lpwhr->lpszPath;
3532     urlComponents.dwUrlPathLength = 0;
3533     urlComponents.lpszExtraInfo = NULL;
3534     urlComponents.dwExtraInfoLength = 0;
3535 
3536     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3537         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3538         return NULL;
3539 
3540     orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3541 
3542     /* convert from bytes to characters */
3543     url_length = url_length / sizeof(WCHAR) - 1;
3544     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3545     {
3546         HeapFree(GetProcessHeap(), 0, orig_url);
3547         return NULL;
3548     }
3549 
3550     url_length = 0;
3551     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3552         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3553     {
3554         HeapFree(GetProcessHeap(), 0, orig_url);
3555         return NULL;
3556     }
3557     combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3558 
3559     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3560     {
3561         HeapFree(GetProcessHeap(), 0, orig_url);
3562         HeapFree(GetProcessHeap(), 0, combined_url);
3563         return NULL;
3564     }
3565     HeapFree(GetProcessHeap(), 0, orig_url);
3566     return combined_url;
3567 }
3568 
3569 
3570 /***********************************************************************
3571  *           HTTP_HandleRedirect (internal)
3572  */
3573 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3574 {
3575     http_session_t *lpwhs = lpwhr->lpHttpSession;
3576     appinfo_t *hIC = lpwhs->lpAppInfo;
3577     BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3578     WCHAR path[INTERNET_MAX_URL_LENGTH];
3579     int index;
3580 
3581     if(lpszUrl[0]=='/')
3582     {
3583         /* if it's an absolute path, keep the same session info */
3584         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3585     }
3586     else
3587     {
3588         URL_COMPONENTSW urlComponents;
3589         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3590         static WCHAR szHttp[] = {'h','t','t','p',0};
3591         static WCHAR szHttps[] = {'h','t','t','p','s',0};
3592 
3593         userName[0] = 0;
3594         hostName[0] = 0;
3595         protocol[0] = 0;
3596 
3597         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3598         urlComponents.lpszScheme = protocol;
3599         urlComponents.dwSchemeLength = 32;
3600         urlComponents.lpszHostName = hostName;
3601         urlComponents.dwHostNameLength = MAXHOSTNAME;
3602         urlComponents.lpszUserName = userName;
3603         urlComponents.dwUserNameLength = 1024;
3604         urlComponents.lpszPassword = NULL;
3605         urlComponents.dwPasswordLength = 0;
3606         urlComponents.lpszUrlPath = path;
3607         urlComponents.dwUrlPathLength = 2048;
3608         urlComponents.lpszExtraInfo = NULL;
3609         urlComponents.dwExtraInfoLength = 0;
3610         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3611             return FALSE;
3612 
3613         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3614             (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3615         {
3616             TRACE("redirect from secure page to non-secure page\n");
3617             /* FIXME: warn about from secure redirect to non-secure page */
3618             lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3619         }
3620         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3621             !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3622         {
3623             TRACE("redirect from non-secure page to secure page\n");
3624             /* FIXME: notify about redirect to secure page */
3625             lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3626         }
3627 
3628         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3629         {
3630             if (lstrlenW(protocol)>4) /*https*/
3631                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3632             else /*http*/
3633                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3634         }
3635 
3636 #if 0
3637         /*
3638          * This upsets redirects to binary files on sourceforge.net 
3639          * and gives an html page instead of the target file
3640          * Examination of the HTTP request sent by native wininet.dll
3641          * reveals that it doesn't send a referrer in that case.
3642          * Maybe there's a flag that enables this, or maybe a referrer
3643          * shouldn't be added in case of a redirect.
3644          */
3645 
3646         /* consider the current host as the referrer */
3647         if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3648             HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3649                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3650                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3651 #endif
3652         
3653         HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3654         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3655             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3656         {
3657             int len;
3658             static const WCHAR fmt[] = {'%','s',':','%','i',0};
3659             len = lstrlenW(hostName);
3660             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3661             lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3662             sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3663         }
3664         else
3665             lpwhs->lpszHostName = heap_strdupW(hostName);
3666 
3667         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3668 
3669         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3670         lpwhs->lpszUserName = NULL;
3671         if (userName[0])
3672             lpwhs->lpszUserName = heap_strdupW(userName);
3673 
3674         if (!using_proxy)
3675         {
3676             if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3677             {
3678                 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3679                 lpwhs->lpszServerName = heap_strdupW(hostName);
3680                 lpwhs->nServerPort = urlComponents.nPort;
3681 
3682                 NETCON_close(&lpwhr->netConnection);
3683                 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3684                 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3685                 lpwhr->read_pos = lpwhr->read_size = 0;
3686                 lpwhr->read_chunked = FALSE;
3687             }
3688         }
3689         else
3690             TRACE("Redirect through proxy\n");
3691     }
3692 
3693     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3694     lpwhr->lpszPath=NULL;
3695     if (*path)
3696     {
3697         DWORD needed = 0;
3698         HRESULT rc;
3699 
3700         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3701         if (rc != E_POINTER)
3702             needed = strlenW(path)+1;
3703         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3704         rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3705                         URL_ESCAPE_SPACES_ONLY);
3706         if (rc != S_OK)
3707         {
3708             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3709             strcpyW(lpwhr->lpszPath,path);
3710         }
3711     }
3712 
3713     /* Remove custom content-type/length headers on redirects.  */
3714     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3715     if (0 <= index)
3716         HTTP_DeleteCustomHeader(lpwhr, index);
3717     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3718     if (0 <= index)
3719         HTTP_DeleteCustomHeader(lpwhr, index);
3720 
3721     return TRUE;
3722 }
3723 
3724 /***********************************************************************
3725  *           HTTP_build_req (internal)
3726  *
3727  *  concatenate all the strings in the request together
3728  */
3729 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3730 {
3731     LPCWSTR *t;
3732     LPWSTR str;
3733 
3734     for( t = list; *t ; t++  )
3735         len += strlenW( *t );
3736     len++;
3737 
3738     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3739     *str = 0;
3740 
3741     for( t = list; *t ; t++ )
3742         strcatW( str, *t );
3743 
3744     return str;
3745 }
3746 
3747 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3748 {
3749     LPWSTR lpszPath;
3750     LPWSTR requestString;
3751     INT len;
3752     INT cnt;
3753     INT responseLen;
3754     char *ascii_req;
3755     BOOL ret;
3756     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3757     static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3758     http_session_t *lpwhs = lpwhr->lpHttpSession;
3759 
3760     TRACE("\n");
3761 
3762     lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3763     sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3764     requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3765     HeapFree( GetProcessHeap(), 0, lpszPath );
3766 
3767     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3768                                 NULL, 0, NULL, NULL );
3769     len--; /* the nul terminator isn't needed */
3770     ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3771     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3772                             ascii_req, len, NULL, NULL );
3773     HeapFree( GetProcessHeap(), 0, requestString );
3774 
3775     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3776 
3777     ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3778     HeapFree( GetProcessHeap(), 0, ascii_req );
3779     if (!ret || cnt < 0)
3780         return FALSE;
3781 
3782     responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3783     if (!responseLen)
3784         return FALSE;
3785 
3786     return TRUE;
3787 }
3788 
3789 static void HTTP_InsertCookies(http_request_t *lpwhr)
3790 {
3791     static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3792     LPWSTR lpszCookies, lpszUrl = NULL;
3793     DWORD nCookieSize, size;
3794     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3795 
3796     size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3797     if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3798     sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3799 
3800     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3801     {
3802         int cnt = 0;
3803         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3804 
3805         size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3806         if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3807         {
3808             cnt += sprintfW(lpszCookies, szCookie);
3809             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3810             strcatW(lpszCookies, szCrLf);
3811 
3812             HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3813             HeapFree(GetProcessHeap(), 0, lpszCookies);
3814         }
3815     }
3816     HeapFree(GetProcessHeap(), 0, lpszUrl);
3817 }
3818 
3819 /***********************************************************************
3820  *           HTTP_HttpSendRequestW (internal)
3821  *
3822  * Sends the specified request to the HTTP server
3823  *
3824  * RETURNS
3825  *    TRUE  on success
3826  *    FALSE on failure
3827  *
3828  */
3829 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3830         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3831         DWORD dwContentLength, BOOL bEndRequest)
3832 {
3833     INT cnt;
3834     BOOL bSuccess = FALSE, redirected = FALSE;
3835     LPWSTR requestString = NULL;
3836     INT responseLen;
3837     BOOL loop_next;
3838     INTERNET_ASYNC_RESULT iar;
3839     static const WCHAR szPost[] = { 'P','O','S','T',0 };
3840     static const WCHAR szContentLength[] =
3841         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3842     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3843 
3844     TRACE("--> %p\n", lpwhr);
3845 
3846     assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3847 
3848     /* if the verb is NULL default to GET */
3849     if (!lpwhr->lpszVerb)
3850         lpwhr->lpszVerb = heap_strdupW(szGET);
3851 
3852     if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3853     {
3854         sprintfW(contentLengthStr, szContentLength, dwContentLength);
3855         HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3856         lpwhr->dwBytesToWrite = dwContentLength;
3857     }
3858     if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3859     {
3860         WCHAR *agent_header;
3861         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3862         int len;
3863 
3864         len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3865         agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3866         sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3867 
3868         HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3869         HeapFree(GetProcessHeap(), 0, agent_header);
3870     }
3871     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3872     {
3873         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3874         HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3875     }
3876     if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3877     {
3878         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3879                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3880         HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3881     }
3882 
3883     do
3884     {
3885         DWORD len;
3886         char *ascii_req;
3887 
3888         loop_next = FALSE;
3889 
3890         /* like native, just in case the caller forgot to call InternetReadFile
3891          * for all the data */
3892         HTTP_DrainContent(lpwhr);
3893         lpwhr->dwContentRead = 0;
3894 
3895         if (TRACE_ON(wininet))
3896         {
3897             LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3898             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3899         }
3900 
3901         HTTP_FixURL(lpwhr);
3902         if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3903         {
3904             HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3905         }
3906         HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3907         HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3908 
3909         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3910             HTTP_InsertCookies(lpwhr);
3911 
3912         /* add the headers the caller supplied */
3913         if( lpszHeaders && dwHeaderLength )
3914         {
3915             HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3916                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3917         }
3918 
3919         if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3920         {
3921             WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3922             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3923             HeapFree(GetProcessHeap(), 0, url);
3924         }
3925         else
3926             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3927 
3928  
3929         TRACE("Request header -> %s\n", debugstr_w(requestString) );
3930 
3931         /* Send the request and store the results */
3932         if (!HTTP_OpenConnection(lpwhr))
3933             goto lend;
3934 
3935         /* send the request as ASCII, tack on the optional data */
3936         if (!lpOptional || redirected)
3937             dwOptionalLength = 0;
3938         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3939                                    NULL, 0, NULL, NULL );
3940         ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3941         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3942                              ascii_req, len, NULL, NULL );
3943         if( lpOptional )
3944             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3945         len = (len + dwOptionalLength - 1);
3946         ascii_req[len] = 0;
3947         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3948 
3949         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3950