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