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