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