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