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