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