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