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