shlwapi: Change a magic HRESULT value to the appropriate name.
[wine] / dlls / wininet / http.c
1 /*
2  * Wininet - Http Implementation
3  *
4  * Copyright 1999 Corel Corporation
5  * Copyright 2002 CodeWeavers Inc.
6  * Copyright 2002 TransGaming Technologies Inc.
7  * Copyright 2004 Mike McCormack for CodeWeavers
8  * Copyright 2005 Aric Stewart for CodeWeavers
9  * Copyright 2006 Robert Shearman for CodeWeavers
10  *
11  * Ulrich Czekalla
12  * David Hammerton
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
35
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 #  include <zlib.h>
53 #endif
54
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
66
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
73
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
84
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
137
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
141
142 #define HTTP_REFERER    szReferer
143 #define HTTP_ACCEPT     szAccept
144 #define HTTP_USERAGENT  szUser_Agent
145
146 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
153
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
155
156 struct HttpAuthInfo
157 {
158     LPWSTR scheme;
159     CredHandle cred;
160     CtxtHandle ctx;
161     TimeStamp exp;
162     ULONG attr;
163     ULONG max_token;
164     void *auth_data;
165     unsigned int auth_data_len;
166     BOOL finished; /* finished authenticating */
167 };
168
169
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172     z_stream zstream;
173 #endif
174     BYTE buf[8192];
175     DWORD buf_size;
176     DWORD buf_pos;
177     BOOL end_of_data;
178 };
179
180 typedef struct _authorizationData
181 {
182     struct list entry;
183
184     LPWSTR lpszwHost;
185     LPWSTR lpszwRealm;
186     LPSTR  lpszAuthorization;
187     UINT   AuthorizationLen;
188 } authorizationData;
189
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
191
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
194 {
195     0, 0, &authcache_cs,
196     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197       0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
198 };
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
200
201 static DWORD HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static 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     DWORD buf_avail;
1956     int zres;
1957
1958     while(read < size && !req->gzip_stream->end_of_data) {
1959         if(!req->read_size) {
1960             if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1961                 break;
1962         }
1963
1964         buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1965
1966         zstream->next_in = req->read_buf+req->read_pos;
1967         zstream->avail_in = buf_avail;
1968         zstream->next_out = buf+read;
1969         zstream->avail_out = size-read;
1970         zres = inflate(zstream, Z_FULL_FLUSH);
1971         read = size - zstream->avail_out;
1972         req->dwContentRead += buf_avail-zstream->avail_in;
1973         remove_data(req, buf_avail-zstream->avail_in);
1974         if(zres == Z_STREAM_END) {
1975             TRACE("end of data\n");
1976             req->gzip_stream->end_of_data = TRUE;
1977             inflateEnd(&req->gzip_stream->zstream);
1978         }else if(zres != Z_OK) {
1979             WARN("inflate failed %d\n", zres);
1980             if(!read)
1981                 ret = ERROR_INTERNET_DECODING_FAILED;
1982             break;
1983         }
1984     }
1985 #endif
1986
1987     *read_ret = read;
1988     return ret;
1989 }
1990
1991 static void refill_gzip_buffer(http_request_t *req)
1992 {
1993     DWORD res;
1994     int len;
1995
1996     if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
1997         return;
1998
1999     if(req->gzip_stream->buf_pos) {
2000         if(req->gzip_stream->buf_size)
2001             memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2002         req->gzip_stream->buf_pos = 0;
2003     }
2004
2005     res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2006             sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2007     if(res == ERROR_SUCCESS)
2008         req->gzip_stream->buf_size += len;
2009 }
2010
2011 /* return the size of data available to be read immediately (the read section must be held) */
2012 static DWORD get_avail_data( http_request_t *req )
2013 {
2014     if (req->gzip_stream) {
2015         refill_gzip_buffer(req);
2016         return req->gzip_stream->buf_size;
2017     }
2018     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2019         return 0;
2020     return min( req->read_size, req->dwContentLength - req->dwContentRead );
2021 }
2022
2023 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2024 {
2025     INTERNET_ASYNC_RESULT iar;
2026     DWORD res;
2027
2028     TRACE("%p\n", req);
2029
2030     EnterCriticalSection( &req->read_section );
2031     if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2032         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2033         iar.dwError = first_notif ? 0 : get_avail_data(req);
2034     }else {
2035         iar.dwResult = 0;
2036         iar.dwError = res;
2037     }
2038     LeaveCriticalSection( &req->read_section );
2039
2040     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2041                           sizeof(INTERNET_ASYNC_RESULT));
2042 }
2043
2044 /* read data from the http connection (the read section must be held) */
2045 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2046 {
2047     BOOL finished_reading = FALSE;
2048     int len, bytes_read = 0;
2049     DWORD ret = ERROR_SUCCESS;
2050
2051     EnterCriticalSection( &req->read_section );
2052
2053     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2054     {
2055         if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2056     }
2057
2058     if(req->gzip_stream) {
2059         if(req->gzip_stream->buf_size) {
2060             bytes_read = min(req->gzip_stream->buf_size, size);
2061             memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2062             req->gzip_stream->buf_pos += bytes_read;
2063             req->gzip_stream->buf_size -= bytes_read;
2064         }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2065             refill_buffer(req);
2066         }
2067
2068         if(size > bytes_read) {
2069             ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2070             if(ret == ERROR_SUCCESS)
2071                 bytes_read += len;
2072         }
2073
2074         finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2075     }else {
2076         if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2077
2078         if (req->read_size) {
2079             bytes_read = min( req->read_size, size );
2080             memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2081             remove_data( req, bytes_read );
2082         }
2083
2084         if (size > bytes_read && (!bytes_read || sync)) {
2085             if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2086                              sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2087                 bytes_read += len;
2088             /* always return success, even if the network layer returns an error */
2089         }
2090
2091         finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2092         req->dwContentRead += bytes_read;
2093     }
2094 done:
2095     *read = bytes_read;
2096
2097     TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2098     LeaveCriticalSection( &req->read_section );
2099
2100     if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2101         BOOL res;
2102         DWORD dwBytesWritten;
2103
2104         res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2105         if(!res)
2106             WARN("WriteFile failed: %u\n", GetLastError());
2107     }
2108
2109     if(finished_reading)
2110         HTTP_FinishedReading(req);
2111
2112     return ret;
2113 }
2114
2115
2116 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2117 {
2118     http_request_t *req = (http_request_t*)hdr;
2119     return HTTPREQ_Read(req, buffer, size, read, TRUE);
2120 }
2121
2122 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2123 {
2124     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2125     http_request_t *req = (http_request_t*)workRequest->hdr;
2126     INTERNET_ASYNC_RESULT iar;
2127     DWORD res;
2128
2129     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2130
2131     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2132             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2133
2134     iar.dwResult = res == ERROR_SUCCESS;
2135     iar.dwError = res;
2136
2137     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2138                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2139                           sizeof(INTERNET_ASYNC_RESULT));
2140 }
2141
2142 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2143         DWORD flags, DWORD_PTR context)
2144 {
2145     http_request_t *req = (http_request_t*)hdr;
2146     DWORD res;
2147
2148     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2149         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2150
2151     if (buffers->dwStructSize != sizeof(*buffers))
2152         return ERROR_INVALID_PARAMETER;
2153
2154     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2155
2156     if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2157     {
2158         WORKREQUEST workRequest;
2159
2160         if (TryEnterCriticalSection( &req->read_section ))
2161         {
2162             if (get_avail_data(req))
2163             {
2164                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2165                                    &buffers->dwBufferLength, FALSE);
2166                 LeaveCriticalSection( &req->read_section );
2167                 goto done;
2168             }
2169             LeaveCriticalSection( &req->read_section );
2170         }
2171
2172         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2173         workRequest.hdr = WININET_AddRef(&req->hdr);
2174         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2175
2176         INTERNET_AsyncCall(&workRequest);
2177
2178         return ERROR_IO_PENDING;
2179     }
2180
2181     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2182             !(flags & IRF_NO_WAIT));
2183
2184 done:
2185     if (res == ERROR_SUCCESS) {
2186         DWORD size = buffers->dwBufferLength;
2187         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2188                 &size, sizeof(size));
2189     }
2190
2191     return res;
2192 }
2193
2194 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2195 {
2196     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2197     http_request_t *req = (http_request_t*)workRequest->hdr;
2198     INTERNET_ASYNC_RESULT iar;
2199     DWORD res;
2200
2201     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2202
2203     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2204             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2205
2206     iar.dwResult = res == ERROR_SUCCESS;
2207     iar.dwError = res;
2208
2209     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2210                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2211                           sizeof(INTERNET_ASYNC_RESULT));
2212 }
2213
2214 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2215         DWORD flags, DWORD_PTR context)
2216 {
2217
2218     http_request_t *req = (http_request_t*)hdr;
2219     DWORD res;
2220
2221     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2222         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2223
2224     if (buffers->dwStructSize != sizeof(*buffers))
2225         return ERROR_INVALID_PARAMETER;
2226
2227     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2228
2229     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2230     {
2231         WORKREQUEST workRequest;
2232
2233         if (TryEnterCriticalSection( &req->read_section ))
2234         {
2235             if (get_avail_data(req))
2236             {
2237                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2238                                    &buffers->dwBufferLength, FALSE);
2239                 LeaveCriticalSection( &req->read_section );
2240                 goto done;
2241             }
2242             LeaveCriticalSection( &req->read_section );
2243         }
2244
2245         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2246         workRequest.hdr = WININET_AddRef(&req->hdr);
2247         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2248
2249         INTERNET_AsyncCall(&workRequest);
2250
2251         return ERROR_IO_PENDING;
2252     }
2253
2254     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2255             !(flags & IRF_NO_WAIT));
2256
2257 done:
2258     if (res == ERROR_SUCCESS) {
2259         DWORD size = buffers->dwBufferLength;
2260         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2261                 &size, sizeof(size));
2262     }
2263
2264     return res;
2265 }
2266
2267 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2268 {
2269     DWORD res;
2270     http_request_t *lpwhr = (http_request_t*)hdr;
2271
2272     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2273
2274     *written = 0;
2275     res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2276     if (res == ERROR_SUCCESS)
2277         lpwhr->dwBytesWritten += *written;
2278
2279     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2280     return res;
2281 }
2282
2283 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2284 {
2285     http_request_t *req = (http_request_t*)workRequest->hdr;
2286
2287     HTTP_ReceiveRequestData(req, FALSE);
2288 }
2289
2290 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2291 {
2292     http_request_t *req = (http_request_t*)hdr;
2293
2294     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2295
2296     if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2297     {
2298         WORKREQUEST workRequest;
2299
2300         /* never wait, if we can't enter the section we queue an async request right away */
2301         if (TryEnterCriticalSection( &req->read_section ))
2302         {
2303             if ((*available = get_avail_data( req ))) goto done;
2304             if (end_of_read_data( req )) goto done;
2305             LeaveCriticalSection( &req->read_section );
2306         }
2307
2308         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2309         workRequest.hdr = WININET_AddRef( &req->hdr );
2310
2311         INTERNET_AsyncCall(&workRequest);
2312
2313         return ERROR_IO_PENDING;
2314     }
2315
2316     EnterCriticalSection( &req->read_section );
2317
2318     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2319     {
2320         refill_buffer( req );
2321         *available = get_avail_data( req );
2322     }
2323
2324 done:
2325     if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
2326     {
2327         DWORD extra;
2328         if (NETCON_query_data_available(&req->netConnection, &extra))
2329             *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2330     }
2331     LeaveCriticalSection( &req->read_section );
2332
2333     TRACE( "returning %u\n", *available );
2334     return ERROR_SUCCESS;
2335 }
2336
2337 static const object_vtbl_t HTTPREQVtbl = {
2338     HTTPREQ_Destroy,
2339     HTTPREQ_CloseConnection,
2340     HTTPREQ_QueryOption,
2341     HTTPREQ_SetOption,
2342     HTTPREQ_ReadFile,
2343     HTTPREQ_ReadFileExA,
2344     HTTPREQ_ReadFileExW,
2345     HTTPREQ_WriteFile,
2346     HTTPREQ_QueryDataAvailable,
2347     NULL
2348 };
2349
2350 /***********************************************************************
2351  *           HTTP_HttpOpenRequestW (internal)
2352  *
2353  * Open a HTTP request handle
2354  *
2355  * RETURNS
2356  *    HINTERNET  a HTTP request handle on success
2357  *    NULL       on failure
2358  *
2359  */
2360 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2361         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2362         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2363         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2364 {
2365     appinfo_t *hIC = NULL;
2366     http_request_t *lpwhr;
2367     LPWSTR lpszHostName = NULL;
2368     HINTERNET handle = NULL;
2369     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2370     DWORD len, res;
2371
2372     TRACE("-->\n");
2373
2374     assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2375     hIC = lpwhs->lpAppInfo;
2376
2377     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2378     if (NULL == lpwhr)
2379     {
2380         res = ERROR_OUTOFMEMORY;
2381         goto lend;
2382     }
2383     lpwhr->hdr.htype = WH_HHTTPREQ;
2384     lpwhr->hdr.vtbl = &HTTPREQVtbl;
2385     lpwhr->hdr.dwFlags = dwFlags;
2386     lpwhr->hdr.dwContext = dwContext;
2387     lpwhr->hdr.refs = 1;
2388     lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2389     lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2390     lpwhr->dwContentLength = ~0u;
2391     InitializeCriticalSection( &lpwhr->read_section );
2392
2393     WININET_AddRef( &lpwhs->hdr );
2394     lpwhr->lpHttpSession = lpwhs;
2395     list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2396
2397     lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2398             (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2399     if (NULL == lpszHostName)
2400     {
2401         res = ERROR_OUTOFMEMORY;
2402         goto lend;
2403     }
2404
2405     handle = WININET_AllocHandle( &lpwhr->hdr );
2406     if (NULL == handle)
2407     {
2408         res = ERROR_OUTOFMEMORY;
2409         goto lend;
2410     }
2411
2412     if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2413     {
2414         InternetCloseHandle( handle );
2415         handle = NULL;
2416         goto lend;
2417     }
2418
2419     if (lpszObjectName && *lpszObjectName) {
2420         HRESULT rc;
2421
2422         len = 0;
2423         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2424         if (rc != E_POINTER)
2425             len = strlenW(lpszObjectName)+1;
2426         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2427         rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2428                    URL_ESCAPE_SPACES_ONLY);
2429         if (rc != S_OK)
2430         {
2431             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2432             strcpyW(lpwhr->lpszPath,lpszObjectName);
2433         }
2434     }else {
2435         static const WCHAR slashW[] = {'/',0};
2436
2437         lpwhr->lpszPath = heap_strdupW(slashW);
2438     }
2439
2440     if (lpszReferrer && *lpszReferrer)
2441         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2442
2443     if (lpszAcceptTypes)
2444     {
2445         int i;
2446         for (i = 0; lpszAcceptTypes[i]; i++)
2447         {
2448             if (!*lpszAcceptTypes[i]) continue;
2449             HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2450                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2451                                HTTP_ADDHDR_FLAG_REQ |
2452                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2453         }
2454     }
2455
2456     lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2457     lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2458
2459     if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2460         lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2461         lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2462     {
2463         sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2464         HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2465                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2466     }
2467     else
2468         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2469                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2470
2471     if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2472         lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2473                         INTERNET_DEFAULT_HTTPS_PORT :
2474                         INTERNET_DEFAULT_HTTP_PORT);
2475
2476     if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2477         lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2478                         INTERNET_DEFAULT_HTTPS_PORT :
2479                         INTERNET_DEFAULT_HTTP_PORT);
2480
2481     if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2482         HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2483
2484     INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2485                           INTERNET_STATUS_HANDLE_CREATED, &handle,
2486                           sizeof(handle));
2487
2488 lend:
2489     HeapFree(GetProcessHeap(), 0, lpszHostName);
2490     if( lpwhr )
2491         WININET_Release( &lpwhr->hdr );
2492
2493     TRACE("<-- %p (%p)\n", handle, lpwhr);
2494     *ret = handle;
2495     return res;
2496 }
2497
2498 /***********************************************************************
2499  *           HttpOpenRequestW (WININET.@)
2500  *
2501  * Open a HTTP request handle
2502  *
2503  * RETURNS
2504  *    HINTERNET  a HTTP request handle on success
2505  *    NULL       on failure
2506  *
2507  */
2508 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2509         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2510         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2511         DWORD dwFlags, DWORD_PTR dwContext)
2512 {
2513     http_session_t *lpwhs;
2514     HINTERNET handle = NULL;
2515     DWORD res;
2516
2517     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2518           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2519           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2520           dwFlags, dwContext);
2521     if(lpszAcceptTypes!=NULL)
2522     {
2523         int i;
2524         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2525             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2526     }
2527
2528     lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2529     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
2530     {
2531         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2532         goto lend;
2533     }
2534
2535     /*
2536      * My tests seem to show that the windows version does not
2537      * become asynchronous until after this point. And anyhow
2538      * if this call was asynchronous then how would you get the
2539      * necessary HINTERNET pointer returned by this function.
2540      *
2541      */
2542     res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2543                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
2544                                 dwFlags, dwContext, &handle);
2545 lend:
2546     if( lpwhs )
2547         WININET_Release( &lpwhs->hdr );
2548     TRACE("returning %p\n", handle);
2549     if(res != ERROR_SUCCESS)
2550         SetLastError(res);
2551     return handle;
2552 }
2553
2554 /* read any content returned by the server so that the connection can be
2555  * reused */
2556 static void HTTP_DrainContent(http_request_t *req)
2557 {
2558     DWORD bytes_read;
2559
2560     if (!NETCON_connected(&req->netConnection)) return;
2561
2562     if (req->dwContentLength == -1)
2563     {
2564         NETCON_close(&req->netConnection);
2565         return;
2566     }
2567     if (!strcmpW(req->lpszVerb, szHEAD)) return;
2568
2569     do
2570     {
2571         char buffer[2048];
2572         if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2573             return;
2574     } while (bytes_read);
2575 }
2576
2577 static const LPCWSTR header_lookup[] = {
2578     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
2579     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
2580     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2581     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
2582     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2583     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
2584     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
2585     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
2586     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
2587     szDate,                     /* HTTP_QUERY_DATE = 9 */
2588     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
2589     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
2590     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
2591     szURI,                      /* HTTP_QUERY_URI = 13 */
2592     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
2593     NULL,                       /* HTTP_QUERY_COST = 15 */
2594     NULL,                       /* HTTP_QUERY_LINK = 16 */
2595     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
2596     NULL,                       /* HTTP_QUERY_VERSION = 18 */
2597     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
2598     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
2599     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
2600     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2601     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
2602     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
2603     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2604     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2605     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2606     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
2607     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2608     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
2609     NULL,                       /* HTTP_QUERY_FROM = 31 */
2610     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2611     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
2612     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
2613     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
2614     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
2615     szServer,                   /* HTTP_QUERY_SERVER = 37 */
2616     NULL,                       /* HTTP_TITLE = 38 */
2617     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
2618     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2619     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2620     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2621     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
2622     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
2623     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
2624     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
2625     NULL,                       /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2626     szAge,                      /* HTTP_QUERY_AGE = 48 */
2627     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
2628     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
2629     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2630     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
2631     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
2632     szETag,                     /* HTTP_QUERY_ETAG = 54 */
2633     hostW,                      /* HTTP_QUERY_HOST = 55 */
2634     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
2635     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2636     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
2637     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2638     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
2639     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2640     szRange,                    /* HTTP_QUERY_RANGE = 62 */
2641     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2642     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
2643     szVary,                     /* HTTP_QUERY_VARY = 65 */
2644     szVia,                      /* HTTP_QUERY_VIA = 66 */
2645     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
2646     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
2647     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2648     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2649 };
2650
2651 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2652
2653 /***********************************************************************
2654  *           HTTP_HttpQueryInfoW (internal)
2655  */
2656 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2657         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2658 {
2659     LPHTTPHEADERW lphttpHdr = NULL;
2660     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2661     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2662     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2663     INT index = -1;
2664
2665     /* Find requested header structure */
2666     switch (level)
2667     {
2668     case HTTP_QUERY_CUSTOM:
2669         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2670         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2671         break;
2672     case HTTP_QUERY_RAW_HEADERS_CRLF:
2673         {
2674             LPWSTR headers;
2675             DWORD len = 0;
2676             DWORD res = ERROR_INVALID_PARAMETER;
2677
2678             if (request_only)
2679                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2680             else
2681                 headers = lpwhr->lpszRawHeaders;
2682
2683             if (headers)
2684                 len = strlenW(headers) * sizeof(WCHAR);
2685
2686             if (len + sizeof(WCHAR) > *lpdwBufferLength)
2687             {
2688                 len += sizeof(WCHAR);
2689                 res = ERROR_INSUFFICIENT_BUFFER;
2690             }
2691             else if (lpBuffer)
2692             {
2693                 if (headers)
2694                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2695                 else
2696                 {
2697                     len = strlenW(szCrLf) * sizeof(WCHAR);
2698                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2699                 }
2700                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2701                 res = ERROR_SUCCESS;
2702             }
2703             *lpdwBufferLength = len;
2704
2705             if (request_only)
2706                 HeapFree(GetProcessHeap(), 0, headers);
2707             return res;
2708         }
2709     case HTTP_QUERY_RAW_HEADERS:
2710         {
2711             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2712             DWORD i, size = 0;
2713             LPWSTR pszString = lpBuffer;
2714
2715             for (i = 0; ppszRawHeaderLines[i]; i++)
2716                 size += strlenW(ppszRawHeaderLines[i]) + 1;
2717
2718             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2719             {
2720                 HTTP_FreeTokens(ppszRawHeaderLines);
2721                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2722                 return ERROR_INSUFFICIENT_BUFFER;
2723             }
2724             if (pszString)
2725             {
2726                 for (i = 0; ppszRawHeaderLines[i]; i++)
2727                 {
2728                     DWORD len = strlenW(ppszRawHeaderLines[i]);
2729                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2730                     pszString += len+1;
2731                 }
2732                 *pszString = '\0';
2733                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2734             }
2735             *lpdwBufferLength = size * sizeof(WCHAR);
2736             HTTP_FreeTokens(ppszRawHeaderLines);
2737
2738             return ERROR_SUCCESS;
2739         }
2740     case HTTP_QUERY_STATUS_TEXT:
2741         if (lpwhr->lpszStatusText)
2742         {
2743             DWORD len = strlenW(lpwhr->lpszStatusText);
2744             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2745             {
2746                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2747                 return ERROR_INSUFFICIENT_BUFFER;
2748             }
2749             if (lpBuffer)
2750             {
2751                 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2752                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2753             }
2754             *lpdwBufferLength = len * sizeof(WCHAR);
2755             return ERROR_SUCCESS;
2756         }
2757         break;
2758     case HTTP_QUERY_VERSION:
2759         if (lpwhr->lpszVersion)
2760         {
2761             DWORD len = strlenW(lpwhr->lpszVersion);
2762             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2763             {
2764                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2765                 return ERROR_INSUFFICIENT_BUFFER;
2766             }
2767             if (lpBuffer)
2768             {
2769                 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2770                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2771             }
2772             *lpdwBufferLength = len * sizeof(WCHAR);
2773             return ERROR_SUCCESS;
2774         }
2775         break;
2776     case HTTP_QUERY_CONTENT_ENCODING:
2777         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2778                 requested_index,request_only);
2779         break;
2780     default:
2781         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2782
2783         if (level < LAST_TABLE_HEADER && header_lookup[level])
2784             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2785                                               requested_index,request_only);
2786     }
2787
2788     if (index >= 0)
2789         lphttpHdr = &lpwhr->pCustHeaders[index];
2790
2791     /* Ensure header satisfies requested attributes */
2792     if (!lphttpHdr ||
2793         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2794          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2795     {
2796         return ERROR_HTTP_HEADER_NOT_FOUND;
2797     }
2798
2799     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2800
2801     /* coalesce value to requested type */
2802     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2803     {
2804         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2805         TRACE(" returning number: %d\n", *(int *)lpBuffer);
2806      }
2807     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2808     {
2809         time_t tmpTime;
2810         struct tm tmpTM;
2811         SYSTEMTIME *STHook;
2812
2813         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2814
2815         tmpTM = *gmtime(&tmpTime);
2816         STHook = (SYSTEMTIME *)lpBuffer;
2817         STHook->wDay = tmpTM.tm_mday;
2818         STHook->wHour = tmpTM.tm_hour;
2819         STHook->wMilliseconds = 0;
2820         STHook->wMinute = tmpTM.tm_min;
2821         STHook->wDayOfWeek = tmpTM.tm_wday;
2822         STHook->wMonth = tmpTM.tm_mon + 1;
2823         STHook->wSecond = tmpTM.tm_sec;
2824         STHook->wYear = tmpTM.tm_year;
2825
2826         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2827               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2828               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2829     }
2830     else if (lphttpHdr->lpszValue)
2831     {
2832         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2833
2834         if (len > *lpdwBufferLength)
2835         {
2836             *lpdwBufferLength = len;
2837             return ERROR_INSUFFICIENT_BUFFER;
2838         }
2839         if (lpBuffer)
2840         {
2841             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2842             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2843         }
2844         *lpdwBufferLength = len - sizeof(WCHAR);
2845     }
2846     return ERROR_SUCCESS;
2847 }
2848
2849 /***********************************************************************
2850  *           HttpQueryInfoW (WININET.@)
2851  *
2852  * Queries for information about an HTTP request
2853  *
2854  * RETURNS
2855  *    TRUE  on success
2856  *    FALSE on failure
2857  *
2858  */
2859 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2860         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2861 {
2862     http_request_t *lpwhr;
2863     DWORD res;
2864
2865     if (TRACE_ON(wininet)) {
2866 #define FE(x) { x, #x }
2867         static const wininet_flag_info query_flags[] = {
2868             FE(HTTP_QUERY_MIME_VERSION),
2869             FE(HTTP_QUERY_CONTENT_TYPE),
2870             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2871             FE(HTTP_QUERY_CONTENT_ID),
2872             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2873             FE(HTTP_QUERY_CONTENT_LENGTH),
2874             FE(HTTP_QUERY_CONTENT_LANGUAGE),
2875             FE(HTTP_QUERY_ALLOW),
2876             FE(HTTP_QUERY_PUBLIC),
2877             FE(HTTP_QUERY_DATE),
2878             FE(HTTP_QUERY_EXPIRES),
2879             FE(HTTP_QUERY_LAST_MODIFIED),
2880             FE(HTTP_QUERY_MESSAGE_ID),
2881             FE(HTTP_QUERY_URI),
2882             FE(HTTP_QUERY_DERIVED_FROM),
2883             FE(HTTP_QUERY_COST),
2884             FE(HTTP_QUERY_LINK),
2885             FE(HTTP_QUERY_PRAGMA),
2886             FE(HTTP_QUERY_VERSION),
2887             FE(HTTP_QUERY_STATUS_CODE),
2888             FE(HTTP_QUERY_STATUS_TEXT),
2889             FE(HTTP_QUERY_RAW_HEADERS),
2890             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2891             FE(HTTP_QUERY_CONNECTION),
2892             FE(HTTP_QUERY_ACCEPT),
2893             FE(HTTP_QUERY_ACCEPT_CHARSET),
2894             FE(HTTP_QUERY_ACCEPT_ENCODING),
2895             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2896             FE(HTTP_QUERY_AUTHORIZATION),
2897             FE(HTTP_QUERY_CONTENT_ENCODING),
2898             FE(HTTP_QUERY_FORWARDED),
2899             FE(HTTP_QUERY_FROM),
2900             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2901             FE(HTTP_QUERY_LOCATION),
2902             FE(HTTP_QUERY_ORIG_URI),
2903             FE(HTTP_QUERY_REFERER),
2904             FE(HTTP_QUERY_RETRY_AFTER),
2905             FE(HTTP_QUERY_SERVER),
2906             FE(HTTP_QUERY_TITLE),
2907             FE(HTTP_QUERY_USER_AGENT),
2908             FE(HTTP_QUERY_WWW_AUTHENTICATE),
2909             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2910             FE(HTTP_QUERY_ACCEPT_RANGES),
2911         FE(HTTP_QUERY_SET_COOKIE),
2912         FE(HTTP_QUERY_COOKIE),
2913             FE(HTTP_QUERY_REQUEST_METHOD),
2914             FE(HTTP_QUERY_REFRESH),
2915             FE(HTTP_QUERY_CONTENT_DISPOSITION),
2916             FE(HTTP_QUERY_AGE),
2917             FE(HTTP_QUERY_CACHE_CONTROL),
2918             FE(HTTP_QUERY_CONTENT_BASE),
2919             FE(HTTP_QUERY_CONTENT_LOCATION),
2920             FE(HTTP_QUERY_CONTENT_MD5),
2921             FE(HTTP_QUERY_CONTENT_RANGE),
2922             FE(HTTP_QUERY_ETAG),
2923             FE(HTTP_QUERY_HOST),
2924             FE(HTTP_QUERY_IF_MATCH),
2925             FE(HTTP_QUERY_IF_NONE_MATCH),
2926             FE(HTTP_QUERY_IF_RANGE),
2927             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2928             FE(HTTP_QUERY_MAX_FORWARDS),
2929             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2930             FE(HTTP_QUERY_RANGE),
2931             FE(HTTP_QUERY_TRANSFER_ENCODING),
2932             FE(HTTP_QUERY_UPGRADE),
2933             FE(HTTP_QUERY_VARY),
2934             FE(HTTP_QUERY_VIA),
2935             FE(HTTP_QUERY_WARNING),
2936             FE(HTTP_QUERY_CUSTOM)
2937         };
2938         static const wininet_flag_info modifier_flags[] = {
2939             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2940             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2941             FE(HTTP_QUERY_FLAG_NUMBER),
2942             FE(HTTP_QUERY_FLAG_COALESCE)
2943         };
2944 #undef FE
2945         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2946         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2947         DWORD i;
2948
2949         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2950         TRACE("  Attribute:");
2951         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2952             if (query_flags[i].val == info) {
2953                 TRACE(" %s", query_flags[i].name);
2954                 break;
2955             }
2956         }
2957         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2958             TRACE(" Unknown (%08x)", info);
2959         }
2960
2961         TRACE(" Modifier:");
2962         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2963             if (modifier_flags[i].val & info_mod) {
2964                 TRACE(" %s", modifier_flags[i].name);
2965                 info_mod &= ~ modifier_flags[i].val;
2966             }
2967         }
2968         
2969         if (info_mod) {
2970             TRACE(" Unknown (%08x)", info_mod);
2971         }
2972         TRACE("\n");
2973     }
2974     
2975     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2976     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
2977     {
2978         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2979         goto lend;
2980     }
2981
2982     if (lpBuffer == NULL)
2983         *lpdwBufferLength = 0;
2984     res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2985                                lpBuffer, lpdwBufferLength, lpdwIndex);
2986
2987 lend:
2988     if( lpwhr )
2989          WININET_Release( &lpwhr->hdr );
2990
2991     TRACE("%u <--\n", res);
2992     if(res != ERROR_SUCCESS)
2993         SetLastError(res);
2994     return res == ERROR_SUCCESS;
2995 }
2996
2997 /***********************************************************************
2998  *           HttpQueryInfoA (WININET.@)
2999  *
3000  * Queries for information about an HTTP request
3001  *
3002  * RETURNS
3003  *    TRUE  on success
3004  *    FALSE on failure
3005  *
3006  */
3007 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3008         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3009 {
3010     BOOL result;
3011     DWORD len;
3012     WCHAR* bufferW;
3013
3014     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3015        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3016     {
3017         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3018                                lpdwBufferLength, lpdwIndex );
3019     }
3020
3021     if (lpBuffer)
3022     {
3023         DWORD alloclen;
3024         len = (*lpdwBufferLength)*sizeof(WCHAR);
3025         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3026         {
3027             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3028             if (alloclen < len)
3029                 alloclen = len;
3030         }
3031         else
3032             alloclen = len;
3033         bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3034         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3035         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3036             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3037     } else
3038     {
3039         bufferW = NULL;
3040         len = 0;
3041     }
3042
3043     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3044                            &len, lpdwIndex );
3045     if( result )
3046     {
3047         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3048                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3049         *lpdwBufferLength = len - 1;
3050
3051         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3052     }
3053     else
3054         /* since the strings being returned from HttpQueryInfoW should be
3055          * only ASCII characters, it is reasonable to assume that all of
3056          * the Unicode characters can be reduced to a single byte */
3057         *lpdwBufferLength = len / sizeof(WCHAR);
3058
3059     HeapFree(GetProcessHeap(), 0, bufferW );
3060
3061     return result;
3062 }
3063
3064 /***********************************************************************
3065  *           HTTP_GetRedirectURL (internal)
3066  */
3067 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3068 {
3069     static WCHAR szHttp[] = {'h','t','t','p',0};
3070     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3071     http_session_t *lpwhs = lpwhr->lpHttpSession;
3072     URL_COMPONENTSW urlComponents;
3073     DWORD url_length = 0;
3074     LPWSTR orig_url;
3075     LPWSTR combined_url;
3076
3077     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3078     urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3079     urlComponents.dwSchemeLength = 0;
3080     urlComponents.lpszHostName = lpwhs->lpszHostName;
3081     urlComponents.dwHostNameLength = 0;
3082     urlComponents.nPort = lpwhs->nHostPort;
3083     urlComponents.lpszUserName = lpwhs->lpszUserName;
3084     urlComponents.dwUserNameLength = 0;
3085     urlComponents.lpszPassword = NULL;
3086     urlComponents.dwPasswordLength = 0;
3087     urlComponents.lpszUrlPath = lpwhr->lpszPath;
3088     urlComponents.dwUrlPathLength = 0;
3089     urlComponents.lpszExtraInfo = NULL;
3090     urlComponents.dwExtraInfoLength = 0;
3091
3092     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3093         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3094         return NULL;
3095
3096     orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3097
3098     /* convert from bytes to characters */
3099     url_length = url_length / sizeof(WCHAR) - 1;
3100     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3101     {
3102         HeapFree(GetProcessHeap(), 0, orig_url);
3103         return NULL;
3104     }
3105
3106     url_length = 0;
3107     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3108         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3109     {
3110         HeapFree(GetProcessHeap(), 0, orig_url);
3111         return NULL;
3112     }
3113     combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3114
3115     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3116     {
3117         HeapFree(GetProcessHeap(), 0, orig_url);
3118         HeapFree(GetProcessHeap(), 0, combined_url);
3119         return NULL;
3120     }
3121     HeapFree(GetProcessHeap(), 0, orig_url);
3122     return combined_url;
3123 }
3124
3125
3126 /***********************************************************************
3127  *           HTTP_HandleRedirect (internal)
3128  */
3129 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3130 {
3131     http_session_t *lpwhs = lpwhr->lpHttpSession;
3132     appinfo_t *hIC = lpwhs->lpAppInfo;
3133     BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3134     WCHAR path[INTERNET_MAX_URL_LENGTH];
3135     int index;
3136
3137     if(lpszUrl[0]=='/')
3138     {
3139         /* if it's an absolute path, keep the same session info */
3140         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3141     }
3142     else
3143     {
3144         URL_COMPONENTSW urlComponents;
3145         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3146         static WCHAR szHttp[] = {'h','t','t','p',0};
3147         static WCHAR szHttps[] = {'h','t','t','p','s',0};
3148
3149         userName[0] = 0;
3150         hostName[0] = 0;
3151         protocol[0] = 0;
3152
3153         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3154         urlComponents.lpszScheme = protocol;
3155         urlComponents.dwSchemeLength = 32;
3156         urlComponents.lpszHostName = hostName;
3157         urlComponents.dwHostNameLength = MAXHOSTNAME;
3158         urlComponents.lpszUserName = userName;
3159         urlComponents.dwUserNameLength = 1024;
3160         urlComponents.lpszPassword = NULL;
3161         urlComponents.dwPasswordLength = 0;
3162         urlComponents.lpszUrlPath = path;
3163         urlComponents.dwUrlPathLength = 2048;
3164         urlComponents.lpszExtraInfo = NULL;
3165         urlComponents.dwExtraInfoLength = 0;
3166         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3167             return INTERNET_GetLastError();
3168
3169         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3170             (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3171         {
3172             TRACE("redirect from secure page to non-secure page\n");
3173             /* FIXME: warn about from secure redirect to non-secure page */
3174             lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3175         }
3176         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3177             !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3178         {
3179             TRACE("redirect from non-secure page to secure page\n");
3180             /* FIXME: notify about redirect to secure page */
3181             lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3182         }
3183
3184         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3185         {
3186             if (lstrlenW(protocol)>4) /*https*/
3187                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3188             else /*http*/
3189                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3190         }
3191
3192 #if 0
3193         /*
3194          * This upsets redirects to binary files on sourceforge.net 
3195          * and gives an html page instead of the target file
3196          * Examination of the HTTP request sent by native wininet.dll
3197          * reveals that it doesn't send a referrer in that case.
3198          * Maybe there's a flag that enables this, or maybe a referrer
3199          * shouldn't be added in case of a redirect.
3200          */
3201
3202         /* consider the current host as the referrer */
3203         if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3204             HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3205                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3206                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3207 #endif
3208         
3209         HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3210         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3211             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3212         {
3213             int len;
3214             static const WCHAR fmt[] = {'%','s',':','%','i',0};
3215             len = lstrlenW(hostName);
3216             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3217             lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3218             sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3219         }
3220         else
3221             lpwhs->lpszHostName = heap_strdupW(hostName);
3222
3223         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3224
3225         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3226         lpwhs->lpszUserName = NULL;
3227         if (userName[0])
3228             lpwhs->lpszUserName = heap_strdupW(userName);
3229
3230         if (!using_proxy)
3231         {
3232             if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3233             {
3234                 DWORD res;
3235
3236                 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3237                 lpwhs->lpszServerName = heap_strdupW(hostName);
3238                 lpwhs->nServerPort = urlComponents.nPort;
3239
3240                 NETCON_close(&lpwhr->netConnection);
3241                 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3242                     return res;
3243
3244                 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3245                 if (res != ERROR_SUCCESS)
3246                     return res;
3247
3248                 lpwhr->read_pos = lpwhr->read_size = 0;
3249                 lpwhr->read_chunked = FALSE;
3250             }
3251         }
3252         else
3253             TRACE("Redirect through proxy\n");
3254     }
3255
3256     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3257     lpwhr->lpszPath=NULL;
3258     if (*path)
3259     {
3260         DWORD needed = 0;
3261         HRESULT rc;
3262
3263         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3264         if (rc != E_POINTER)
3265             needed = strlenW(path)+1;
3266         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3267         rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3268                         URL_ESCAPE_SPACES_ONLY);
3269         if (rc != S_OK)
3270         {
3271             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3272             strcpyW(lpwhr->lpszPath,path);
3273         }
3274     }
3275
3276     /* Remove custom content-type/length headers on redirects.  */
3277     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3278     if (0 <= index)
3279         HTTP_DeleteCustomHeader(lpwhr, index);
3280     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3281     if (0 <= index)
3282         HTTP_DeleteCustomHeader(lpwhr, index);
3283
3284     return ERROR_SUCCESS;
3285 }
3286
3287 /***********************************************************************
3288  *           HTTP_build_req (internal)
3289  *
3290  *  concatenate all the strings in the request together
3291  */
3292 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3293 {
3294     LPCWSTR *t;
3295     LPWSTR str;
3296
3297     for( t = list; *t ; t++  )
3298         len += strlenW( *t );
3299     len++;
3300
3301     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3302     *str = 0;
3303
3304     for( t = list; *t ; t++ )
3305         strcatW( str, *t );
3306
3307     return str;
3308 }
3309
3310 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3311 {
3312     LPWSTR lpszPath;
3313     LPWSTR requestString;
3314     INT len;
3315     INT cnt;
3316     INT responseLen;
3317     char *ascii_req;
3318     DWORD res;
3319     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3320     static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3321     http_session_t *lpwhs = lpwhr->lpHttpSession;
3322
3323     TRACE("\n");
3324
3325     lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3326     sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3327     requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3328     HeapFree( GetProcessHeap(), 0, lpszPath );
3329
3330     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3331                                 NULL, 0, NULL, NULL );
3332     len--; /* the nul terminator isn't needed */
3333     ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3334     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3335                             ascii_req, len, NULL, NULL );
3336     HeapFree( GetProcessHeap(), 0, requestString );
3337
3338     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3339
3340     res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3341     HeapFree( GetProcessHeap(), 0, ascii_req );
3342     if (res != ERROR_SUCCESS)
3343         return res;
3344
3345     responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3346     if (!responseLen)
3347         return ERROR_HTTP_INVALID_HEADER;
3348
3349     return ERROR_SUCCESS;
3350 }
3351
3352 static void HTTP_InsertCookies(http_request_t *lpwhr)
3353 {
3354     static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3355     LPWSTR lpszCookies, lpszUrl = NULL;
3356     DWORD nCookieSize, size;
3357     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3358
3359     size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3360     if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3361     sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3362
3363     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3364     {
3365         int cnt = 0;
3366         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3367
3368         size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3369         if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3370         {
3371             cnt += sprintfW(lpszCookies, szCookie);
3372             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3373             strcatW(lpszCookies, szCrLf);
3374
3375             HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3376             HeapFree(GetProcessHeap(), 0, lpszCookies);
3377         }
3378     }
3379     HeapFree(GetProcessHeap(), 0, lpszUrl);
3380 }
3381
3382 /***********************************************************************
3383  *           HTTP_HttpSendRequestW (internal)
3384  *
3385  * Sends the specified request to the HTTP server
3386  *
3387  * RETURNS
3388  *    TRUE  on success
3389  *    FALSE on failure
3390  *
3391  */
3392 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3393         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3394         DWORD dwContentLength, BOOL bEndRequest)
3395 {
3396     INT cnt;
3397     BOOL redirected = FALSE;
3398     LPWSTR requestString = NULL;
3399     INT responseLen;
3400     BOOL loop_next;
3401     INTERNET_ASYNC_RESULT iar;
3402     static const WCHAR szPost[] = { 'P','O','S','T',0 };
3403     static const WCHAR szContentLength[] =
3404         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3405     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3406     DWORD res;
3407
3408     TRACE("--> %p\n", lpwhr);
3409
3410     assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3411
3412     /* if the verb is NULL default to GET */
3413     if (!lpwhr->lpszVerb)
3414         lpwhr->lpszVerb = heap_strdupW(szGET);
3415
3416     if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3417     {
3418         sprintfW(contentLengthStr, szContentLength, dwContentLength);
3419         HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3420         lpwhr->dwBytesToWrite = dwContentLength;
3421     }
3422     if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3423     {
3424         WCHAR *agent_header;
3425         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3426         int len;
3427
3428         len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3429         agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3430         sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3431
3432         HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3433         HeapFree(GetProcessHeap(), 0, agent_header);
3434     }
3435     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3436     {
3437         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3438         HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3439     }
3440     if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3441     {
3442         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3443                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3444         HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3445     }
3446
3447     do
3448     {
3449         DWORD len;
3450         char *ascii_req;
3451
3452         loop_next = FALSE;
3453
3454         /* like native, just in case the caller forgot to call InternetReadFile
3455          * for all the data */
3456         HTTP_DrainContent(lpwhr);
3457         lpwhr->dwContentRead = 0;
3458
3459         if (TRACE_ON(wininet))
3460         {
3461             LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3462             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3463         }
3464
3465         HTTP_FixURL(lpwhr);
3466         if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3467         {
3468             HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3469         }
3470         HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3471         HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3472
3473         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3474             HTTP_InsertCookies(lpwhr);
3475
3476         /* add the headers the caller supplied */
3477         if( lpszHeaders && dwHeaderLength )
3478         {
3479             HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3480                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3481         }
3482
3483         if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3484         {
3485             WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3486             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3487             HeapFree(GetProcessHeap(), 0, url);
3488         }
3489         else
3490             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3491
3492  
3493         TRACE("Request header -> %s\n", debugstr_w(requestString) );
3494
3495         /* Send the request and store the results */
3496         if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3497             goto lend;
3498
3499         /* send the request as ASCII, tack on the optional data */
3500         if (!lpOptional || redirected)
3501             dwOptionalLength = 0;
3502         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3503                                    NULL, 0, NULL, NULL );
3504         ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3505         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3506                              ascii_req, len, NULL, NULL );
3507         if( lpOptional )
3508             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3509         len = (len + dwOptionalLength - 1);
3510         ascii_req[len] = 0;
3511         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3512
3513         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3514                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3515
3516         res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3517         HeapFree( GetProcessHeap(), 0, ascii_req );
3518
3519         lpwhr->dwBytesWritten = dwOptionalLength;
3520
3521         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3522                               INTERNET_STATUS_REQUEST_SENT,
3523                               &len, sizeof(DWORD));
3524
3525         if (bEndRequest)
3526         {
3527             DWORD dwBufferSize;
3528             DWORD dwStatusCode;
3529
3530             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3531                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3532     
3533             if (res != ERROR_SUCCESS)
3534                 goto lend;
3535     
3536             responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3537     
3538             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3539                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3540                                 sizeof(DWORD));
3541
3542             HTTP_ProcessCookies(lpwhr);
3543
3544             if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3545
3546             dwBufferSize = sizeof(dwStatusCode);
3547             if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3548                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3549                 dwStatusCode = 0;
3550
3551             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3552             {
3553                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3554                 dwBufferSize=sizeof(szNewLocation);
3555                 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3556                     HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3557                 {
3558                     if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3559                     {
3560                         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3561                         lpwhr->lpszVerb = heap_strdupW(szGET);
3562                     }
3563                     HTTP_DrainContent(lpwhr);
3564                     if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3565                     {
3566                         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3567                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3568                         res = HTTP_HandleRedirect(lpwhr, new_url);
3569                         if (res == ERROR_SUCCESS)
3570                         {
3571                             HeapFree(GetProcessHeap(), 0, requestString);
3572                             loop_next = TRUE;
3573                         }
3574                         HeapFree( GetProcessHeap(), 0, new_url );
3575                     }
3576                     redirected = TRUE;
3577                 }
3578             }
3579             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3580             {
3581                 WCHAR szAuthValue[2048];
3582                 dwBufferSize=2048;
3583                 if (dwStatusCode == HTTP_STATUS_DENIED)
3584                 {
3585                     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3586                     DWORD dwIndex = 0;
3587                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3588                     {
3589                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3590                                                  &lpwhr->pAuthInfo,
3591                                                  lpwhr->lpHttpSession->lpszUserName,
3592                                                  lpwhr->lpHttpSession->lpszPassword,
3593                                                  Host->lpszValue))
3594                         {
3595                             HeapFree(GetProcessHeap(), 0, requestString);
3596                             loop_next = TRUE;
3597                             break;
3598                         }
3599                     }
3600                 }
3601                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3602                 {
3603                     DWORD dwIndex = 0;
3604                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3605                     {
3606                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3607                                                  &lpwhr->pProxyAuthInfo,
3608                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3609                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3610                                                  NULL))
3611                         {
3612                             loop_next = TRUE;
3613                             break;
3614                         }
3615                     }
3616                 }
3617             }
3618         }
3619         else
3620             res = ERROR_SUCCESS;
3621     }
3622     while (loop_next);
3623
3624     if(res == ERROR_SUCCESS) {
3625         WCHAR url[INTERNET_MAX_URL_LENGTH];
3626         WCHAR cacheFileName[MAX_PATH+1];
3627         BOOL b;
3628
3629         b = HTTP_GetRequestURL(lpwhr, url);
3630         if(!b) {
3631             WARN("Could not get URL\n");
3632             goto lend;
3633         }
3634
3635         b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3636         if(b) {
3637             HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3638             CloseHandle(lpwhr->hCacheFile);
3639
3640             lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3641             lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3642                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3643             if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3644                 WARN("Could not create file: %u\n", GetLastError());
3645                 lpwhr->hCacheFile = NULL;
3646             }
3647         }else {
3648             WARN("Could not create cache entry: %08x\n", GetLastError());
3649         }
3650     }
3651
3652 lend:
3653
3654     HeapFree(GetProcessHeap(), 0, requestString);
3655
3656     /* TODO: send notification for P3P header */
3657
3658     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3659     {
3660         if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3661             HTTP_ReceiveRequestData(lpwhr, TRUE);
3662         else
3663         {
3664             iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3665             iar.dwError = res;
3666
3667             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3668                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3669                                   sizeof(INTERNET_ASYNC_RESULT));
3670         }
3671     }
3672
3673     TRACE("<--\n");
3674     return res;
3675 }
3676
3677 /***********************************************************************
3678  *
3679  * Helper functions for the HttpSendRequest(Ex) functions
3680  *
3681  */
3682 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3683 {
3684     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3685     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3686
3687     TRACE("%p\n", lpwhr);
3688
3689     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3690             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3691             req->dwContentLength, req->bEndRequest);
3692
3693     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3694 }
3695
3696
3697 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3698 {
3699     INT responseLen;
3700     DWORD dwBufferSize;
3701     INTERNET_ASYNC_RESULT iar;
3702     DWORD res = ERROR_SUCCESS;
3703
3704     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3705                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3706
3707     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3708     if (!responseLen)
3709         res = ERROR_HTTP_HEADER_NOT_FOUND;
3710
3711     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3712                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3713
3714     /* process cookies here. Is this right? */
3715     HTTP_ProcessCookies(lpwhr);
3716
3717     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3718
3719     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3720     {
3721         DWORD dwCode,dwCodeLength = sizeof(DWORD);
3722         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3723             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3724         {
3725             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3726             dwBufferSize=sizeof(szNewLocation);
3727             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3728             {
3729                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3730                 {
3731                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3732                     lpwhr->lpszVerb = heap_strdupW(szGET);
3733                 }
3734                 HTTP_DrainContent(lpwhr);
3735                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3736                 {
3737                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3738                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3739                     res = HTTP_HandleRedirect(lpwhr, new_url);
3740                     if (res == ERROR_SUCCESS)
3741                         res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3742                     HeapFree( GetProcessHeap(), 0, new_url );
3743                 }
3744             }
3745         }
3746     }
3747
3748     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3749     iar.dwError = res;
3750
3751     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3752                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3753                           sizeof(INTERNET_ASYNC_RESULT));
3754     return res;
3755 }
3756
3757 /***********************************************************************
3758  *           HttpEndRequestA (WININET.@)
3759  *
3760  * Ends an HTTP request that was started by HttpSendRequestEx
3761  *
3762  * RETURNS
3763  *    TRUE      if successful
3764  *    FALSE     on failure
3765  *
3766  */
3767 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3768         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3769 {
3770     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3771
3772     if (lpBuffersOut)
3773     {
3774         SetLastError(ERROR_INVALID_PARAMETER);
3775         return FALSE;
3776     }
3777
3778     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3779 }
3780
3781 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3782 {
3783     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3784     http_request_t *lpwhr = (http_request_t*)work->hdr;
3785
3786     TRACE("%p\n", lpwhr);
3787
3788     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3789 }
3790
3791 /***********************************************************************
3792  *           HttpEndRequestW (WININET.@)
3793  *
3794  * Ends an HTTP request that was started by HttpSendRequestEx
3795  *
3796  * RETURNS
3797  *    TRUE      if successful
3798  *    FALSE     on failure
3799  *
3800  */
3801 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3802         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3803 {
3804     http_request_t *lpwhr;
3805     DWORD res;
3806
3807     TRACE("-->\n");
3808
3809     if (lpBuffersOut)
3810     {
3811         SetLastError(ERROR_INVALID_PARAMETER);
3812         return FALSE;
3813     }
3814
3815     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3816
3817     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3818     {
3819         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3820         if (lpwhr)
3821             WININET_Release( &lpwhr->hdr );
3822         return FALSE;
3823     }
3824     lpwhr->hdr.dwFlags |= dwFlags;
3825
3826     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3827     {
3828         WORKREQUEST work;
3829         struct WORKREQ_HTTPENDREQUESTW *request;
3830
3831         work.asyncproc = AsyncHttpEndRequestProc;
3832         work.hdr = WININET_AddRef( &lpwhr->hdr );
3833
3834         request = &work.u.HttpEndRequestW;
3835         request->dwFlags = dwFlags;
3836         request->dwContext = dwContext;
3837
3838         INTERNET_AsyncCall(&work);
3839         res = ERROR_IO_PENDING;
3840     }
3841     else
3842         res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3843
3844     WININET_Release( &lpwhr->hdr );
3845     TRACE("%u <--\n", res);
3846     if(res != ERROR_SUCCESS)
3847         SetLastError(res);
3848     return res == ERROR_SUCCESS;
3849 }
3850
3851 /***********************************************************************
3852  *           HttpSendRequestExA (WININET.@)
3853  *
3854  * Sends the specified request to the HTTP server and allows chunked
3855  * transfers.
3856  *
3857  * RETURNS
3858  *  Success: TRUE
3859  *  Failure: FALSE, call GetLastError() for more information.
3860  */
3861 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3862                                LPINTERNET_BUFFERSA lpBuffersIn,
3863                                LPINTERNET_BUFFERSA lpBuffersOut,
3864                                DWORD dwFlags, DWORD_PTR dwContext)
3865 {
3866     INTERNET_BUFFERSW BuffersInW;
3867     BOOL rc = FALSE;
3868     DWORD headerlen;
3869     LPWSTR header = NULL;
3870
3871     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3872             lpBuffersOut, dwFlags, dwContext);
3873
3874     if (lpBuffersIn)
3875     {
3876         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3877         if (lpBuffersIn->lpcszHeader)
3878         {
3879             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3880                     lpBuffersIn->dwHeadersLength,0,0);
3881             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3882             if (!(BuffersInW.lpcszHeader = header))
3883             {
3884                 SetLastError(ERROR_OUTOFMEMORY);
3885                 return FALSE;
3886             }
3887             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3888                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3889                     header, headerlen);
3890         }
3891         else
3892             BuffersInW.lpcszHeader = NULL;
3893         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3894         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3895         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3896         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3897         BuffersInW.Next = NULL;
3898     }
3899
3900     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3901
3902     HeapFree(GetProcessHeap(),0,header);
3903
3904     return rc;
3905 }
3906
3907 /***********************************************************************
3908  *           HttpSendRequestExW (WININET.@)
3909  *
3910  * Sends the specified request to the HTTP server and allows chunked
3911  * transfers
3912  *
3913  * RETURNS
3914  *  Success: TRUE
3915  *  Failure: FALSE, call GetLastError() for more information.
3916  */
3917 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3918                    LPINTERNET_BUFFERSW lpBuffersIn,
3919                    LPINTERNET_BUFFERSW lpBuffersOut,
3920                    DWORD dwFlags, DWORD_PTR dwContext)
3921 {
3922     http_request_t *lpwhr;
3923     http_session_t *lpwhs;
3924     appinfo_t *hIC;
3925     DWORD res;
3926
3927     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3928             lpBuffersOut, dwFlags, dwContext);
3929
3930     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3931
3932     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3933     {
3934         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3935         goto lend;
3936     }
3937
3938     lpwhs = lpwhr->lpHttpSession;
3939     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3940     hIC = lpwhs->lpAppInfo;
3941     assert(hIC->hdr.htype == WH_HINIT);
3942
3943     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3944     {
3945         WORKREQUEST workRequest;
3946         struct WORKREQ_HTTPSENDREQUESTW *req;
3947
3948         workRequest.asyncproc = AsyncHttpSendRequestProc;
3949         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3950         req = &workRequest.u.HttpSendRequestW;
3951         if (lpBuffersIn)
3952         {
3953             DWORD size = 0;
3954
3955             if (lpBuffersIn->lpcszHeader)
3956             {
3957                 if (lpBuffersIn->dwHeadersLength == ~0u)
3958                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3959                 else
3960                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3961
3962                 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3963                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3964             }
3965             else req->lpszHeader = NULL;
3966
3967             req->dwHeaderLength = size / sizeof(WCHAR);
3968             req->lpOptional = lpBuffersIn->lpvBuffer;
3969             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3970             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3971         }
3972         else
3973         {
3974             req->lpszHeader = NULL;
3975             req->dwHeaderLength = 0;
3976             req->lpOptional = NULL;
3977             req->dwOptionalLength = 0;
3978             req->dwContentLength = 0;
3979         }
3980
3981         req->bEndRequest = FALSE;
3982
3983         INTERNET_AsyncCall(&workRequest);
3984         /*
3985          * This is from windows.
3986          */
3987         res = ERROR_IO_PENDING;
3988     }
3989     else
3990     {
3991         if (lpBuffersIn)
3992             res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3993                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3994                                         lpBuffersIn->dwBufferTotal, FALSE);
3995         else
3996             res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3997     }
3998
3999 lend:
4000     if ( lpwhr )
4001         WININET_Release( &lpwhr->hdr );
4002
4003     TRACE("<---\n");
4004     SetLastError(res);
4005     return res == ERROR_SUCCESS;
4006 }
4007
4008 /***********************************************************************
4009  *           HttpSendRequestW (WININET.@)
4010  *
4011  * Sends the specified request to the HTTP server
4012  *
4013  * RETURNS
4014  *    TRUE  on success
4015  *    FALSE on failure
4016  *
4017  */
4018 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4019         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4020 {
4021     http_request_t *lpwhr;
4022     http_session_t *lpwhs = NULL;
4023     appinfo_t *hIC = NULL;
4024     DWORD res = ERROR_SUCCESS;
4025
4026     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4027             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4028
4029     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4030     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4031     {
4032         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4033         goto lend;
4034     }
4035
4036     lpwhs = lpwhr->lpHttpSession;
4037     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
4038     {
4039         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4040         goto lend;
4041     }
4042
4043     hIC = lpwhs->lpAppInfo;
4044     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
4045     {
4046         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4047         goto lend;
4048     }
4049
4050     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4051     {
4052         WORKREQUEST workRequest;
4053         struct WORKREQ_HTTPSENDREQUESTW *req;
4054
4055         workRequest.asyncproc = AsyncHttpSendRequestProc;
4056         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4057         req = &workRequest.u.HttpSendRequestW;
4058         if (lpszHeaders)
4059         {
4060             DWORD size;
4061
4062             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4063             else size = dwHeaderLength * sizeof(WCHAR);
4064
4065             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4066             memcpy(req->lpszHeader, lpszHeaders, size);
4067         }
4068         else
4069             req->lpszHeader = 0;
4070         req->dwHeaderLength = dwHeaderLength;
4071         req->lpOptional = lpOptional;
4072         req->dwOptionalLength = dwOptionalLength;
4073         req->dwContentLength = dwOptionalLength;
4074         req->bEndRequest = TRUE;
4075
4076         INTERNET_AsyncCall(&workRequest);
4077         /*
4078          * This is from windows.
4079          */
4080         res = ERROR_IO_PENDING;
4081     }
4082     else
4083     {
4084         res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4085                 dwHeaderLength, lpOptional, dwOptionalLength,
4086                 dwOptionalLength, TRUE);
4087     }
4088 lend:
4089     if( lpwhr )
4090         WININET_Release( &lpwhr->hdr );
4091
4092     SetLastError(res);
4093     return res == ERROR_SUCCESS;
4094 }
4095
4096 /***********************************************************************
4097  *           HttpSendRequestA (WININET.@)
4098  *
4099  * Sends the specified request to the HTTP server
4100  *
4101  * RETURNS
4102  *    TRUE  on success
4103  *    FALSE on failure
4104  *
4105  */
4106 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4107         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4108 {
4109     BOOL result;
4110     LPWSTR szHeaders=NULL;
4111     DWORD nLen=dwHeaderLength;
4112     if(lpszHeaders!=NULL)
4113     {
4114         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4115         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4116         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4117     }
4118     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4119     HeapFree(GetProcessHeap(),0,szHeaders);
4120     return result;
4121 }
4122
4123 /***********************************************************************
4124  *           HTTPSESSION_Destroy (internal)
4125  *
4126  * Deallocate session handle
4127  *
4128  */
4129 static void HTTPSESSION_Destroy(object_header_t *hdr)
4130 {
4131     http_session_t *lpwhs = (http_session_t*) hdr;
4132
4133     TRACE("%p\n", lpwhs);
4134
4135     WININET_Release(&lpwhs->lpAppInfo->hdr);
4136
4137     HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4138     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4139     HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4140     HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4141     HeapFree(GetProcessHeap(), 0, lpwhs);
4142 }
4143
4144 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4145 {
4146     switch(option) {
4147     case INTERNET_OPTION_HANDLE_TYPE:
4148         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4149
4150         if (*size < sizeof(ULONG))
4151             return ERROR_INSUFFICIENT_BUFFER;
4152
4153         *size = sizeof(DWORD);
4154         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4155         return ERROR_SUCCESS;
4156     }
4157
4158     return INET_QueryOption(option, buffer, size, unicode);
4159 }
4160
4161 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4162 {
4163     http_session_t *ses = (http_session_t*)hdr;
4164
4165     switch(option) {
4166     case INTERNET_OPTION_USERNAME:
4167     {
4168         HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4169         if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4170         return ERROR_SUCCESS;
4171     }
4172     case INTERNET_OPTION_PASSWORD:
4173     {
4174         HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4175         if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4176         return ERROR_SUCCESS;
4177     }
4178     default: break;
4179     }
4180
4181     return ERROR_INTERNET_INVALID_OPTION;
4182 }
4183
4184 static const object_vtbl_t HTTPSESSIONVtbl = {
4185     HTTPSESSION_Destroy,
4186     NULL,
4187     HTTPSESSION_QueryOption,
4188     HTTPSESSION_SetOption,
4189     NULL,
4190     NULL,
4191     NULL,
4192     NULL,
4193     NULL
4194 };
4195
4196
4197 /***********************************************************************
4198  *           HTTP_Connect  (internal)
4199  *
4200  * Create http session handle
4201  *
4202  * RETURNS
4203  *   HINTERNET a session handle on success
4204  *   NULL on failure
4205  *
4206  */
4207 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4208         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4209         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4210         DWORD dwInternalFlags, HINTERNET *ret)
4211 {
4212     http_session_t *lpwhs = NULL;
4213     HINTERNET handle = NULL;
4214     DWORD res = ERROR_SUCCESS;
4215
4216     TRACE("-->\n");
4217
4218     if (!lpszServerName || !lpszServerName[0])
4219         return ERROR_INVALID_PARAMETER;
4220
4221     assert( hIC->hdr.htype == WH_HINIT );
4222
4223     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4224     if (!lpwhs)
4225         return ERROR_OUTOFMEMORY;
4226
4227    /*
4228     * According to my tests. The name is not resolved until a request is sent
4229     */
4230
4231     lpwhs->hdr.htype = WH_HHTTPSESSION;
4232     lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4233     lpwhs->hdr.dwFlags = dwFlags;
4234     lpwhs->hdr.dwContext = dwContext;
4235     lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4236     lpwhs->hdr.refs = 1;
4237     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4238
4239     WININET_AddRef( &hIC->hdr );
4240     lpwhs->lpAppInfo = hIC;
4241     list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4242
4243     handle = WININET_AllocHandle( &lpwhs->hdr );
4244     if (NULL == handle)
4245     {
4246         ERR("Failed to alloc handle\n");
4247         res = ERROR_OUTOFMEMORY;
4248         goto lerror;
4249     }
4250
4251     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4252         if(strchrW(hIC->lpszProxy, ' '))
4253             FIXME("Several proxies not implemented.\n");
4254         if(hIC->lpszProxyBypass)
4255             FIXME("Proxy bypass is ignored.\n");
4256     }
4257     if (lpszServerName && lpszServerName[0])
4258     {
4259         lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4260         lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4261     }
4262     if (lpszUserName && lpszUserName[0])
4263         lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4264     if (lpszPassword && lpszPassword[0])
4265         lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4266     lpwhs->nServerPort = nServerPort;
4267     lpwhs->nHostPort = nServerPort;
4268
4269     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4270     if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4271     {
4272         INTERNET_SendCallback(&hIC->hdr, dwContext,
4273                               INTERNET_STATUS_HANDLE_CREATED, &handle,
4274                               sizeof(handle));
4275     }
4276
4277 lerror:
4278     if( lpwhs )
4279         WININET_Release( &lpwhs->hdr );
4280
4281 /*
4282  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4283  * windows
4284  */
4285
4286     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4287
4288     if(res == ERROR_SUCCESS)
4289         *ret = handle;
4290     return res;
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 }