winmm: Use proper structure where possible to fix command string parsing on 64-bit.
[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         FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1705         return ERROR_SUCCESS;
1706     }
1707
1708     case INTERNET_OPTION_HANDLE_TYPE:
1709         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1710
1711         if (*size < sizeof(ULONG))
1712             return ERROR_INSUFFICIENT_BUFFER;
1713
1714         *size = sizeof(DWORD);
1715         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1716         return ERROR_SUCCESS;
1717
1718     case INTERNET_OPTION_URL: {
1719         WCHAR url[INTERNET_MAX_URL_LENGTH];
1720         HTTPHEADERW *host;
1721         DWORD len;
1722         WCHAR *pch;
1723
1724         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1725
1726         TRACE("INTERNET_OPTION_URL\n");
1727
1728         host = HTTP_GetHeader(req, hostW);
1729         strcpyW(url, httpW);
1730         strcatW(url, host->lpszValue);
1731         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1732             *pch = 0;
1733         strcatW(url, req->lpszPath);
1734
1735         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1736
1737         if(unicode) {
1738             len = (strlenW(url)+1) * sizeof(WCHAR);
1739             if(*size < len)
1740                 return ERROR_INSUFFICIENT_BUFFER;
1741
1742             *size = len;
1743             strcpyW(buffer, url);
1744             return ERROR_SUCCESS;
1745         }else {
1746             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1747             if(len > *size)
1748                 return ERROR_INSUFFICIENT_BUFFER;
1749
1750             *size = len;
1751             return ERROR_SUCCESS;
1752         }
1753     }
1754
1755     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1756         INTERNET_CACHE_ENTRY_INFOW *info;
1757         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1758         WCHAR url[INTERNET_MAX_URL_LENGTH];
1759         DWORD nbytes, error;
1760         BOOL ret;
1761
1762         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1763
1764         if (*size < sizeof(*ts))
1765         {
1766             *size = sizeof(*ts);
1767             return ERROR_INSUFFICIENT_BUFFER;
1768         }
1769         nbytes = 0;
1770         HTTP_GetRequestURL(req, url);
1771         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1772         error = GetLastError();
1773         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1774         {
1775             if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1776                 return ERROR_OUTOFMEMORY;
1777
1778             GetUrlCacheEntryInfoW(url, info, &nbytes);
1779
1780             ts->ftExpires = info->ExpireTime;
1781             ts->ftLastModified = info->LastModifiedTime;
1782
1783             HeapFree(GetProcessHeap(), 0, info);
1784             *size = sizeof(*ts);
1785             return ERROR_SUCCESS;
1786         }
1787         return error;
1788     }
1789
1790     case INTERNET_OPTION_DATAFILE_NAME: {
1791         DWORD req_size;
1792
1793         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1794
1795         if(!req->lpszCacheFile) {
1796             *size = 0;
1797             return ERROR_INTERNET_ITEM_NOT_FOUND;
1798         }
1799
1800         if(unicode) {
1801             req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1802             if(*size < req_size)
1803                 return ERROR_INSUFFICIENT_BUFFER;
1804
1805             *size = req_size;
1806             memcpy(buffer, req->lpszCacheFile, *size);
1807             return ERROR_SUCCESS;
1808         }else {
1809             req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1810             if (req_size > *size)
1811                 return ERROR_INSUFFICIENT_BUFFER;
1812
1813             *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1814                     -1, buffer, *size, NULL, NULL);
1815             return ERROR_SUCCESS;
1816         }
1817     }
1818
1819     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1820         PCCERT_CONTEXT context;
1821
1822         if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1823             *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1824             return ERROR_INSUFFICIENT_BUFFER;
1825         }
1826
1827         context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1828         if(context) {
1829             INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1830             DWORD len;
1831
1832             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1833             info->ftExpiry = context->pCertInfo->NotAfter;
1834             info->ftStart = context->pCertInfo->NotBefore;
1835             if(unicode) {
1836                 len = CertNameToStrW(context->dwCertEncodingType,
1837                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1838                 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1839                 if(info->lpszSubjectInfo)
1840                     CertNameToStrW(context->dwCertEncodingType,
1841                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1842                              info->lpszSubjectInfo, len);
1843                 len = CertNameToStrW(context->dwCertEncodingType,
1844                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1845                 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1846                 if (info->lpszIssuerInfo)
1847                     CertNameToStrW(context->dwCertEncodingType,
1848                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1849                              info->lpszIssuerInfo, len);
1850             }else {
1851                 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1852
1853                 len = CertNameToStrA(context->dwCertEncodingType,
1854                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1855                 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1856                 if(infoA->lpszSubjectInfo)
1857                     CertNameToStrA(context->dwCertEncodingType,
1858                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1859                              infoA->lpszSubjectInfo, len);
1860                 len = CertNameToStrA(context->dwCertEncodingType,
1861                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1862                 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1863                 if(infoA->lpszIssuerInfo)
1864                     CertNameToStrA(context->dwCertEncodingType,
1865                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1866                              infoA->lpszIssuerInfo, len);
1867             }
1868
1869             /*
1870              * Contrary to MSDN, these do not appear to be set.
1871              * lpszProtocolName
1872              * lpszSignatureAlgName
1873              * lpszEncryptionAlgName
1874              * dwKeySize
1875              */
1876             CertFreeCertificateContext(context);
1877             return ERROR_SUCCESS;
1878         }
1879     }
1880     }
1881
1882     return INET_QueryOption(hdr, option, buffer, size, unicode);
1883 }
1884
1885 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1886 {
1887     http_request_t *req = (http_request_t*)hdr;
1888
1889     switch(option) {
1890     case INTERNET_OPTION_SEND_TIMEOUT:
1891     case INTERNET_OPTION_RECEIVE_TIMEOUT:
1892         TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1893
1894         if (size != sizeof(DWORD))
1895             return ERROR_INVALID_PARAMETER;
1896
1897         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1898                     *(DWORD*)buffer);
1899
1900     case INTERNET_OPTION_USERNAME:
1901         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1902         if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1903         return ERROR_SUCCESS;
1904
1905     case INTERNET_OPTION_PASSWORD:
1906         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1907         if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1908         return ERROR_SUCCESS;
1909     case INTERNET_OPTION_HTTP_DECODING:
1910         if(size != sizeof(BOOL))
1911             return ERROR_INVALID_PARAMETER;
1912         req->decoding = *(BOOL*)buffer;
1913         return ERROR_SUCCESS;
1914     }
1915
1916     return ERROR_INTERNET_INVALID_OPTION;
1917 }
1918
1919 /* read some more data into the read buffer (the read section must be held) */
1920 static DWORD read_more_data( http_request_t *req, int maxlen )
1921 {
1922     DWORD res;
1923     int len;
1924
1925     if (req->read_pos)
1926     {
1927         /* move existing data to the start of the buffer */
1928         if(req->read_size)
1929             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1930         req->read_pos = 0;
1931     }
1932
1933     if (maxlen == -1) maxlen = sizeof(req->read_buf);
1934
1935     res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1936                        maxlen - req->read_size, 0, &len );
1937     if(res == ERROR_SUCCESS)
1938         req->read_size += len;
1939
1940     return res;
1941 }
1942
1943 /* remove some amount of data from the read buffer (the read section must be held) */
1944 static void remove_data( http_request_t *req, int count )
1945 {
1946     if (!(req->read_size -= count)) req->read_pos = 0;
1947     else req->read_pos += count;
1948 }
1949
1950 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1951 {
1952     int count, bytes_read, pos = 0;
1953     DWORD res;
1954
1955     EnterCriticalSection( &req->read_section );
1956     for (;;)
1957     {
1958         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1959
1960         if (eol)
1961         {
1962             count = eol - (req->read_buf + req->read_pos);
1963             bytes_read = count + 1;
1964         }
1965         else count = bytes_read = req->read_size;
1966
1967         count = min( count, *len - pos );
1968         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1969         pos += count;
1970         remove_data( req, bytes_read );
1971         if (eol) break;
1972
1973         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1974         {
1975             *len = 0;
1976             TRACE( "returning empty string\n" );
1977             LeaveCriticalSection( &req->read_section );
1978             INTERNET_SetLastError(res);
1979             return FALSE;
1980         }
1981     }
1982     LeaveCriticalSection( &req->read_section );
1983
1984     if (pos < *len)
1985     {
1986         if (pos && buffer[pos - 1] == '\r') pos--;
1987         *len = pos + 1;
1988     }
1989     buffer[*len - 1] = 0;
1990     TRACE( "returning %s\n", debugstr_a(buffer));
1991     return TRUE;
1992 }
1993
1994 /* discard data contents until we reach end of line (the read section must be held) */
1995 static DWORD discard_eol( http_request_t *req )
1996 {
1997     DWORD res;
1998
1999     do
2000     {
2001         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2002         if (eol)
2003         {
2004             remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2005             break;
2006         }
2007         req->read_pos = req->read_size = 0;  /* discard everything */
2008         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2009     } while (req->read_size);
2010     return ERROR_SUCCESS;
2011 }
2012
2013 /* read the size of the next chunk (the read section must be held) */
2014 static DWORD start_next_chunk( http_request_t *req )
2015 {
2016     DWORD chunk_size = 0, res;
2017
2018     if (!req->dwContentLength) return ERROR_SUCCESS;
2019     if (req->dwContentLength == req->dwContentRead)
2020     {
2021         /* read terminator for the previous chunk */
2022         if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2023         req->dwContentLength = ~0u;
2024         req->dwContentRead = 0;
2025     }
2026     for (;;)
2027     {
2028         while (req->read_size)
2029         {
2030             char ch = req->read_buf[req->read_pos];
2031             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2032             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2033             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2034             else if (ch == ';' || ch == '\r' || ch == '\n')
2035             {
2036                 TRACE( "reading %u byte chunk\n", chunk_size );
2037                 req->dwContentLength = chunk_size;
2038                 req->dwContentRead = 0;
2039                 return discard_eol( req );
2040             }
2041             remove_data( req, 1 );
2042         }
2043         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2044         if (!req->read_size)
2045         {
2046             req->dwContentLength = req->dwContentRead = 0;
2047             return ERROR_SUCCESS;
2048         }
2049     }
2050 }
2051
2052 /* check if we have reached the end of the data to read (the read section must be held) */
2053 static BOOL end_of_read_data( http_request_t *req )
2054 {
2055     if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2056     if (req->read_chunked) return (req->dwContentLength == 0);
2057     if (req->dwContentLength == ~0u) return FALSE;
2058     return (req->dwContentLength == req->dwContentRead);
2059 }
2060
2061 /* fetch some more data into the read buffer (the read section must be held) */
2062 static DWORD refill_buffer( http_request_t *req )
2063 {
2064     int len = sizeof(req->read_buf);
2065     DWORD res;
2066
2067     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2068     {
2069         if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2070     }
2071
2072     if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2073     if (len <= req->read_size) return ERROR_SUCCESS;
2074
2075     if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2076     if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2077     return ERROR_SUCCESS;
2078 }
2079
2080 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2081 {
2082     DWORD ret = ERROR_SUCCESS;
2083     int read = 0;
2084
2085 #ifdef HAVE_ZLIB
2086     z_stream *zstream = &req->gzip_stream->zstream;
2087     DWORD buf_avail;
2088     int zres;
2089
2090     while(read < size && !req->gzip_stream->end_of_data) {
2091         if(!req->read_size) {
2092             if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2093                 break;
2094         }
2095
2096         if(req->dwContentRead == req->dwContentLength)
2097             break;
2098
2099         buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2100
2101         zstream->next_in = req->read_buf+req->read_pos;
2102         zstream->avail_in = buf_avail;
2103         zstream->next_out = buf+read;
2104         zstream->avail_out = size-read;
2105         zres = inflate(zstream, Z_FULL_FLUSH);
2106         read = size - zstream->avail_out;
2107         req->dwContentRead += buf_avail-zstream->avail_in;
2108         remove_data(req, buf_avail-zstream->avail_in);
2109         if(zres == Z_STREAM_END) {
2110             TRACE("end of data\n");
2111             req->gzip_stream->end_of_data = TRUE;
2112             inflateEnd(&req->gzip_stream->zstream);
2113         }else if(zres != Z_OK) {
2114             WARN("inflate failed %d\n", zres);
2115             if(!read)
2116                 ret = ERROR_INTERNET_DECODING_FAILED;
2117             break;
2118         }
2119     }
2120 #endif
2121
2122     *read_ret = read;
2123     return ret;
2124 }
2125
2126 static void refill_gzip_buffer(http_request_t *req)
2127 {
2128     DWORD res;
2129     int len;
2130
2131     if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2132         return;
2133
2134     if(req->gzip_stream->buf_pos) {
2135         if(req->gzip_stream->buf_size)
2136             memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2137         req->gzip_stream->buf_pos = 0;
2138     }
2139
2140     res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2141             sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2142     if(res == ERROR_SUCCESS)
2143         req->gzip_stream->buf_size += len;
2144 }
2145
2146 /* return the size of data available to be read immediately (the read section must be held) */
2147 static DWORD get_avail_data( http_request_t *req )
2148 {
2149     if (req->gzip_stream) {
2150         refill_gzip_buffer(req);
2151         return req->gzip_stream->buf_size;
2152     }
2153     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2154         return 0;
2155     return min( req->read_size, req->dwContentLength - req->dwContentRead );
2156 }
2157
2158 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2159 {
2160     INTERNET_ASYNC_RESULT iar;
2161     DWORD res;
2162
2163     TRACE("%p\n", req);
2164
2165     EnterCriticalSection( &req->read_section );
2166     if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2167         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2168         iar.dwError = first_notif ? 0 : get_avail_data(req);
2169     }else {
2170         iar.dwResult = 0;
2171         iar.dwError = res;
2172     }
2173     LeaveCriticalSection( &req->read_section );
2174
2175     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2176                           sizeof(INTERNET_ASYNC_RESULT));
2177 }
2178
2179 /* read data from the http connection (the read section must be held) */
2180 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2181 {
2182     BOOL finished_reading = FALSE;
2183     int len, bytes_read = 0;
2184     DWORD ret = ERROR_SUCCESS;
2185
2186     EnterCriticalSection( &req->read_section );
2187
2188     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2189     {
2190         if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2191     }
2192
2193     if(req->gzip_stream) {
2194         if(req->gzip_stream->buf_size) {
2195             bytes_read = min(req->gzip_stream->buf_size, size);
2196             memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2197             req->gzip_stream->buf_pos += bytes_read;
2198             req->gzip_stream->buf_size -= bytes_read;
2199         }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2200             refill_buffer(req);
2201         }
2202
2203         if(size > bytes_read) {
2204             ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2205             if(ret == ERROR_SUCCESS)
2206                 bytes_read += len;
2207         }
2208
2209         finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2210     }else {
2211         if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2212
2213         if (req->read_size) {
2214             bytes_read = min( req->read_size, size );
2215             memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2216             remove_data( req, bytes_read );
2217         }
2218
2219         if (size > bytes_read && (!bytes_read || sync)) {
2220             if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2221                              sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2222                 bytes_read += len;
2223             /* always return success, even if the network layer returns an error */
2224         }
2225
2226         finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2227         req->dwContentRead += bytes_read;
2228     }
2229 done:
2230     *read = bytes_read;
2231
2232     TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2233     LeaveCriticalSection( &req->read_section );
2234
2235     if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2236         BOOL res;
2237         DWORD dwBytesWritten;
2238
2239         res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2240         if(!res)
2241             WARN("WriteFile failed: %u\n", GetLastError());
2242     }
2243
2244     if(finished_reading)
2245         HTTP_FinishedReading(req);
2246
2247     return ret;
2248 }
2249
2250
2251 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2252 {
2253     http_request_t *req = (http_request_t*)hdr;
2254     DWORD res;
2255
2256     EnterCriticalSection( &req->read_section );
2257     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2258         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2259
2260     res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2261     if(res == ERROR_SUCCESS)
2262         res = hdr->dwError;
2263     LeaveCriticalSection( &req->read_section );
2264
2265     return res;
2266 }
2267
2268 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2269 {
2270     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2271     http_request_t *req = (http_request_t*)workRequest->hdr;
2272     INTERNET_ASYNC_RESULT iar;
2273     DWORD res;
2274
2275     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2276
2277     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2278             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2279
2280     iar.dwResult = res == ERROR_SUCCESS;
2281     iar.dwError = res;
2282
2283     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2284                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2285                           sizeof(INTERNET_ASYNC_RESULT));
2286 }
2287
2288 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2289         DWORD flags, DWORD_PTR context)
2290 {
2291     http_request_t *req = (http_request_t*)hdr;
2292     DWORD res, size, read, error = ERROR_SUCCESS;
2293
2294     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2295         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2296
2297     if (buffers->dwStructSize != sizeof(*buffers))
2298         return ERROR_INVALID_PARAMETER;
2299
2300     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2301
2302     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2303     {
2304         WORKREQUEST workRequest;
2305
2306         if (TryEnterCriticalSection( &req->read_section ))
2307         {
2308             if (get_avail_data(req))
2309             {
2310                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2311                                    &buffers->dwBufferLength, FALSE);
2312                 size = buffers->dwBufferLength;
2313                 LeaveCriticalSection( &req->read_section );
2314                 goto done;
2315             }
2316             LeaveCriticalSection( &req->read_section );
2317         }
2318
2319         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2320         workRequest.hdr = WININET_AddRef(&req->hdr);
2321         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2322
2323         INTERNET_AsyncCall(&workRequest);
2324
2325         return ERROR_IO_PENDING;
2326     }
2327
2328     read = 0;
2329     size = buffers->dwBufferLength;
2330
2331     EnterCriticalSection( &req->read_section );
2332     if(hdr->dwError == ERROR_SUCCESS)
2333         hdr->dwError = INTERNET_HANDLE_IN_USE;
2334     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2335         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2336
2337     while(1) {
2338         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2339                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2340         if(res == ERROR_SUCCESS)
2341             read += buffers->dwBufferLength;
2342         else
2343             break;
2344
2345         if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2346                 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2347             break;
2348         LeaveCriticalSection( &req->read_section );
2349
2350         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2351                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2352         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2353                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2354
2355         EnterCriticalSection( &req->read_section );
2356     }
2357
2358     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2359         hdr->dwError = ERROR_SUCCESS;
2360     else
2361         error = hdr->dwError;
2362
2363     LeaveCriticalSection( &req->read_section );
2364     size = buffers->dwBufferLength;
2365     buffers->dwBufferLength = read;
2366
2367 done:
2368     if (res == ERROR_SUCCESS) {
2369         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2370                 &size, sizeof(size));
2371     }
2372
2373     return res==ERROR_SUCCESS ? error : res;
2374 }
2375
2376 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2377 {
2378     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2379     http_request_t *req = (http_request_t*)workRequest->hdr;
2380     INTERNET_ASYNC_RESULT iar;
2381     DWORD res;
2382
2383     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2384
2385     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2386             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2387
2388     iar.dwResult = res == ERROR_SUCCESS;
2389     iar.dwError = res;
2390
2391     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2392                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2393                           sizeof(INTERNET_ASYNC_RESULT));
2394 }
2395
2396 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2397         DWORD flags, DWORD_PTR context)
2398 {
2399
2400     http_request_t *req = (http_request_t*)hdr;
2401     DWORD res, size, read, error = ERROR_SUCCESS;
2402
2403     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2404         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2405
2406     if (buffers->dwStructSize != sizeof(*buffers))
2407         return ERROR_INVALID_PARAMETER;
2408
2409     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2410
2411     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2412     {
2413         WORKREQUEST workRequest;
2414
2415         if (TryEnterCriticalSection( &req->read_section ))
2416         {
2417             if (get_avail_data(req))
2418             {
2419                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2420                                    &buffers->dwBufferLength, FALSE);
2421                 size = buffers->dwBufferLength;
2422                 LeaveCriticalSection( &req->read_section );
2423                 goto done;
2424             }
2425             LeaveCriticalSection( &req->read_section );
2426         }
2427
2428         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2429         workRequest.hdr = WININET_AddRef(&req->hdr);
2430         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2431
2432         INTERNET_AsyncCall(&workRequest);
2433
2434         return ERROR_IO_PENDING;
2435     }
2436
2437     read = 0;
2438     size = buffers->dwBufferLength;
2439
2440     EnterCriticalSection( &req->read_section );
2441     if(hdr->dwError == ERROR_SUCCESS)
2442         hdr->dwError = INTERNET_HANDLE_IN_USE;
2443     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2444         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2445
2446     while(1) {
2447         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2448                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2449         if(res == ERROR_SUCCESS)
2450             read += buffers->dwBufferLength;
2451         else
2452             break;
2453
2454         if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2455                 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2456             break;
2457         LeaveCriticalSection( &req->read_section );
2458
2459         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2460                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2461         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2462                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2463
2464         EnterCriticalSection( &req->read_section );
2465     }
2466
2467     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2468         hdr->dwError = ERROR_SUCCESS;
2469     else
2470         error = hdr->dwError;
2471
2472     LeaveCriticalSection( &req->read_section );
2473     size = buffers->dwBufferLength;
2474     buffers->dwBufferLength = read;
2475
2476 done:
2477     if (res == ERROR_SUCCESS) {
2478         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2479                 &size, sizeof(size));
2480     }
2481
2482     return res==ERROR_SUCCESS ? error : res;
2483 }
2484
2485 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2486 {
2487     DWORD res;
2488     http_request_t *lpwhr = (http_request_t*)hdr;
2489
2490     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2491
2492     *written = 0;
2493     res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2494     if (res == ERROR_SUCCESS)
2495         lpwhr->dwBytesWritten += *written;
2496
2497     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2498     return res;
2499 }
2500
2501 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2502 {
2503     http_request_t *req = (http_request_t*)workRequest->hdr;
2504
2505     HTTP_ReceiveRequestData(req, FALSE);
2506 }
2507
2508 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2509 {
2510     http_request_t *req = (http_request_t*)hdr;
2511
2512     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2513
2514     if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2515     {
2516         WORKREQUEST workRequest;
2517
2518         /* never wait, if we can't enter the section we queue an async request right away */
2519         if (TryEnterCriticalSection( &req->read_section ))
2520         {
2521             if ((*available = get_avail_data( req ))) goto done;
2522             if (end_of_read_data( req )) goto done;
2523             LeaveCriticalSection( &req->read_section );
2524         }
2525
2526         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2527         workRequest.hdr = WININET_AddRef( &req->hdr );
2528
2529         INTERNET_AsyncCall(&workRequest);
2530
2531         return ERROR_IO_PENDING;
2532     }
2533
2534     EnterCriticalSection( &req->read_section );
2535
2536     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2537     {
2538         refill_buffer( req );
2539         *available = get_avail_data( req );
2540     }
2541
2542 done:
2543     if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
2544     {
2545         DWORD extra;
2546         if (NETCON_query_data_available(&req->netConnection, &extra))
2547             *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2548     }
2549     LeaveCriticalSection( &req->read_section );
2550
2551     TRACE( "returning %u\n", *available );
2552     return ERROR_SUCCESS;
2553 }
2554
2555 static const object_vtbl_t HTTPREQVtbl = {
2556     HTTPREQ_Destroy,
2557     HTTPREQ_CloseConnection,
2558     HTTPREQ_QueryOption,
2559     HTTPREQ_SetOption,
2560     HTTPREQ_ReadFile,
2561     HTTPREQ_ReadFileExA,
2562     HTTPREQ_ReadFileExW,
2563     HTTPREQ_WriteFile,
2564     HTTPREQ_QueryDataAvailable,
2565     NULL
2566 };
2567
2568 /***********************************************************************
2569  *           HTTP_HttpOpenRequestW (internal)
2570  *
2571  * Open a HTTP request handle
2572  *
2573  * RETURNS
2574  *    HINTERNET  a HTTP request handle on success
2575  *    NULL       on failure
2576  *
2577  */
2578 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2579         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2580         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2581         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2582 {
2583     appinfo_t *hIC = NULL;
2584     http_request_t *lpwhr;
2585     LPWSTR lpszHostName = NULL;
2586     HINTERNET handle = NULL;
2587     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2588     DWORD len, res;
2589
2590     TRACE("-->\n");
2591
2592     assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2593     hIC = lpwhs->lpAppInfo;
2594
2595     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2596     if (NULL == lpwhr)
2597     {
2598         res = ERROR_OUTOFMEMORY;
2599         goto lend;
2600     }
2601     lpwhr->hdr.htype = WH_HHTTPREQ;
2602     lpwhr->hdr.vtbl = &HTTPREQVtbl;
2603     lpwhr->hdr.dwFlags = dwFlags;
2604     lpwhr->hdr.dwContext = dwContext;
2605     lpwhr->hdr.refs = 1;
2606     lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2607     lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2608     lpwhr->dwContentLength = ~0u;
2609     InitializeCriticalSection( &lpwhr->read_section );
2610
2611     WININET_AddRef( &lpwhs->hdr );
2612     lpwhr->lpHttpSession = lpwhs;
2613     list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2614
2615     lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2616             (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2617     if (NULL == lpszHostName)
2618     {
2619         res = ERROR_OUTOFMEMORY;
2620         goto lend;
2621     }
2622
2623     handle = WININET_AllocHandle( &lpwhr->hdr );
2624     if (NULL == handle)
2625     {
2626         res = ERROR_OUTOFMEMORY;
2627         goto lend;
2628     }
2629
2630     if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2631     {
2632         InternetCloseHandle( handle );
2633         handle = NULL;
2634         goto lend;
2635     }
2636
2637     if (lpszObjectName && *lpszObjectName) {
2638         HRESULT rc;
2639
2640         len = 0;
2641         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2642         if (rc != E_POINTER)
2643             len = strlenW(lpszObjectName)+1;
2644         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2645         rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2646                    URL_ESCAPE_SPACES_ONLY);
2647         if (rc != S_OK)
2648         {
2649             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2650             strcpyW(lpwhr->lpszPath,lpszObjectName);
2651         }
2652     }else {
2653         static const WCHAR slashW[] = {'/',0};
2654
2655         lpwhr->lpszPath = heap_strdupW(slashW);
2656     }
2657
2658     if (lpszReferrer && *lpszReferrer)
2659         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2660
2661     if (lpszAcceptTypes)
2662     {
2663         int i;
2664         for (i = 0; lpszAcceptTypes[i]; i++)
2665         {
2666             if (!*lpszAcceptTypes[i]) continue;
2667             HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2668                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2669                                HTTP_ADDHDR_FLAG_REQ |
2670                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2671         }
2672     }
2673
2674     lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2675     lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2676
2677     if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2678         lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2679         lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2680     {
2681         sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2682         HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2683                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2684     }
2685     else
2686         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2687                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2688
2689     if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2690         lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2691                         INTERNET_DEFAULT_HTTPS_PORT :
2692                         INTERNET_DEFAULT_HTTP_PORT);
2693
2694     if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2695         lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2696                         INTERNET_DEFAULT_HTTPS_PORT :
2697                         INTERNET_DEFAULT_HTTP_PORT);
2698
2699     if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2700         HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2701
2702     INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2703                           INTERNET_STATUS_HANDLE_CREATED, &handle,
2704                           sizeof(handle));
2705
2706 lend:
2707     HeapFree(GetProcessHeap(), 0, lpszHostName);
2708     if( lpwhr )
2709         WININET_Release( &lpwhr->hdr );
2710
2711     TRACE("<-- %p (%p)\n", handle, lpwhr);
2712     *ret = handle;
2713     return res;
2714 }
2715
2716 /***********************************************************************
2717  *           HttpOpenRequestW (WININET.@)
2718  *
2719  * Open a HTTP request handle
2720  *
2721  * RETURNS
2722  *    HINTERNET  a HTTP request handle on success
2723  *    NULL       on failure
2724  *
2725  */
2726 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2727         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2728         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2729         DWORD dwFlags, DWORD_PTR dwContext)
2730 {
2731     http_session_t *lpwhs;
2732     HINTERNET handle = NULL;
2733     DWORD res;
2734
2735     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2736           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2737           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2738           dwFlags, dwContext);
2739     if(lpszAcceptTypes!=NULL)
2740     {
2741         int i;
2742         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2743             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2744     }
2745
2746     lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2747     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
2748     {
2749         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2750         goto lend;
2751     }
2752
2753     /*
2754      * My tests seem to show that the windows version does not
2755      * become asynchronous until after this point. And anyhow
2756      * if this call was asynchronous then how would you get the
2757      * necessary HINTERNET pointer returned by this function.
2758      *
2759      */
2760     res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2761                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
2762                                 dwFlags, dwContext, &handle);
2763 lend:
2764     if( lpwhs )
2765         WININET_Release( &lpwhs->hdr );
2766     TRACE("returning %p\n", handle);
2767     if(res != ERROR_SUCCESS)
2768         SetLastError(res);
2769     return handle;
2770 }
2771
2772 /* read any content returned by the server so that the connection can be
2773  * reused */
2774 static void HTTP_DrainContent(http_request_t *req)
2775 {
2776     DWORD bytes_read;
2777
2778     if (!NETCON_connected(&req->netConnection)) return;
2779
2780     if (req->dwContentLength == -1)
2781     {
2782         NETCON_close(&req->netConnection);
2783         return;
2784     }
2785     if (!strcmpW(req->lpszVerb, szHEAD)) return;
2786
2787     do
2788     {
2789         char buffer[2048];
2790         if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2791             return;
2792     } while (bytes_read);
2793 }
2794
2795 static const LPCWSTR header_lookup[] = {
2796     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
2797     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
2798     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2799     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
2800     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2801     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
2802     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
2803     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
2804     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
2805     szDate,                     /* HTTP_QUERY_DATE = 9 */
2806     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
2807     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
2808     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
2809     szURI,                      /* HTTP_QUERY_URI = 13 */
2810     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
2811     NULL,                       /* HTTP_QUERY_COST = 15 */
2812     NULL,                       /* HTTP_QUERY_LINK = 16 */
2813     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
2814     NULL,                       /* HTTP_QUERY_VERSION = 18 */
2815     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
2816     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
2817     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
2818     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2819     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
2820     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
2821     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2822     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2823     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2824     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
2825     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2826     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
2827     NULL,                       /* HTTP_QUERY_FROM = 31 */
2828     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2829     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
2830     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
2831     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
2832     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
2833     szServer,                   /* HTTP_QUERY_SERVER = 37 */
2834     NULL,                       /* HTTP_TITLE = 38 */
2835     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
2836     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2837     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2838     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2839     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
2840     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
2841     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
2842     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
2843     NULL,                       /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2844     szAge,                      /* HTTP_QUERY_AGE = 48 */
2845     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
2846     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
2847     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2848     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
2849     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
2850     szETag,                     /* HTTP_QUERY_ETAG = 54 */
2851     hostW,                      /* HTTP_QUERY_HOST = 55 */
2852     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
2853     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2854     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
2855     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2856     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
2857     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2858     szRange,                    /* HTTP_QUERY_RANGE = 62 */
2859     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2860     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
2861     szVary,                     /* HTTP_QUERY_VARY = 65 */
2862     szVia,                      /* HTTP_QUERY_VIA = 66 */
2863     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
2864     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
2865     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2866     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2867 };
2868
2869 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2870
2871 /***********************************************************************
2872  *           HTTP_HttpQueryInfoW (internal)
2873  */
2874 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2875         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2876 {
2877     LPHTTPHEADERW lphttpHdr = NULL;
2878     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2879     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2880     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2881     INT index = -1;
2882
2883     /* Find requested header structure */
2884     switch (level)
2885     {
2886     case HTTP_QUERY_CUSTOM:
2887         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2888         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2889         break;
2890     case HTTP_QUERY_RAW_HEADERS_CRLF:
2891         {
2892             LPWSTR headers;
2893             DWORD len = 0;
2894             DWORD res = ERROR_INVALID_PARAMETER;
2895
2896             if (request_only)
2897                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2898             else
2899                 headers = lpwhr->lpszRawHeaders;
2900
2901             if (headers)
2902                 len = strlenW(headers) * sizeof(WCHAR);
2903
2904             if (len + sizeof(WCHAR) > *lpdwBufferLength)
2905             {
2906                 len += sizeof(WCHAR);
2907                 res = ERROR_INSUFFICIENT_BUFFER;
2908             }
2909             else if (lpBuffer)
2910             {
2911                 if (headers)
2912                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2913                 else
2914                 {
2915                     len = strlenW(szCrLf) * sizeof(WCHAR);
2916                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2917                 }
2918                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2919                 res = ERROR_SUCCESS;
2920             }
2921             *lpdwBufferLength = len;
2922
2923             if (request_only)
2924                 HeapFree(GetProcessHeap(), 0, headers);
2925             return res;
2926         }
2927     case HTTP_QUERY_RAW_HEADERS:
2928         {
2929             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2930             DWORD i, size = 0;
2931             LPWSTR pszString = lpBuffer;
2932
2933             for (i = 0; ppszRawHeaderLines[i]; i++)
2934                 size += strlenW(ppszRawHeaderLines[i]) + 1;
2935
2936             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2937             {
2938                 HTTP_FreeTokens(ppszRawHeaderLines);
2939                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2940                 return ERROR_INSUFFICIENT_BUFFER;
2941             }
2942             if (pszString)
2943             {
2944                 for (i = 0; ppszRawHeaderLines[i]; i++)
2945                 {
2946                     DWORD len = strlenW(ppszRawHeaderLines[i]);
2947                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2948                     pszString += len+1;
2949                 }
2950                 *pszString = '\0';
2951                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2952             }
2953             *lpdwBufferLength = size * sizeof(WCHAR);
2954             HTTP_FreeTokens(ppszRawHeaderLines);
2955
2956             return ERROR_SUCCESS;
2957         }
2958     case HTTP_QUERY_STATUS_TEXT:
2959         if (lpwhr->lpszStatusText)
2960         {
2961             DWORD len = strlenW(lpwhr->lpszStatusText);
2962             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2963             {
2964                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2965                 return ERROR_INSUFFICIENT_BUFFER;
2966             }
2967             if (lpBuffer)
2968             {
2969                 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2970                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2971             }
2972             *lpdwBufferLength = len * sizeof(WCHAR);
2973             return ERROR_SUCCESS;
2974         }
2975         break;
2976     case HTTP_QUERY_VERSION:
2977         if (lpwhr->lpszVersion)
2978         {
2979             DWORD len = strlenW(lpwhr->lpszVersion);
2980             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2981             {
2982                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2983                 return ERROR_INSUFFICIENT_BUFFER;
2984             }
2985             if (lpBuffer)
2986             {
2987                 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2988                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2989             }
2990             *lpdwBufferLength = len * sizeof(WCHAR);
2991             return ERROR_SUCCESS;
2992         }
2993         break;
2994     case HTTP_QUERY_CONTENT_ENCODING:
2995         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2996                 requested_index,request_only);
2997         break;
2998     default:
2999         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3000
3001         if (level < LAST_TABLE_HEADER && header_lookup[level])
3002             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
3003                                               requested_index,request_only);
3004     }
3005
3006     if (index >= 0)
3007         lphttpHdr = &lpwhr->pCustHeaders[index];
3008
3009     /* Ensure header satisfies requested attributes */
3010     if (!lphttpHdr ||
3011         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3012          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3013     {
3014         return ERROR_HTTP_HEADER_NOT_FOUND;
3015     }
3016
3017     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3018
3019     /* coalesce value to requested type */
3020     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3021     {
3022         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3023         TRACE(" returning number: %d\n", *(int *)lpBuffer);
3024      }
3025     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3026     {
3027         time_t tmpTime;
3028         struct tm tmpTM;
3029         SYSTEMTIME *STHook;
3030
3031         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3032
3033         tmpTM = *gmtime(&tmpTime);
3034         STHook = (SYSTEMTIME *)lpBuffer;
3035         STHook->wDay = tmpTM.tm_mday;
3036         STHook->wHour = tmpTM.tm_hour;
3037         STHook->wMilliseconds = 0;
3038         STHook->wMinute = tmpTM.tm_min;
3039         STHook->wDayOfWeek = tmpTM.tm_wday;
3040         STHook->wMonth = tmpTM.tm_mon + 1;
3041         STHook->wSecond = tmpTM.tm_sec;
3042         STHook->wYear = tmpTM.tm_year;
3043
3044         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3045               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3046               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3047     }
3048     else if (lphttpHdr->lpszValue)
3049     {
3050         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3051
3052         if (len > *lpdwBufferLength)
3053         {
3054             *lpdwBufferLength = len;
3055             return ERROR_INSUFFICIENT_BUFFER;
3056         }
3057         if (lpBuffer)
3058         {
3059             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3060             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3061         }
3062         *lpdwBufferLength = len - sizeof(WCHAR);
3063     }
3064     return ERROR_SUCCESS;
3065 }
3066
3067 /***********************************************************************
3068  *           HttpQueryInfoW (WININET.@)
3069  *
3070  * Queries for information about an HTTP request
3071  *
3072  * RETURNS
3073  *    TRUE  on success
3074  *    FALSE on failure
3075  *
3076  */
3077 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3078         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3079 {
3080     http_request_t *lpwhr;
3081     DWORD res;
3082
3083     if (TRACE_ON(wininet)) {
3084 #define FE(x) { x, #x }
3085         static const wininet_flag_info query_flags[] = {
3086             FE(HTTP_QUERY_MIME_VERSION),
3087             FE(HTTP_QUERY_CONTENT_TYPE),
3088             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3089             FE(HTTP_QUERY_CONTENT_ID),
3090             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3091             FE(HTTP_QUERY_CONTENT_LENGTH),
3092             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3093             FE(HTTP_QUERY_ALLOW),
3094             FE(HTTP_QUERY_PUBLIC),
3095             FE(HTTP_QUERY_DATE),
3096             FE(HTTP_QUERY_EXPIRES),
3097             FE(HTTP_QUERY_LAST_MODIFIED),
3098             FE(HTTP_QUERY_MESSAGE_ID),
3099             FE(HTTP_QUERY_URI),
3100             FE(HTTP_QUERY_DERIVED_FROM),
3101             FE(HTTP_QUERY_COST),
3102             FE(HTTP_QUERY_LINK),
3103             FE(HTTP_QUERY_PRAGMA),
3104             FE(HTTP_QUERY_VERSION),
3105             FE(HTTP_QUERY_STATUS_CODE),
3106             FE(HTTP_QUERY_STATUS_TEXT),
3107             FE(HTTP_QUERY_RAW_HEADERS),
3108             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3109             FE(HTTP_QUERY_CONNECTION),
3110             FE(HTTP_QUERY_ACCEPT),
3111             FE(HTTP_QUERY_ACCEPT_CHARSET),
3112             FE(HTTP_QUERY_ACCEPT_ENCODING),
3113             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3114             FE(HTTP_QUERY_AUTHORIZATION),
3115             FE(HTTP_QUERY_CONTENT_ENCODING),
3116             FE(HTTP_QUERY_FORWARDED),
3117             FE(HTTP_QUERY_FROM),
3118             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3119             FE(HTTP_QUERY_LOCATION),
3120             FE(HTTP_QUERY_ORIG_URI),
3121             FE(HTTP_QUERY_REFERER),
3122             FE(HTTP_QUERY_RETRY_AFTER),
3123             FE(HTTP_QUERY_SERVER),
3124             FE(HTTP_QUERY_TITLE),
3125             FE(HTTP_QUERY_USER_AGENT),
3126             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3127             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3128             FE(HTTP_QUERY_ACCEPT_RANGES),
3129         FE(HTTP_QUERY_SET_COOKIE),
3130         FE(HTTP_QUERY_COOKIE),
3131             FE(HTTP_QUERY_REQUEST_METHOD),
3132             FE(HTTP_QUERY_REFRESH),
3133             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3134             FE(HTTP_QUERY_AGE),
3135             FE(HTTP_QUERY_CACHE_CONTROL),
3136             FE(HTTP_QUERY_CONTENT_BASE),
3137             FE(HTTP_QUERY_CONTENT_LOCATION),
3138             FE(HTTP_QUERY_CONTENT_MD5),
3139             FE(HTTP_QUERY_CONTENT_RANGE),
3140             FE(HTTP_QUERY_ETAG),
3141             FE(HTTP_QUERY_HOST),
3142             FE(HTTP_QUERY_IF_MATCH),
3143             FE(HTTP_QUERY_IF_NONE_MATCH),
3144             FE(HTTP_QUERY_IF_RANGE),
3145             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3146             FE(HTTP_QUERY_MAX_FORWARDS),
3147             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3148             FE(HTTP_QUERY_RANGE),
3149             FE(HTTP_QUERY_TRANSFER_ENCODING),
3150             FE(HTTP_QUERY_UPGRADE),
3151             FE(HTTP_QUERY_VARY),
3152             FE(HTTP_QUERY_VIA),
3153             FE(HTTP_QUERY_WARNING),
3154             FE(HTTP_QUERY_CUSTOM)
3155         };
3156         static const wininet_flag_info modifier_flags[] = {
3157             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3158             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3159             FE(HTTP_QUERY_FLAG_NUMBER),
3160             FE(HTTP_QUERY_FLAG_COALESCE)
3161         };
3162 #undef FE
3163         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3164         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3165         DWORD i;
3166
3167         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3168         TRACE("  Attribute:");
3169         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3170             if (query_flags[i].val == info) {
3171                 TRACE(" %s", query_flags[i].name);
3172                 break;
3173             }
3174         }
3175         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3176             TRACE(" Unknown (%08x)", info);
3177         }
3178
3179         TRACE(" Modifier:");
3180         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3181             if (modifier_flags[i].val & info_mod) {
3182                 TRACE(" %s", modifier_flags[i].name);
3183                 info_mod &= ~ modifier_flags[i].val;
3184             }
3185         }
3186         
3187         if (info_mod) {
3188             TRACE(" Unknown (%08x)", info_mod);
3189         }
3190         TRACE("\n");
3191     }
3192     
3193     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3194     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
3195     {
3196         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3197         goto lend;
3198     }
3199
3200     if (lpBuffer == NULL)
3201         *lpdwBufferLength = 0;
3202     res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3203                                lpBuffer, lpdwBufferLength, lpdwIndex);
3204
3205 lend:
3206     if( lpwhr )
3207          WININET_Release( &lpwhr->hdr );
3208
3209     TRACE("%u <--\n", res);
3210     if(res != ERROR_SUCCESS)
3211         SetLastError(res);
3212     return res == ERROR_SUCCESS;
3213 }
3214
3215 /***********************************************************************
3216  *           HttpQueryInfoA (WININET.@)
3217  *
3218  * Queries for information about an HTTP request
3219  *
3220  * RETURNS
3221  *    TRUE  on success
3222  *    FALSE on failure
3223  *
3224  */
3225 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3226         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3227 {
3228     BOOL result;
3229     DWORD len;
3230     WCHAR* bufferW;
3231
3232     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3233        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3234     {
3235         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3236                                lpdwBufferLength, lpdwIndex );
3237     }
3238
3239     if (lpBuffer)
3240     {
3241         DWORD alloclen;
3242         len = (*lpdwBufferLength)*sizeof(WCHAR);
3243         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3244         {
3245             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3246             if (alloclen < len)
3247                 alloclen = len;
3248         }
3249         else
3250             alloclen = len;
3251         bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3252         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3253         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3254             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3255     } else
3256     {
3257         bufferW = NULL;
3258         len = 0;
3259     }
3260
3261     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3262                            &len, lpdwIndex );
3263     if( result )
3264     {
3265         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3266                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3267         *lpdwBufferLength = len - 1;
3268
3269         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3270     }
3271     else
3272         /* since the strings being returned from HttpQueryInfoW should be
3273          * only ASCII characters, it is reasonable to assume that all of
3274          * the Unicode characters can be reduced to a single byte */
3275         *lpdwBufferLength = len / sizeof(WCHAR);
3276
3277     HeapFree(GetProcessHeap(), 0, bufferW );
3278
3279     return result;
3280 }
3281
3282 /***********************************************************************
3283  *           HTTP_GetRedirectURL (internal)
3284  */
3285 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3286 {
3287     static WCHAR szHttp[] = {'h','t','t','p',0};
3288     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3289     http_session_t *lpwhs = lpwhr->lpHttpSession;
3290     URL_COMPONENTSW urlComponents;
3291     DWORD url_length = 0;
3292     LPWSTR orig_url;
3293     LPWSTR combined_url;
3294
3295     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3296     urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3297     urlComponents.dwSchemeLength = 0;
3298     urlComponents.lpszHostName = lpwhs->lpszHostName;
3299     urlComponents.dwHostNameLength = 0;
3300     urlComponents.nPort = lpwhs->nHostPort;
3301     urlComponents.lpszUserName = lpwhs->lpszUserName;
3302     urlComponents.dwUserNameLength = 0;
3303     urlComponents.lpszPassword = NULL;
3304     urlComponents.dwPasswordLength = 0;
3305     urlComponents.lpszUrlPath = lpwhr->lpszPath;
3306     urlComponents.dwUrlPathLength = 0;
3307     urlComponents.lpszExtraInfo = NULL;
3308     urlComponents.dwExtraInfoLength = 0;
3309
3310     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3311         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3312         return NULL;
3313
3314     orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3315
3316     /* convert from bytes to characters */
3317     url_length = url_length / sizeof(WCHAR) - 1;
3318     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3319     {
3320         HeapFree(GetProcessHeap(), 0, orig_url);
3321         return NULL;
3322     }
3323
3324     url_length = 0;
3325     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3326         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3327     {
3328         HeapFree(GetProcessHeap(), 0, orig_url);
3329         return NULL;
3330     }
3331     combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3332
3333     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3334     {
3335         HeapFree(GetProcessHeap(), 0, orig_url);
3336         HeapFree(GetProcessHeap(), 0, combined_url);
3337         return NULL;
3338     }
3339     HeapFree(GetProcessHeap(), 0, orig_url);
3340     return combined_url;
3341 }
3342
3343
3344 /***********************************************************************
3345  *           HTTP_HandleRedirect (internal)
3346  */
3347 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3348 {
3349     http_session_t *lpwhs = lpwhr->lpHttpSession;
3350     appinfo_t *hIC = lpwhs->lpAppInfo;
3351     BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3352     WCHAR path[INTERNET_MAX_URL_LENGTH];
3353     int index;
3354
3355     if(lpszUrl[0]=='/')
3356     {
3357         /* if it's an absolute path, keep the same session info */
3358         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3359     }
3360     else
3361     {
3362         URL_COMPONENTSW urlComponents;
3363         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3364         static WCHAR szHttp[] = {'h','t','t','p',0};
3365         static WCHAR szHttps[] = {'h','t','t','p','s',0};
3366
3367         userName[0] = 0;
3368         hostName[0] = 0;
3369         protocol[0] = 0;
3370
3371         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3372         urlComponents.lpszScheme = protocol;
3373         urlComponents.dwSchemeLength = 32;
3374         urlComponents.lpszHostName = hostName;
3375         urlComponents.dwHostNameLength = MAXHOSTNAME;
3376         urlComponents.lpszUserName = userName;
3377         urlComponents.dwUserNameLength = 1024;
3378         urlComponents.lpszPassword = NULL;
3379         urlComponents.dwPasswordLength = 0;
3380         urlComponents.lpszUrlPath = path;
3381         urlComponents.dwUrlPathLength = 2048;
3382         urlComponents.lpszExtraInfo = NULL;
3383         urlComponents.dwExtraInfoLength = 0;
3384         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3385             return INTERNET_GetLastError();
3386
3387         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3388             (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3389         {
3390             TRACE("redirect from secure page to non-secure page\n");
3391             /* FIXME: warn about from secure redirect to non-secure page */
3392             lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3393         }
3394         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3395             !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3396         {
3397             TRACE("redirect from non-secure page to secure page\n");
3398             /* FIXME: notify about redirect to secure page */
3399             lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3400         }
3401
3402         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3403         {
3404             if (lstrlenW(protocol)>4) /*https*/
3405                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3406             else /*http*/
3407                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3408         }
3409
3410 #if 0
3411         /*
3412          * This upsets redirects to binary files on sourceforge.net 
3413          * and gives an html page instead of the target file
3414          * Examination of the HTTP request sent by native wininet.dll
3415          * reveals that it doesn't send a referrer in that case.
3416          * Maybe there's a flag that enables this, or maybe a referrer
3417          * shouldn't be added in case of a redirect.
3418          */
3419
3420         /* consider the current host as the referrer */
3421         if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3422             HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3423                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3424                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3425 #endif
3426         
3427         HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3428         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3429             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3430         {
3431             int len;
3432             static const WCHAR fmt[] = {'%','s',':','%','i',0};
3433             len = lstrlenW(hostName);
3434             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3435             lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3436             sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3437         }
3438         else
3439             lpwhs->lpszHostName = heap_strdupW(hostName);
3440
3441         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3442
3443         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3444         lpwhs->lpszUserName = NULL;
3445         if (userName[0])
3446             lpwhs->lpszUserName = heap_strdupW(userName);
3447
3448         if (!using_proxy)
3449         {
3450             if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3451             {
3452                 DWORD res;
3453
3454                 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3455                 lpwhs->lpszServerName = heap_strdupW(hostName);
3456                 lpwhs->nServerPort = urlComponents.nPort;
3457
3458                 NETCON_close(&lpwhr->netConnection);
3459                 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3460                     return res;
3461
3462                 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3463                 if (res != ERROR_SUCCESS)
3464                     return res;
3465
3466                 lpwhr->read_pos = lpwhr->read_size = 0;
3467                 lpwhr->read_chunked = FALSE;
3468             }
3469         }
3470         else
3471             TRACE("Redirect through proxy\n");
3472     }
3473
3474     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3475     lpwhr->lpszPath=NULL;
3476     if (*path)
3477     {
3478         DWORD needed = 0;
3479         HRESULT rc;
3480
3481         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3482         if (rc != E_POINTER)
3483             needed = strlenW(path)+1;
3484         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3485         rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3486                         URL_ESCAPE_SPACES_ONLY);
3487         if (rc != S_OK)
3488         {
3489             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3490             strcpyW(lpwhr->lpszPath,path);
3491         }
3492     }
3493
3494     /* Remove custom content-type/length headers on redirects.  */
3495     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3496     if (0 <= index)
3497         HTTP_DeleteCustomHeader(lpwhr, index);
3498     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3499     if (0 <= index)
3500         HTTP_DeleteCustomHeader(lpwhr, index);
3501
3502     return ERROR_SUCCESS;
3503 }
3504
3505 /***********************************************************************
3506  *           HTTP_build_req (internal)
3507  *
3508  *  concatenate all the strings in the request together
3509  */
3510 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3511 {
3512     LPCWSTR *t;
3513     LPWSTR str;
3514
3515     for( t = list; *t ; t++  )
3516         len += strlenW( *t );
3517     len++;
3518
3519     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3520     *str = 0;
3521
3522     for( t = list; *t ; t++ )
3523         strcatW( str, *t );
3524
3525     return str;
3526 }
3527
3528 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3529 {
3530     LPWSTR lpszPath;
3531     LPWSTR requestString;
3532     INT len;
3533     INT cnt;
3534     INT responseLen;
3535     char *ascii_req;
3536     DWORD res;
3537     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3538     static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3539     http_session_t *lpwhs = lpwhr->lpHttpSession;
3540
3541     TRACE("\n");
3542
3543     lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3544     sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3545     requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3546     HeapFree( GetProcessHeap(), 0, lpszPath );
3547
3548     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3549                                 NULL, 0, NULL, NULL );
3550     len--; /* the nul terminator isn't needed */
3551     ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3552     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3553                             ascii_req, len, NULL, NULL );
3554     HeapFree( GetProcessHeap(), 0, requestString );
3555
3556     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3557
3558     res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3559     HeapFree( GetProcessHeap(), 0, ascii_req );
3560     if (res != ERROR_SUCCESS)
3561         return res;
3562
3563     responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3564     if (!responseLen)
3565         return ERROR_HTTP_INVALID_HEADER;
3566
3567     return ERROR_SUCCESS;
3568 }
3569
3570 static void HTTP_InsertCookies(http_request_t *lpwhr)
3571 {
3572     static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3573     LPWSTR lpszCookies, lpszUrl = NULL;
3574     DWORD nCookieSize, size;
3575     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3576
3577     size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3578     if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3579     sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3580
3581     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3582     {
3583         int cnt = 0;
3584         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3585
3586         size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3587         if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3588         {
3589             cnt += sprintfW(lpszCookies, szCookie);
3590             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3591             strcatW(lpszCookies, szCrLf);
3592
3593             HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3594             HeapFree(GetProcessHeap(), 0, lpszCookies);
3595         }
3596     }
3597     HeapFree(GetProcessHeap(), 0, lpszUrl);
3598 }
3599
3600 /***********************************************************************
3601  *           HTTP_HttpSendRequestW (internal)
3602  *
3603  * Sends the specified request to the HTTP server
3604  *
3605  * RETURNS
3606  *    TRUE  on success
3607  *    FALSE on failure
3608  *
3609  */
3610 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3611         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3612         DWORD dwContentLength, BOOL bEndRequest)
3613 {
3614     INT cnt;
3615     BOOL redirected = FALSE;
3616     LPWSTR requestString = NULL;
3617     INT responseLen;
3618     BOOL loop_next;
3619     INTERNET_ASYNC_RESULT iar;
3620     static const WCHAR szPost[] = { 'P','O','S','T',0 };
3621     static const WCHAR szContentLength[] =
3622         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3623     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3624     DWORD res;
3625
3626     TRACE("--> %p\n", lpwhr);
3627
3628     assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3629
3630     /* if the verb is NULL default to GET */
3631     if (!lpwhr->lpszVerb)
3632         lpwhr->lpszVerb = heap_strdupW(szGET);
3633
3634     if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3635     {
3636         sprintfW(contentLengthStr, szContentLength, dwContentLength);
3637         HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3638         lpwhr->dwBytesToWrite = dwContentLength;
3639     }
3640     if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3641     {
3642         WCHAR *agent_header;
3643         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3644         int len;
3645
3646         len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3647         agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3648         sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3649
3650         HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3651         HeapFree(GetProcessHeap(), 0, agent_header);
3652     }
3653     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3654     {
3655         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3656         HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3657     }
3658     if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3659     {
3660         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3661                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3662         HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3663     }
3664
3665     do
3666     {
3667         DWORD len;
3668         BOOL reusing_connection;
3669         char *ascii_req;
3670
3671         loop_next = FALSE;
3672
3673         /* like native, just in case the caller forgot to call InternetReadFile
3674          * for all the data */
3675         HTTP_DrainContent(lpwhr);
3676         lpwhr->dwContentRead = 0;
3677         if(redirected) {
3678             lpwhr->dwContentLength = ~0u;
3679             lpwhr->dwBytesToWrite = 0;
3680         }
3681
3682         if (TRACE_ON(wininet))
3683         {
3684             LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3685             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3686         }
3687
3688         HTTP_FixURL(lpwhr);
3689         if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3690         {
3691             HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3692         }
3693         HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3694         HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3695
3696         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3697             HTTP_InsertCookies(lpwhr);
3698
3699         /* add the headers the caller supplied */
3700         if( lpszHeaders && dwHeaderLength )
3701         {
3702             HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3703                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3704         }
3705
3706         if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3707         {
3708             WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3709             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3710             HeapFree(GetProcessHeap(), 0, url);
3711         }
3712         else
3713             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3714
3715  
3716         TRACE("Request header -> %s\n", debugstr_w(requestString) );
3717
3718         /* Send the request and store the results */
3719         if(NETCON_connected(&lpwhr->netConnection))
3720             reusing_connection = TRUE;
3721         else
3722             reusing_connection = FALSE;
3723
3724         if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3725             goto lend;
3726
3727         /* send the request as ASCII, tack on the optional data */
3728         if (!lpOptional || redirected)
3729             dwOptionalLength = 0;
3730         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3731                                    NULL, 0, NULL, NULL );
3732         ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3733         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3734                              ascii_req, len, NULL, NULL );
3735         if( lpOptional )
3736             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3737         len = (len + dwOptionalLength - 1);
3738         ascii_req[len] = 0;
3739         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3740
3741         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3742                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3743
3744         res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3745         HeapFree( GetProcessHeap(), 0, ascii_req );
3746
3747         lpwhr->dwBytesWritten = dwOptionalLength;
3748
3749         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3750                               INTERNET_STATUS_REQUEST_SENT,
3751                               &len, sizeof(DWORD));
3752
3753         if (bEndRequest)
3754         {
3755             DWORD dwBufferSize;
3756             DWORD dwStatusCode;
3757
3758             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3759                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3760     
3761             if (res != ERROR_SUCCESS)
3762                 goto lend;
3763     
3764             responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3765             /* FIXME: We should know that connection is closed before sending
3766              * headers. Otherwise wrong callbacks are executed */
3767             if(!responseLen && reusing_connection) {
3768                 TRACE("Connection closed by server, reconnecting\n");
3769                 loop_next = TRUE;
3770                 continue;
3771             }
3772     
3773             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3774                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3775                                 sizeof(DWORD));
3776
3777             HTTP_ProcessCookies(lpwhr);
3778
3779             if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3780
3781             dwBufferSize = sizeof(dwStatusCode);
3782             if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3783                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3784                 dwStatusCode = 0;
3785
3786             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3787             {
3788                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3789                 dwBufferSize=sizeof(szNewLocation);
3790                 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3791                      dwStatusCode == HTTP_STATUS_MOVED ||
3792                      dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3793                     HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3794                 {
3795                     if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3796                     {
3797                         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3798                         lpwhr->lpszVerb = heap_strdupW(szGET);
3799                     }
3800                     HTTP_DrainContent(lpwhr);
3801                     if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3802                     {
3803                         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3804                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3805                         res = HTTP_HandleRedirect(lpwhr, new_url);
3806                         if (res == ERROR_SUCCESS)
3807                         {
3808                             HeapFree(GetProcessHeap(), 0, requestString);
3809                             loop_next = TRUE;
3810                         }
3811                         HeapFree( GetProcessHeap(), 0, new_url );
3812                     }
3813                     redirected = TRUE;
3814                 }
3815             }
3816             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3817             {
3818                 WCHAR szAuthValue[2048];
3819                 dwBufferSize=2048;
3820                 if (dwStatusCode == HTTP_STATUS_DENIED)
3821                 {
3822                     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3823                     DWORD dwIndex = 0;
3824                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3825                     {
3826                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3827                                                  &lpwhr->pAuthInfo,
3828                                                  lpwhr->lpHttpSession->lpszUserName,
3829                                                  lpwhr->lpHttpSession->lpszPassword,
3830                                                  Host->lpszValue))
3831                         {
3832                             HeapFree(GetProcessHeap(), 0, requestString);
3833                             loop_next = TRUE;
3834                             break;
3835                         }
3836                     }
3837
3838                     if(!loop_next) {
3839                         TRACE("Cleaning wrong authorization data\n");
3840                         destroy_authinfo(lpwhr->pAuthInfo);
3841                         lpwhr->pAuthInfo = NULL;
3842                     }
3843                 }
3844                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3845                 {
3846                     DWORD dwIndex = 0;
3847                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3848                     {
3849                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3850                                                  &lpwhr->pProxyAuthInfo,
3851                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3852                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3853                                                  NULL))
3854                         {
3855                             loop_next = TRUE;
3856                             break;
3857                         }
3858                     }
3859
3860                     if(!loop_next) {
3861                         TRACE("Cleaning wrong proxy authorization data\n");
3862                         destroy_authinfo(lpwhr->pProxyAuthInfo);
3863                         lpwhr->pProxyAuthInfo = NULL;
3864                     }
3865                 }
3866             }
3867         }
3868         else
3869             res = ERROR_SUCCESS;
3870     }
3871     while (loop_next);
3872
3873     if(res == ERROR_SUCCESS) {
3874         WCHAR url[INTERNET_MAX_URL_LENGTH];
3875         WCHAR cacheFileName[MAX_PATH+1];
3876         BOOL b;
3877
3878         b = HTTP_GetRequestURL(lpwhr, url);
3879         if(!b) {
3880             WARN("Could not get URL\n");
3881             goto lend;
3882         }
3883
3884         b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3885         if(b) {
3886             HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3887             CloseHandle(lpwhr->hCacheFile);
3888
3889             lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3890             lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3891                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3892             if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3893                 WARN("Could not create file: %u\n", GetLastError());
3894                 lpwhr->hCacheFile = NULL;
3895             }
3896         }else {
3897             WARN("Could not create cache entry: %08x\n", GetLastError());
3898         }
3899     }
3900
3901 lend:
3902
3903     HeapFree(GetProcessHeap(), 0, requestString);
3904
3905     /* TODO: send notification for P3P header */
3906
3907     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3908     {
3909         if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3910             HTTP_ReceiveRequestData(lpwhr, TRUE);
3911         else
3912         {
3913             iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3914             iar.dwError = res;
3915
3916             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3917                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3918                                   sizeof(INTERNET_ASYNC_RESULT));
3919         }
3920     }
3921
3922     TRACE("<--\n");
3923     return res;
3924 }
3925
3926 /***********************************************************************
3927  *
3928  * Helper functions for the HttpSendRequest(Ex) functions
3929  *
3930  */
3931 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3932 {
3933     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3934     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3935
3936     TRACE("%p\n", lpwhr);
3937
3938     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3939             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3940             req->dwContentLength, req->bEndRequest);
3941
3942     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3943 }
3944
3945
3946 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3947 {
3948     INT responseLen;
3949     DWORD dwBufferSize;
3950     INTERNET_ASYNC_RESULT iar;
3951     DWORD res = ERROR_SUCCESS;
3952
3953     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3954                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3955
3956     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3957     if (!responseLen)
3958         res = ERROR_HTTP_HEADER_NOT_FOUND;
3959
3960     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3961                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3962
3963     /* process cookies here. Is this right? */
3964     HTTP_ProcessCookies(lpwhr);
3965
3966     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3967
3968     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3969     {
3970         DWORD dwCode,dwCodeLength = sizeof(DWORD);
3971         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3972             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3973         {
3974             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3975             dwBufferSize=sizeof(szNewLocation);
3976             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3977             {
3978                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3979                 {
3980                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3981                     lpwhr->lpszVerb = heap_strdupW(szGET);
3982                 }
3983                 HTTP_DrainContent(lpwhr);
3984                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3985                 {
3986                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3987                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3988                     res = HTTP_HandleRedirect(lpwhr, new_url);
3989                     if (res == ERROR_SUCCESS)
3990                         res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3991                     HeapFree( GetProcessHeap(), 0, new_url );
3992                 }
3993             }
3994         }
3995     }
3996
3997     iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3998     iar.dwError = res;
3999
4000     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4001                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4002                           sizeof(INTERNET_ASYNC_RESULT));
4003     return res;
4004 }
4005
4006 /***********************************************************************
4007  *           HttpEndRequestA (WININET.@)
4008  *
4009  * Ends an HTTP request that was started by HttpSendRequestEx
4010  *
4011  * RETURNS
4012  *    TRUE      if successful
4013  *    FALSE     on failure
4014  *
4015  */
4016 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4017         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4018 {
4019     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4020
4021     if (lpBuffersOut)
4022     {
4023         SetLastError(ERROR_INVALID_PARAMETER);
4024         return FALSE;
4025     }
4026
4027     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4028 }
4029
4030 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4031 {
4032     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4033     http_request_t *lpwhr = (http_request_t*)work->hdr;
4034
4035     TRACE("%p\n", lpwhr);
4036
4037     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4038 }
4039
4040 /***********************************************************************
4041  *           HttpEndRequestW (WININET.@)
4042  *
4043  * Ends an HTTP request that was started by HttpSendRequestEx
4044  *
4045  * RETURNS
4046  *    TRUE      if successful
4047  *    FALSE     on failure
4048  *
4049  */
4050 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4051         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4052 {
4053     http_request_t *lpwhr;
4054     DWORD res;
4055
4056     TRACE("-->\n");
4057
4058     if (lpBuffersOut)
4059     {
4060         SetLastError(ERROR_INVALID_PARAMETER);
4061         return FALSE;
4062     }
4063
4064     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4065
4066     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4067     {
4068         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4069         if (lpwhr)
4070             WININET_Release( &lpwhr->hdr );
4071         return FALSE;
4072     }
4073     lpwhr->hdr.dwFlags |= dwFlags;
4074
4075     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4076     {
4077         WORKREQUEST work;
4078         struct WORKREQ_HTTPENDREQUESTW *request;
4079
4080         work.asyncproc = AsyncHttpEndRequestProc;
4081         work.hdr = WININET_AddRef( &lpwhr->hdr );
4082
4083         request = &work.u.HttpEndRequestW;
4084         request->dwFlags = dwFlags;
4085         request->dwContext = dwContext;
4086
4087         INTERNET_AsyncCall(&work);
4088         res = ERROR_IO_PENDING;
4089     }
4090     else
4091         res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4092
4093     WININET_Release( &lpwhr->hdr );
4094     TRACE("%u <--\n", res);
4095     if(res != ERROR_SUCCESS)
4096         SetLastError(res);
4097     return res == ERROR_SUCCESS;
4098 }
4099
4100 /***********************************************************************
4101  *           HttpSendRequestExA (WININET.@)
4102  *
4103  * Sends the specified request to the HTTP server and allows chunked
4104  * transfers.
4105  *
4106  * RETURNS
4107  *  Success: TRUE
4108  *  Failure: FALSE, call GetLastError() for more information.
4109  */
4110 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4111                                LPINTERNET_BUFFERSA lpBuffersIn,
4112                                LPINTERNET_BUFFERSA lpBuffersOut,
4113                                DWORD dwFlags, DWORD_PTR dwContext)
4114 {
4115     INTERNET_BUFFERSW BuffersInW;
4116     BOOL rc = FALSE;
4117     DWORD headerlen;
4118     LPWSTR header = NULL;
4119
4120     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4121             lpBuffersOut, dwFlags, dwContext);
4122
4123     if (lpBuffersIn)
4124     {
4125         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4126         if (lpBuffersIn->lpcszHeader)
4127         {
4128             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4129                     lpBuffersIn->dwHeadersLength,0,0);
4130             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4131             if (!(BuffersInW.lpcszHeader = header))
4132             {
4133                 SetLastError(ERROR_OUTOFMEMORY);
4134                 return FALSE;
4135             }
4136             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4137                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4138                     header, headerlen);
4139         }
4140         else
4141             BuffersInW.lpcszHeader = NULL;
4142         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4143         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4144         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4145         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4146         BuffersInW.Next = NULL;
4147     }
4148
4149     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4150
4151     HeapFree(GetProcessHeap(),0,header);
4152
4153     return rc;
4154 }
4155
4156 /***********************************************************************
4157  *           HttpSendRequestExW (WININET.@)
4158  *
4159  * Sends the specified request to the HTTP server and allows chunked
4160  * transfers
4161  *
4162  * RETURNS
4163  *  Success: TRUE
4164  *  Failure: FALSE, call GetLastError() for more information.
4165  */
4166 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4167                    LPINTERNET_BUFFERSW lpBuffersIn,
4168                    LPINTERNET_BUFFERSW lpBuffersOut,
4169                    DWORD dwFlags, DWORD_PTR dwContext)
4170 {
4171     http_request_t *lpwhr;
4172     http_session_t *lpwhs;
4173     appinfo_t *hIC;
4174     DWORD res;
4175
4176     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4177             lpBuffersOut, dwFlags, dwContext);
4178
4179     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4180
4181     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4182     {
4183         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4184         goto lend;
4185     }
4186
4187     lpwhs = lpwhr->lpHttpSession;
4188     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4189     hIC = lpwhs->lpAppInfo;
4190     assert(hIC->hdr.htype == WH_HINIT);
4191
4192     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4193     {
4194         WORKREQUEST workRequest;
4195         struct WORKREQ_HTTPSENDREQUESTW *req;
4196
4197         workRequest.asyncproc = AsyncHttpSendRequestProc;
4198         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4199         req = &workRequest.u.HttpSendRequestW;
4200         if (lpBuffersIn)
4201         {
4202             DWORD size = 0;
4203
4204             if (lpBuffersIn->lpcszHeader)
4205             {
4206                 if (lpBuffersIn->dwHeadersLength == ~0u)
4207                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4208                 else
4209                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4210
4211                 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4212                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4213             }
4214             else req->lpszHeader = NULL;
4215
4216             req->dwHeaderLength = size / sizeof(WCHAR);
4217             req->lpOptional = lpBuffersIn->lpvBuffer;
4218             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4219             req->dwContentLength = lpBuffersIn->dwBufferTotal;
4220         }
4221         else
4222         {
4223             req->lpszHeader = NULL;
4224             req->dwHeaderLength = 0;
4225             req->lpOptional = NULL;
4226             req->dwOptionalLength = 0;
4227             req->dwContentLength = 0;
4228         }
4229
4230         req->bEndRequest = FALSE;
4231
4232         INTERNET_AsyncCall(&workRequest);
4233         /*
4234          * This is from windows.
4235          */
4236         res = ERROR_IO_PENDING;
4237     }
4238     else
4239     {
4240         if (lpBuffersIn)
4241             res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4242                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4243                                         lpBuffersIn->dwBufferTotal, FALSE);
4244         else
4245             res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4246     }
4247
4248 lend:
4249     if ( lpwhr )
4250         WININET_Release( &lpwhr->hdr );
4251
4252     TRACE("<---\n");
4253     SetLastError(res);
4254     return res == ERROR_SUCCESS;
4255 }
4256
4257 /***********************************************************************
4258  *           HttpSendRequestW (WININET.@)
4259  *
4260  * Sends the specified request to the HTTP server
4261  *
4262  * RETURNS
4263  *    TRUE  on success
4264  *    FALSE on failure
4265  *
4266  */
4267 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4268         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4269 {
4270     http_request_t *lpwhr;
4271     http_session_t *lpwhs = NULL;
4272     appinfo_t *hIC = NULL;
4273     DWORD res = ERROR_SUCCESS;
4274
4275     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4276             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4277
4278     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4279     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4280     {
4281         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4282         goto lend;
4283     }
4284
4285     lpwhs = lpwhr->lpHttpSession;
4286     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
4287     {
4288         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4289         goto lend;
4290     }
4291
4292     hIC = lpwhs->lpAppInfo;
4293     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
4294     {
4295         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4296         goto lend;
4297     }
4298
4299     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4300     {
4301         WORKREQUEST workRequest;
4302         struct WORKREQ_HTTPSENDREQUESTW *req;
4303
4304         workRequest.asyncproc = AsyncHttpSendRequestProc;
4305         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4306         req = &workRequest.u.HttpSendRequestW;
4307         if (lpszHeaders)
4308         {
4309             DWORD size;
4310
4311             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4312             else size = dwHeaderLength * sizeof(WCHAR);
4313
4314             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4315             memcpy(req->lpszHeader, lpszHeaders, size);
4316         }
4317         else
4318             req->lpszHeader = 0;
4319         req->dwHeaderLength = dwHeaderLength;
4320         req->lpOptional = lpOptional;
4321         req->dwOptionalLength = dwOptionalLength;
4322         req->dwContentLength = dwOptionalLength;
4323         req->bEndRequest = TRUE;
4324
4325         INTERNET_AsyncCall(&workRequest);
4326         /*
4327          * This is from windows.
4328          */
4329         res = ERROR_IO_PENDING;
4330     }
4331     else
4332     {
4333         res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4334                 dwHeaderLength, lpOptional, dwOptionalLength,
4335                 dwOptionalLength, TRUE);
4336     }
4337 lend:
4338     if( lpwhr )
4339         WININET_Release( &lpwhr->hdr );
4340
4341     SetLastError(res);
4342     return res == ERROR_SUCCESS;
4343 }
4344
4345 /***********************************************************************
4346  *           HttpSendRequestA (WININET.@)
4347  *
4348  * Sends the specified request to the HTTP server
4349  *
4350  * RETURNS
4351  *    TRUE  on success
4352  *    FALSE on failure
4353  *
4354  */
4355 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4356         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4357 {
4358     BOOL result;
4359     LPWSTR szHeaders=NULL;
4360     DWORD nLen=dwHeaderLength;
4361     if(lpszHeaders!=NULL)
4362     {
4363         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4364         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4365         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4366     }
4367     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4368     HeapFree(GetProcessHeap(),0,szHeaders);
4369     return result;
4370 }
4371
4372 /***********************************************************************
4373  *           HTTPSESSION_Destroy (internal)
4374  *
4375  * Deallocate session handle
4376  *
4377  */
4378 static void HTTPSESSION_Destroy(object_header_t *hdr)
4379 {
4380     http_session_t *lpwhs = (http_session_t*) hdr;
4381
4382     TRACE("%p\n", lpwhs);
4383
4384     WININET_Release(&lpwhs->lpAppInfo->hdr);
4385
4386     HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4387     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4388     HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4389     HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4390     HeapFree(GetProcessHeap(), 0, lpwhs);
4391 }
4392
4393 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4394 {
4395     switch(option) {
4396     case INTERNET_OPTION_HANDLE_TYPE:
4397         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4398
4399         if (*size < sizeof(ULONG))
4400             return ERROR_INSUFFICIENT_BUFFER;
4401
4402         *size = sizeof(DWORD);
4403         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4404         return ERROR_SUCCESS;
4405     }
4406
4407     return INET_QueryOption(hdr, option, buffer, size, unicode);
4408 }
4409
4410 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4411 {
4412     http_session_t *ses = (http_session_t*)hdr;
4413
4414     switch(option) {
4415     case INTERNET_OPTION_USERNAME:
4416     {
4417         HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4418         if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4419         return ERROR_SUCCESS;
4420     }
4421     case INTERNET_OPTION_PASSWORD:
4422     {
4423         HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4424         if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4425         return ERROR_SUCCESS;
4426     }
4427     default: break;
4428     }
4429
4430     return ERROR_INTERNET_INVALID_OPTION;
4431 }
4432
4433 static const object_vtbl_t HTTPSESSIONVtbl = {
4434     HTTPSESSION_Destroy,
4435     NULL,
4436     HTTPSESSION_QueryOption,
4437     HTTPSESSION_SetOption,
4438     NULL,
4439     NULL,
4440     NULL,
4441     NULL,
4442     NULL
4443 };
4444
4445
4446 /***********************************************************************
4447  *           HTTP_Connect  (internal)
4448  *
4449  * Create http session handle
4450  *
4451  * RETURNS
4452  *   HINTERNET a session handle on success
4453  *   NULL on failure
4454  *
4455  */
4456 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4457         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4458         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4459         DWORD dwInternalFlags, HINTERNET *ret)
4460 {
4461     http_session_t *lpwhs = NULL;
4462     HINTERNET handle = NULL;
4463     DWORD res = ERROR_SUCCESS;
4464
4465     TRACE("-->\n");
4466
4467     if (!lpszServerName || !lpszServerName[0])
4468         return ERROR_INVALID_PARAMETER;
4469
4470     assert( hIC->hdr.htype == WH_HINIT );
4471
4472     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4473     if (!lpwhs)
4474         return ERROR_OUTOFMEMORY;
4475
4476    /*
4477     * According to my tests. The name is not resolved until a request is sent
4478     */
4479
4480     lpwhs->hdr.htype = WH_HHTTPSESSION;
4481     lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4482     lpwhs->hdr.dwFlags = dwFlags;
4483     lpwhs->hdr.dwContext = dwContext;
4484     lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4485     lpwhs->hdr.refs = 1;
4486     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4487
4488     WININET_AddRef( &hIC->hdr );
4489     lpwhs->lpAppInfo = hIC;
4490     list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4491
4492     handle = WININET_AllocHandle( &lpwhs->hdr );
4493     if (NULL == handle)
4494     {
4495         ERR("Failed to alloc handle\n");
4496         res = ERROR_OUTOFMEMORY;
4497         goto lerror;
4498     }
4499
4500     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4501         if(hIC->lpszProxyBypass)
4502             FIXME("Proxy bypass is ignored.\n");
4503     }
4504     lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4505     lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4506     if (lpszUserName && lpszUserName[0])
4507         lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4508     if (lpszPassword && lpszPassword[0])
4509         lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4510     lpwhs->nServerPort = nServerPort;
4511     lpwhs->nHostPort = nServerPort;
4512
4513     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4514     if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4515     {
4516         INTERNET_SendCallback(&hIC->hdr, dwContext,
4517                               INTERNET_STATUS_HANDLE_CREATED, &handle,
4518                               sizeof(handle));
4519     }
4520
4521 lerror:
4522     if( lpwhs )
4523         WININET_Release( &lpwhs->hdr );
4524
4525 /*
4526  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4527  * windows
4528  */
4529
4530     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4531
4532     if(res == ERROR_SUCCESS)
4533         *ret = handle;
4534     return res;
4535 }
4536
4537
4538 /***********************************************************************
4539  *           HTTP_OpenConnection (internal)
4540  *
4541  * Connect to a web server
4542  *
4543  * RETURNS
4544  *
4545  *   TRUE  on success
4546  *   FALSE on failure
4547  */
4548 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4549 {
4550     http_session_t *lpwhs;
4551     appinfo_t *hIC = NULL;
4552     char szaddr[INET6_ADDRSTRLEN];
4553     const void *addr;
4554     DWORD res = ERROR_SUCCESS;
4555
4556     TRACE("-->\n");
4557
4558
4559     if (lpwhr->hdr.htype != WH_HHTTPREQ)
4560     {
4561         res = ERROR_INVALID_PARAMETER;
4562         goto lend;
4563     }
4564
4565     if (NETCON_connected(&lpwhr->netConnection))
4566         goto lend;
4567     if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4568
4569     lpwhs = lpwhr->lpHttpSession;
4570
4571     hIC = lpwhs->lpAppInfo;
4572     switch (lpwhs->socketAddress.ss_family)
4573     {
4574     case AF_INET:
4575         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4576         break;
4577     case AF_INET6:
4578         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4579         break;
4580     default:
4581         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4582         return ERROR_INTERNET_NAME_NOT_RESOLVED;
4583     }
4584     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4585     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4586                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4587                           szaddr,
4588                           strlen(szaddr)+1);
4589
4590     res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4591     if (res != ERROR_SUCCESS)
4592     {
4593         WARN("Socket creation failed: %u\n", res);
4594         goto lend;
4595     }
4596
4597     res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4598                          lpwhs->sa_len);
4599     if(res != ERROR_SUCCESS)
4600        goto lend;
4601
4602     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4603             INTERNET_STATUS_CONNECTED_TO_SERVER,
4604             szaddr, strlen(szaddr)+1);
4605
4606     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4607     {
4608         /* Note: we differ from Microsoft's WinINet here. they seem to have
4609          * a bug that causes no status callbacks to be sent when starting
4610          * a tunnel to a proxy server using the CONNECT verb. i believe our
4611          * behaviour to be more correct and to not cause any incompatibilities
4612          * because using a secure connection through a proxy server is a rare
4613          * case that would be hard for anyone to depend on */
4614         if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4615             HTTPREQ_CloseConnection(&lpwhr->hdr);
4616             goto lend;
4617         }
4618
4619         res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4620         if(res != ERROR_SUCCESS)
4621         {
4622             WARN("Couldn't connect securely to host\n");
4623
4624             if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4625                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4626                     || res == ERROR_INTERNET_INVALID_CA
4627                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
4628                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4629                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
4630                     || res == ERROR_INTERNET_SEC_INVALID_CERT
4631                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4632                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4633
4634             HTTPREQ_CloseConnection(&lpwhr->hdr);
4635             goto lend;
4636         }
4637     }
4638
4639
4640 lend:
4641     lpwhr->read_pos = lpwhr->read_size = 0;
4642     lpwhr->read_chunked = FALSE;
4643
4644     TRACE("%d <--\n", res);
4645     return res;
4646 }
4647
4648
4649 /***********************************************************************
4650  *           HTTP_clear_response_headers (internal)
4651  *
4652  * clear out any old response headers
4653  */
4654 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4655 {
4656     DWORD i;
4657
4658     for( i=0; i<lpwhr->nCustHeaders; i++)
4659     {
4660         if( !lpwhr->pCustHeaders[i].lpszField )
4661             continue;
4662         if( !lpwhr->pCustHeaders[i].lpszValue )
4663             continue;
4664         if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4665             continue;
4666         HTTP_DeleteCustomHeader( lpwhr, i );
4667         i--;
4668     }
4669 }
4670
4671 /***********************************************************************
4672  *           HTTP_GetResponseHeaders (internal)
4673  *
4674  * Read server response
4675  *
4676  * RETURNS
4677  *
4678  *   TRUE  on success
4679  *   FALSE on error
4680  */
4681 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4682 {
4683     INT cbreaks = 0;
4684     WCHAR buffer[MAX_REPLY_LEN];
4685     DWORD buflen = MAX_REPLY_LEN;
4686     BOOL bSuccess = FALSE;
4687     INT  rc = 0;
4688     char bufferA[MAX_REPLY_LEN];
4689     LPWSTR status_code = NULL, status_text = NULL;
4690     DWORD cchMaxRawHeaders = 1024;
4691     LPWSTR lpszRawHeaders = NULL;
4692     LPWSTR temp;
4693     DWORD cchRawHeaders = 0;
4694     BOOL codeHundred = FALSE;
4695
4696     TRACE("-->\n");
4697
4698     if (!NETCON_connected(&lpwhr->netConnection))
4699         goto lend;
4700
4701     do {
4702         static const WCHAR szHundred[] = {'1','0','0',0};
4703         /*
4704          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4705          */
4706         buflen = MAX_REPLY_LEN;
4707         if (!read_line(lpwhr, bufferA, &buflen))
4708             goto lend;
4709
4710         /* clear old response headers (eg. from a redirect response) */
4711         if (clear) {
4712             HTTP_clear_response_headers( lpwhr );
4713             clear = FALSE;
4714         }
4715
4716         rc += buflen;
4717         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4718         /* check is this a status code line? */
4719         if (!strncmpW(buffer, g_szHttp1_0, 4))
4720         {
4721             /* split the version from the status code */
4722             status_code = strchrW( buffer, ' ' );
4723             if( !status_code )
4724                 goto lend;
4725             *status_code++=0;
4726
4727             /* split the status code from the status text */
4728             status_text = strchrW( status_code, ' ' );
4729             if( !status_text )
4730                 goto lend;
4731             *status_text++=0;
4732
4733             TRACE("version [%s] status code [%s] status text [%s]\n",
4734                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4735
4736             codeHundred = (!strcmpW(status_code, szHundred));
4737         }
4738         else if (!codeHundred)
4739         {
4740             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4741
4742             HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4743             HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4744
4745             lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4746             lpwhr->lpszStatusText = heap_strdupW(szOK);
4747
4748             HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4749             lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4750
4751             bSuccess = TRUE;
4752             goto lend;
4753         }
4754     } while (codeHundred);
4755
4756     /* Add status code */
4757     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4758             HTTP_ADDHDR_FLAG_REPLACE);
4759
4760     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4761     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4762
4763     lpwhr->lpszVersion = heap_strdupW(buffer);
4764     lpwhr->lpszStatusText = heap_strdupW(status_text);
4765
4766     /* Restore the spaces */
4767     *(status_code-1) = ' ';
4768     *(status_text-1) = ' ';
4769
4770     /* regenerate raw headers */
4771     lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4772     if (!lpszRawHeaders) goto lend;
4773
4774     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4775         cchMaxRawHeaders *= 2;
4776     temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4777     if (temp == NULL) goto lend;
4778     lpszRawHeaders = temp;
4779     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4780     cchRawHeaders += (buflen-1);
4781     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4782     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4783     lpszRawHeaders[cchRawHeaders] = '\0';
4784
4785     /* Parse each response line */
4786     do
4787     {
4788         buflen = MAX_REPLY_LEN;
4789         if (read_line(lpwhr, bufferA, &buflen))
4790         {
4791             LPWSTR * pFieldAndValue;
4792
4793             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4794
4795             if (!bufferA[0]) break;
4796             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4797
4798             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4799             if (pFieldAndValue)
4800             {
4801                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4802                     cchMaxRawHeaders *= 2;
4803                 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4804                 if (temp == NULL) goto lend;
4805                 lpszRawHeaders = temp;
4806                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4807                 cchRawHeaders += (buflen-1);
4808                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4809                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4810                 lpszRawHeaders[cchRawHeaders] = '\0';
4811
4812                 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4813                                    HTTP_ADDREQ_FLAG_ADD );
4814
4815                 HTTP_FreeTokens(pFieldAndValue);
4816             }
4817         }
4818         else
4819         {
4820             cbreaks++;
4821             if (cbreaks >= 2)
4822                break;
4823         }
4824     }while(1);
4825
4826     /* make sure the response header is terminated with an empty line.  Some apps really
4827        truly care about that empty line being there for some reason.  Just add it to the
4828        header. */
4829     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4830     {
4831         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4832         temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4833         if (temp == NULL) goto lend;
4834         lpszRawHeaders = temp;
4835     }
4836
4837     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4838
4839     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4840     lpwhr->lpszRawHeaders = lpszRawHeaders;
4841     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4842     bSuccess = TRUE;
4843
4844 lend:
4845
4846     TRACE("<--\n");
4847     if (bSuccess)
4848         return rc;
4849     else
4850     {
4851         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4852         return 0;
4853     }
4854 }
4855
4856 /***********************************************************************
4857  *           HTTP_InterpretHttpHeader (internal)
4858  *
4859  * Parse server response
4860  *
4861  * RETURNS
4862  *
4863  *   Pointer to array of field, value, NULL on success.
4864  *   NULL on error.
4865  */
4866 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4867 {
4868     LPWSTR * pTokenPair;
4869     LPWSTR pszColon;
4870     INT len;
4871
4872     pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4873
4874     pszColon = strchrW(buffer, ':');
4875     /* must have two tokens */
4876     if (!pszColon)
4877     {
4878         HTTP_FreeTokens(pTokenPair);
4879         if (buffer[0])
4880             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4881         return NULL;
4882     }
4883
4884     pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4885     if (!pTokenPair[0])
4886     {
4887         HTTP_FreeTokens(pTokenPair);
4888         return NULL;
4889     }
4890     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4891     pTokenPair[0][pszColon - buffer] = '\0';
4892
4893     /* skip colon */
4894     pszColon++;
4895     len = strlenW(pszColon);
4896     pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4897     if (!pTokenPair[1])
4898     {
4899         HTTP_FreeTokens(pTokenPair);
4900         return NULL;
4901     }
4902     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4903
4904     strip_spaces(pTokenPair[0]);
4905     strip_spaces(pTokenPair[1]);
4906
4907     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4908     return pTokenPair;
4909 }
4910
4911 /***********************************************************************
4912  *           HTTP_ProcessHeader (internal)
4913  *
4914  * Stuff header into header tables according to <dwModifier>
4915  *
4916  */
4917
4918 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4919
4920 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4921 {
4922     LPHTTPHEADERW lphttpHdr = NULL;
4923     INT index = -1;
4924     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4925     DWORD res = ERROR_HTTP_INVALID_HEADER;
4926
4927     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4928
4929     /* REPLACE wins out over ADD */
4930     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4931         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4932     
4933     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4934         index = -1;
4935     else
4936         index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4937
4938     if (index >= 0)
4939     {
4940         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4941             return ERROR_HTTP_INVALID_HEADER;
4942         lphttpHdr = &lpwhr->pCustHeaders[index];
4943     }
4944     else if (value)
4945     {
4946         HTTPHEADERW hdr;
4947
4948         hdr.lpszField = (LPWSTR)field;
4949         hdr.lpszValue = (LPWSTR)value;
4950         hdr.wFlags = hdr.wCount = 0;
4951
4952         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4953             hdr.wFlags |= HDR_ISREQUEST;
4954
4955         return HTTP_InsertCustomHeader(lpwhr, &hdr);
4956     }
4957     /* no value to delete */
4958     else return ERROR_SUCCESS;
4959
4960     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4961             lphttpHdr->wFlags |= HDR_ISREQUEST;
4962     else
4963         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4964
4965     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4966     {
4967         HTTP_DeleteCustomHeader( lpwhr, index );
4968
4969         if (value)
4970         {
4971             HTTPHEADERW hdr;
4972
4973             hdr.lpszField = (LPWSTR)field;
4974             hdr.lpszValue = (LPWSTR)value;
4975             hdr.wFlags = hdr.wCount = 0;
4976
4977             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4978                 hdr.wFlags |= HDR_ISREQUEST;
4979
4980             return HTTP_InsertCustomHeader(lpwhr, &hdr);
4981         }
4982
4983         return ERROR_SUCCESS;
4984     }
4985     else if (dwModifier & COALESCEFLAGS)
4986     {
4987         LPWSTR lpsztmp;
4988         WCHAR ch = 0;
4989         INT len = 0;
4990         INT origlen = strlenW(lphttpHdr->lpszValue);
4991         INT valuelen = strlenW(value);
4992
4993         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4994         {
4995             ch = ',';
4996             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4997         }
4998         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4999         {
5000             ch = ';';
5001             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5002         }
5003
5004         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5005
5006         lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5007         if (lpsztmp)
5008         {
5009             lphttpHdr->lpszValue = lpsztmp;
5010     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5011             if (ch > 0)
5012             {
5013                 lphttpHdr->lpszValue[origlen] = ch;
5014                 origlen++;
5015                 lphttpHdr->lpszValue[origlen] = ' ';
5016                 origlen++;
5017             }
5018
5019             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5020             lphttpHdr->lpszValue[len] = '\0';
5021             res = ERROR_SUCCESS;
5022         }
5023         else
5024         {
5025             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5026             res = ERROR_OUTOFMEMORY;
5027         }
5028     }
5029     TRACE("<-- %d\n", res);
5030     return res;
5031 }
5032
5033
5034 /***********************************************************************
5035  *           HTTP_FinishedReading (internal)
5036  *
5037  * Called when all content from server has been read by client.
5038  *
5039  */
5040 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5041 {
5042     BOOL keepalive = HTTP_KeepAlive(lpwhr);
5043
5044     TRACE("\n");
5045
5046
5047     if (!keepalive)
5048     {
5049         HTTPREQ_CloseConnection(&lpwhr->hdr);
5050     }
5051
5052     /* FIXME: store data in the URL cache here */
5053
5054     return TRUE;
5055 }
5056
5057
5058 /***********************************************************************
5059  *           HTTP_GetCustomHeaderIndex (internal)
5060  *
5061  * Return index of custom header from header array
5062  *
5063  */
5064 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5065                                      int requested_index, BOOL request_only)
5066 {
5067     DWORD index;
5068
5069     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5070
5071     for (index = 0; index < lpwhr->nCustHeaders; index++)
5072     {
5073         if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5074             continue;
5075
5076         if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5077             continue;
5078
5079         if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5080             continue;
5081
5082         if (requested_index == 0)
5083             break;
5084         requested_index --;
5085     }
5086
5087     if (index >= lpwhr->nCustHeaders)
5088         index = -1;
5089
5090     TRACE("Return: %d\n", index);
5091     return index;
5092 }
5093
5094
5095 /***********************************************************************
5096  *           HTTP_InsertCustomHeader (internal)
5097  *
5098  * Insert header into array
5099  *
5100  */
5101 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5102 {
5103     INT count;
5104     LPHTTPHEADERW lph = NULL;
5105
5106     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5107     count = lpwhr->nCustHeaders + 1;
5108     if (count > 1)
5109         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5110     else
5111         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5112
5113     if (!lph)
5114         return ERROR_OUTOFMEMORY;
5115
5116     lpwhr->pCustHeaders = lph;
5117     lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5118     lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5119     lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5120     lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5121     lpwhr->nCustHeaders++;
5122
5123     return ERROR_SUCCESS;
5124 }
5125
5126
5127 /***********************************************************************
5128  *           HTTP_DeleteCustomHeader (internal)
5129  *
5130  * Delete header from array
5131  *  If this function is called, the indexs may change.
5132  */
5133 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5134 {
5135     if( lpwhr->nCustHeaders <= 0 )
5136         return FALSE;
5137     if( index >= lpwhr->nCustHeaders )
5138         return FALSE;
5139     lpwhr->nCustHeaders--;
5140
5141     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5142     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5143
5144     memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5145              (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5146     memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5147
5148     return TRUE;
5149 }
5150
5151
5152 /***********************************************************************
5153  *           HTTP_VerifyValidHeader (internal)
5154  *
5155  * Verify the given header is not invalid for the given http request
5156  *
5157  */
5158 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5159 {
5160     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5161     if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5162         return ERROR_HTTP_INVALID_HEADER;
5163
5164     return ERROR_SUCCESS;
5165 }
5166
5167 /***********************************************************************
5168  *          IsHostInProxyBypassList (@)
5169  *
5170  * Undocumented
5171  *
5172  */
5173 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5174 {
5175    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5176    return FALSE;
5177 }