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