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