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