winhttp: Return error directly from netconn_verify_cert.
[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 DWORD 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) != ERROR_SUCCESS)
288         lpwhr->dwContentLength = ~0u;
289
290     size = sizeof(encoding);
291     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
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) == ERROR_SUCCESS)
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, &dwBufferSize, NULL) == ERROR_SUCCESS
1511         && !strcmpiW(szVersion, g_szHttp1_1))
1512     {
1513         keepalive = TRUE;
1514     }
1515
1516     dwBufferSize = sizeof(szConnectionResponse);
1517     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1518         || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1519     {
1520         keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1521     }
1522
1523     return keepalive;
1524 }
1525
1526 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1527 {
1528     http_request_t *req = (http_request_t*)hdr;
1529
1530     switch(option) {
1531     case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1532     {
1533         http_session_t *lpwhs = req->lpHttpSession;
1534         INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1535
1536         FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1537
1538         if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1539             return ERROR_INSUFFICIENT_BUFFER;
1540         *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1541         /* FIXME: can't get a SOCKET from our connection since we don't use
1542          * winsock
1543          */
1544         info->Socket = 0;
1545         /* FIXME: get source port from req->netConnection */
1546         info->SourcePort = 0;
1547         info->DestPort = lpwhs->nHostPort;
1548         info->Flags = 0;
1549         if (HTTP_KeepAlive(req))
1550             info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1551         if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1552             info->Flags |= IDSI_FLAG_PROXY;
1553         if (req->netConnection.useSSL)
1554             info->Flags |= IDSI_FLAG_SECURE;
1555
1556         return ERROR_SUCCESS;
1557     }
1558
1559     case INTERNET_OPTION_SECURITY_FLAGS:
1560     {
1561         http_session_t *lpwhs;
1562         lpwhs = req->lpHttpSession;
1563
1564         if (*size < sizeof(ULONG))
1565             return ERROR_INSUFFICIENT_BUFFER;
1566
1567         *size = sizeof(DWORD);
1568         if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1569             *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1570         else
1571             *(DWORD*)buffer = 0;
1572         FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1573         return ERROR_SUCCESS;
1574     }
1575
1576     case INTERNET_OPTION_HANDLE_TYPE:
1577         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1578
1579         if (*size < sizeof(ULONG))
1580             return ERROR_INSUFFICIENT_BUFFER;
1581
1582         *size = sizeof(DWORD);
1583         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1584         return ERROR_SUCCESS;
1585
1586     case INTERNET_OPTION_URL: {
1587         WCHAR url[INTERNET_MAX_URL_LENGTH];
1588         HTTPHEADERW *host;
1589         DWORD len;
1590         WCHAR *pch;
1591
1592         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1593
1594         TRACE("INTERNET_OPTION_URL\n");
1595
1596         host = HTTP_GetHeader(req, hostW);
1597         strcpyW(url, httpW);
1598         strcatW(url, host->lpszValue);
1599         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1600             *pch = 0;
1601         strcatW(url, req->lpszPath);
1602
1603         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1604
1605         if(unicode) {
1606             len = (strlenW(url)+1) * sizeof(WCHAR);
1607             if(*size < len)
1608                 return ERROR_INSUFFICIENT_BUFFER;
1609
1610             *size = len;
1611             strcpyW(buffer, url);
1612             return ERROR_SUCCESS;
1613         }else {
1614             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1615             if(len > *size)
1616                 return ERROR_INSUFFICIENT_BUFFER;
1617
1618             *size = len;
1619             return ERROR_SUCCESS;
1620         }
1621     }
1622
1623     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1624         INTERNET_CACHE_ENTRY_INFOW *info;
1625         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1626         WCHAR url[INTERNET_MAX_URL_LENGTH];
1627         DWORD nbytes, error;
1628         BOOL ret;
1629
1630         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1631
1632         if (*size < sizeof(*ts))
1633         {
1634             *size = sizeof(*ts);
1635             return ERROR_INSUFFICIENT_BUFFER;
1636         }
1637         nbytes = 0;
1638         HTTP_GetRequestURL(req, url);
1639         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1640         error = GetLastError();
1641         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1642         {
1643             if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1644                 return ERROR_OUTOFMEMORY;
1645
1646             GetUrlCacheEntryInfoW(url, info, &nbytes);
1647
1648             ts->ftExpires = info->ExpireTime;
1649             ts->ftLastModified = info->LastModifiedTime;
1650
1651             HeapFree(GetProcessHeap(), 0, info);
1652             *size = sizeof(*ts);
1653             return ERROR_SUCCESS;
1654         }
1655         return error;
1656     }
1657
1658     case INTERNET_OPTION_DATAFILE_NAME: {
1659         DWORD req_size;
1660
1661         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1662
1663         if(!req->lpszCacheFile) {
1664             *size = 0;
1665             return ERROR_INTERNET_ITEM_NOT_FOUND;
1666         }
1667
1668         if(unicode) {
1669             req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1670             if(*size < req_size)
1671                 return ERROR_INSUFFICIENT_BUFFER;
1672
1673             *size = req_size;
1674             memcpy(buffer, req->lpszCacheFile, *size);
1675             return ERROR_SUCCESS;
1676         }else {
1677             req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1678             if (req_size > *size)
1679                 return ERROR_INSUFFICIENT_BUFFER;
1680
1681             *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1682                     -1, buffer, *size, NULL, NULL);
1683             return ERROR_SUCCESS;
1684         }
1685     }
1686
1687     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1688         PCCERT_CONTEXT context;
1689
1690         if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1691             *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1692             return ERROR_INSUFFICIENT_BUFFER;
1693         }
1694
1695         context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1696         if(context) {
1697             INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1698             DWORD len;
1699
1700             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1701             info->ftExpiry = context->pCertInfo->NotAfter;
1702             info->ftStart = context->pCertInfo->NotBefore;
1703             if(unicode) {
1704                 len = CertNameToStrW(context->dwCertEncodingType,
1705                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1706                 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1707                 if(info->lpszSubjectInfo)
1708                     CertNameToStrW(context->dwCertEncodingType,
1709                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1710                              info->lpszSubjectInfo, len);
1711                 len = CertNameToStrW(context->dwCertEncodingType,
1712                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1713                 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1714                 if (info->lpszIssuerInfo)
1715                     CertNameToStrW(context->dwCertEncodingType,
1716                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1717                              info->lpszIssuerInfo, len);
1718             }else {
1719                 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1720
1721                 len = CertNameToStrA(context->dwCertEncodingType,
1722                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1723                 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1724                 if(infoA->lpszSubjectInfo)
1725                     CertNameToStrA(context->dwCertEncodingType,
1726                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1727                              infoA->lpszSubjectInfo, len);
1728                 len = CertNameToStrA(context->dwCertEncodingType,
1729                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1730                 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1731                 if(infoA->lpszIssuerInfo)
1732                     CertNameToStrA(context->dwCertEncodingType,
1733                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1734                              infoA->lpszIssuerInfo, len);
1735             }
1736
1737             /*
1738              * Contrary to MSDN, these do not appear to be set.
1739              * lpszProtocolName
1740              * lpszSignatureAlgName
1741              * lpszEncryptionAlgName
1742              * dwKeySize
1743              */
1744             CertFreeCertificateContext(context);
1745             return ERROR_SUCCESS;
1746         }
1747     }
1748     }
1749
1750     return INET_QueryOption(option, buffer, size, unicode);
1751 }
1752
1753 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1754 {
1755     http_request_t *req = (http_request_t*)hdr;
1756
1757     switch(option) {
1758     case INTERNET_OPTION_SEND_TIMEOUT:
1759     case INTERNET_OPTION_RECEIVE_TIMEOUT:
1760         TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1761
1762         if (size != sizeof(DWORD))
1763             return ERROR_INVALID_PARAMETER;
1764
1765         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1766                     *(DWORD*)buffer);
1767
1768     case INTERNET_OPTION_USERNAME:
1769         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1770         if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1771         return ERROR_SUCCESS;
1772
1773     case INTERNET_OPTION_PASSWORD:
1774         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1775         if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1776         return ERROR_SUCCESS;
1777     case INTERNET_OPTION_HTTP_DECODING:
1778         if(size != sizeof(BOOL))
1779             return ERROR_INVALID_PARAMETER;
1780         req->decoding = *(BOOL*)buffer;
1781         return ERROR_SUCCESS;
1782     }
1783
1784     return ERROR_INTERNET_INVALID_OPTION;
1785 }
1786
1787 /* read some more data into the read buffer (the read section must be held) */
1788 static DWORD read_more_data( http_request_t *req, int maxlen )
1789 {
1790     DWORD res;
1791     int len;
1792
1793     if (req->read_pos)
1794     {
1795         /* move existing data to the start of the buffer */
1796         if(req->read_size)
1797             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1798         req->read_pos = 0;
1799     }
1800
1801     if (maxlen == -1) maxlen = sizeof(req->read_buf);
1802
1803     res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1804                        maxlen - req->read_size, 0, &len );
1805     if(res == ERROR_SUCCESS)
1806         req->read_size += len;
1807
1808     return res;
1809 }
1810
1811 /* remove some amount of data from the read buffer (the read section must be held) */
1812 static void remove_data( http_request_t *req, int count )
1813 {
1814     if (!(req->read_size -= count)) req->read_pos = 0;
1815     else req->read_pos += count;
1816 }
1817
1818 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1819 {
1820     int count, bytes_read, pos = 0;
1821     DWORD res;
1822
1823     EnterCriticalSection( &req->read_section );
1824     for (;;)
1825     {
1826         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1827
1828         if (eol)
1829         {
1830             count = eol - (req->read_buf + req->read_pos);
1831             bytes_read = count + 1;
1832         }
1833         else count = bytes_read = req->read_size;
1834
1835         count = min( count, *len - pos );
1836         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1837         pos += count;
1838         remove_data( req, bytes_read );
1839         if (eol) break;
1840
1841         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1842         {
1843             *len = 0;
1844             TRACE( "returning empty string\n" );
1845             LeaveCriticalSection( &req->read_section );
1846             INTERNET_SetLastError(res);
1847             return FALSE;
1848         }
1849     }
1850     LeaveCriticalSection( &req->read_section );
1851
1852     if (pos < *len)
1853     {
1854         if (pos && buffer[pos - 1] == '\r') pos--;
1855         *len = pos + 1;
1856     }
1857     buffer[*len - 1] = 0;
1858     TRACE( "returning %s\n", debugstr_a(buffer));
1859     return TRUE;
1860 }
1861
1862 /* discard data contents until we reach end of line (the read section must be held) */
1863 static DWORD discard_eol( http_request_t *req )
1864 {
1865     DWORD res;
1866
1867     do
1868     {
1869         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1870         if (eol)
1871         {
1872             remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1873             break;
1874         }
1875         req->read_pos = req->read_size = 0;  /* discard everything */
1876         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1877     } while (req->read_size);
1878     return ERROR_SUCCESS;
1879 }
1880
1881 /* read the size of the next chunk (the read section must be held) */
1882 static DWORD start_next_chunk( http_request_t *req )
1883 {
1884     DWORD chunk_size = 0, res;
1885
1886     if (!req->dwContentLength) return ERROR_SUCCESS;
1887     if (req->dwContentLength == req->dwContentRead)
1888     {
1889         /* read terminator for the previous chunk */
1890         if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1891         req->dwContentLength = ~0u;
1892         req->dwContentRead = 0;
1893     }
1894     for (;;)
1895     {
1896         while (req->read_size)
1897         {
1898             char ch = req->read_buf[req->read_pos];
1899             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1900             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1901             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1902             else if (ch == ';' || ch == '\r' || ch == '\n')
1903             {
1904                 TRACE( "reading %u byte chunk\n", chunk_size );
1905                 req->dwContentLength = chunk_size;
1906                 req->dwContentRead = 0;
1907                 return discard_eol( req );
1908             }
1909             remove_data( req, 1 );
1910         }
1911         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1912         if (!req->read_size)
1913         {
1914             req->dwContentLength = req->dwContentRead = 0;
1915             return ERROR_SUCCESS;
1916         }
1917     }
1918 }
1919
1920 /* check if we have reached the end of the data to read (the read section must be held) */
1921 static BOOL end_of_read_data( http_request_t *req )
1922 {
1923     if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1924     if (req->read_chunked) return (req->dwContentLength == 0);
1925     if (req->dwContentLength == ~0u) return FALSE;
1926     return (req->dwContentLength == req->dwContentRead);
1927 }
1928
1929 /* fetch some more data into the read buffer (the read section must be held) */
1930 static DWORD refill_buffer( http_request_t *req )
1931 {
1932     int len = sizeof(req->read_buf);
1933     DWORD res;
1934
1935     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1936     {
1937         if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1938     }
1939
1940     if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1941     if (len <= req->read_size) return ERROR_SUCCESS;
1942
1943     if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1944     if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1945     return ERROR_SUCCESS;
1946 }
1947
1948 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1949 {
1950     DWORD ret = ERROR_SUCCESS;
1951     int read = 0;
1952
1953 #ifdef HAVE_ZLIB
1954     z_stream *zstream = &req->gzip_stream->zstream;
1955     int zres;
1956
1957     while(read < size && !req->gzip_stream->end_of_data) {
1958         if(!req->read_size) {
1959             if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1960                 break;
1961         }
1962
1963         zstream->next_in = req->read_buf+req->read_pos;
1964         zstream->avail_in = req->read_size;
1965         zstream->next_out = buf+read;
1966         zstream->avail_out = size-read;
1967         zres = inflate(zstream, Z_FULL_FLUSH);
1968         read = size - zstream->avail_out;
1969         req->dwContentRead += req->read_size-zstream->avail_in;
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         req->dwContentRead += bytes_read;
2090     }
2091 done:
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 DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2654         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2655 {
2656     LPHTTPHEADERW lphttpHdr = NULL;
2657     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2658     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2659     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2660     INT index = -1;
2661
2662     /* Find requested header structure */
2663     switch (level)
2664     {
2665     case HTTP_QUERY_CUSTOM:
2666         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2667         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2668         break;
2669     case HTTP_QUERY_RAW_HEADERS_CRLF:
2670         {
2671             LPWSTR headers;
2672             DWORD len = 0;
2673             DWORD res = ERROR_INVALID_PARAMETER;
2674
2675             if (request_only)
2676                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2677             else
2678                 headers = lpwhr->lpszRawHeaders;
2679
2680             if (headers)
2681                 len = strlenW(headers) * sizeof(WCHAR);
2682
2683             if (len + sizeof(WCHAR) > *lpdwBufferLength)
2684             {
2685                 len += sizeof(WCHAR);
2686                 res = ERROR_INSUFFICIENT_BUFFER;
2687             }
2688             else if (lpBuffer)
2689             {
2690                 if (headers)
2691                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2692                 else
2693                 {
2694                     len = strlenW(szCrLf) * sizeof(WCHAR);
2695                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2696                 }
2697                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2698                 res = ERROR_SUCCESS;
2699             }
2700             *lpdwBufferLength = len;
2701
2702             if (request_only)
2703                 HeapFree(GetProcessHeap(), 0, headers);
2704             return res;
2705         }
2706     case HTTP_QUERY_RAW_HEADERS:
2707         {
2708             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2709             DWORD i, size = 0;
2710             LPWSTR pszString = lpBuffer;
2711
2712             for (i = 0; ppszRawHeaderLines[i]; i++)
2713                 size += strlenW(ppszRawHeaderLines[i]) + 1;
2714
2715             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2716             {
2717                 HTTP_FreeTokens(ppszRawHeaderLines);
2718                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2719                 return ERROR_INSUFFICIENT_BUFFER;
2720             }
2721             if (pszString)
2722             {
2723                 for (i = 0; ppszRawHeaderLines[i]; i++)
2724                 {
2725                     DWORD len = strlenW(ppszRawHeaderLines[i]);
2726                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2727                     pszString += len+1;
2728                 }
2729                 *pszString = '\0';
2730                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2731             }
2732             *lpdwBufferLength = size * sizeof(WCHAR);
2733             HTTP_FreeTokens(ppszRawHeaderLines);
2734
2735             return ERROR_SUCCESS;
2736         }
2737     case HTTP_QUERY_STATUS_TEXT:
2738         if (lpwhr->lpszStatusText)
2739         {
2740             DWORD len = strlenW(lpwhr->lpszStatusText);
2741             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2742             {
2743                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2744                 return ERROR_INSUFFICIENT_BUFFER;
2745             }
2746             if (lpBuffer)
2747             {
2748                 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2749                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2750             }
2751             *lpdwBufferLength = len * sizeof(WCHAR);
2752             return ERROR_SUCCESS;
2753         }
2754         break;
2755     case HTTP_QUERY_VERSION:
2756         if (lpwhr->lpszVersion)
2757         {
2758             DWORD len = strlenW(lpwhr->lpszVersion);
2759             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2760             {
2761                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2762                 return ERROR_INSUFFICIENT_BUFFER;
2763             }
2764             if (lpBuffer)
2765             {
2766                 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2767                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2768             }
2769             *lpdwBufferLength = len * sizeof(WCHAR);
2770             return ERROR_SUCCESS;
2771         }
2772         break;
2773     case HTTP_QUERY_CONTENT_ENCODING:
2774         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2775                 requested_index,request_only);
2776         break;
2777     default:
2778         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2779
2780         if (level < LAST_TABLE_HEADER && header_lookup[level])
2781             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2782                                               requested_index,request_only);
2783     }
2784
2785     if (index >= 0)
2786         lphttpHdr = &lpwhr->pCustHeaders[index];
2787
2788     /* Ensure header satisfies requested attributes */
2789     if (!lphttpHdr ||
2790         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2791          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2792     {
2793         return ERROR_HTTP_HEADER_NOT_FOUND;
2794     }
2795
2796     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2797
2798     /* coalesce value to requested type */
2799     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2800     {
2801         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2802         TRACE(" returning number: %d\n", *(int *)lpBuffer);
2803      }
2804     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2805     {
2806         time_t tmpTime;
2807         struct tm tmpTM;
2808         SYSTEMTIME *STHook;
2809
2810         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2811
2812         tmpTM = *gmtime(&tmpTime);
2813         STHook = (SYSTEMTIME *)lpBuffer;
2814         STHook->wDay = tmpTM.tm_mday;
2815         STHook->wHour = tmpTM.tm_hour;
2816         STHook->wMilliseconds = 0;
2817         STHook->wMinute = tmpTM.tm_min;
2818         STHook->wDayOfWeek = tmpTM.tm_wday;
2819         STHook->wMonth = tmpTM.tm_mon + 1;
2820         STHook->wSecond = tmpTM.tm_sec;
2821         STHook->wYear = tmpTM.tm_year;
2822
2823         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2824               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2825               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2826     }
2827     else if (lphttpHdr->lpszValue)
2828     {
2829         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2830
2831         if (len > *lpdwBufferLength)
2832         {
2833             *lpdwBufferLength = len;
2834             return ERROR_INSUFFICIENT_BUFFER;
2835         }
2836         if (lpBuffer)
2837         {
2838             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2839             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2840         }
2841         *lpdwBufferLength = len - sizeof(WCHAR);
2842     }
2843     return ERROR_SUCCESS;
2844 }
2845
2846 /***********************************************************************
2847  *           HttpQueryInfoW (WININET.@)
2848  *
2849  * Queries for information about an HTTP request
2850  *
2851  * RETURNS
2852  *    TRUE  on success
2853  *    FALSE on failure
2854  *
2855  */
2856 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2857         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2858 {
2859     http_request_t *lpwhr;
2860     DWORD res;
2861
2862     if (TRACE_ON(wininet)) {
2863 #define FE(x) { x, #x }
2864         static const wininet_flag_info query_flags[] = {
2865             FE(HTTP_QUERY_MIME_VERSION),
2866             FE(HTTP_QUERY_CONTENT_TYPE),
2867             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2868             FE(HTTP_QUERY_CONTENT_ID),
2869             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2870             FE(HTTP_QUERY_CONTENT_LENGTH),
2871             FE(HTTP_QUERY_CONTENT_LANGUAGE),
2872             FE(HTTP_QUERY_ALLOW),
2873             FE(HTTP_QUERY_PUBLIC),
2874             FE(HTTP_QUERY_DATE),
2875             FE(HTTP_QUERY_EXPIRES),
2876             FE(HTTP_QUERY_LAST_MODIFIED),
2877             FE(HTTP_QUERY_MESSAGE_ID),
2878             FE(HTTP_QUERY_URI),
2879             FE(HTTP_QUERY_DERIVED_FROM),
2880             FE(HTTP_QUERY_COST),
2881             FE(HTTP_QUERY_LINK),
2882             FE(HTTP_QUERY_PRAGMA),
2883             FE(HTTP_QUERY_VERSION),
2884             FE(HTTP_QUERY_STATUS_CODE),
2885             FE(HTTP_QUERY_STATUS_TEXT),
2886             FE(HTTP_QUERY_RAW_HEADERS),
2887             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2888             FE(HTTP_QUERY_CONNECTION),
2889             FE(HTTP_QUERY_ACCEPT),
2890             FE(HTTP_QUERY_ACCEPT_CHARSET),
2891             FE(HTTP_QUERY_ACCEPT_ENCODING),
2892             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2893             FE(HTTP_QUERY_AUTHORIZATION),
2894             FE(HTTP_QUERY_CONTENT_ENCODING),
2895             FE(HTTP_QUERY_FORWARDED),
2896             FE(HTTP_QUERY_FROM),
2897             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2898             FE(HTTP_QUERY_LOCATION),
2899             FE(HTTP_QUERY_ORIG_URI),
2900             FE(HTTP_QUERY_REFERER),
2901             FE(HTTP_QUERY_RETRY_AFTER),
2902             FE(HTTP_QUERY_SERVER),
2903             FE(HTTP_QUERY_TITLE),
2904             FE(HTTP_QUERY_USER_AGENT),
2905             FE(HTTP_QUERY_WWW_AUTHENTICATE),
2906             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2907             FE(HTTP_QUERY_ACCEPT_RANGES),
2908         FE(HTTP_QUERY_SET_COOKIE),
2909         FE(HTTP_QUERY_COOKIE),
2910             FE(HTTP_QUERY_REQUEST_METHOD),
2911             FE(HTTP_QUERY_REFRESH),
2912             FE(HTTP_QUERY_CONTENT_DISPOSITION),
2913             FE(HTTP_QUERY_AGE),
2914             FE(HTTP_QUERY_CACHE_CONTROL),
2915             FE(HTTP_QUERY_CONTENT_BASE),
2916             FE(HTTP_QUERY_CONTENT_LOCATION),
2917             FE(HTTP_QUERY_CONTENT_MD5),
2918             FE(HTTP_QUERY_CONTENT_RANGE),
2919             FE(HTTP_QUERY_ETAG),
2920             FE(HTTP_QUERY_HOST),
2921             FE(HTTP_QUERY_IF_MATCH),
2922             FE(HTTP_QUERY_IF_NONE_MATCH),
2923             FE(HTTP_QUERY_IF_RANGE),
2924             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2925             FE(HTTP_QUERY_MAX_FORWARDS),
2926             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2927             FE(HTTP_QUERY_RANGE),
2928             FE(HTTP_QUERY_TRANSFER_ENCODING),
2929             FE(HTTP_QUERY_UPGRADE),
2930             FE(HTTP_QUERY_VARY),
2931             FE(HTTP_QUERY_VIA),
2932             FE(HTTP_QUERY_WARNING),
2933             FE(HTTP_QUERY_CUSTOM)
2934         };
2935         static const wininet_flag_info modifier_flags[] = {
2936             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2937             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2938             FE(HTTP_QUERY_FLAG_NUMBER),
2939             FE(HTTP_QUERY_FLAG_COALESCE)
2940         };
2941 #undef FE
2942         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2943         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2944         DWORD i;
2945
2946         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2947         TRACE("  Attribute:");
2948         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2949             if (query_flags[i].val == info) {
2950                 TRACE(" %s", query_flags[i].name);
2951                 break;
2952             }
2953         }
2954         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2955             TRACE(" Unknown (%08x)", info);
2956         }
2957
2958         TRACE(" Modifier:");
2959         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2960             if (modifier_flags[i].val & info_mod) {
2961                 TRACE(" %s", modifier_flags[i].name);
2962                 info_mod &= ~ modifier_flags[i].val;
2963             }
2964         }
2965         
2966         if (info_mod) {
2967             TRACE(" Unknown (%08x)", info_mod);
2968         }
2969         TRACE("\n");
2970     }
2971     
2972     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2973     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
2974     {
2975         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2976         goto lend;
2977     }
2978
2979     if (lpBuffer == NULL)
2980         *lpdwBufferLength = 0;
2981     res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2982                                lpBuffer, lpdwBufferLength, lpdwIndex);
2983
2984 lend:
2985     if( lpwhr )
2986          WININET_Release( &lpwhr->hdr );
2987
2988     TRACE("%u <--\n", res);
2989     if(res != ERROR_SUCCESS)
2990         SetLastError(res);
2991     return res == ERROR_SUCCESS;
2992 }
2993
2994 /***********************************************************************
2995  *           HttpQueryInfoA (WININET.@)
2996  *
2997  * Queries for information about an HTTP request
2998  *
2999  * RETURNS
3000  *    TRUE  on success
3001  *    FALSE on failure
3002  *
3003  */
3004 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3005         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3006 {
3007     BOOL result;
3008     DWORD len;
3009     WCHAR* bufferW;
3010
3011     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3012        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3013     {
3014         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3015                                lpdwBufferLength, lpdwIndex );
3016     }
3017
3018     if (lpBuffer)
3019     {
3020         DWORD alloclen;
3021         len = (*lpdwBufferLength)*sizeof(WCHAR);
3022         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3023         {
3024             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3025             if (alloclen < len)
3026                 alloclen = len;
3027         }
3028         else
3029             alloclen = len;
3030         bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3031         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3032         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3033             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3034     } else
3035     {
3036         bufferW = NULL;
3037         len = 0;
3038     }
3039
3040     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3041                            &len, lpdwIndex );
3042     if( result )
3043     {
3044         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3045                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3046         *lpdwBufferLength = len - 1;
3047
3048         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3049     }
3050     else
3051         /* since the strings being returned from HttpQueryInfoW should be
3052          * only ASCII characters, it is reasonable to assume that all of
3053          * the Unicode characters can be reduced to a single byte */
3054         *lpdwBufferLength = len / sizeof(WCHAR);
3055
3056     HeapFree(GetProcessHeap(), 0, bufferW );
3057
3058     return result;
3059 }
3060
3061 /***********************************************************************
3062  *           HTTP_GetRedirectURL (internal)
3063  */
3064 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3065 {
3066     static WCHAR szHttp[] = {'h','t','t','p',0};
3067     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3068     http_session_t *lpwhs = lpwhr->lpHttpSession;
3069     URL_COMPONENTSW urlComponents;
3070     DWORD url_length = 0;
3071     LPWSTR orig_url;
3072     LPWSTR combined_url;
3073
3074     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3075     urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3076     urlComponents.dwSchemeLength = 0;
3077     urlComponents.lpszHostName = lpwhs->lpszHostName;
3078     urlComponents.dwHostNameLength = 0;
3079     urlComponents.nPort = lpwhs->nHostPort;
3080     urlComponents.lpszUserName = lpwhs->lpszUserName;
3081     urlComponents.dwUserNameLength = 0;
3082     urlComponents.lpszPassword = NULL;
3083     urlComponents.dwPasswordLength = 0;
3084     urlComponents.lpszUrlPath = lpwhr->lpszPath;
3085     urlComponents.dwUrlPathLength = 0;
3086     urlComponents.lpszExtraInfo = NULL;
3087     urlComponents.dwExtraInfoLength = 0;
3088
3089     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3090         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3091         return NULL;
3092
3093     orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3094
3095     /* convert from bytes to characters */
3096     url_length = url_length / sizeof(WCHAR) - 1;
3097     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3098     {
3099         HeapFree(GetProcessHeap(), 0, orig_url);
3100         return NULL;
3101     }
3102
3103     url_length = 0;
3104     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3105         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3106     {
3107         HeapFree(GetProcessHeap(), 0, orig_url);
3108         return NULL;
3109     }
3110     combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3111
3112     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3113     {
3114         HeapFree(GetProcessHeap(), 0, orig_url);
3115         HeapFree(GetProcessHeap(), 0, combined_url);
3116         return NULL;
3117     }
3118     HeapFree(GetProcessHeap(), 0, orig_url);
3119     return combined_url;
3120 }
3121
3122
3123 /***********************************************************************
3124  *           HTTP_HandleRedirect (internal)
3125  */
3126 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3127 {
3128     http_session_t *lpwhs = lpwhr->lpHttpSession;
3129     appinfo_t *hIC = lpwhs->lpAppInfo;
3130     BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3131     WCHAR path[INTERNET_MAX_URL_LENGTH];
3132     int index;
3133
3134     if(lpszUrl[0]=='/')
3135     {
3136         /* if it's an absolute path, keep the same session info */
3137         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3138     }
3139     else
3140     {
3141         URL_COMPONENTSW urlComponents;
3142         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3143         static WCHAR szHttp[] = {'h','t','t','p',0};
3144         static WCHAR szHttps[] = {'h','t','t','p','s',0};
3145
3146         userName[0] = 0;
3147         hostName[0] = 0;
3148         protocol[0] = 0;
3149
3150         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3151         urlComponents.lpszScheme = protocol;
3152         urlComponents.dwSchemeLength = 32;
3153         urlComponents.lpszHostName = hostName;
3154         urlComponents.dwHostNameLength = MAXHOSTNAME;
3155         urlComponents.lpszUserName = userName;
3156         urlComponents.dwUserNameLength = 1024;
3157         urlComponents.lpszPassword = NULL;
3158         urlComponents.dwPasswordLength = 0;
3159         urlComponents.lpszUrlPath = path;
3160         urlComponents.dwUrlPathLength = 2048;
3161         urlComponents.lpszExtraInfo = NULL;
3162         urlComponents.dwExtraInfoLength = 0;
3163         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3164             return INTERNET_GetLastError();
3165
3166         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3167             (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3168         {
3169             TRACE("redirect from secure page to non-secure page\n");
3170             /* FIXME: warn about from secure redirect to non-secure page */
3171             lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3172         }
3173         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3174             !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3175         {
3176             TRACE("redirect from non-secure page to secure page\n");
3177             /* FIXME: notify about redirect to secure page */
3178             lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3179         }
3180
3181         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3182         {
3183             if (lstrlenW(protocol)>4) /*https*/
3184                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3185             else /*http*/
3186                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3187         }
3188
3189 #if 0
3190         /*
3191          * This upsets redirects to binary files on sourceforge.net 
3192          * and gives an html page instead of the target file
3193          * Examination of the HTTP request sent by native wininet.dll
3194          * reveals that it doesn't send a referrer in that case.
3195          * Maybe there's a flag that enables this, or maybe a referrer
3196          * shouldn't be added in case of a redirect.
3197          */
3198
3199         /* consider the current host as the referrer */
3200         if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3201             HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3202                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3203                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3204 #endif
3205         
3206         HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3207         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3208             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3209         {
3210             int len;
3211             static const WCHAR fmt[] = {'%','s',':','%','i',0};
3212             len = lstrlenW(hostName);
3213             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3214             lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3215             sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3216         }
3217         else
3218             lpwhs->lpszHostName = heap_strdupW(hostName);
3219
3220         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3221
3222         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3223         lpwhs->lpszUserName = NULL;
3224         if (userName[0])
3225             lpwhs->lpszUserName = heap_strdupW(userName);
3226
3227         if (!using_proxy)
3228         {
3229             if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3230             {
3231                 DWORD res;
3232
3233                 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3234                 lpwhs->lpszServerName = heap_strdupW(hostName);
3235                 lpwhs->nServerPort = urlComponents.nPort;
3236
3237                 NETCON_close(&lpwhr->netConnection);
3238                 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3239                     return res;
3240
3241                 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3242                 if (res != ERROR_SUCCESS)
3243                     return res;
3244
3245                 lpwhr->read_pos = lpwhr->read_size = 0;
3246                 lpwhr->read_chunked = FALSE;
3247             }
3248         }
3249         else
3250             TRACE("Redirect through proxy\n");
3251     }
3252
3253     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3254     lpwhr->lpszPath=NULL;
3255     if (*path)
3256     {
3257         DWORD needed = 0;
3258         HRESULT rc;
3259
3260         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3261         if (rc != E_POINTER)
3262             needed = strlenW(path)+1;
3263         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3264         rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3265                         URL_ESCAPE_SPACES_ONLY);
3266         if (rc != S_OK)
3267         {
3268             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3269             strcpyW(lpwhr->lpszPath,path);
3270         }
3271     }
3272
3273     /* Remove custom content-type/length headers on redirects.  */
3274     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3275     if (0 <= index)
3276         HTTP_DeleteCustomHeader(lpwhr, index);
3277     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3278     if (0 <= index)
3279         HTTP_DeleteCustomHeader(lpwhr, index);
3280
3281     return ERROR_SUCCESS;
3282 }
3283
3284 /***********************************************************************
3285  *           HTTP_build_req (internal)
3286  *
3287  *  concatenate all the strings in the request together
3288  */
3289 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3290 {
3291     LPCWSTR *t;
3292     LPWSTR str;
3293
3294     for( t = list; *t ; t++  )
3295         len += strlenW( *t );
3296     len++;
3297
3298     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3299     *str = 0;
3300
3301     for( t = list; *t ; t++ )
3302         strcatW( str, *t );
3303
3304     return str;
3305 }
3306
3307 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3308 {
3309     LPWSTR lpszPath;
3310     LPWSTR requestString;
3311     INT len;
3312     INT cnt;
3313     INT responseLen;
3314     char *ascii_req;
3315     DWORD res;
3316     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3317     static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3318     http_session_t *lpwhs = lpwhr->lpHttpSession;
3319
3320     TRACE("\n");
3321
3322     lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3323     sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3324     requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3325     HeapFree( GetProcessHeap(), 0, lpszPath );
3326
3327     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3328                                 NULL, 0, NULL, NULL );
3329     len--; /* the nul terminator isn't needed */
3330     ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3331     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3332                             ascii_req, len, NULL, NULL );
3333     HeapFree( GetProcessHeap(), 0, requestString );
3334
3335     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3336
3337     res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3338     HeapFree( GetProcessHeap(), 0, ascii_req );
3339     if (res != ERROR_SUCCESS)
3340         return res;
3341
3342     responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3343     if (!responseLen)
3344         return ERROR_HTTP_INVALID_HEADER;
3345
3346     return ERROR_SUCCESS;
3347 }
3348
3349 static void HTTP_InsertCookies(http_request_t *lpwhr)
3350 {
3351     static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3352     LPWSTR lpszCookies, lpszUrl = NULL;
3353     DWORD nCookieSize, size;
3354     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3355
3356     size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3357     if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3358     sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3359
3360     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3361     {
3362         int cnt = 0;
3363         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3364
3365         size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3366         if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3367         {
3368             cnt += sprintfW(lpszCookies, szCookie);
3369             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3370             strcatW(lpszCookies, szCrLf);
3371
3372             HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3373             HeapFree(GetProcessHeap(), 0, lpszCookies);
3374         }
3375     }
3376     HeapFree(GetProcessHeap(), 0, lpszUrl);
3377 }
3378
3379 /***********************************************************************
3380  *           HTTP_HttpSendRequestW (internal)
3381  *
3382  * Sends the specified request to the HTTP server
3383  *
3384  * RETURNS
3385  *    TRUE  on success
3386  *    FALSE on failure
3387  *
3388  */
3389 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3390         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3391         DWORD dwContentLength, BOOL bEndRequest)
3392 {
3393     INT cnt;
3394     BOOL redirected = FALSE;
3395     LPWSTR requestString = NULL;
3396     INT responseLen;
3397     BOOL loop_next;
3398     INTERNET_ASYNC_RESULT iar;
3399     static const WCHAR szPost[] = { 'P','O','S','T',0 };
3400     static const WCHAR szContentLength[] =
3401         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3402     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3403     DWORD res;
3404
3405     TRACE("--> %p\n", lpwhr);
3406
3407     assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3408
3409     /* if the verb is NULL default to GET */
3410     if (!lpwhr->lpszVerb)
3411         lpwhr->lpszVerb = heap_strdupW(szGET);
3412
3413     if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3414     {
3415         sprintfW(contentLengthStr, szContentLength, dwContentLength);
3416         HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3417         lpwhr->dwBytesToWrite = dwContentLength;
3418     }
3419     if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3420     {
3421         WCHAR *agent_header;
3422         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3423         int len;
3424
3425         len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3426         agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3427         sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3428
3429         HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3430         HeapFree(GetProcessHeap(), 0, agent_header);
3431     }
3432     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3433     {
3434         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3435         HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3436     }
3437     if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3438     {
3439         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3440                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3441         HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3442     }
3443
3444     do
3445     {
3446         DWORD len;
3447         char *ascii_req;
3448
3449         loop_next = FALSE;
3450
3451         /* like native, just in case the caller forgot to call InternetReadFile
3452          * for all the data */
3453         HTTP_DrainContent(lpwhr);
3454         lpwhr->dwContentRead = 0;
3455
3456         if (TRACE_ON(wininet))
3457         {
3458             LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3459             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3460         }
3461
3462         HTTP_FixURL(lpwhr);
3463         if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3464         {
3465             HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3466         }
3467         HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3468         HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3469
3470         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3471             HTTP_InsertCookies(lpwhr);
3472
3473         /* add the headers the caller supplied */
3474         if( lpszHeaders && dwHeaderLength )
3475         {
3476             HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3477                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3478         }
3479
3480         if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3481         {
3482             WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3483             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3484             HeapFree(GetProcessHeap(), 0, url);
3485         }
3486         else
3487             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3488
3489  
3490         TRACE("Request header -> %s\n", debugstr_w(requestString) );
3491
3492         /* Send the request and store the results */
3493         if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3494             goto lend;
3495
3496         /* send the request as ASCII, tack on the optional data */
3497         if (!lpOptional || redirected)
3498             dwOptionalLength = 0;
3499         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3500                                    NULL, 0, NULL, NULL );
3501         ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3502         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3503                              ascii_req, len, NULL, NULL );
3504         if( lpOptional )
3505             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3506         len = (len + dwOptionalLength - 1);
3507         ascii_req[len] = 0;
3508         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3509
3510         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3511                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3512
3513         res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3514         HeapFree( GetProcessHeap(), 0, ascii_req );
3515
3516         lpwhr->dwBytesWritten = dwOptionalLength;
3517
3518         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3519                               INTERNET_STATUS_REQUEST_SENT,
3520                               &len, sizeof(DWORD));
3521
3522         if (bEndRequest)
3523         {
3524             DWORD dwBufferSize;
3525             DWORD dwStatusCode;
3526
3527             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3528                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3529     
3530             if (res != ERROR_SUCCESS)
3531                 goto lend;
3532     
3533             responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3534     
3535             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3536                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3537                                 sizeof(DWORD));
3538
3539             HTTP_ProcessCookies(lpwhr);
3540
3541             if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3542
3543             dwBufferSize = sizeof(dwStatusCode);
3544             if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3545                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3546                 dwStatusCode = 0;
3547
3548             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3549             {
3550                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3551                 dwBufferSize=sizeof(szNewLocation);
3552                 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3553                     HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3554                 {
3555                     if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3556                     {
3557                         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3558                         lpwhr->lpszVerb = heap_strdupW(szGET);
3559                     }
3560                     HTTP_DrainContent(lpwhr);
3561                     if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3562                     {
3563                         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3564                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3565                         res = HTTP_HandleRedirect(lpwhr, new_url);
3566                         if (res == ERROR_SUCCESS)
3567                         {
3568                             HeapFree(GetProcessHeap(), 0, requestString);
3569                             loop_next = TRUE;
3570                         }
3571                         HeapFree( GetProcessHeap(), 0, new_url );
3572                     }
3573                     redirected = TRUE;
3574                 }
3575             }
3576             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3577             {
3578                 WCHAR szAuthValue[2048];
3579                 dwBufferSize=2048;
3580                 if (dwStatusCode == HTTP_STATUS_DENIED)
3581                 {
3582                     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3583                     DWORD dwIndex = 0;
3584                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3585                     {
3586                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3587                                                  &lpwhr->pAuthInfo,
3588                                                  lpwhr->lpHttpSession->lpszUserName,
3589                                                  lpwhr->lpHttpSession->lpszPassword,
3590                                                  Host->lpszValue))
3591                         {
3592                             loop_next = TRUE;
3593                             break;
3594                         }
3595                     }
3596                 }
3597                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3598                 {
3599                     DWORD dwIndex = 0;
3600                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3601                     {
3602                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3603                                                  &lpwhr->pProxyAuthInfo,
3604                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3605                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3606                                                  NULL))
3607                         {
3608                             loop_next = TRUE;
3609                             break;
3610                         }
3611                     }
3612                 }
3613             }
3614         }
3615         else
3616             res = ERROR_SUCCESS;
3617     }
3618     while (loop_next);
3619
3620     if(res == ERROR_SUCCESS) {
3621         WCHAR url[INTERNET_MAX_URL_LENGTH];
3622         WCHAR cacheFileName[MAX_PATH+1];
3623         BOOL b;
3624
3625         b = HTTP_GetRequestURL(lpwhr, url);
3626         if(!b) {
3627             WARN("Could not get URL\n");
3628             goto lend;
3629         }
3630
3631         b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3632         if(b) {
3633             HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3634             CloseHandle(lpwhr->hCacheFile);
3635
3636             lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3637             lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3638                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3639             if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3640                 WARN("Could not create file: %u\n", GetLastError());
3641                 lpwhr->hCacheFile = NULL;
3642             }
3643         }else {
3644             WARN("Could not create cache entry: %08x\n", GetLastError());
3645         }
3646     }
3647
3648 lend:
3649
3650     HeapFree(GetProcessHeap(), 0, requestString);
3651
3652     /* TODO: send notification for P3P header */
3653
3654     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3655     {
3656         if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3657             HTTP_ReceiveRequestData(lpwhr, TRUE);
3658         else
3659         {
3660             iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3661             iar.dwError = res;
3662
3663             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3664                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3665                                   sizeof(INTERNET_ASYNC_RESULT));
3666         }
3667     }
3668
3669     TRACE("<--\n");
3670     return res;
3671 }
3672
3673 /***********************************************************************
3674  *
3675  * Helper functions for the HttpSendRequest(Ex) functions
3676  *
3677  */
3678 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3679 {
3680     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3681     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3682
3683     TRACE("%p\n", lpwhr);
3684
3685     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3686             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3687             req->dwContentLength, req->bEndRequest);
3688
3689     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3690 }
3691
3692
3693 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3694 {
3695     INT responseLen;
3696     DWORD dwBufferSize;
3697     INTERNET_ASYNC_RESULT iar;
3698     DWORD res = ERROR_SUCCESS;
3699
3700     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3701                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3702
3703     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3704     if (!responseLen)
3705         res = ERROR_HTTP_HEADER_NOT_FOUND;
3706
3707     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3708                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3709
3710     /* process cookies here. Is this right? */
3711     HTTP_ProcessCookies(lpwhr);
3712
3713     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3714
3715     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3716     {
3717         DWORD dwCode,dwCodeLength = sizeof(DWORD);
3718         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3719             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3720         {
3721             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3722             dwBufferSize=sizeof(szNewLocation);
3723             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3724             {
3725                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3726                 {
3727                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3728                     lpwhr->lpszVerb = heap_strdupW(szGET);
3729                 }
3730                 HTTP_DrainContent(lpwhr);
3731                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3732                 {
3733                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3734                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3735                     res = HTTP_HandleRedirect(lpwhr, new_url);
3736                     if (res == ERROR_SUCCESS)
3737                         res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3738                     HeapFree( GetProcessHeap(), 0, new_url );
3739                 }
3740             }
3741         }
3742     }
3743
3744     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3745     iar.dwError = res;
3746
3747     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3748                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3749                           sizeof(INTERNET_ASYNC_RESULT));
3750     return res;
3751 }
3752
3753 /***********************************************************************
3754  *           HttpEndRequestA (WININET.@)
3755  *
3756  * Ends an HTTP request that was started by HttpSendRequestEx
3757  *
3758  * RETURNS
3759  *    TRUE      if successful
3760  *    FALSE     on failure
3761  *
3762  */
3763 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3764         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3765 {
3766     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3767
3768     if (lpBuffersOut)
3769     {
3770         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3771         return FALSE;
3772     }
3773
3774     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3775 }
3776
3777 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3778 {
3779     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3780     http_request_t *lpwhr = (http_request_t*)work->hdr;
3781
3782     TRACE("%p\n", lpwhr);
3783
3784     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3785 }
3786
3787 /***********************************************************************
3788  *           HttpEndRequestW (WININET.@)
3789  *
3790  * Ends an HTTP request that was started by HttpSendRequestEx
3791  *
3792  * RETURNS
3793  *    TRUE      if successful
3794  *    FALSE     on failure
3795  *
3796  */
3797 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3798         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3799 {
3800     http_request_t *lpwhr;
3801     DWORD res;
3802
3803     TRACE("-->\n");
3804
3805     if (lpBuffersOut)
3806     {
3807         SetLastError(ERROR_INVALID_PARAMETER);
3808         return FALSE;
3809     }
3810
3811     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3812
3813     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3814     {
3815         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3816         if (lpwhr)
3817             WININET_Release( &lpwhr->hdr );
3818         return FALSE;
3819     }
3820     lpwhr->hdr.dwFlags |= dwFlags;
3821
3822     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3823     {
3824         WORKREQUEST work;
3825         struct WORKREQ_HTTPENDREQUESTW *request;
3826
3827         work.asyncproc = AsyncHttpEndRequestProc;
3828         work.hdr = WININET_AddRef( &lpwhr->hdr );
3829
3830         request = &work.u.HttpEndRequestW;
3831         request->dwFlags = dwFlags;
3832         request->dwContext = dwContext;
3833
3834         INTERNET_AsyncCall(&work);
3835         res = ERROR_IO_PENDING;
3836     }
3837     else
3838         res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3839
3840     WININET_Release( &lpwhr->hdr );
3841     TRACE("%u <--\n", res);
3842     if(res != ERROR_SUCCESS)
3843         SetLastError(res);
3844     return res == ERROR_SUCCESS;
3845 }
3846
3847 /***********************************************************************
3848  *           HttpSendRequestExA (WININET.@)
3849  *
3850  * Sends the specified request to the HTTP server and allows chunked
3851  * transfers.
3852  *
3853  * RETURNS
3854  *  Success: TRUE
3855  *  Failure: FALSE, call GetLastError() for more information.
3856  */
3857 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3858                                LPINTERNET_BUFFERSA lpBuffersIn,
3859                                LPINTERNET_BUFFERSA lpBuffersOut,
3860                                DWORD dwFlags, DWORD_PTR dwContext)
3861 {
3862     INTERNET_BUFFERSW BuffersInW;
3863     BOOL rc = FALSE;
3864     DWORD headerlen;
3865     LPWSTR header = NULL;
3866
3867     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3868             lpBuffersOut, dwFlags, dwContext);
3869
3870     if (lpBuffersIn)
3871     {
3872         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3873         if (lpBuffersIn->lpcszHeader)
3874         {
3875             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3876                     lpBuffersIn->dwHeadersLength,0,0);
3877             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3878             if (!(BuffersInW.lpcszHeader = header))
3879             {
3880                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3881                 return FALSE;
3882             }
3883             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3884                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3885                     header, headerlen);
3886         }
3887         else
3888             BuffersInW.lpcszHeader = NULL;
3889         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3890         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3891         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3892         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3893         BuffersInW.Next = NULL;
3894     }
3895
3896     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3897
3898     HeapFree(GetProcessHeap(),0,header);
3899
3900     return rc;
3901 }
3902
3903 /***********************************************************************
3904  *           HttpSendRequestExW (WININET.@)
3905  *
3906  * Sends the specified request to the HTTP server and allows chunked
3907  * transfers
3908  *
3909  * RETURNS
3910  *  Success: TRUE
3911  *  Failure: FALSE, call GetLastError() for more information.
3912  */
3913 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3914                    LPINTERNET_BUFFERSW lpBuffersIn,
3915                    LPINTERNET_BUFFERSW lpBuffersOut,
3916                    DWORD dwFlags, DWORD_PTR dwContext)
3917 {
3918     http_request_t *lpwhr;
3919     http_session_t *lpwhs;
3920     appinfo_t *hIC;
3921     DWORD res;
3922
3923     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3924             lpBuffersOut, dwFlags, dwContext);
3925
3926     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3927
3928     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3929     {
3930         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3931         goto lend;
3932     }
3933
3934     lpwhs = lpwhr->lpHttpSession;
3935     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3936     hIC = lpwhs->lpAppInfo;
3937     assert(hIC->hdr.htype == WH_HINIT);
3938
3939     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3940     {
3941         WORKREQUEST workRequest;
3942         struct WORKREQ_HTTPSENDREQUESTW *req;
3943
3944         workRequest.asyncproc = AsyncHttpSendRequestProc;
3945         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3946         req = &workRequest.u.HttpSendRequestW;
3947         if (lpBuffersIn)
3948         {
3949             DWORD size = 0;
3950
3951             if (lpBuffersIn->lpcszHeader)
3952             {
3953                 if (lpBuffersIn->dwHeadersLength == ~0u)
3954                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3955                 else
3956                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3957
3958                 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3959                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3960             }
3961             else req->lpszHeader = NULL;
3962
3963             req->dwHeaderLength = size / sizeof(WCHAR);
3964             req->lpOptional = lpBuffersIn->lpvBuffer;
3965             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3966             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3967         }
3968         else
3969         {
3970             req->lpszHeader = NULL;
3971             req->dwHeaderLength = 0;
3972             req->lpOptional = NULL;
3973             req->dwOptionalLength = 0;
3974             req->dwContentLength = 0;
3975         }
3976
3977         req->bEndRequest = FALSE;
3978
3979         INTERNET_AsyncCall(&workRequest);
3980         /*
3981          * This is from windows.
3982          */
3983         res = ERROR_IO_PENDING;
3984     }
3985     else
3986     {
3987         if (lpBuffersIn)
3988             res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3989                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3990                                         lpBuffersIn->dwBufferTotal, FALSE);
3991         else
3992             res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3993     }
3994
3995 lend:
3996     if ( lpwhr )
3997         WININET_Release( &lpwhr->hdr );
3998
3999     TRACE("<---\n");
4000     if(res != ERROR_SUCCESS)
4001         SetLastError(res);
4002     return res == ERROR_SUCCESS;
4003 }
4004
4005 /***********************************************************************
4006  *           HttpSendRequestW (WININET.@)
4007  *
4008  * Sends the specified request to the HTTP server
4009  *
4010  * RETURNS
4011  *    TRUE  on success
4012  *    FALSE on failure
4013  *
4014  */
4015 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4016         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4017 {
4018     http_request_t *lpwhr;
4019     http_session_t *lpwhs = NULL;
4020     appinfo_t *hIC = NULL;
4021     DWORD res = ERROR_SUCCESS;
4022
4023     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4024             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4025
4026     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4027     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4028     {
4029         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4030         goto lend;
4031     }
4032
4033     lpwhs = lpwhr->lpHttpSession;
4034     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
4035     {
4036         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4037         goto lend;
4038     }
4039
4040     hIC = lpwhs->lpAppInfo;
4041     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
4042     {
4043         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4044         goto lend;
4045     }
4046
4047     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4048     {
4049         WORKREQUEST workRequest;
4050         struct WORKREQ_HTTPSENDREQUESTW *req;
4051
4052         workRequest.asyncproc = AsyncHttpSendRequestProc;
4053         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4054         req = &workRequest.u.HttpSendRequestW;
4055         if (lpszHeaders)
4056         {
4057             DWORD size;
4058
4059             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4060             else size = dwHeaderLength * sizeof(WCHAR);
4061
4062             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4063             memcpy(req->lpszHeader, lpszHeaders, size);
4064         }
4065         else
4066             req->lpszHeader = 0;
4067         req->dwHeaderLength = dwHeaderLength;
4068         req->lpOptional = lpOptional;
4069         req->dwOptionalLength = dwOptionalLength;
4070         req->dwContentLength = dwOptionalLength;
4071         req->bEndRequest = TRUE;
4072
4073         INTERNET_AsyncCall(&workRequest);
4074         /*
4075          * This is from windows.
4076          */
4077         res = ERROR_IO_PENDING;
4078     }
4079     else
4080     {
4081         res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4082                 dwHeaderLength, lpOptional, dwOptionalLength,
4083                 dwOptionalLength, TRUE);
4084     }
4085 lend:
4086     if( lpwhr )
4087         WININET_Release( &lpwhr->hdr );
4088
4089     if(res != ERROR_SUCCESS)
4090         SetLastError(res);
4091     return res == ERROR_SUCCESS;
4092 }
4093
4094 /***********************************************************************
4095  *           HttpSendRequestA (WININET.@)
4096  *
4097  * Sends the specified request to the HTTP server
4098  *
4099  * RETURNS
4100  *    TRUE  on success
4101  *    FALSE on failure
4102  *
4103  */
4104 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4105         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4106 {
4107     BOOL result;
4108     LPWSTR szHeaders=NULL;
4109     DWORD nLen=dwHeaderLength;
4110     if(lpszHeaders!=NULL)
4111     {
4112         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4113         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4114         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4115     }
4116     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4117     HeapFree(GetProcessHeap(),0,szHeaders);
4118     return result;
4119 }
4120
4121 /***********************************************************************
4122  *           HTTPSESSION_Destroy (internal)
4123  *
4124  * Deallocate session handle
4125  *
4126  */
4127 static void HTTPSESSION_Destroy(object_header_t *hdr)
4128 {
4129     http_session_t *lpwhs = (http_session_t*) hdr;
4130
4131     TRACE("%p\n", lpwhs);
4132
4133     WININET_Release(&lpwhs->lpAppInfo->hdr);
4134
4135     HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4136     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4137     HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4138     HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4139     HeapFree(GetProcessHeap(), 0, lpwhs);
4140 }
4141
4142 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4143 {
4144     switch(option) {
4145     case INTERNET_OPTION_HANDLE_TYPE:
4146         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4147
4148         if (*size < sizeof(ULONG))
4149             return ERROR_INSUFFICIENT_BUFFER;
4150
4151         *size = sizeof(DWORD);
4152         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4153         return ERROR_SUCCESS;
4154     }
4155
4156     return INET_QueryOption(option, buffer, size, unicode);
4157 }
4158
4159 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4160 {
4161     http_session_t *ses = (http_session_t*)hdr;
4162
4163     switch(option) {
4164     case INTERNET_OPTION_USERNAME:
4165     {
4166         HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4167         if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4168         return ERROR_SUCCESS;
4169     }
4170     case INTERNET_OPTION_PASSWORD:
4171     {
4172         HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4173         if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4174         return ERROR_SUCCESS;
4175     }
4176     default: break;
4177     }
4178
4179     return ERROR_INTERNET_INVALID_OPTION;
4180 }
4181
4182 static const object_vtbl_t HTTPSESSIONVtbl = {
4183     HTTPSESSION_Destroy,
4184     NULL,
4185     HTTPSESSION_QueryOption,
4186     HTTPSESSION_SetOption,
4187     NULL,
4188     NULL,
4189     NULL,
4190     NULL,
4191     NULL
4192 };
4193
4194
4195 /***********************************************************************
4196  *           HTTP_Connect  (internal)
4197  *
4198  * Create http session handle
4199  *
4200  * RETURNS
4201  *   HINTERNET a session handle on success
4202  *   NULL on failure
4203  *
4204  */
4205 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4206         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4207         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4208         DWORD dwInternalFlags)
4209 {
4210     http_session_t *lpwhs = NULL;
4211     HINTERNET handle = NULL;
4212
4213     TRACE("-->\n");
4214
4215     if (!lpszServerName || !lpszServerName[0])
4216     {
4217         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4218         goto lerror;
4219     }
4220
4221     assert( hIC->hdr.htype == WH_HINIT );
4222
4223     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4224     if (NULL == lpwhs)
4225     {
4226         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4227         goto lerror;
4228     }
4229
4230    /*
4231     * According to my tests. The name is not resolved until a request is sent
4232     */
4233
4234     lpwhs->hdr.htype = WH_HHTTPSESSION;
4235     lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4236     lpwhs->hdr.dwFlags = dwFlags;
4237     lpwhs->hdr.dwContext = dwContext;
4238     lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4239     lpwhs->hdr.refs = 1;
4240     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4241
4242     WININET_AddRef( &hIC->hdr );
4243     lpwhs->lpAppInfo = hIC;
4244     list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4245
4246     handle = WININET_AllocHandle( &lpwhs->hdr );
4247     if (NULL == handle)
4248     {
4249         ERR("Failed to alloc handle\n");
4250         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4251         goto lerror;
4252     }
4253
4254     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4255         if(strchrW(hIC->lpszProxy, ' '))
4256             FIXME("Several proxies not implemented.\n");
4257         if(hIC->lpszProxyBypass)
4258             FIXME("Proxy bypass is ignored.\n");
4259     }
4260     if (lpszServerName && lpszServerName[0])
4261     {
4262         lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4263         lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4264     }
4265     if (lpszUserName && lpszUserName[0])
4266         lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4267     if (lpszPassword && lpszPassword[0])
4268         lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4269     lpwhs->nServerPort = nServerPort;
4270     lpwhs->nHostPort = nServerPort;
4271
4272     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4273     if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4274     {
4275         INTERNET_SendCallback(&hIC->hdr, dwContext,
4276                               INTERNET_STATUS_HANDLE_CREATED, &handle,
4277                               sizeof(handle));
4278     }
4279
4280 lerror:
4281     if( lpwhs )
4282         WININET_Release( &lpwhs->hdr );
4283
4284 /*
4285  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4286  * windows
4287  */
4288
4289     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4290     return handle;
4291 }
4292
4293
4294 /***********************************************************************
4295  *           HTTP_OpenConnection (internal)
4296  *
4297  * Connect to a web server
4298  *
4299  * RETURNS
4300  *
4301  *   TRUE  on success
4302  *   FALSE on failure
4303  */
4304 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4305 {
4306     http_session_t *lpwhs;
4307     appinfo_t *hIC = NULL;
4308     char szaddr[INET6_ADDRSTRLEN];
4309     const void *addr;
4310     DWORD res = ERROR_SUCCESS;
4311
4312     TRACE("-->\n");
4313
4314
4315     if (lpwhr->hdr.htype != WH_HHTTPREQ)
4316     {
4317         res = ERROR_INVALID_PARAMETER;
4318         goto lend;
4319     }
4320
4321     if (NETCON_connected(&lpwhr->netConnection))
4322         goto lend;
4323     if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4324
4325     lpwhs = lpwhr->lpHttpSession;
4326
4327     hIC = lpwhs->lpAppInfo;
4328     switch (lpwhs->socketAddress.ss_family)
4329     {
4330     case AF_INET:
4331         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4332         break;
4333     case AF_INET6:
4334         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4335         break;
4336     default:
4337         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4338         return ERROR_INTERNET_NAME_NOT_RESOLVED;
4339     }
4340     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4341     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4342                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4343                           szaddr,
4344                           strlen(szaddr)+1);
4345
4346     res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4347     if (res != ERROR_SUCCESS)
4348     {
4349         WARN("Socket creation failed: %u\n", res);
4350         goto lend;
4351     }
4352
4353     res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4354                          lpwhs->sa_len);
4355     if(res != ERROR_SUCCESS)
4356        goto lend;
4357
4358     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4359     {
4360         /* Note: we differ from Microsoft's WinINet here. they seem to have
4361          * a bug that causes no status callbacks to be sent when starting
4362          * a tunnel to a proxy server using the CONNECT verb. i believe our
4363          * behaviour to be more correct and to not cause any incompatibilities
4364          * because using a secure connection through a proxy server is a rare
4365          * case that would be hard for anyone to depend on */
4366         if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4367             goto lend;
4368
4369         res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4370         if(res != ERROR_SUCCESS)
4371         {
4372             WARN("Couldn't connect securely to host\n");
4373             goto lend;
4374         }
4375     }
4376
4377     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4378                           INTERNET_STATUS_CONNECTED_TO_SERVER,
4379                           szaddr, strlen(szaddr)+1);
4380
4381 lend:
4382     lpwhr->read_pos = lpwhr->read_size = 0;
4383     lpwhr->read_chunked = FALSE;
4384
4385     TRACE("%d <--\n", res);
4386     return res;
4387 }
4388
4389
4390 /***********************************************************************
4391  *           HTTP_clear_response_headers (internal)
4392  *
4393  * clear out any old response headers
4394  */
4395 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4396 {
4397     DWORD i;
4398
4399     for( i=0; i<lpwhr->nCustHeaders; i++)
4400     {
4401         if( !lpwhr->pCustHeaders[i].lpszField )
4402             continue;
4403         if( !lpwhr->pCustHeaders[i].lpszValue )
4404             continue;
4405         if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4406             continue;
4407         HTTP_DeleteCustomHeader( lpwhr, i );
4408         i--;
4409     }
4410 }
4411
4412 /***********************************************************************
4413  *           HTTP_GetResponseHeaders (internal)
4414  *
4415  * Read server response
4416  *
4417  * RETURNS
4418  *
4419  *   TRUE  on success
4420  *   FALSE on error
4421  */
4422 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4423 {
4424     INT cbreaks = 0;
4425     WCHAR buffer[MAX_REPLY_LEN];
4426     DWORD buflen = MAX_REPLY_LEN;
4427     BOOL bSuccess = FALSE;
4428     INT  rc = 0;
4429     char bufferA[MAX_REPLY_LEN];
4430     LPWSTR status_code = NULL, status_text = NULL;
4431     DWORD cchMaxRawHeaders = 1024;
4432     LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4433     LPWSTR temp;
4434     DWORD cchRawHeaders = 0;
4435     BOOL codeHundred = FALSE;
4436
4437     TRACE("-->\n");
4438
4439     /* clear old response headers (eg. from a redirect response) */
4440     if (clear) HTTP_clear_response_headers( lpwhr );
4441
4442     if (!NETCON_connected(&lpwhr->netConnection))
4443         goto lend;
4444
4445     do {
4446         static const WCHAR szHundred[] = {'1','0','0',0};
4447         /*
4448          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4449          */
4450         buflen = MAX_REPLY_LEN;
4451         if (!read_line(lpwhr, bufferA, &buflen))
4452             goto lend;
4453         rc += buflen;
4454         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4455         /* check is this a status code line? */
4456         if (!strncmpW(buffer, g_szHttp1_0, 4))
4457         {
4458             /* split the version from the status code */
4459             status_code = strchrW( buffer, ' ' );
4460             if( !status_code )
4461                 goto lend;
4462             *status_code++=0;
4463
4464             /* split the status code from the status text */
4465             status_text = strchrW( status_code, ' ' );
4466             if( !status_text )
4467                 goto lend;
4468             *status_text++=0;
4469
4470             TRACE("version [%s] status code [%s] status text [%s]\n",
4471                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4472
4473             codeHundred = (!strcmpW(status_code, szHundred));
4474         }
4475         else if (!codeHundred)
4476         {
4477             FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4478             goto lend;
4479         }
4480     } while (codeHundred);
4481
4482     /* Add status code */
4483     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4484             HTTP_ADDHDR_FLAG_REPLACE);
4485
4486     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4487     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4488
4489     lpwhr->lpszVersion = heap_strdupW(buffer);
4490     lpwhr->lpszStatusText = heap_strdupW(status_text);
4491
4492     /* Restore the spaces */
4493     *(status_code-1) = ' ';
4494     *(status_text-1) = ' ';
4495
4496     /* regenerate raw headers */
4497     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4498         cchMaxRawHeaders *= 2;
4499     temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4500     if (temp == NULL) goto lend;
4501     lpszRawHeaders = temp;
4502     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4503     cchRawHeaders += (buflen-1);
4504     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4505     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4506     lpszRawHeaders[cchRawHeaders] = '\0';
4507
4508     /* Parse each response line */
4509     do
4510     {
4511         buflen = MAX_REPLY_LEN;
4512         if (read_line(lpwhr, bufferA, &buflen))
4513         {
4514             LPWSTR * pFieldAndValue;
4515
4516             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4517
4518             if (!bufferA[0]) break;
4519             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4520
4521             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4522             if (pFieldAndValue)
4523             {
4524                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4525                     cchMaxRawHeaders *= 2;
4526                 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4527                 if (temp == NULL) goto lend;
4528                 lpszRawHeaders = temp;
4529                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4530                 cchRawHeaders += (buflen-1);
4531                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4532                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4533                 lpszRawHeaders[cchRawHeaders] = '\0';
4534
4535                 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4536                                    HTTP_ADDREQ_FLAG_ADD );
4537
4538                 HTTP_FreeTokens(pFieldAndValue);
4539             }
4540         }
4541         else
4542         {
4543             cbreaks++;
4544             if (cbreaks >= 2)
4545                break;
4546         }
4547     }while(1);
4548
4549     /* make sure the response header is terminated with an empty line.  Some apps really
4550        truly care about that empty line being there for some reason.  Just add it to the
4551        header. */
4552     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4553     {
4554         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4555         temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4556         if (temp == NULL) goto lend;
4557         lpszRawHeaders = temp;
4558     }
4559
4560     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4561
4562     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4563     lpwhr->lpszRawHeaders = lpszRawHeaders;
4564     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4565     bSuccess = TRUE;
4566
4567 lend:
4568
4569     TRACE("<--\n");
4570     if (bSuccess)
4571         return rc;
4572     else
4573     {
4574         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4575         return 0;
4576     }
4577 }
4578
4579 /***********************************************************************
4580  *           HTTP_InterpretHttpHeader (internal)
4581  *
4582  * Parse server response
4583  *
4584  * RETURNS
4585  *
4586  *   Pointer to array of field, value, NULL on success.
4587  *   NULL on error.
4588  */
4589 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4590 {
4591     LPWSTR * pTokenPair;
4592     LPWSTR pszColon;
4593     INT len;
4594
4595     pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4596
4597     pszColon = strchrW(buffer, ':');
4598     /* must have two tokens */
4599     if (!pszColon)
4600     {
4601         HTTP_FreeTokens(pTokenPair);
4602         if (buffer[0])
4603             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4604         return NULL;
4605     }
4606
4607     pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4608     if (!pTokenPair[0])
4609     {
4610         HTTP_FreeTokens(pTokenPair);
4611         return NULL;
4612     }
4613     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4614     pTokenPair[0][pszColon - buffer] = '\0';
4615
4616     /* skip colon */
4617     pszColon++;
4618     len = strlenW(pszColon);
4619     pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4620     if (!pTokenPair[1])
4621     {
4622         HTTP_FreeTokens(pTokenPair);
4623         return NULL;
4624     }
4625     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4626
4627     strip_spaces(pTokenPair[0]);
4628     strip_spaces(pTokenPair[1]);
4629
4630     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4631     return pTokenPair;
4632 }
4633
4634 /***********************************************************************
4635  *           HTTP_ProcessHeader (internal)
4636  *
4637  * Stuff header into header tables according to <dwModifier>
4638  *
4639  */
4640
4641 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4642
4643 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4644 {
4645     LPHTTPHEADERW lphttpHdr = NULL;
4646     INT index = -1;
4647     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4648     DWORD res = ERROR_HTTP_INVALID_HEADER;
4649
4650     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4651
4652     /* REPLACE wins out over ADD */
4653     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4654         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4655     
4656     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4657         index = -1;
4658     else
4659         index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4660
4661     if (index >= 0)
4662     {
4663         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4664             return ERROR_HTTP_INVALID_HEADER;
4665         lphttpHdr = &lpwhr->pCustHeaders[index];
4666     }
4667     else if (value)
4668     {
4669         HTTPHEADERW hdr;
4670
4671         hdr.lpszField = (LPWSTR)field;
4672         hdr.lpszValue = (LPWSTR)value;
4673         hdr.wFlags = hdr.wCount = 0;
4674
4675         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4676             hdr.wFlags |= HDR_ISREQUEST;
4677
4678         return HTTP_InsertCustomHeader(lpwhr, &hdr);
4679     }
4680     /* no value to delete */
4681     else return ERROR_SUCCESS;
4682
4683     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4684             lphttpHdr->wFlags |= HDR_ISREQUEST;
4685     else
4686         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4687
4688     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4689     {
4690         HTTP_DeleteCustomHeader( lpwhr, index );
4691
4692         if (value)
4693         {
4694             HTTPHEADERW hdr;
4695
4696             hdr.lpszField = (LPWSTR)field;
4697             hdr.lpszValue = (LPWSTR)value;
4698             hdr.wFlags = hdr.wCount = 0;
4699
4700             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4701                 hdr.wFlags |= HDR_ISREQUEST;
4702
4703             return HTTP_InsertCustomHeader(lpwhr, &hdr);
4704         }
4705
4706         return ERROR_SUCCESS;
4707     }
4708     else if (dwModifier & COALESCEFLAGS)
4709     {
4710         LPWSTR lpsztmp;
4711         WCHAR ch = 0;
4712         INT len = 0;
4713         INT origlen = strlenW(lphttpHdr->lpszValue);
4714         INT valuelen = strlenW(value);
4715
4716         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4717         {
4718             ch = ',';
4719             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4720         }
4721         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4722         {
4723             ch = ';';
4724             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4725         }
4726
4727         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4728
4729         lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4730         if (lpsztmp)
4731         {
4732             lphttpHdr->lpszValue = lpsztmp;
4733     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4734             if (ch > 0)
4735             {
4736                 lphttpHdr->lpszValue[origlen] = ch;
4737                 origlen++;
4738                 lphttpHdr->lpszValue[origlen] = ' ';
4739                 origlen++;
4740             }
4741
4742             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4743             lphttpHdr->lpszValue[len] = '\0';
4744             res = ERROR_SUCCESS;
4745         }
4746         else
4747         {
4748             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4749             res = ERROR_OUTOFMEMORY;
4750         }
4751     }
4752     TRACE("<-- %d\n", res);
4753     return res;
4754 }
4755
4756
4757 /***********************************************************************
4758  *           HTTP_FinishedReading (internal)
4759  *
4760  * Called when all content from server has been read by client.
4761  *
4762  */
4763 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4764 {
4765     BOOL keepalive = HTTP_KeepAlive(lpwhr);
4766
4767     TRACE("\n");
4768
4769
4770     if (!keepalive)
4771     {
4772         HTTPREQ_CloseConnection(&lpwhr->hdr);
4773     }
4774
4775     /* FIXME: store data in the URL cache here */
4776
4777     return TRUE;
4778 }
4779
4780
4781 /***********************************************************************
4782  *           HTTP_GetCustomHeaderIndex (internal)
4783  *
4784  * Return index of custom header from header array
4785  *
4786  */
4787 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4788                                      int requested_index, BOOL request_only)
4789 {
4790     DWORD index;
4791
4792     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4793
4794     for (index = 0; index < lpwhr->nCustHeaders; index++)
4795     {
4796         if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4797             continue;
4798
4799         if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4800             continue;
4801
4802         if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4803             continue;
4804
4805         if (requested_index == 0)
4806             break;
4807         requested_index --;
4808     }
4809
4810     if (index >= lpwhr->nCustHeaders)
4811         index = -1;
4812
4813     TRACE("Return: %d\n", index);
4814     return index;
4815 }
4816
4817
4818 /***********************************************************************
4819  *           HTTP_InsertCustomHeader (internal)
4820  *
4821  * Insert header into array
4822  *
4823  */
4824 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4825 {
4826     INT count;
4827     LPHTTPHEADERW lph = NULL;
4828
4829     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4830     count = lpwhr->nCustHeaders + 1;
4831     if (count > 1)
4832         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4833     else
4834         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4835
4836     if (!lph)
4837         return ERROR_OUTOFMEMORY;
4838
4839     lpwhr->pCustHeaders = lph;
4840     lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4841     lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4842     lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4843     lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4844     lpwhr->nCustHeaders++;
4845
4846     return ERROR_SUCCESS;
4847 }
4848
4849
4850 /***********************************************************************
4851  *           HTTP_DeleteCustomHeader (internal)
4852  *
4853  * Delete header from array
4854  *  If this function is called, the indexs may change.
4855  */
4856 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4857 {
4858     if( lpwhr->nCustHeaders <= 0 )
4859         return FALSE;
4860     if( index >= lpwhr->nCustHeaders )
4861         return FALSE;
4862     lpwhr->nCustHeaders--;
4863
4864     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4865     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4866
4867     memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4868              (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4869     memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4870
4871     return TRUE;
4872 }
4873
4874
4875 /***********************************************************************
4876  *           HTTP_VerifyValidHeader (internal)
4877  *
4878  * Verify the given header is not invalid for the given http request
4879  *
4880  */
4881 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4882 {
4883     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4884     if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4885         return ERROR_HTTP_INVALID_HEADER;
4886
4887     return ERROR_SUCCESS;
4888 }
4889
4890 /***********************************************************************
4891  *          IsHostInProxyBypassList (@)
4892  *
4893  * Undocumented
4894  *
4895  */
4896 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4897 {
4898    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4899    return FALSE;
4900 }