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