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