ole32: Fix memory leaks in the storage test.
[wine] / dlls / wininet / http.c
1 /*
2  * Wininet - Http Implementation
3  *
4  * Copyright 1999 Corel Corporation
5  * Copyright 2002 CodeWeavers Inc.
6  * Copyright 2002 TransGaming Technologies Inc.
7  * Copyright 2004 Mike McCormack for CodeWeavers
8  * Copyright 2005 Aric Stewart for CodeWeavers
9  * Copyright 2006 Robert Shearman for CodeWeavers
10  *
11  * Ulrich Czekalla
12  * David Hammerton
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
35
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 #  include <zlib.h>
53 #endif
54
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
66
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
73
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
84
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
137
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
141
142 #define HTTP_REFERER    szReferer
143 #define HTTP_ACCEPT     szAccept
144 #define HTTP_USERAGENT  szUser_Agent
145
146 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
153
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
155
156 struct HttpAuthInfo
157 {
158     LPWSTR scheme;
159     CredHandle cred;
160     CtxtHandle ctx;
161     TimeStamp exp;
162     ULONG attr;
163     ULONG max_token;
164     void *auth_data;
165     unsigned int auth_data_len;
166     BOOL finished; /* finished authenticating */
167 };
168
169
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172     z_stream zstream;
173 #endif
174     BYTE buf[8192];
175     DWORD buf_size;
176     DWORD buf_pos;
177     BOOL end_of_data;
178 };
179
180 typedef struct _authorizationData
181 {
182     struct list entry;
183
184     LPWSTR lpszwHost;
185     LPWSTR lpszwRealm;
186     LPSTR  lpszAuthorization;
187     UINT   AuthorizationLen;
188 } authorizationData;
189
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
191
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
194 {
195     0, 0, &authcache_cs,
196     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197       0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
198 };
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
200
201 static DWORD HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static 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                             loop_next = TRUE;
3596                             break;
3597                         }
3598                     }
3599                 }
3600                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3601                 {
3602                     DWORD dwIndex = 0;
3603                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3604                     {
3605                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3606                                                  &lpwhr->pProxyAuthInfo,
3607                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3608                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3609                                                  NULL))
3610                         {
3611                             loop_next = TRUE;
3612                             break;
3613                         }
3614                     }
3615                 }
3616             }
3617         }
3618         else
3619             res = ERROR_SUCCESS;
3620     }
3621     while (loop_next);
3622
3623     if(res == ERROR_SUCCESS) {
3624         WCHAR url[INTERNET_MAX_URL_LENGTH];
3625         WCHAR cacheFileName[MAX_PATH+1];
3626         BOOL b;
3627
3628         b = HTTP_GetRequestURL(lpwhr, url);
3629         if(!b) {
3630             WARN("Could not get URL\n");
3631             goto lend;
3632         }
3633
3634         b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3635         if(b) {
3636             HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3637             CloseHandle(lpwhr->hCacheFile);
3638
3639             lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3640             lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3641                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3642             if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3643                 WARN("Could not create file: %u\n", GetLastError());
3644                 lpwhr->hCacheFile = NULL;
3645             }
3646         }else {
3647             WARN("Could not create cache entry: %08x\n", GetLastError());
3648         }
3649     }
3650
3651 lend:
3652
3653     HeapFree(GetProcessHeap(), 0, requestString);
3654
3655     /* TODO: send notification for P3P header */
3656
3657     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3658     {
3659         if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3660             HTTP_ReceiveRequestData(lpwhr, TRUE);
3661         else
3662         {
3663             iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3664             iar.dwError = res;
3665
3666             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3667                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3668                                   sizeof(INTERNET_ASYNC_RESULT));
3669         }
3670     }
3671
3672     TRACE("<--\n");
3673     return res;
3674 }
3675
3676 /***********************************************************************
3677  *
3678  * Helper functions for the HttpSendRequest(Ex) functions
3679  *
3680  */
3681 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3682 {
3683     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3684     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3685
3686     TRACE("%p\n", lpwhr);
3687
3688     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3689             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3690             req->dwContentLength, req->bEndRequest);
3691
3692     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3693 }
3694
3695
3696 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3697 {
3698     INT responseLen;
3699     DWORD dwBufferSize;
3700     INTERNET_ASYNC_RESULT iar;
3701     DWORD res = ERROR_SUCCESS;
3702
3703     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3704                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3705
3706     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3707     if (!responseLen)
3708         res = ERROR_HTTP_HEADER_NOT_FOUND;
3709
3710     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3711                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3712
3713     /* process cookies here. Is this right? */
3714     HTTP_ProcessCookies(lpwhr);
3715
3716     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3717
3718     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3719     {
3720         DWORD dwCode,dwCodeLength = sizeof(DWORD);
3721         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3722             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3723         {
3724             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3725             dwBufferSize=sizeof(szNewLocation);
3726             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3727             {
3728                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3729                 {
3730                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3731                     lpwhr->lpszVerb = heap_strdupW(szGET);
3732                 }
3733                 HTTP_DrainContent(lpwhr);
3734                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3735                 {
3736                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3737                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3738                     res = HTTP_HandleRedirect(lpwhr, new_url);
3739                     if (res == ERROR_SUCCESS)
3740                         res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3741                     HeapFree( GetProcessHeap(), 0, new_url );
3742                 }
3743             }
3744         }
3745     }
3746
3747     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3748     iar.dwError = res;
3749
3750     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3751                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3752                           sizeof(INTERNET_ASYNC_RESULT));
3753     return res;
3754 }
3755
3756 /***********************************************************************
3757  *           HttpEndRequestA (WININET.@)
3758  *
3759  * Ends an HTTP request that was started by HttpSendRequestEx
3760  *
3761  * RETURNS
3762  *    TRUE      if successful
3763  *    FALSE     on failure
3764  *
3765  */
3766 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3767         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3768 {
3769     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3770
3771     if (lpBuffersOut)
3772     {
3773         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3774         return FALSE;
3775     }
3776
3777     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3778 }
3779
3780 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3781 {
3782     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3783     http_request_t *lpwhr = (http_request_t*)work->hdr;
3784
3785     TRACE("%p\n", lpwhr);
3786
3787     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3788 }
3789
3790 /***********************************************************************
3791  *           HttpEndRequestW (WININET.@)
3792  *
3793  * Ends an HTTP request that was started by HttpSendRequestEx
3794  *
3795  * RETURNS
3796  *    TRUE      if successful
3797  *    FALSE     on failure
3798  *
3799  */
3800 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3801         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3802 {
3803     http_request_t *lpwhr;
3804     DWORD res;
3805
3806     TRACE("-->\n");
3807
3808     if (lpBuffersOut)
3809     {
3810         SetLastError(ERROR_INVALID_PARAMETER);
3811         return FALSE;
3812     }
3813
3814     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3815
3816     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3817     {
3818         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3819         if (lpwhr)
3820             WININET_Release( &lpwhr->hdr );
3821         return FALSE;
3822     }
3823     lpwhr->hdr.dwFlags |= dwFlags;
3824
3825     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3826     {
3827         WORKREQUEST work;
3828         struct WORKREQ_HTTPENDREQUESTW *request;
3829
3830         work.asyncproc = AsyncHttpEndRequestProc;
3831         work.hdr = WININET_AddRef( &lpwhr->hdr );
3832
3833         request = &work.u.HttpEndRequestW;
3834         request->dwFlags = dwFlags;
3835         request->dwContext = dwContext;
3836
3837         INTERNET_AsyncCall(&work);
3838         res = ERROR_IO_PENDING;
3839     }
3840     else
3841         res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3842
3843     WININET_Release( &lpwhr->hdr );
3844     TRACE("%u <--\n", res);
3845     if(res != ERROR_SUCCESS)
3846         SetLastError(res);
3847     return res == ERROR_SUCCESS;
3848 }
3849
3850 /***********************************************************************
3851  *           HttpSendRequestExA (WININET.@)
3852  *
3853  * Sends the specified request to the HTTP server and allows chunked
3854  * transfers.
3855  *
3856  * RETURNS
3857  *  Success: TRUE
3858  *  Failure: FALSE, call GetLastError() for more information.
3859  */
3860 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3861                                LPINTERNET_BUFFERSA lpBuffersIn,
3862                                LPINTERNET_BUFFERSA lpBuffersOut,
3863                                DWORD dwFlags, DWORD_PTR dwContext)
3864 {
3865     INTERNET_BUFFERSW BuffersInW;
3866     BOOL rc = FALSE;
3867     DWORD headerlen;
3868     LPWSTR header = NULL;
3869
3870     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3871             lpBuffersOut, dwFlags, dwContext);
3872
3873     if (lpBuffersIn)
3874     {
3875         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3876         if (lpBuffersIn->lpcszHeader)
3877         {
3878             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3879                     lpBuffersIn->dwHeadersLength,0,0);
3880             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3881             if (!(BuffersInW.lpcszHeader = header))
3882             {
3883                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3884                 return FALSE;
3885             }
3886             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3887                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3888                     header, headerlen);
3889         }
3890         else
3891             BuffersInW.lpcszHeader = NULL;
3892         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3893         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3894         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3895         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3896         BuffersInW.Next = NULL;
3897     }
3898
3899     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3900
3901     HeapFree(GetProcessHeap(),0,header);
3902
3903     return rc;
3904 }
3905
3906 /***********************************************************************
3907  *           HttpSendRequestExW (WININET.@)
3908  *
3909  * Sends the specified request to the HTTP server and allows chunked
3910  * transfers
3911  *
3912  * RETURNS
3913  *  Success: TRUE
3914  *  Failure: FALSE, call GetLastError() for more information.
3915  */
3916 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3917                    LPINTERNET_BUFFERSW lpBuffersIn,
3918                    LPINTERNET_BUFFERSW lpBuffersOut,
3919                    DWORD dwFlags, DWORD_PTR dwContext)
3920 {
3921     http_request_t *lpwhr;
3922     http_session_t *lpwhs;
3923     appinfo_t *hIC;
3924     DWORD res;
3925
3926     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3927             lpBuffersOut, dwFlags, dwContext);
3928
3929     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3930
3931     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3932     {
3933         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3934         goto lend;
3935     }
3936
3937     lpwhs = lpwhr->lpHttpSession;
3938     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3939     hIC = lpwhs->lpAppInfo;
3940     assert(hIC->hdr.htype == WH_HINIT);
3941
3942     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3943     {
3944         WORKREQUEST workRequest;
3945         struct WORKREQ_HTTPSENDREQUESTW *req;
3946
3947         workRequest.asyncproc = AsyncHttpSendRequestProc;
3948         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3949         req = &workRequest.u.HttpSendRequestW;
3950         if (lpBuffersIn)
3951         {
3952             DWORD size = 0;
3953
3954             if (lpBuffersIn->lpcszHeader)
3955             {
3956                 if (lpBuffersIn->dwHeadersLength == ~0u)
3957                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3958                 else
3959                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3960
3961                 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3962                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3963             }
3964             else req->lpszHeader = NULL;
3965
3966             req->dwHeaderLength = size / sizeof(WCHAR);
3967             req->lpOptional = lpBuffersIn->lpvBuffer;
3968             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3969             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3970         }
3971         else
3972         {
3973             req->lpszHeader = NULL;
3974             req->dwHeaderLength = 0;
3975             req->lpOptional = NULL;
3976             req->dwOptionalLength = 0;
3977             req->dwContentLength = 0;
3978         }
3979
3980         req->bEndRequest = FALSE;
3981
3982         INTERNET_AsyncCall(&workRequest);
3983         /*
3984          * This is from windows.
3985          */
3986         res = ERROR_IO_PENDING;
3987     }
3988     else
3989     {
3990         if (lpBuffersIn)
3991             res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3992                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3993                                         lpBuffersIn->dwBufferTotal, FALSE);
3994         else
3995             res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3996     }
3997
3998 lend:
3999     if ( lpwhr )
4000         WININET_Release( &lpwhr->hdr );
4001
4002     TRACE("<---\n");
4003     if(res != ERROR_SUCCESS)
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     if(res != ERROR_SUCCESS)
4093         SetLastError(res);
4094     return res == ERROR_SUCCESS;
4095 }
4096
4097 /***********************************************************************
4098  *           HttpSendRequestA (WININET.@)
4099  *
4100  * Sends the specified request to the HTTP server
4101  *
4102  * RETURNS
4103  *    TRUE  on success
4104  *    FALSE on failure
4105  *
4106  */
4107 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4108         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4109 {
4110     BOOL result;
4111     LPWSTR szHeaders=NULL;
4112     DWORD nLen=dwHeaderLength;
4113     if(lpszHeaders!=NULL)
4114     {
4115         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4116         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4117         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4118     }
4119     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4120     HeapFree(GetProcessHeap(),0,szHeaders);
4121     return result;
4122 }
4123
4124 /***********************************************************************
4125  *           HTTPSESSION_Destroy (internal)
4126  *
4127  * Deallocate session handle
4128  *
4129  */
4130 static void HTTPSESSION_Destroy(object_header_t *hdr)
4131 {
4132     http_session_t *lpwhs = (http_session_t*) hdr;
4133
4134     TRACE("%p\n", lpwhs);
4135
4136     WININET_Release(&lpwhs->lpAppInfo->hdr);
4137
4138     HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4139     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4140     HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4141     HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4142     HeapFree(GetProcessHeap(), 0, lpwhs);
4143 }
4144
4145 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4146 {
4147     switch(option) {
4148     case INTERNET_OPTION_HANDLE_TYPE:
4149         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4150
4151         if (*size < sizeof(ULONG))
4152             return ERROR_INSUFFICIENT_BUFFER;
4153
4154         *size = sizeof(DWORD);
4155         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4156         return ERROR_SUCCESS;
4157     }
4158
4159     return INET_QueryOption(option, buffer, size, unicode);
4160 }
4161
4162 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4163 {
4164     http_session_t *ses = (http_session_t*)hdr;
4165
4166     switch(option) {
4167     case INTERNET_OPTION_USERNAME:
4168     {
4169         HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4170         if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4171         return ERROR_SUCCESS;
4172     }
4173     case INTERNET_OPTION_PASSWORD:
4174     {
4175         HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4176         if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4177         return ERROR_SUCCESS;
4178     }
4179     default: break;
4180     }
4181
4182     return ERROR_INTERNET_INVALID_OPTION;
4183 }
4184
4185 static const object_vtbl_t HTTPSESSIONVtbl = {
4186     HTTPSESSION_Destroy,
4187     NULL,
4188     HTTPSESSION_QueryOption,
4189     HTTPSESSION_SetOption,
4190     NULL,
4191     NULL,
4192     NULL,
4193     NULL,
4194     NULL
4195 };
4196
4197
4198 /***********************************************************************
4199  *           HTTP_Connect  (internal)
4200  *
4201  * Create http session handle
4202  *
4203  * RETURNS
4204  *   HINTERNET a session handle on success
4205  *   NULL on failure
4206  *
4207  */
4208 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4209         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4210         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4211         DWORD dwInternalFlags)
4212 {
4213     http_session_t *lpwhs = NULL;
4214     HINTERNET handle = NULL;
4215
4216     TRACE("-->\n");
4217
4218     if (!lpszServerName || !lpszServerName[0])
4219     {
4220         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4221         goto lerror;
4222     }
4223
4224     assert( hIC->hdr.htype == WH_HINIT );
4225
4226     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4227     if (NULL == lpwhs)
4228     {
4229         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4230         goto lerror;
4231     }
4232
4233    /*
4234     * According to my tests. The name is not resolved until a request is sent
4235     */
4236
4237     lpwhs->hdr.htype = WH_HHTTPSESSION;
4238     lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4239     lpwhs->hdr.dwFlags = dwFlags;
4240     lpwhs->hdr.dwContext = dwContext;
4241     lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4242     lpwhs->hdr.refs = 1;
4243     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4244
4245     WININET_AddRef( &hIC->hdr );
4246     lpwhs->lpAppInfo = hIC;
4247     list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4248
4249     handle = WININET_AllocHandle( &lpwhs->hdr );
4250     if (NULL == handle)
4251     {
4252         ERR("Failed to alloc handle\n");
4253         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4254         goto lerror;
4255     }
4256
4257     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4258         if(strchrW(hIC->lpszProxy, ' '))
4259             FIXME("Several proxies not implemented.\n");
4260         if(hIC->lpszProxyBypass)
4261             FIXME("Proxy bypass is ignored.\n");
4262     }
4263     if (lpszServerName && lpszServerName[0])
4264     {
4265         lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4266         lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4267     }
4268     if (lpszUserName && lpszUserName[0])
4269         lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4270     if (lpszPassword && lpszPassword[0])
4271         lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4272     lpwhs->nServerPort = nServerPort;
4273     lpwhs->nHostPort = nServerPort;
4274
4275     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4276     if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4277     {
4278         INTERNET_SendCallback(&hIC->hdr, dwContext,
4279                               INTERNET_STATUS_HANDLE_CREATED, &handle,
4280                               sizeof(handle));
4281     }
4282
4283 lerror:
4284     if( lpwhs )
4285         WININET_Release( &lpwhs->hdr );
4286
4287 /*
4288  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4289  * windows
4290  */
4291
4292     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4293     return handle;
4294 }
4295
4296
4297 /***********************************************************************
4298  *           HTTP_OpenConnection (internal)
4299  *
4300  * Connect to a web server
4301  *
4302  * RETURNS
4303  *
4304  *   TRUE  on success
4305  *   FALSE on failure
4306  */
4307 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4308 {
4309     http_session_t *lpwhs;
4310     appinfo_t *hIC = NULL;
4311     char szaddr[INET6_ADDRSTRLEN];
4312     const void *addr;
4313     DWORD res = ERROR_SUCCESS;
4314
4315     TRACE("-->\n");
4316
4317
4318     if (lpwhr->hdr.htype != WH_HHTTPREQ)
4319     {
4320         res = ERROR_INVALID_PARAMETER;
4321         goto lend;
4322     }
4323
4324     if (NETCON_connected(&lpwhr->netConnection))
4325         goto lend;
4326     if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4327
4328     lpwhs = lpwhr->lpHttpSession;
4329
4330     hIC = lpwhs->lpAppInfo;
4331     switch (lpwhs->socketAddress.ss_family)
4332     {
4333     case AF_INET:
4334         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4335         break;
4336     case AF_INET6:
4337         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4338         break;
4339     default:
4340         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4341         return ERROR_INTERNET_NAME_NOT_RESOLVED;
4342     }
4343     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4344     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4345                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4346                           szaddr,
4347                           strlen(szaddr)+1);
4348
4349     res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4350     if (res != ERROR_SUCCESS)
4351     {
4352         WARN("Socket creation failed: %u\n", res);
4353         goto lend;
4354     }
4355
4356     res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4357                          lpwhs->sa_len);
4358     if(res != ERROR_SUCCESS)
4359        goto lend;
4360
4361     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4362     {
4363         /* Note: we differ from Microsoft's WinINet here. they seem to have
4364          * a bug that causes no status callbacks to be sent when starting
4365          * a tunnel to a proxy server using the CONNECT verb. i believe our
4366          * behaviour to be more correct and to not cause any incompatibilities
4367          * because using a secure connection through a proxy server is a rare
4368          * case that would be hard for anyone to depend on */
4369         if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4370             goto lend;
4371
4372         res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4373         if(res != ERROR_SUCCESS)
4374         {
4375             WARN("Couldn't connect securely to host\n");
4376             goto lend;
4377         }
4378     }
4379
4380     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4381                           INTERNET_STATUS_CONNECTED_TO_SERVER,
4382                           szaddr, strlen(szaddr)+1);
4383
4384 lend:
4385     lpwhr->read_pos = lpwhr->read_size = 0;
4386     lpwhr->read_chunked = FALSE;
4387
4388     TRACE("%d <--\n", res);
4389     return res;
4390 }
4391
4392
4393 /***********************************************************************
4394  *           HTTP_clear_response_headers (internal)
4395  *
4396  * clear out any old response headers
4397  */
4398 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4399 {
4400     DWORD i;
4401
4402     for( i=0; i<lpwhr->nCustHeaders; i++)
4403     {
4404         if( !lpwhr->pCustHeaders[i].lpszField )
4405             continue;
4406         if( !lpwhr->pCustHeaders[i].lpszValue )
4407             continue;
4408         if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4409             continue;
4410         HTTP_DeleteCustomHeader( lpwhr, i );
4411         i--;
4412     }
4413 }
4414
4415 /***********************************************************************
4416  *           HTTP_GetResponseHeaders (internal)
4417  *
4418  * Read server response
4419  *
4420  * RETURNS
4421  *
4422  *   TRUE  on success
4423  *   FALSE on error
4424  */
4425 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4426 {
4427     INT cbreaks = 0;
4428     WCHAR buffer[MAX_REPLY_LEN];
4429     DWORD buflen = MAX_REPLY_LEN;
4430     BOOL bSuccess = FALSE;
4431     INT  rc = 0;
4432     char bufferA[MAX_REPLY_LEN];
4433     LPWSTR status_code = NULL, status_text = NULL;
4434     DWORD cchMaxRawHeaders = 1024;
4435     LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4436     LPWSTR temp;
4437     DWORD cchRawHeaders = 0;
4438     BOOL codeHundred = FALSE;
4439
4440     TRACE("-->\n");
4441
4442     /* clear old response headers (eg. from a redirect response) */
4443     if (clear) HTTP_clear_response_headers( lpwhr );
4444
4445     if (!NETCON_connected(&lpwhr->netConnection))
4446         goto lend;
4447
4448     do {
4449         static const WCHAR szHundred[] = {'1','0','0',0};
4450         /*
4451          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4452          */
4453         buflen = MAX_REPLY_LEN;
4454         if (!read_line(lpwhr, bufferA, &buflen))
4455             goto lend;
4456         rc += buflen;
4457         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4458         /* check is this a status code line? */
4459         if (!strncmpW(buffer, g_szHttp1_0, 4))
4460         {
4461             /* split the version from the status code */
4462             status_code = strchrW( buffer, ' ' );
4463             if( !status_code )
4464                 goto lend;
4465             *status_code++=0;
4466
4467             /* split the status code from the status text */
4468             status_text = strchrW( status_code, ' ' );
4469             if( !status_text )
4470                 goto lend;
4471             *status_text++=0;
4472
4473             TRACE("version [%s] status code [%s] status text [%s]\n",
4474                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4475
4476             codeHundred = (!strcmpW(status_code, szHundred));
4477         }
4478         else if (!codeHundred)
4479         {
4480             FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4481             goto lend;
4482         }
4483     } while (codeHundred);
4484
4485     /* Add status code */
4486     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4487             HTTP_ADDHDR_FLAG_REPLACE);
4488
4489     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4490     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4491
4492     lpwhr->lpszVersion = heap_strdupW(buffer);
4493     lpwhr->lpszStatusText = heap_strdupW(status_text);
4494
4495     /* Restore the spaces */
4496     *(status_code-1) = ' ';
4497     *(status_text-1) = ' ';
4498
4499     /* regenerate raw headers */
4500     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4501         cchMaxRawHeaders *= 2;
4502     temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4503     if (temp == NULL) goto lend;
4504     lpszRawHeaders = temp;
4505     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4506     cchRawHeaders += (buflen-1);
4507     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4508     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4509     lpszRawHeaders[cchRawHeaders] = '\0';
4510
4511     /* Parse each response line */
4512     do
4513     {
4514         buflen = MAX_REPLY_LEN;
4515         if (read_line(lpwhr, bufferA, &buflen))
4516         {
4517             LPWSTR * pFieldAndValue;
4518
4519             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4520
4521             if (!bufferA[0]) break;
4522             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4523
4524             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4525             if (pFieldAndValue)
4526             {
4527                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4528                     cchMaxRawHeaders *= 2;
4529                 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4530                 if (temp == NULL) goto lend;
4531                 lpszRawHeaders = temp;
4532                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4533                 cchRawHeaders += (buflen-1);
4534                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4535                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4536                 lpszRawHeaders[cchRawHeaders] = '\0';
4537
4538                 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4539                                    HTTP_ADDREQ_FLAG_ADD );
4540
4541                 HTTP_FreeTokens(pFieldAndValue);
4542             }
4543         }
4544         else
4545         {
4546             cbreaks++;
4547             if (cbreaks >= 2)
4548                break;
4549         }
4550     }while(1);
4551
4552     /* make sure the response header is terminated with an empty line.  Some apps really
4553        truly care about that empty line being there for some reason.  Just add it to the
4554        header. */
4555     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4556     {
4557         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4558         temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4559         if (temp == NULL) goto lend;
4560         lpszRawHeaders = temp;
4561     }
4562
4563     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4564
4565     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4566     lpwhr->lpszRawHeaders = lpszRawHeaders;
4567     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4568     bSuccess = TRUE;
4569
4570 lend:
4571
4572     TRACE("<--\n");
4573     if (bSuccess)
4574         return rc;
4575     else
4576     {
4577         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4578         return 0;
4579     }
4580 }
4581
4582 /***********************************************************************
4583  *           HTTP_InterpretHttpHeader (internal)
4584  *
4585  * Parse server response
4586  *
4587  * RETURNS
4588  *
4589  *   Pointer to array of field, value, NULL on success.
4590  *   NULL on error.
4591  */
4592 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4593 {
4594     LPWSTR * pTokenPair;
4595     LPWSTR pszColon;
4596     INT len;
4597
4598     pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4599
4600     pszColon = strchrW(buffer, ':');
4601     /* must have two tokens */
4602     if (!pszColon)
4603     {
4604         HTTP_FreeTokens(pTokenPair);
4605         if (buffer[0])
4606             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4607         return NULL;
4608     }
4609
4610     pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4611     if (!pTokenPair[0])
4612     {
4613         HTTP_FreeTokens(pTokenPair);
4614         return NULL;
4615     }
4616     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4617     pTokenPair[0][pszColon - buffer] = '\0';
4618
4619     /* skip colon */
4620     pszColon++;
4621     len = strlenW(pszColon);
4622     pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4623     if (!pTokenPair[1])
4624     {
4625         HTTP_FreeTokens(pTokenPair);
4626         return NULL;
4627     }
4628     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4629
4630     strip_spaces(pTokenPair[0]);
4631     strip_spaces(pTokenPair[1]);
4632
4633     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4634     return pTokenPair;
4635 }
4636
4637 /***********************************************************************
4638  *           HTTP_ProcessHeader (internal)
4639  *
4640  * Stuff header into header tables according to <dwModifier>
4641  *
4642  */
4643
4644 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4645
4646 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4647 {
4648     LPHTTPHEADERW lphttpHdr = NULL;
4649     INT index = -1;
4650     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4651     DWORD res = ERROR_HTTP_INVALID_HEADER;
4652
4653     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4654
4655     /* REPLACE wins out over ADD */
4656     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4657         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4658     
4659     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4660         index = -1;
4661     else
4662         index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4663
4664     if (index >= 0)
4665     {
4666         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4667             return ERROR_HTTP_INVALID_HEADER;
4668         lphttpHdr = &lpwhr->pCustHeaders[index];
4669     }
4670     else if (value)
4671     {
4672         HTTPHEADERW hdr;
4673
4674         hdr.lpszField = (LPWSTR)field;
4675         hdr.lpszValue = (LPWSTR)value;
4676         hdr.wFlags = hdr.wCount = 0;
4677
4678         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4679             hdr.wFlags |= HDR_ISREQUEST;
4680
4681         return HTTP_InsertCustomHeader(lpwhr, &hdr);
4682     }
4683     /* no value to delete */
4684     else return ERROR_SUCCESS;
4685
4686     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4687             lphttpHdr->wFlags |= HDR_ISREQUEST;
4688     else
4689         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4690
4691     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4692     {
4693         HTTP_DeleteCustomHeader( lpwhr, index );
4694
4695         if (value)
4696         {
4697             HTTPHEADERW hdr;
4698
4699             hdr.lpszField = (LPWSTR)field;
4700             hdr.lpszValue = (LPWSTR)value;
4701             hdr.wFlags = hdr.wCount = 0;
4702
4703             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4704                 hdr.wFlags |= HDR_ISREQUEST;
4705
4706             return HTTP_InsertCustomHeader(lpwhr, &hdr);
4707         }
4708
4709         return ERROR_SUCCESS;
4710     }
4711     else if (dwModifier & COALESCEFLAGS)
4712     {
4713         LPWSTR lpsztmp;
4714         WCHAR ch = 0;
4715         INT len = 0;
4716         INT origlen = strlenW(lphttpHdr->lpszValue);
4717         INT valuelen = strlenW(value);
4718
4719         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4720         {
4721             ch = ',';
4722             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4723         }
4724         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4725         {
4726             ch = ';';
4727             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4728         }
4729
4730         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4731
4732         lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4733         if (lpsztmp)
4734         {
4735             lphttpHdr->lpszValue = lpsztmp;
4736     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4737             if (ch > 0)
4738             {
4739                 lphttpHdr->lpszValue[origlen] = ch;
4740                 origlen++;
4741                 lphttpHdr->lpszValue[origlen] = ' ';
4742                 origlen++;
4743             }
4744
4745             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4746             lphttpHdr->lpszValue[len] = '\0';
4747             res = ERROR_SUCCESS;
4748         }
4749         else
4750         {
4751             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4752             res = ERROR_OUTOFMEMORY;
4753         }
4754     }
4755     TRACE("<-- %d\n", res);
4756     return res;
4757 }
4758
4759
4760 /***********************************************************************
4761  *           HTTP_FinishedReading (internal)
4762  *
4763  * Called when all content from server has been read by client.
4764  *
4765  */
4766 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4767 {
4768     BOOL keepalive = HTTP_KeepAlive(lpwhr);
4769
4770     TRACE("\n");
4771
4772
4773     if (!keepalive)
4774     {
4775         HTTPREQ_CloseConnection(&lpwhr->hdr);
4776     }
4777
4778     /* FIXME: store data in the URL cache here */
4779
4780     return TRUE;
4781 }
4782
4783
4784 /***********************************************************************
4785  *           HTTP_GetCustomHeaderIndex (internal)
4786  *
4787  * Return index of custom header from header array
4788  *
4789  */
4790 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4791                                      int requested_index, BOOL request_only)
4792 {
4793     DWORD index;
4794
4795     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4796
4797     for (index = 0; index < lpwhr->nCustHeaders; index++)
4798     {
4799         if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4800             continue;
4801
4802         if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4803             continue;
4804
4805         if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4806             continue;
4807
4808         if (requested_index == 0)
4809             break;
4810         requested_index --;
4811     }
4812
4813     if (index >= lpwhr->nCustHeaders)
4814         index = -1;
4815
4816     TRACE("Return: %d\n", index);
4817     return index;
4818 }
4819
4820
4821 /***********************************************************************
4822  *           HTTP_InsertCustomHeader (internal)
4823  *
4824  * Insert header into array
4825  *
4826  */
4827 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4828 {
4829     INT count;
4830     LPHTTPHEADERW lph = NULL;
4831
4832     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4833     count = lpwhr->nCustHeaders + 1;
4834     if (count > 1)
4835         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4836     else
4837         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4838
4839     if (!lph)
4840         return ERROR_OUTOFMEMORY;
4841
4842     lpwhr->pCustHeaders = lph;
4843     lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4844     lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4845     lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4846     lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4847     lpwhr->nCustHeaders++;
4848
4849     return ERROR_SUCCESS;
4850 }
4851
4852
4853 /***********************************************************************
4854  *           HTTP_DeleteCustomHeader (internal)
4855  *
4856  * Delete header from array
4857  *  If this function is called, the indexs may change.
4858  */
4859 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4860 {
4861     if( lpwhr->nCustHeaders <= 0 )
4862         return FALSE;
4863     if( index >= lpwhr->nCustHeaders )
4864         return FALSE;
4865     lpwhr->nCustHeaders--;
4866
4867     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4868     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4869
4870     memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4871              (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4872     memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4873
4874     return TRUE;
4875 }
4876
4877
4878 /***********************************************************************
4879  *           HTTP_VerifyValidHeader (internal)
4880  *
4881  * Verify the given header is not invalid for the given http request
4882  *
4883  */
4884 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4885 {
4886     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4887     if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4888         return ERROR_HTTP_INVALID_HEADER;
4889
4890     return ERROR_SUCCESS;
4891 }
4892
4893 /***********************************************************************
4894  *          IsHostInProxyBypassList (@)
4895  *
4896  * Undocumented
4897  *
4898  */
4899 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4900 {
4901    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4902    return FALSE;
4903 }