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