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