hlink/tests: Comment out a test for an uninitialized 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 protoProxy[MAXHOSTNAME + 15];
1347     DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1348     WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1349     static WCHAR szNul[] = { 0 };
1350     URL_COMPONENTSW UrlComponents;
1351     static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1352     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1353     static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1354
1355     memset( &UrlComponents, 0, sizeof UrlComponents );
1356     UrlComponents.dwStructSize = sizeof UrlComponents;
1357     UrlComponents.lpszHostName = buf;
1358     UrlComponents.dwHostNameLength = MAXHOSTNAME;
1359
1360     if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1361         return FALSE;
1362     if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1363                                  protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1364         sprintfW(proxy, szFormat, protoProxy);
1365     else
1366         strcpyW(proxy, protoProxy);
1367     if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1368         return FALSE;
1369     if( UrlComponents.dwHostNameLength == 0 )
1370         return FALSE;
1371
1372     if( !lpwhr->lpszPath )
1373         lpwhr->lpszPath = szNul;
1374
1375     if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1376         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1377
1378     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1379     lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1380     lpwhs->nServerPort = UrlComponents.nPort;
1381
1382     TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1383     return TRUE;
1384 }
1385
1386 #ifndef INET6_ADDRSTRLEN
1387 #define INET6_ADDRSTRLEN 46
1388 #endif
1389
1390 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1391 {
1392     char szaddr[INET6_ADDRSTRLEN];
1393     http_session_t *lpwhs = lpwhr->lpHttpSession;
1394     const void *addr;
1395
1396     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1397                           INTERNET_STATUS_RESOLVING_NAME,
1398                           lpwhs->lpszServerName,
1399                           (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1400
1401     lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1402     if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1403                     (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1404         return ERROR_INTERNET_NAME_NOT_RESOLVED;
1405
1406     switch (lpwhs->socketAddress.ss_family)
1407     {
1408     case AF_INET:
1409         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1410         break;
1411     case AF_INET6:
1412         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1413         break;
1414     default:
1415         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1416         return ERROR_INTERNET_NAME_NOT_RESOLVED;
1417     }
1418     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1419     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1420                           INTERNET_STATUS_NAME_RESOLVED,
1421                           szaddr, strlen(szaddr)+1);
1422
1423     TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1424     return ERROR_SUCCESS;
1425 }
1426
1427
1428 /***********************************************************************
1429  *           HTTPREQ_Destroy (internal)
1430  *
1431  * Deallocate request handle
1432  *
1433  */
1434 static void HTTPREQ_Destroy(object_header_t *hdr)
1435 {
1436     http_request_t *lpwhr = (http_request_t*) hdr;
1437     DWORD i;
1438
1439     TRACE("\n");
1440
1441     if(lpwhr->hCacheFile)
1442         CloseHandle(lpwhr->hCacheFile);
1443
1444     HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1445
1446     DeleteCriticalSection( &lpwhr->read_section );
1447     WININET_Release(&lpwhr->lpHttpSession->hdr);
1448
1449     destroy_authinfo(lpwhr->pAuthInfo);
1450     destroy_authinfo(lpwhr->pProxyAuthInfo);
1451
1452     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1453     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1454     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1455     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1456     HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1457
1458     for (i = 0; i < lpwhr->nCustHeaders; i++)
1459     {
1460         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1461         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1462     }
1463
1464 #ifdef HAVE_ZLIB
1465     if(lpwhr->gzip_stream) {
1466         if(!lpwhr->gzip_stream->end_of_data)
1467             inflateEnd(&lpwhr->gzip_stream->zstream);
1468         HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1469     }
1470 #endif
1471
1472     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1473     HeapFree(GetProcessHeap(), 0, lpwhr);
1474 }
1475
1476 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1477 {
1478     http_request_t *lpwhr = (http_request_t*) hdr;
1479
1480     TRACE("%p\n",lpwhr);
1481
1482     if (!NETCON_connected(&lpwhr->netConnection))
1483         return;
1484
1485     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1486                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1487
1488     NETCON_close(&lpwhr->netConnection);
1489
1490     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1491                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1492 }
1493
1494 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1495 {
1496     LPHTTPHEADERW host_header;
1497
1498     static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1499
1500     host_header = HTTP_GetHeader(req, hostW);
1501     if(!host_header)
1502         return FALSE;
1503
1504     sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1505     return TRUE;
1506 }
1507
1508 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1509 {
1510     WCHAR szVersion[10];
1511     WCHAR szConnectionResponse[20];
1512     DWORD dwBufferSize = sizeof(szVersion);
1513     BOOL keepalive = FALSE;
1514
1515     /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1516      * the connection is keep-alive by default */
1517     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1518         && !strcmpiW(szVersion, g_szHttp1_1))
1519     {
1520         keepalive = TRUE;
1521     }
1522
1523     dwBufferSize = sizeof(szConnectionResponse);
1524     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1525         || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1526     {
1527         keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1528     }
1529
1530     return keepalive;
1531 }
1532
1533 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1534 {
1535     http_request_t *req = (http_request_t*)hdr;
1536
1537     switch(option) {
1538     case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1539     {
1540         http_session_t *lpwhs = req->lpHttpSession;
1541         INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1542
1543         FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1544
1545         if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1546             return ERROR_INSUFFICIENT_BUFFER;
1547         *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1548         /* FIXME: can't get a SOCKET from our connection since we don't use
1549          * winsock
1550          */
1551         info->Socket = 0;
1552         /* FIXME: get source port from req->netConnection */
1553         info->SourcePort = 0;
1554         info->DestPort = lpwhs->nHostPort;
1555         info->Flags = 0;
1556         if (HTTP_KeepAlive(req))
1557             info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1558         if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1559             info->Flags |= IDSI_FLAG_PROXY;
1560         if (req->netConnection.useSSL)
1561             info->Flags |= IDSI_FLAG_SECURE;
1562
1563         return ERROR_SUCCESS;
1564     }
1565
1566     case INTERNET_OPTION_SECURITY_FLAGS:
1567     {
1568         http_session_t *lpwhs;
1569         lpwhs = req->lpHttpSession;
1570
1571         if (*size < sizeof(ULONG))
1572             return ERROR_INSUFFICIENT_BUFFER;
1573
1574         *size = sizeof(DWORD);
1575         if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1576             *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1577         else
1578             *(DWORD*)buffer = 0;
1579         FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1580         return ERROR_SUCCESS;
1581     }
1582
1583     case INTERNET_OPTION_HANDLE_TYPE:
1584         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1585
1586         if (*size < sizeof(ULONG))
1587             return ERROR_INSUFFICIENT_BUFFER;
1588
1589         *size = sizeof(DWORD);
1590         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1591         return ERROR_SUCCESS;
1592
1593     case INTERNET_OPTION_URL: {
1594         WCHAR url[INTERNET_MAX_URL_LENGTH];
1595         HTTPHEADERW *host;
1596         DWORD len;
1597         WCHAR *pch;
1598
1599         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1600
1601         TRACE("INTERNET_OPTION_URL\n");
1602
1603         host = HTTP_GetHeader(req, hostW);
1604         strcpyW(url, httpW);
1605         strcatW(url, host->lpszValue);
1606         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1607             *pch = 0;
1608         strcatW(url, req->lpszPath);
1609
1610         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1611
1612         if(unicode) {
1613             len = (strlenW(url)+1) * sizeof(WCHAR);
1614             if(*size < len)
1615                 return ERROR_INSUFFICIENT_BUFFER;
1616
1617             *size = len;
1618             strcpyW(buffer, url);
1619             return ERROR_SUCCESS;
1620         }else {
1621             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1622             if(len > *size)
1623                 return ERROR_INSUFFICIENT_BUFFER;
1624
1625             *size = len;
1626             return ERROR_SUCCESS;
1627         }
1628     }
1629
1630     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1631         INTERNET_CACHE_ENTRY_INFOW *info;
1632         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1633         WCHAR url[INTERNET_MAX_URL_LENGTH];
1634         DWORD nbytes, error;
1635         BOOL ret;
1636
1637         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1638
1639         if (*size < sizeof(*ts))
1640         {
1641             *size = sizeof(*ts);
1642             return ERROR_INSUFFICIENT_BUFFER;
1643         }
1644         nbytes = 0;
1645         HTTP_GetRequestURL(req, url);
1646         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1647         error = GetLastError();
1648         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1649         {
1650             if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1651                 return ERROR_OUTOFMEMORY;
1652
1653             GetUrlCacheEntryInfoW(url, info, &nbytes);
1654
1655             ts->ftExpires = info->ExpireTime;
1656             ts->ftLastModified = info->LastModifiedTime;
1657
1658             HeapFree(GetProcessHeap(), 0, info);
1659             *size = sizeof(*ts);
1660             return ERROR_SUCCESS;
1661         }
1662         return error;
1663     }
1664
1665     case INTERNET_OPTION_DATAFILE_NAME: {
1666         DWORD req_size;
1667
1668         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1669
1670         if(!req->lpszCacheFile) {
1671             *size = 0;
1672             return ERROR_INTERNET_ITEM_NOT_FOUND;
1673         }
1674
1675         if(unicode) {
1676             req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1677             if(*size < req_size)
1678                 return ERROR_INSUFFICIENT_BUFFER;
1679
1680             *size = req_size;
1681             memcpy(buffer, req->lpszCacheFile, *size);
1682             return ERROR_SUCCESS;
1683         }else {
1684             req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1685             if (req_size > *size)
1686                 return ERROR_INSUFFICIENT_BUFFER;
1687
1688             *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1689                     -1, buffer, *size, NULL, NULL);
1690             return ERROR_SUCCESS;
1691         }
1692     }
1693
1694     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1695         PCCERT_CONTEXT context;
1696
1697         if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1698             *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1699             return ERROR_INSUFFICIENT_BUFFER;
1700         }
1701
1702         context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1703         if(context) {
1704             INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1705             DWORD len;
1706
1707             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1708             info->ftExpiry = context->pCertInfo->NotAfter;
1709             info->ftStart = context->pCertInfo->NotBefore;
1710             if(unicode) {
1711                 len = CertNameToStrW(context->dwCertEncodingType,
1712                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1713                 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1714                 if(info->lpszSubjectInfo)
1715                     CertNameToStrW(context->dwCertEncodingType,
1716                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1717                              info->lpszSubjectInfo, len);
1718                 len = CertNameToStrW(context->dwCertEncodingType,
1719                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1720                 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1721                 if (info->lpszIssuerInfo)
1722                     CertNameToStrW(context->dwCertEncodingType,
1723                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1724                              info->lpszIssuerInfo, len);
1725             }else {
1726                 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1727
1728                 len = CertNameToStrA(context->dwCertEncodingType,
1729                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1730                 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1731                 if(infoA->lpszSubjectInfo)
1732                     CertNameToStrA(context->dwCertEncodingType,
1733                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1734                              infoA->lpszSubjectInfo, len);
1735                 len = CertNameToStrA(context->dwCertEncodingType,
1736                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1737                 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1738                 if(infoA->lpszIssuerInfo)
1739                     CertNameToStrA(context->dwCertEncodingType,
1740                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1741                              infoA->lpszIssuerInfo, len);
1742             }
1743
1744             /*
1745              * Contrary to MSDN, these do not appear to be set.
1746              * lpszProtocolName
1747              * lpszSignatureAlgName
1748              * lpszEncryptionAlgName
1749              * dwKeySize
1750              */
1751             CertFreeCertificateContext(context);
1752             return ERROR_SUCCESS;
1753         }
1754     }
1755     }
1756
1757     return INET_QueryOption(hdr, option, buffer, size, unicode);
1758 }
1759
1760 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1761 {
1762     http_request_t *req = (http_request_t*)hdr;
1763
1764     switch(option) {
1765     case INTERNET_OPTION_SEND_TIMEOUT:
1766     case INTERNET_OPTION_RECEIVE_TIMEOUT:
1767         TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1768
1769         if (size != sizeof(DWORD))
1770             return ERROR_INVALID_PARAMETER;
1771
1772         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1773                     *(DWORD*)buffer);
1774
1775     case INTERNET_OPTION_USERNAME:
1776         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1777         if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1778         return ERROR_SUCCESS;
1779
1780     case INTERNET_OPTION_PASSWORD:
1781         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1782         if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1783         return ERROR_SUCCESS;
1784     case INTERNET_OPTION_HTTP_DECODING:
1785         if(size != sizeof(BOOL))
1786             return ERROR_INVALID_PARAMETER;
1787         req->decoding = *(BOOL*)buffer;
1788         return ERROR_SUCCESS;
1789     }
1790
1791     return ERROR_INTERNET_INVALID_OPTION;
1792 }
1793
1794 /* read some more data into the read buffer (the read section must be held) */
1795 static DWORD read_more_data( http_request_t *req, int maxlen )
1796 {
1797     DWORD res;
1798     int len;
1799
1800     if (req->read_pos)
1801     {
1802         /* move existing data to the start of the buffer */
1803         if(req->read_size)
1804             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1805         req->read_pos = 0;
1806     }
1807
1808     if (maxlen == -1) maxlen = sizeof(req->read_buf);
1809
1810     res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1811                        maxlen - req->read_size, 0, &len );
1812     if(res == ERROR_SUCCESS)
1813         req->read_size += len;
1814
1815     return res;
1816 }
1817
1818 /* remove some amount of data from the read buffer (the read section must be held) */
1819 static void remove_data( http_request_t *req, int count )
1820 {
1821     if (!(req->read_size -= count)) req->read_pos = 0;
1822     else req->read_pos += count;
1823 }
1824
1825 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1826 {
1827     int count, bytes_read, pos = 0;
1828     DWORD res;
1829
1830     EnterCriticalSection( &req->read_section );
1831     for (;;)
1832     {
1833         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1834
1835         if (eol)
1836         {
1837             count = eol - (req->read_buf + req->read_pos);
1838             bytes_read = count + 1;
1839         }
1840         else count = bytes_read = req->read_size;
1841
1842         count = min( count, *len - pos );
1843         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1844         pos += count;
1845         remove_data( req, bytes_read );
1846         if (eol) break;
1847
1848         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1849         {
1850             *len = 0;
1851             TRACE( "returning empty string\n" );
1852             LeaveCriticalSection( &req->read_section );
1853             INTERNET_SetLastError(res);
1854             return FALSE;
1855         }
1856     }
1857     LeaveCriticalSection( &req->read_section );
1858
1859     if (pos < *len)
1860     {
1861         if (pos && buffer[pos - 1] == '\r') pos--;
1862         *len = pos + 1;
1863     }
1864     buffer[*len - 1] = 0;
1865     TRACE( "returning %s\n", debugstr_a(buffer));
1866     return TRUE;
1867 }
1868
1869 /* discard data contents until we reach end of line (the read section must be held) */
1870 static DWORD discard_eol( http_request_t *req )
1871 {
1872     DWORD res;
1873
1874     do
1875     {
1876         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1877         if (eol)
1878         {
1879             remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1880             break;
1881         }
1882         req->read_pos = req->read_size = 0;  /* discard everything */
1883         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1884     } while (req->read_size);
1885     return ERROR_SUCCESS;
1886 }
1887
1888 /* read the size of the next chunk (the read section must be held) */
1889 static DWORD start_next_chunk( http_request_t *req )
1890 {
1891     DWORD chunk_size = 0, res;
1892
1893     if (!req->dwContentLength) return ERROR_SUCCESS;
1894     if (req->dwContentLength == req->dwContentRead)
1895     {
1896         /* read terminator for the previous chunk */
1897         if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1898         req->dwContentLength = ~0u;
1899         req->dwContentRead = 0;
1900     }
1901     for (;;)
1902     {
1903         while (req->read_size)
1904         {
1905             char ch = req->read_buf[req->read_pos];
1906             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1907             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1908             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1909             else if (ch == ';' || ch == '\r' || ch == '\n')
1910             {
1911                 TRACE( "reading %u byte chunk\n", chunk_size );
1912                 req->dwContentLength = chunk_size;
1913                 req->dwContentRead = 0;
1914                 return discard_eol( req );
1915             }
1916             remove_data( req, 1 );
1917         }
1918         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1919         if (!req->read_size)
1920         {
1921             req->dwContentLength = req->dwContentRead = 0;
1922             return ERROR_SUCCESS;
1923         }
1924     }
1925 }
1926
1927 /* check if we have reached the end of the data to read (the read section must be held) */
1928 static BOOL end_of_read_data( http_request_t *req )
1929 {
1930     if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1931     if (req->read_chunked) return (req->dwContentLength == 0);
1932     if (req->dwContentLength == ~0u) return FALSE;
1933     return (req->dwContentLength == req->dwContentRead);
1934 }
1935
1936 /* fetch some more data into the read buffer (the read section must be held) */
1937 static DWORD refill_buffer( http_request_t *req )
1938 {
1939     int len = sizeof(req->read_buf);
1940     DWORD res;
1941
1942     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1943     {
1944         if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1945     }
1946
1947     if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1948     if (len <= req->read_size) return ERROR_SUCCESS;
1949
1950     if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1951     if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1952     return ERROR_SUCCESS;
1953 }
1954
1955 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1956 {
1957     DWORD ret = ERROR_SUCCESS;
1958     int read = 0;
1959
1960 #ifdef HAVE_ZLIB
1961     z_stream *zstream = &req->gzip_stream->zstream;
1962     DWORD buf_avail;
1963     int zres;
1964
1965     while(read < size && !req->gzip_stream->end_of_data) {
1966         if(!req->read_size) {
1967             if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1968                 break;
1969         }
1970
1971         buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1972
1973         zstream->next_in = req->read_buf+req->read_pos;
1974         zstream->avail_in = buf_avail;
1975         zstream->next_out = buf+read;
1976         zstream->avail_out = size-read;
1977         zres = inflate(zstream, Z_FULL_FLUSH);
1978         read = size - zstream->avail_out;
1979         req->dwContentRead += buf_avail-zstream->avail_in;
1980         remove_data(req, buf_avail-zstream->avail_in);
1981         if(zres == Z_STREAM_END) {
1982             TRACE("end of data\n");
1983             req->gzip_stream->end_of_data = TRUE;
1984             inflateEnd(&req->gzip_stream->zstream);
1985         }else if(zres != Z_OK) {
1986             WARN("inflate failed %d\n", zres);
1987             if(!read)
1988                 ret = ERROR_INTERNET_DECODING_FAILED;
1989             break;
1990         }
1991     }
1992 #endif
1993
1994     *read_ret = read;
1995     return ret;
1996 }
1997
1998 static void refill_gzip_buffer(http_request_t *req)
1999 {
2000     DWORD res;
2001     int len;
2002
2003     if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2004         return;
2005
2006     if(req->gzip_stream->buf_pos) {
2007         if(req->gzip_stream->buf_size)
2008             memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2009         req->gzip_stream->buf_pos = 0;
2010     }
2011
2012     res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2013             sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2014     if(res == ERROR_SUCCESS)
2015         req->gzip_stream->buf_size += len;
2016 }
2017
2018 /* return the size of data available to be read immediately (the read section must be held) */
2019 static DWORD get_avail_data( http_request_t *req )
2020 {
2021     if (req->gzip_stream) {
2022         refill_gzip_buffer(req);
2023         return req->gzip_stream->buf_size;
2024     }
2025     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2026         return 0;
2027     return min( req->read_size, req->dwContentLength - req->dwContentRead );
2028 }
2029
2030 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2031 {
2032     INTERNET_ASYNC_RESULT iar;
2033     DWORD res;
2034
2035     TRACE("%p\n", req);
2036
2037     EnterCriticalSection( &req->read_section );
2038     if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2039         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2040         iar.dwError = first_notif ? 0 : get_avail_data(req);
2041     }else {
2042         iar.dwResult = 0;
2043         iar.dwError = res;
2044     }
2045     LeaveCriticalSection( &req->read_section );
2046
2047     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2048                           sizeof(INTERNET_ASYNC_RESULT));
2049 }
2050
2051 /* read data from the http connection (the read section must be held) */
2052 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2053 {
2054     BOOL finished_reading = FALSE;
2055     int len, bytes_read = 0;
2056     DWORD ret = ERROR_SUCCESS;
2057
2058     EnterCriticalSection( &req->read_section );
2059
2060     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2061     {
2062         if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2063     }
2064
2065     if(req->gzip_stream) {
2066         if(req->gzip_stream->buf_size) {
2067             bytes_read = min(req->gzip_stream->buf_size, size);
2068             memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2069             req->gzip_stream->buf_pos += bytes_read;
2070             req->gzip_stream->buf_size -= bytes_read;
2071         }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2072             refill_buffer(req);
2073         }
2074
2075         if(size > bytes_read) {
2076             ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2077             if(ret == ERROR_SUCCESS)
2078                 bytes_read += len;
2079         }
2080
2081         finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2082     }else {
2083         if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2084
2085         if (req->read_size) {
2086             bytes_read = min( req->read_size, size );
2087             memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2088             remove_data( req, bytes_read );
2089         }
2090
2091         if (size > bytes_read && (!bytes_read || sync)) {
2092             if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2093                              sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2094                 bytes_read += len;
2095             /* always return success, even if the network layer returns an error */
2096         }
2097
2098         finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2099         req->dwContentRead += bytes_read;
2100     }
2101 done:
2102     *read = bytes_read;
2103
2104     TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2105     LeaveCriticalSection( &req->read_section );
2106
2107     if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2108         BOOL res;
2109         DWORD dwBytesWritten;
2110
2111         res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2112         if(!res)
2113             WARN("WriteFile failed: %u\n", GetLastError());
2114     }
2115
2116     if(finished_reading)
2117         HTTP_FinishedReading(req);
2118
2119     return ret;
2120 }
2121
2122
2123 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2124 {
2125     http_request_t *req = (http_request_t*)hdr;
2126     return HTTPREQ_Read(req, buffer, size, read, TRUE);
2127 }
2128
2129 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2130 {
2131     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2132     http_request_t *req = (http_request_t*)workRequest->hdr;
2133     INTERNET_ASYNC_RESULT iar;
2134     DWORD res;
2135
2136     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2137
2138     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2139             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2140
2141     iar.dwResult = res == ERROR_SUCCESS;
2142     iar.dwError = res;
2143
2144     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2145                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2146                           sizeof(INTERNET_ASYNC_RESULT));
2147 }
2148
2149 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2150         DWORD flags, DWORD_PTR context)
2151 {
2152     http_request_t *req = (http_request_t*)hdr;
2153     DWORD res;
2154
2155     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2156         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2157
2158     if (buffers->dwStructSize != sizeof(*buffers))
2159         return ERROR_INVALID_PARAMETER;
2160
2161     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2162
2163     if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2164     {
2165         WORKREQUEST workRequest;
2166
2167         if (TryEnterCriticalSection( &req->read_section ))
2168         {
2169             if (get_avail_data(req))
2170             {
2171                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2172                                    &buffers->dwBufferLength, FALSE);
2173                 LeaveCriticalSection( &req->read_section );
2174                 goto done;
2175             }
2176             LeaveCriticalSection( &req->read_section );
2177         }
2178
2179         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2180         workRequest.hdr = WININET_AddRef(&req->hdr);
2181         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2182
2183         INTERNET_AsyncCall(&workRequest);
2184
2185         return ERROR_IO_PENDING;
2186     }
2187
2188     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2189             !(flags & IRF_NO_WAIT));
2190
2191 done:
2192     if (res == ERROR_SUCCESS) {
2193         DWORD size = buffers->dwBufferLength;
2194         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2195                 &size, sizeof(size));
2196     }
2197
2198     return res;
2199 }
2200
2201 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2202 {
2203     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2204     http_request_t *req = (http_request_t*)workRequest->hdr;
2205     INTERNET_ASYNC_RESULT iar;
2206     DWORD res;
2207
2208     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2209
2210     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2211             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2212
2213     iar.dwResult = res == ERROR_SUCCESS;
2214     iar.dwError = res;
2215
2216     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2217                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2218                           sizeof(INTERNET_ASYNC_RESULT));
2219 }
2220
2221 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2222         DWORD flags, DWORD_PTR context)
2223 {
2224
2225     http_request_t *req = (http_request_t*)hdr;
2226     DWORD res;
2227
2228     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2229         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2230
2231     if (buffers->dwStructSize != sizeof(*buffers))
2232         return ERROR_INVALID_PARAMETER;
2233
2234     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2235
2236     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2237     {
2238         WORKREQUEST workRequest;
2239
2240         if (TryEnterCriticalSection( &req->read_section ))
2241         {
2242             if (get_avail_data(req))
2243             {
2244                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2245                                    &buffers->dwBufferLength, FALSE);
2246                 LeaveCriticalSection( &req->read_section );
2247                 goto done;
2248             }
2249             LeaveCriticalSection( &req->read_section );
2250         }
2251
2252         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2253         workRequest.hdr = WININET_AddRef(&req->hdr);
2254         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2255
2256         INTERNET_AsyncCall(&workRequest);
2257
2258         return ERROR_IO_PENDING;
2259     }
2260
2261     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2262             !(flags & IRF_NO_WAIT));
2263
2264 done:
2265     if (res == ERROR_SUCCESS) {
2266         DWORD size = buffers->dwBufferLength;
2267         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2268                 &size, sizeof(size));
2269     }
2270
2271     return res;
2272 }
2273
2274 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2275 {
2276     DWORD res;
2277     http_request_t *lpwhr = (http_request_t*)hdr;
2278
2279     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2280
2281     *written = 0;
2282     res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2283     if (res == ERROR_SUCCESS)
2284         lpwhr->dwBytesWritten += *written;
2285
2286     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2287     return res;
2288 }
2289
2290 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2291 {
2292     http_request_t *req = (http_request_t*)workRequest->hdr;
2293
2294     HTTP_ReceiveRequestData(req, FALSE);
2295 }
2296
2297 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2298 {
2299     http_request_t *req = (http_request_t*)hdr;
2300
2301     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2302
2303     if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2304     {
2305         WORKREQUEST workRequest;
2306
2307         /* never wait, if we can't enter the section we queue an async request right away */
2308         if (TryEnterCriticalSection( &req->read_section ))
2309         {
2310             if ((*available = get_avail_data( req ))) goto done;
2311             if (end_of_read_data( req )) goto done;
2312             LeaveCriticalSection( &req->read_section );
2313         }
2314
2315         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2316         workRequest.hdr = WININET_AddRef( &req->hdr );
2317
2318         INTERNET_AsyncCall(&workRequest);
2319
2320         return ERROR_IO_PENDING;
2321     }
2322
2323     EnterCriticalSection( &req->read_section );
2324
2325     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2326     {
2327         refill_buffer( req );
2328         *available = get_avail_data( req );
2329     }
2330
2331 done:
2332     if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
2333     {
2334         DWORD extra;
2335         if (NETCON_query_data_available(&req->netConnection, &extra))
2336             *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2337     }
2338     LeaveCriticalSection( &req->read_section );
2339
2340     TRACE( "returning %u\n", *available );
2341     return ERROR_SUCCESS;
2342 }
2343
2344 static const object_vtbl_t HTTPREQVtbl = {
2345     HTTPREQ_Destroy,
2346     HTTPREQ_CloseConnection,
2347     HTTPREQ_QueryOption,
2348     HTTPREQ_SetOption,
2349     HTTPREQ_ReadFile,
2350     HTTPREQ_ReadFileExA,
2351     HTTPREQ_ReadFileExW,
2352     HTTPREQ_WriteFile,
2353     HTTPREQ_QueryDataAvailable,
2354     NULL
2355 };
2356
2357 /***********************************************************************
2358  *           HTTP_HttpOpenRequestW (internal)
2359  *
2360  * Open a HTTP request handle
2361  *
2362  * RETURNS
2363  *    HINTERNET  a HTTP request handle on success
2364  *    NULL       on failure
2365  *
2366  */
2367 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2368         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2369         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2370         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2371 {
2372     appinfo_t *hIC = NULL;
2373     http_request_t *lpwhr;
2374     LPWSTR lpszHostName = NULL;
2375     HINTERNET handle = NULL;
2376     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2377     DWORD len, res;
2378
2379     TRACE("-->\n");
2380
2381     assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2382     hIC = lpwhs->lpAppInfo;
2383
2384     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2385     if (NULL == lpwhr)
2386     {
2387         res = ERROR_OUTOFMEMORY;
2388         goto lend;
2389     }
2390     lpwhr->hdr.htype = WH_HHTTPREQ;
2391     lpwhr->hdr.vtbl = &HTTPREQVtbl;
2392     lpwhr->hdr.dwFlags = dwFlags;
2393     lpwhr->hdr.dwContext = dwContext;
2394     lpwhr->hdr.refs = 1;
2395     lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2396     lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2397     lpwhr->dwContentLength = ~0u;
2398     InitializeCriticalSection( &lpwhr->read_section );
2399
2400     WININET_AddRef( &lpwhs->hdr );
2401     lpwhr->lpHttpSession = lpwhs;
2402     list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2403
2404     lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2405             (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2406     if (NULL == lpszHostName)
2407     {
2408         res = ERROR_OUTOFMEMORY;
2409         goto lend;
2410     }
2411
2412     handle = WININET_AllocHandle( &lpwhr->hdr );
2413     if (NULL == handle)
2414     {
2415         res = ERROR_OUTOFMEMORY;
2416         goto lend;
2417     }
2418
2419     if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2420     {
2421         InternetCloseHandle( handle );
2422         handle = NULL;
2423         goto lend;
2424     }
2425
2426     if (lpszObjectName && *lpszObjectName) {
2427         HRESULT rc;
2428
2429         len = 0;
2430         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2431         if (rc != E_POINTER)
2432             len = strlenW(lpszObjectName)+1;
2433         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2434         rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2435                    URL_ESCAPE_SPACES_ONLY);
2436         if (rc != S_OK)
2437         {
2438             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2439             strcpyW(lpwhr->lpszPath,lpszObjectName);
2440         }
2441     }else {
2442         static const WCHAR slashW[] = {'/',0};
2443
2444         lpwhr->lpszPath = heap_strdupW(slashW);
2445     }
2446
2447     if (lpszReferrer && *lpszReferrer)
2448         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2449
2450     if (lpszAcceptTypes)
2451     {
2452         int i;
2453         for (i = 0; lpszAcceptTypes[i]; i++)
2454         {
2455             if (!*lpszAcceptTypes[i]) continue;
2456             HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2457                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2458                                HTTP_ADDHDR_FLAG_REQ |
2459                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2460         }
2461     }
2462
2463     lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2464     lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2465
2466     if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2467         lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2468         lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2469     {
2470         sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2471         HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2472                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2473     }
2474     else
2475         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2476                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2477
2478     if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2479         lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2480                         INTERNET_DEFAULT_HTTPS_PORT :
2481                         INTERNET_DEFAULT_HTTP_PORT);
2482
2483     if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2484         lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2485                         INTERNET_DEFAULT_HTTPS_PORT :
2486                         INTERNET_DEFAULT_HTTP_PORT);
2487
2488     if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2489         HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2490
2491     INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2492                           INTERNET_STATUS_HANDLE_CREATED, &handle,
2493                           sizeof(handle));
2494
2495 lend:
2496     HeapFree(GetProcessHeap(), 0, lpszHostName);
2497     if( lpwhr )
2498         WININET_Release( &lpwhr->hdr );
2499
2500     TRACE("<-- %p (%p)\n", handle, lpwhr);
2501     *ret = handle;
2502     return res;
2503 }
2504
2505 /***********************************************************************
2506  *           HttpOpenRequestW (WININET.@)
2507  *
2508  * Open a HTTP request handle
2509  *
2510  * RETURNS
2511  *    HINTERNET  a HTTP request handle on success
2512  *    NULL       on failure
2513  *
2514  */
2515 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2516         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2517         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2518         DWORD dwFlags, DWORD_PTR dwContext)
2519 {
2520     http_session_t *lpwhs;
2521     HINTERNET handle = NULL;
2522     DWORD res;
2523
2524     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2525           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2526           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2527           dwFlags, dwContext);
2528     if(lpszAcceptTypes!=NULL)
2529     {
2530         int i;
2531         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2532             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2533     }
2534
2535     lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2536     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
2537     {
2538         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2539         goto lend;
2540     }
2541
2542     /*
2543      * My tests seem to show that the windows version does not
2544      * become asynchronous until after this point. And anyhow
2545      * if this call was asynchronous then how would you get the
2546      * necessary HINTERNET pointer returned by this function.
2547      *
2548      */
2549     res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2550                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
2551                                 dwFlags, dwContext, &handle);
2552 lend:
2553     if( lpwhs )
2554         WININET_Release( &lpwhs->hdr );
2555     TRACE("returning %p\n", handle);
2556     if(res != ERROR_SUCCESS)
2557         SetLastError(res);
2558     return handle;
2559 }
2560
2561 /* read any content returned by the server so that the connection can be
2562  * reused */
2563 static void HTTP_DrainContent(http_request_t *req)
2564 {
2565     DWORD bytes_read;
2566
2567     if (!NETCON_connected(&req->netConnection)) return;
2568
2569     if (req->dwContentLength == -1)
2570     {
2571         NETCON_close(&req->netConnection);
2572         return;
2573     }
2574     if (!strcmpW(req->lpszVerb, szHEAD)) return;
2575
2576     do
2577     {
2578         char buffer[2048];
2579         if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2580             return;
2581     } while (bytes_read);
2582 }
2583
2584 static const LPCWSTR header_lookup[] = {
2585     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
2586     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
2587     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2588     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
2589     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2590     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
2591     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
2592     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
2593     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
2594     szDate,                     /* HTTP_QUERY_DATE = 9 */
2595     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
2596     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
2597     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
2598     szURI,                      /* HTTP_QUERY_URI = 13 */
2599     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
2600     NULL,                       /* HTTP_QUERY_COST = 15 */
2601     NULL,                       /* HTTP_QUERY_LINK = 16 */
2602     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
2603     NULL,                       /* HTTP_QUERY_VERSION = 18 */
2604     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
2605     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
2606     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
2607     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2608     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
2609     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
2610     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2611     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2612     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2613     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
2614     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2615     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
2616     NULL,                       /* HTTP_QUERY_FROM = 31 */
2617     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2618     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
2619     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
2620     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
2621     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
2622     szServer,                   /* HTTP_QUERY_SERVER = 37 */
2623     NULL,                       /* HTTP_TITLE = 38 */
2624     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
2625     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2626     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2627     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2628     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
2629     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
2630     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
2631     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
2632     NULL,                       /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2633     szAge,                      /* HTTP_QUERY_AGE = 48 */
2634     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
2635     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
2636     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2637     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
2638     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
2639     szETag,                     /* HTTP_QUERY_ETAG = 54 */
2640     hostW,                      /* HTTP_QUERY_HOST = 55 */
2641     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
2642     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2643     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
2644     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2645     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
2646     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2647     szRange,                    /* HTTP_QUERY_RANGE = 62 */
2648     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2649     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
2650     szVary,                     /* HTTP_QUERY_VARY = 65 */
2651     szVia,                      /* HTTP_QUERY_VIA = 66 */
2652     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
2653     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
2654     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2655     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2656 };
2657
2658 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2659
2660 /***********************************************************************
2661  *           HTTP_HttpQueryInfoW (internal)
2662  */
2663 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2664         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2665 {
2666     LPHTTPHEADERW lphttpHdr = NULL;
2667     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2668     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2669     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2670     INT index = -1;
2671
2672     /* Find requested header structure */
2673     switch (level)
2674     {
2675     case HTTP_QUERY_CUSTOM:
2676         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2677         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2678         break;
2679     case HTTP_QUERY_RAW_HEADERS_CRLF:
2680         {
2681             LPWSTR headers;
2682             DWORD len = 0;
2683             DWORD res = ERROR_INVALID_PARAMETER;
2684
2685             if (request_only)
2686                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2687             else
2688                 headers = lpwhr->lpszRawHeaders;
2689
2690             if (headers)
2691                 len = strlenW(headers) * sizeof(WCHAR);
2692
2693             if (len + sizeof(WCHAR) > *lpdwBufferLength)
2694             {
2695                 len += sizeof(WCHAR);
2696                 res = ERROR_INSUFFICIENT_BUFFER;
2697             }
2698             else if (lpBuffer)
2699             {
2700                 if (headers)
2701                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2702                 else
2703                 {
2704                     len = strlenW(szCrLf) * sizeof(WCHAR);
2705                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2706                 }
2707                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2708                 res = ERROR_SUCCESS;
2709             }
2710             *lpdwBufferLength = len;
2711
2712             if (request_only)
2713                 HeapFree(GetProcessHeap(), 0, headers);
2714             return res;
2715         }
2716     case HTTP_QUERY_RAW_HEADERS:
2717         {
2718             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2719             DWORD i, size = 0;
2720             LPWSTR pszString = lpBuffer;
2721
2722             for (i = 0; ppszRawHeaderLines[i]; i++)
2723                 size += strlenW(ppszRawHeaderLines[i]) + 1;
2724
2725             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2726             {
2727                 HTTP_FreeTokens(ppszRawHeaderLines);
2728                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2729                 return ERROR_INSUFFICIENT_BUFFER;
2730             }
2731             if (pszString)
2732             {
2733                 for (i = 0; ppszRawHeaderLines[i]; i++)
2734                 {
2735                     DWORD len = strlenW(ppszRawHeaderLines[i]);
2736                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2737                     pszString += len+1;
2738                 }
2739                 *pszString = '\0';
2740                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2741             }
2742             *lpdwBufferLength = size * sizeof(WCHAR);
2743             HTTP_FreeTokens(ppszRawHeaderLines);
2744
2745             return ERROR_SUCCESS;
2746         }
2747     case HTTP_QUERY_STATUS_TEXT:
2748         if (lpwhr->lpszStatusText)
2749         {
2750             DWORD len = strlenW(lpwhr->lpszStatusText);
2751             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2752             {
2753                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2754                 return ERROR_INSUFFICIENT_BUFFER;
2755             }
2756             if (lpBuffer)
2757             {
2758                 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2759                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2760             }
2761             *lpdwBufferLength = len * sizeof(WCHAR);
2762             return ERROR_SUCCESS;
2763         }
2764         break;
2765     case HTTP_QUERY_VERSION:
2766         if (lpwhr->lpszVersion)
2767         {
2768             DWORD len = strlenW(lpwhr->lpszVersion);
2769             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2770             {
2771                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2772                 return ERROR_INSUFFICIENT_BUFFER;
2773             }
2774             if (lpBuffer)
2775             {
2776                 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2777                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2778             }
2779             *lpdwBufferLength = len * sizeof(WCHAR);
2780             return ERROR_SUCCESS;
2781         }
2782         break;
2783     case HTTP_QUERY_CONTENT_ENCODING:
2784         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2785                 requested_index,request_only);
2786         break;
2787     default:
2788         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2789
2790         if (level < LAST_TABLE_HEADER && header_lookup[level])
2791             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2792                                               requested_index,request_only);
2793     }
2794
2795     if (index >= 0)
2796         lphttpHdr = &lpwhr->pCustHeaders[index];
2797
2798     /* Ensure header satisfies requested attributes */
2799     if (!lphttpHdr ||
2800         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2801          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2802     {
2803         return ERROR_HTTP_HEADER_NOT_FOUND;
2804     }
2805
2806     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2807
2808     /* coalesce value to requested type */
2809     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2810     {
2811         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2812         TRACE(" returning number: %d\n", *(int *)lpBuffer);
2813      }
2814     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2815     {
2816         time_t tmpTime;
2817         struct tm tmpTM;
2818         SYSTEMTIME *STHook;
2819
2820         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2821
2822         tmpTM = *gmtime(&tmpTime);
2823         STHook = (SYSTEMTIME *)lpBuffer;
2824         STHook->wDay = tmpTM.tm_mday;
2825         STHook->wHour = tmpTM.tm_hour;
2826         STHook->wMilliseconds = 0;
2827         STHook->wMinute = tmpTM.tm_min;
2828         STHook->wDayOfWeek = tmpTM.tm_wday;
2829         STHook->wMonth = tmpTM.tm_mon + 1;
2830         STHook->wSecond = tmpTM.tm_sec;
2831         STHook->wYear = tmpTM.tm_year;
2832
2833         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2834               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2835               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2836     }
2837     else if (lphttpHdr->lpszValue)
2838     {
2839         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2840
2841         if (len > *lpdwBufferLength)
2842         {
2843             *lpdwBufferLength = len;
2844             return ERROR_INSUFFICIENT_BUFFER;
2845         }
2846         if (lpBuffer)
2847         {
2848             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2849             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2850         }
2851         *lpdwBufferLength = len - sizeof(WCHAR);
2852     }
2853     return ERROR_SUCCESS;
2854 }
2855
2856 /***********************************************************************
2857  *           HttpQueryInfoW (WININET.@)
2858  *
2859  * Queries for information about an HTTP request
2860  *
2861  * RETURNS
2862  *    TRUE  on success
2863  *    FALSE on failure
2864  *
2865  */
2866 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2867         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2868 {
2869     http_request_t *lpwhr;
2870     DWORD res;
2871
2872     if (TRACE_ON(wininet)) {
2873 #define FE(x) { x, #x }
2874         static const wininet_flag_info query_flags[] = {
2875             FE(HTTP_QUERY_MIME_VERSION),
2876             FE(HTTP_QUERY_CONTENT_TYPE),
2877             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2878             FE(HTTP_QUERY_CONTENT_ID),
2879             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2880             FE(HTTP_QUERY_CONTENT_LENGTH),
2881             FE(HTTP_QUERY_CONTENT_LANGUAGE),
2882             FE(HTTP_QUERY_ALLOW),
2883             FE(HTTP_QUERY_PUBLIC),
2884             FE(HTTP_QUERY_DATE),
2885             FE(HTTP_QUERY_EXPIRES),
2886             FE(HTTP_QUERY_LAST_MODIFIED),
2887             FE(HTTP_QUERY_MESSAGE_ID),
2888             FE(HTTP_QUERY_URI),
2889             FE(HTTP_QUERY_DERIVED_FROM),
2890             FE(HTTP_QUERY_COST),
2891             FE(HTTP_QUERY_LINK),
2892             FE(HTTP_QUERY_PRAGMA),
2893             FE(HTTP_QUERY_VERSION),
2894             FE(HTTP_QUERY_STATUS_CODE),
2895             FE(HTTP_QUERY_STATUS_TEXT),
2896             FE(HTTP_QUERY_RAW_HEADERS),
2897             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2898             FE(HTTP_QUERY_CONNECTION),
2899             FE(HTTP_QUERY_ACCEPT),
2900             FE(HTTP_QUERY_ACCEPT_CHARSET),
2901             FE(HTTP_QUERY_ACCEPT_ENCODING),
2902             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2903             FE(HTTP_QUERY_AUTHORIZATION),
2904             FE(HTTP_QUERY_CONTENT_ENCODING),
2905             FE(HTTP_QUERY_FORWARDED),
2906             FE(HTTP_QUERY_FROM),
2907             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2908             FE(HTTP_QUERY_LOCATION),
2909             FE(HTTP_QUERY_ORIG_URI),
2910             FE(HTTP_QUERY_REFERER),
2911             FE(HTTP_QUERY_RETRY_AFTER),
2912             FE(HTTP_QUERY_SERVER),
2913             FE(HTTP_QUERY_TITLE),
2914             FE(HTTP_QUERY_USER_AGENT),
2915             FE(HTTP_QUERY_WWW_AUTHENTICATE),
2916             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2917             FE(HTTP_QUERY_ACCEPT_RANGES),
2918         FE(HTTP_QUERY_SET_COOKIE),
2919         FE(HTTP_QUERY_COOKIE),
2920             FE(HTTP_QUERY_REQUEST_METHOD),
2921             FE(HTTP_QUERY_REFRESH),
2922             FE(HTTP_QUERY_CONTENT_DISPOSITION),
2923             FE(HTTP_QUERY_AGE),
2924             FE(HTTP_QUERY_CACHE_CONTROL),
2925             FE(HTTP_QUERY_CONTENT_BASE),
2926             FE(HTTP_QUERY_CONTENT_LOCATION),
2927             FE(HTTP_QUERY_CONTENT_MD5),
2928             FE(HTTP_QUERY_CONTENT_RANGE),
2929             FE(HTTP_QUERY_ETAG),
2930             FE(HTTP_QUERY_HOST),
2931             FE(HTTP_QUERY_IF_MATCH),
2932             FE(HTTP_QUERY_IF_NONE_MATCH),
2933             FE(HTTP_QUERY_IF_RANGE),
2934             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2935             FE(HTTP_QUERY_MAX_FORWARDS),
2936             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2937             FE(HTTP_QUERY_RANGE),
2938             FE(HTTP_QUERY_TRANSFER_ENCODING),
2939             FE(HTTP_QUERY_UPGRADE),
2940             FE(HTTP_QUERY_VARY),
2941             FE(HTTP_QUERY_VIA),
2942             FE(HTTP_QUERY_WARNING),
2943             FE(HTTP_QUERY_CUSTOM)
2944         };
2945         static const wininet_flag_info modifier_flags[] = {
2946             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2947             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2948             FE(HTTP_QUERY_FLAG_NUMBER),
2949             FE(HTTP_QUERY_FLAG_COALESCE)
2950         };
2951 #undef FE
2952         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2953         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2954         DWORD i;
2955
2956         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2957         TRACE("  Attribute:");
2958         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2959             if (query_flags[i].val == info) {
2960                 TRACE(" %s", query_flags[i].name);
2961                 break;
2962             }
2963         }
2964         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2965             TRACE(" Unknown (%08x)", info);
2966         }
2967
2968         TRACE(" Modifier:");
2969         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2970             if (modifier_flags[i].val & info_mod) {
2971                 TRACE(" %s", modifier_flags[i].name);
2972                 info_mod &= ~ modifier_flags[i].val;
2973             }
2974         }
2975         
2976         if (info_mod) {
2977             TRACE(" Unknown (%08x)", info_mod);
2978         }
2979         TRACE("\n");
2980     }
2981     
2982     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2983     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
2984     {
2985         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2986         goto lend;
2987     }
2988
2989     if (lpBuffer == NULL)
2990         *lpdwBufferLength = 0;
2991     res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2992                                lpBuffer, lpdwBufferLength, lpdwIndex);
2993
2994 lend:
2995     if( lpwhr )
2996          WININET_Release( &lpwhr->hdr );
2997
2998     TRACE("%u <--\n", res);
2999     if(res != ERROR_SUCCESS)
3000         SetLastError(res);
3001     return res == ERROR_SUCCESS;
3002 }
3003
3004 /***********************************************************************
3005  *           HttpQueryInfoA (WININET.@)
3006  *
3007  * Queries for information about an HTTP request
3008  *
3009  * RETURNS
3010  *    TRUE  on success
3011  *    FALSE on failure
3012  *
3013  */
3014 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3015         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3016 {
3017     BOOL result;
3018     DWORD len;
3019     WCHAR* bufferW;
3020
3021     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3022        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3023     {
3024         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3025                                lpdwBufferLength, lpdwIndex );
3026     }
3027
3028     if (lpBuffer)
3029     {
3030         DWORD alloclen;
3031         len = (*lpdwBufferLength)*sizeof(WCHAR);
3032         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3033         {
3034             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3035             if (alloclen < len)
3036                 alloclen = len;
3037         }
3038         else
3039             alloclen = len;
3040         bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3041         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3042         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3043             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3044     } else
3045     {
3046         bufferW = NULL;
3047         len = 0;
3048     }
3049
3050     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3051                            &len, lpdwIndex );
3052     if( result )
3053     {
3054         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3055                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3056         *lpdwBufferLength = len - 1;
3057
3058         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3059     }
3060     else
3061         /* since the strings being returned from HttpQueryInfoW should be
3062          * only ASCII characters, it is reasonable to assume that all of
3063          * the Unicode characters can be reduced to a single byte */
3064         *lpdwBufferLength = len / sizeof(WCHAR);
3065
3066     HeapFree(GetProcessHeap(), 0, bufferW );
3067
3068     return result;
3069 }
3070
3071 /***********************************************************************
3072  *           HTTP_GetRedirectURL (internal)
3073  */
3074 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3075 {
3076     static WCHAR szHttp[] = {'h','t','t','p',0};
3077     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3078     http_session_t *lpwhs = lpwhr->lpHttpSession;
3079     URL_COMPONENTSW urlComponents;
3080     DWORD url_length = 0;
3081     LPWSTR orig_url;
3082     LPWSTR combined_url;
3083
3084     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3085     urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3086     urlComponents.dwSchemeLength = 0;
3087     urlComponents.lpszHostName = lpwhs->lpszHostName;
3088     urlComponents.dwHostNameLength = 0;
3089     urlComponents.nPort = lpwhs->nHostPort;
3090     urlComponents.lpszUserName = lpwhs->lpszUserName;
3091     urlComponents.dwUserNameLength = 0;
3092     urlComponents.lpszPassword = NULL;
3093     urlComponents.dwPasswordLength = 0;
3094     urlComponents.lpszUrlPath = lpwhr->lpszPath;
3095     urlComponents.dwUrlPathLength = 0;
3096     urlComponents.lpszExtraInfo = NULL;
3097     urlComponents.dwExtraInfoLength = 0;
3098
3099     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3100         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3101         return NULL;
3102
3103     orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3104
3105     /* convert from bytes to characters */
3106     url_length = url_length / sizeof(WCHAR) - 1;
3107     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3108     {
3109         HeapFree(GetProcessHeap(), 0, orig_url);
3110         return NULL;
3111     }
3112
3113     url_length = 0;
3114     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3115         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3116     {
3117         HeapFree(GetProcessHeap(), 0, orig_url);
3118         return NULL;
3119     }
3120     combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3121
3122     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3123     {
3124         HeapFree(GetProcessHeap(), 0, orig_url);
3125         HeapFree(GetProcessHeap(), 0, combined_url);
3126         return NULL;
3127     }
3128     HeapFree(GetProcessHeap(), 0, orig_url);
3129     return combined_url;
3130 }
3131
3132
3133 /***********************************************************************
3134  *           HTTP_HandleRedirect (internal)
3135  */
3136 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3137 {
3138     http_session_t *lpwhs = lpwhr->lpHttpSession;
3139     appinfo_t *hIC = lpwhs->lpAppInfo;
3140     BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3141     WCHAR path[INTERNET_MAX_URL_LENGTH];
3142     int index;
3143
3144     if(lpszUrl[0]=='/')
3145     {
3146         /* if it's an absolute path, keep the same session info */
3147         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3148     }
3149     else
3150     {
3151         URL_COMPONENTSW urlComponents;
3152         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3153         static WCHAR szHttp[] = {'h','t','t','p',0};
3154         static WCHAR szHttps[] = {'h','t','t','p','s',0};
3155
3156         userName[0] = 0;
3157         hostName[0] = 0;
3158         protocol[0] = 0;
3159
3160         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3161         urlComponents.lpszScheme = protocol;
3162         urlComponents.dwSchemeLength = 32;
3163         urlComponents.lpszHostName = hostName;
3164         urlComponents.dwHostNameLength = MAXHOSTNAME;
3165         urlComponents.lpszUserName = userName;
3166         urlComponents.dwUserNameLength = 1024;
3167         urlComponents.lpszPassword = NULL;
3168         urlComponents.dwPasswordLength = 0;
3169         urlComponents.lpszUrlPath = path;
3170         urlComponents.dwUrlPathLength = 2048;
3171         urlComponents.lpszExtraInfo = NULL;
3172         urlComponents.dwExtraInfoLength = 0;
3173         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3174             return INTERNET_GetLastError();
3175
3176         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3177             (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3178         {
3179             TRACE("redirect from secure page to non-secure page\n");
3180             /* FIXME: warn about from secure redirect to non-secure page */
3181             lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3182         }
3183         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3184             !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3185         {
3186             TRACE("redirect from non-secure page to secure page\n");
3187             /* FIXME: notify about redirect to secure page */
3188             lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3189         }
3190
3191         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3192         {
3193             if (lstrlenW(protocol)>4) /*https*/
3194                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3195             else /*http*/
3196                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3197         }
3198
3199 #if 0
3200         /*
3201          * This upsets redirects to binary files on sourceforge.net 
3202          * and gives an html page instead of the target file
3203          * Examination of the HTTP request sent by native wininet.dll
3204          * reveals that it doesn't send a referrer in that case.
3205          * Maybe there's a flag that enables this, or maybe a referrer
3206          * shouldn't be added in case of a redirect.
3207          */
3208
3209         /* consider the current host as the referrer */
3210         if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3211             HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3212                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3213                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3214 #endif
3215         
3216         HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3217         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3218             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3219         {
3220             int len;
3221             static const WCHAR fmt[] = {'%','s',':','%','i',0};
3222             len = lstrlenW(hostName);
3223             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3224             lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3225             sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3226         }
3227         else
3228             lpwhs->lpszHostName = heap_strdupW(hostName);
3229
3230         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3231
3232         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3233         lpwhs->lpszUserName = NULL;
3234         if (userName[0])
3235             lpwhs->lpszUserName = heap_strdupW(userName);
3236
3237         if (!using_proxy)
3238         {
3239             if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3240             {
3241                 DWORD res;
3242
3243                 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3244                 lpwhs->lpszServerName = heap_strdupW(hostName);
3245                 lpwhs->nServerPort = urlComponents.nPort;
3246
3247                 NETCON_close(&lpwhr->netConnection);
3248                 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3249                     return res;
3250
3251                 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3252                 if (res != ERROR_SUCCESS)
3253                     return res;
3254
3255                 lpwhr->read_pos = lpwhr->read_size = 0;
3256                 lpwhr->read_chunked = FALSE;
3257             }
3258         }
3259         else
3260             TRACE("Redirect through proxy\n");
3261     }
3262
3263     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3264     lpwhr->lpszPath=NULL;
3265     if (*path)
3266     {
3267         DWORD needed = 0;
3268         HRESULT rc;
3269
3270         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3271         if (rc != E_POINTER)
3272             needed = strlenW(path)+1;
3273         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3274         rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3275                         URL_ESCAPE_SPACES_ONLY);
3276         if (rc != S_OK)
3277         {
3278             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3279             strcpyW(lpwhr->lpszPath,path);
3280         }
3281     }
3282
3283     /* Remove custom content-type/length headers on redirects.  */
3284     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3285     if (0 <= index)
3286         HTTP_DeleteCustomHeader(lpwhr, index);
3287     index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3288     if (0 <= index)
3289         HTTP_DeleteCustomHeader(lpwhr, index);
3290
3291     return ERROR_SUCCESS;
3292 }
3293
3294 /***********************************************************************
3295  *           HTTP_build_req (internal)
3296  *
3297  *  concatenate all the strings in the request together
3298  */
3299 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3300 {
3301     LPCWSTR *t;
3302     LPWSTR str;
3303
3304     for( t = list; *t ; t++  )
3305         len += strlenW( *t );
3306     len++;
3307
3308     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3309     *str = 0;
3310
3311     for( t = list; *t ; t++ )
3312         strcatW( str, *t );
3313
3314     return str;
3315 }
3316
3317 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3318 {
3319     LPWSTR lpszPath;
3320     LPWSTR requestString;
3321     INT len;
3322     INT cnt;
3323     INT responseLen;
3324     char *ascii_req;
3325     DWORD res;
3326     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3327     static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3328     http_session_t *lpwhs = lpwhr->lpHttpSession;
3329
3330     TRACE("\n");
3331
3332     lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3333     sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3334     requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3335     HeapFree( GetProcessHeap(), 0, lpszPath );
3336
3337     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3338                                 NULL, 0, NULL, NULL );
3339     len--; /* the nul terminator isn't needed */
3340     ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3341     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3342                             ascii_req, len, NULL, NULL );
3343     HeapFree( GetProcessHeap(), 0, requestString );
3344
3345     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3346
3347     res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3348     HeapFree( GetProcessHeap(), 0, ascii_req );
3349     if (res != ERROR_SUCCESS)
3350         return res;
3351
3352     responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3353     if (!responseLen)
3354         return ERROR_HTTP_INVALID_HEADER;
3355
3356     return ERROR_SUCCESS;
3357 }
3358
3359 static void HTTP_InsertCookies(http_request_t *lpwhr)
3360 {
3361     static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3362     LPWSTR lpszCookies, lpszUrl = NULL;
3363     DWORD nCookieSize, size;
3364     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3365
3366     size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3367     if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3368     sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3369
3370     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3371     {
3372         int cnt = 0;
3373         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3374
3375         size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3376         if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3377         {
3378             cnt += sprintfW(lpszCookies, szCookie);
3379             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3380             strcatW(lpszCookies, szCrLf);
3381
3382             HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3383             HeapFree(GetProcessHeap(), 0, lpszCookies);
3384         }
3385     }
3386     HeapFree(GetProcessHeap(), 0, lpszUrl);
3387 }
3388
3389 /***********************************************************************
3390  *           HTTP_HttpSendRequestW (internal)
3391  *
3392  * Sends the specified request to the HTTP server
3393  *
3394  * RETURNS
3395  *    TRUE  on success
3396  *    FALSE on failure
3397  *
3398  */
3399 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3400         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3401         DWORD dwContentLength, BOOL bEndRequest)
3402 {
3403     INT cnt;
3404     BOOL redirected = FALSE;
3405     LPWSTR requestString = NULL;
3406     INT responseLen;
3407     BOOL loop_next;
3408     INTERNET_ASYNC_RESULT iar;
3409     static const WCHAR szPost[] = { 'P','O','S','T',0 };
3410     static const WCHAR szContentLength[] =
3411         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3412     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3413     DWORD res;
3414
3415     TRACE("--> %p\n", lpwhr);
3416
3417     assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3418
3419     /* if the verb is NULL default to GET */
3420     if (!lpwhr->lpszVerb)
3421         lpwhr->lpszVerb = heap_strdupW(szGET);
3422
3423     if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3424     {
3425         sprintfW(contentLengthStr, szContentLength, dwContentLength);
3426         HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3427         lpwhr->dwBytesToWrite = dwContentLength;
3428     }
3429     if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3430     {
3431         WCHAR *agent_header;
3432         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3433         int len;
3434
3435         len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3436         agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3437         sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3438
3439         HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3440         HeapFree(GetProcessHeap(), 0, agent_header);
3441     }
3442     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3443     {
3444         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3445         HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3446     }
3447     if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3448     {
3449         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3450                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3451         HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3452     }
3453
3454     do
3455     {
3456         DWORD len;
3457         char *ascii_req;
3458
3459         loop_next = FALSE;
3460
3461         /* like native, just in case the caller forgot to call InternetReadFile
3462          * for all the data */
3463         HTTP_DrainContent(lpwhr);
3464         lpwhr->dwContentRead = 0;
3465         if(redirected) {
3466             lpwhr->dwContentLength = ~0u;
3467             lpwhr->dwBytesToWrite = 0;
3468         }
3469
3470         if (TRACE_ON(wininet))
3471         {
3472             LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3473             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3474         }
3475
3476         HTTP_FixURL(lpwhr);
3477         if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3478         {
3479             HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3480         }
3481         HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3482         HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3483
3484         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3485             HTTP_InsertCookies(lpwhr);
3486
3487         /* add the headers the caller supplied */
3488         if( lpszHeaders && dwHeaderLength )
3489         {
3490             HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3491                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3492         }
3493
3494         if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3495         {
3496             WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3497             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3498             HeapFree(GetProcessHeap(), 0, url);
3499         }
3500         else
3501             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3502
3503  
3504         TRACE("Request header -> %s\n", debugstr_w(requestString) );
3505
3506         /* Send the request and store the results */
3507         if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3508             goto lend;
3509
3510         /* send the request as ASCII, tack on the optional data */
3511         if (!lpOptional || redirected)
3512             dwOptionalLength = 0;
3513         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3514                                    NULL, 0, NULL, NULL );
3515         ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3516         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3517                              ascii_req, len, NULL, NULL );
3518         if( lpOptional )
3519             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3520         len = (len + dwOptionalLength - 1);
3521         ascii_req[len] = 0;
3522         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3523
3524         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3525                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3526
3527         res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3528         HeapFree( GetProcessHeap(), 0, ascii_req );
3529
3530         lpwhr->dwBytesWritten = dwOptionalLength;
3531
3532         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3533                               INTERNET_STATUS_REQUEST_SENT,
3534                               &len, sizeof(DWORD));
3535
3536         if (bEndRequest)
3537         {
3538             DWORD dwBufferSize;
3539             DWORD dwStatusCode;
3540
3541             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3542                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3543     
3544             if (res != ERROR_SUCCESS)
3545                 goto lend;
3546     
3547             responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3548     
3549             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3550                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3551                                 sizeof(DWORD));
3552
3553             HTTP_ProcessCookies(lpwhr);
3554
3555             if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3556
3557             dwBufferSize = sizeof(dwStatusCode);
3558             if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3559                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3560                 dwStatusCode = 0;
3561
3562             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3563             {
3564                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3565                 dwBufferSize=sizeof(szNewLocation);
3566                 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3567                     HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3568                 {
3569                     if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3570                     {
3571                         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3572                         lpwhr->lpszVerb = heap_strdupW(szGET);
3573                     }
3574                     HTTP_DrainContent(lpwhr);
3575                     if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3576                     {
3577                         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3578                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3579                         res = HTTP_HandleRedirect(lpwhr, new_url);
3580                         if (res == ERROR_SUCCESS)
3581                         {
3582                             HeapFree(GetProcessHeap(), 0, requestString);
3583                             loop_next = TRUE;
3584                         }
3585                         HeapFree( GetProcessHeap(), 0, new_url );
3586                     }
3587                     redirected = TRUE;
3588                 }
3589             }
3590             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3591             {
3592                 WCHAR szAuthValue[2048];
3593                 dwBufferSize=2048;
3594                 if (dwStatusCode == HTTP_STATUS_DENIED)
3595                 {
3596                     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3597                     DWORD dwIndex = 0;
3598                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3599                     {
3600                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3601                                                  &lpwhr->pAuthInfo,
3602                                                  lpwhr->lpHttpSession->lpszUserName,
3603                                                  lpwhr->lpHttpSession->lpszPassword,
3604                                                  Host->lpszValue))
3605                         {
3606                             HeapFree(GetProcessHeap(), 0, requestString);
3607                             loop_next = TRUE;
3608                             break;
3609                         }
3610                     }
3611                 }
3612                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3613                 {
3614                     DWORD dwIndex = 0;
3615                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3616                     {
3617                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3618                                                  &lpwhr->pProxyAuthInfo,
3619                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3620                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3621                                                  NULL))
3622                         {
3623                             loop_next = TRUE;
3624                             break;
3625                         }
3626                     }
3627                 }
3628             }
3629         }
3630         else
3631             res = ERROR_SUCCESS;
3632     }
3633     while (loop_next);
3634
3635     if(res == ERROR_SUCCESS) {
3636         WCHAR url[INTERNET_MAX_URL_LENGTH];
3637         WCHAR cacheFileName[MAX_PATH+1];
3638         BOOL b;
3639
3640         b = HTTP_GetRequestURL(lpwhr, url);
3641         if(!b) {
3642             WARN("Could not get URL\n");
3643             goto lend;
3644         }
3645
3646         b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3647         if(b) {
3648             HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3649             CloseHandle(lpwhr->hCacheFile);
3650
3651             lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3652             lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3653                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3654             if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3655                 WARN("Could not create file: %u\n", GetLastError());
3656                 lpwhr->hCacheFile = NULL;
3657             }
3658         }else {
3659             WARN("Could not create cache entry: %08x\n", GetLastError());
3660         }
3661     }
3662
3663 lend:
3664
3665     HeapFree(GetProcessHeap(), 0, requestString);
3666
3667     /* TODO: send notification for P3P header */
3668
3669     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3670     {
3671         if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3672             HTTP_ReceiveRequestData(lpwhr, TRUE);
3673         else
3674         {
3675             iar.dwResult = 0;
3676             iar.dwError = res;
3677
3678             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3679                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3680                                   sizeof(INTERNET_ASYNC_RESULT));
3681         }
3682     }
3683
3684     TRACE("<--\n");
3685     return res;
3686 }
3687
3688 /***********************************************************************
3689  *
3690  * Helper functions for the HttpSendRequest(Ex) functions
3691  *
3692  */
3693 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3694 {
3695     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3696     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3697
3698     TRACE("%p\n", lpwhr);
3699
3700     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3701             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3702             req->dwContentLength, req->bEndRequest);
3703
3704     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3705 }
3706
3707
3708 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3709 {
3710     INT responseLen;
3711     DWORD dwBufferSize;
3712     INTERNET_ASYNC_RESULT iar;
3713     DWORD res = ERROR_SUCCESS;
3714
3715     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3716                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3717
3718     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3719     if (!responseLen)
3720         res = ERROR_HTTP_HEADER_NOT_FOUND;
3721
3722     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3723                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3724
3725     /* process cookies here. Is this right? */
3726     HTTP_ProcessCookies(lpwhr);
3727
3728     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3729
3730     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3731     {
3732         DWORD dwCode,dwCodeLength = sizeof(DWORD);
3733         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3734             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3735         {
3736             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3737             dwBufferSize=sizeof(szNewLocation);
3738             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3739             {
3740                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3741                 {
3742                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3743                     lpwhr->lpszVerb = heap_strdupW(szGET);
3744                 }
3745                 HTTP_DrainContent(lpwhr);
3746                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3747                 {
3748                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3749                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3750                     res = HTTP_HandleRedirect(lpwhr, new_url);
3751                     if (res == ERROR_SUCCESS)
3752                         res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3753                     HeapFree( GetProcessHeap(), 0, new_url );
3754                 }
3755             }
3756         }
3757     }
3758
3759     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3760     iar.dwError = res;
3761
3762     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3763                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3764                           sizeof(INTERNET_ASYNC_RESULT));
3765     return res;
3766 }
3767
3768 /***********************************************************************
3769  *           HttpEndRequestA (WININET.@)
3770  *
3771  * Ends an HTTP request that was started by HttpSendRequestEx
3772  *
3773  * RETURNS
3774  *    TRUE      if successful
3775  *    FALSE     on failure
3776  *
3777  */
3778 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3779         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3780 {
3781     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3782
3783     if (lpBuffersOut)
3784     {
3785         SetLastError(ERROR_INVALID_PARAMETER);
3786         return FALSE;
3787     }
3788
3789     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3790 }
3791
3792 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3793 {
3794     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3795     http_request_t *lpwhr = (http_request_t*)work->hdr;
3796
3797     TRACE("%p\n", lpwhr);
3798
3799     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3800 }
3801
3802 /***********************************************************************
3803  *           HttpEndRequestW (WININET.@)
3804  *
3805  * Ends an HTTP request that was started by HttpSendRequestEx
3806  *
3807  * RETURNS
3808  *    TRUE      if successful
3809  *    FALSE     on failure
3810  *
3811  */
3812 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3813         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3814 {
3815     http_request_t *lpwhr;
3816     DWORD res;
3817
3818     TRACE("-->\n");
3819
3820     if (lpBuffersOut)
3821     {
3822         SetLastError(ERROR_INVALID_PARAMETER);
3823         return FALSE;
3824     }
3825
3826     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3827
3828     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3829     {
3830         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3831         if (lpwhr)
3832             WININET_Release( &lpwhr->hdr );
3833         return FALSE;
3834     }
3835     lpwhr->hdr.dwFlags |= dwFlags;
3836
3837     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3838     {
3839         WORKREQUEST work;
3840         struct WORKREQ_HTTPENDREQUESTW *request;
3841
3842         work.asyncproc = AsyncHttpEndRequestProc;
3843         work.hdr = WININET_AddRef( &lpwhr->hdr );
3844
3845         request = &work.u.HttpEndRequestW;
3846         request->dwFlags = dwFlags;
3847         request->dwContext = dwContext;
3848
3849         INTERNET_AsyncCall(&work);
3850         res = ERROR_IO_PENDING;
3851     }
3852     else
3853         res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3854
3855     WININET_Release( &lpwhr->hdr );
3856     TRACE("%u <--\n", res);
3857     if(res != ERROR_SUCCESS)
3858         SetLastError(res);
3859     return res == ERROR_SUCCESS;
3860 }
3861
3862 /***********************************************************************
3863  *           HttpSendRequestExA (WININET.@)
3864  *
3865  * Sends the specified request to the HTTP server and allows chunked
3866  * transfers.
3867  *
3868  * RETURNS
3869  *  Success: TRUE
3870  *  Failure: FALSE, call GetLastError() for more information.
3871  */
3872 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3873                                LPINTERNET_BUFFERSA lpBuffersIn,
3874                                LPINTERNET_BUFFERSA lpBuffersOut,
3875                                DWORD dwFlags, DWORD_PTR dwContext)
3876 {
3877     INTERNET_BUFFERSW BuffersInW;
3878     BOOL rc = FALSE;
3879     DWORD headerlen;
3880     LPWSTR header = NULL;
3881
3882     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3883             lpBuffersOut, dwFlags, dwContext);
3884
3885     if (lpBuffersIn)
3886     {
3887         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3888         if (lpBuffersIn->lpcszHeader)
3889         {
3890             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3891                     lpBuffersIn->dwHeadersLength,0,0);
3892             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3893             if (!(BuffersInW.lpcszHeader = header))
3894             {
3895                 SetLastError(ERROR_OUTOFMEMORY);
3896                 return FALSE;
3897             }
3898             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3899                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3900                     header, headerlen);
3901         }
3902         else
3903             BuffersInW.lpcszHeader = NULL;
3904         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3905         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3906         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3907         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3908         BuffersInW.Next = NULL;
3909     }
3910
3911     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3912
3913     HeapFree(GetProcessHeap(),0,header);
3914
3915     return rc;
3916 }
3917
3918 /***********************************************************************
3919  *           HttpSendRequestExW (WININET.@)
3920  *
3921  * Sends the specified request to the HTTP server and allows chunked
3922  * transfers
3923  *
3924  * RETURNS
3925  *  Success: TRUE
3926  *  Failure: FALSE, call GetLastError() for more information.
3927  */
3928 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3929                    LPINTERNET_BUFFERSW lpBuffersIn,
3930                    LPINTERNET_BUFFERSW lpBuffersOut,
3931                    DWORD dwFlags, DWORD_PTR dwContext)
3932 {
3933     http_request_t *lpwhr;
3934     http_session_t *lpwhs;
3935     appinfo_t *hIC;
3936     DWORD res;
3937
3938     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3939             lpBuffersOut, dwFlags, dwContext);
3940
3941     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3942
3943     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3944     {
3945         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3946         goto lend;
3947     }
3948
3949     lpwhs = lpwhr->lpHttpSession;
3950     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3951     hIC = lpwhs->lpAppInfo;
3952     assert(hIC->hdr.htype == WH_HINIT);
3953
3954     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3955     {
3956         WORKREQUEST workRequest;
3957         struct WORKREQ_HTTPSENDREQUESTW *req;
3958
3959         workRequest.asyncproc = AsyncHttpSendRequestProc;
3960         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3961         req = &workRequest.u.HttpSendRequestW;
3962         if (lpBuffersIn)
3963         {
3964             DWORD size = 0;
3965
3966             if (lpBuffersIn->lpcszHeader)
3967             {
3968                 if (lpBuffersIn->dwHeadersLength == ~0u)
3969                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3970                 else
3971                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3972
3973                 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3974                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3975             }
3976             else req->lpszHeader = NULL;
3977
3978             req->dwHeaderLength = size / sizeof(WCHAR);
3979             req->lpOptional = lpBuffersIn->lpvBuffer;
3980             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3981             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3982         }
3983         else
3984         {
3985             req->lpszHeader = NULL;
3986             req->dwHeaderLength = 0;
3987             req->lpOptional = NULL;
3988             req->dwOptionalLength = 0;
3989             req->dwContentLength = 0;
3990         }
3991
3992         req->bEndRequest = FALSE;
3993
3994         INTERNET_AsyncCall(&workRequest);
3995         /*
3996          * This is from windows.
3997          */
3998         res = ERROR_IO_PENDING;
3999     }
4000     else
4001     {
4002         if (lpBuffersIn)
4003             res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4004                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4005                                         lpBuffersIn->dwBufferTotal, FALSE);
4006         else
4007             res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4008     }
4009
4010 lend:
4011     if ( lpwhr )
4012         WININET_Release( &lpwhr->hdr );
4013
4014     TRACE("<---\n");
4015     SetLastError(res);
4016     return res == ERROR_SUCCESS;
4017 }
4018
4019 /***********************************************************************
4020  *           HttpSendRequestW (WININET.@)
4021  *
4022  * Sends the specified request to the HTTP server
4023  *
4024  * RETURNS
4025  *    TRUE  on success
4026  *    FALSE on failure
4027  *
4028  */
4029 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4030         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4031 {
4032     http_request_t *lpwhr;
4033     http_session_t *lpwhs = NULL;
4034     appinfo_t *hIC = NULL;
4035     DWORD res = ERROR_SUCCESS;
4036
4037     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4038             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4039
4040     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4041     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4042     {
4043         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4044         goto lend;
4045     }
4046
4047     lpwhs = lpwhr->lpHttpSession;
4048     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
4049     {
4050         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4051         goto lend;
4052     }
4053
4054     hIC = lpwhs->lpAppInfo;
4055     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
4056     {
4057         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4058         goto lend;
4059     }
4060
4061     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4062     {
4063         WORKREQUEST workRequest;
4064         struct WORKREQ_HTTPSENDREQUESTW *req;
4065
4066         workRequest.asyncproc = AsyncHttpSendRequestProc;
4067         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4068         req = &workRequest.u.HttpSendRequestW;
4069         if (lpszHeaders)
4070         {
4071             DWORD size;
4072
4073             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4074             else size = dwHeaderLength * sizeof(WCHAR);
4075
4076             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4077             memcpy(req->lpszHeader, lpszHeaders, size);
4078         }
4079         else
4080             req->lpszHeader = 0;
4081         req->dwHeaderLength = dwHeaderLength;
4082         req->lpOptional = lpOptional;
4083         req->dwOptionalLength = dwOptionalLength;
4084         req->dwContentLength = dwOptionalLength;
4085         req->bEndRequest = TRUE;
4086
4087         INTERNET_AsyncCall(&workRequest);
4088         /*
4089          * This is from windows.
4090          */
4091         res = ERROR_IO_PENDING;
4092     }
4093     else
4094     {
4095         res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4096                 dwHeaderLength, lpOptional, dwOptionalLength,
4097                 dwOptionalLength, TRUE);
4098     }
4099 lend:
4100     if( lpwhr )
4101         WININET_Release( &lpwhr->hdr );
4102
4103     SetLastError(res);
4104     return res == ERROR_SUCCESS;
4105 }
4106
4107 /***********************************************************************
4108  *           HttpSendRequestA (WININET.@)
4109  *
4110  * Sends the specified request to the HTTP server
4111  *
4112  * RETURNS
4113  *    TRUE  on success
4114  *    FALSE on failure
4115  *
4116  */
4117 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4118         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4119 {
4120     BOOL result;
4121     LPWSTR szHeaders=NULL;
4122     DWORD nLen=dwHeaderLength;
4123     if(lpszHeaders!=NULL)
4124     {
4125         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4126         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4127         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4128     }
4129     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4130     HeapFree(GetProcessHeap(),0,szHeaders);
4131     return result;
4132 }
4133
4134 /***********************************************************************
4135  *           HTTPSESSION_Destroy (internal)
4136  *
4137  * Deallocate session handle
4138  *
4139  */
4140 static void HTTPSESSION_Destroy(object_header_t *hdr)
4141 {
4142     http_session_t *lpwhs = (http_session_t*) hdr;
4143
4144     TRACE("%p\n", lpwhs);
4145
4146     WININET_Release(&lpwhs->lpAppInfo->hdr);
4147
4148     HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4149     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4150     HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4151     HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4152     HeapFree(GetProcessHeap(), 0, lpwhs);
4153 }
4154
4155 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4156 {
4157     switch(option) {
4158     case INTERNET_OPTION_HANDLE_TYPE:
4159         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4160
4161         if (*size < sizeof(ULONG))
4162             return ERROR_INSUFFICIENT_BUFFER;
4163
4164         *size = sizeof(DWORD);
4165         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4166         return ERROR_SUCCESS;
4167     }
4168
4169     return INET_QueryOption(hdr, option, buffer, size, unicode);
4170 }
4171
4172 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4173 {
4174     http_session_t *ses = (http_session_t*)hdr;
4175
4176     switch(option) {
4177     case INTERNET_OPTION_USERNAME:
4178     {
4179         HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4180         if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4181         return ERROR_SUCCESS;
4182     }
4183     case INTERNET_OPTION_PASSWORD:
4184     {
4185         HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4186         if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4187         return ERROR_SUCCESS;
4188     }
4189     default: break;
4190     }
4191
4192     return ERROR_INTERNET_INVALID_OPTION;
4193 }
4194
4195 static const object_vtbl_t HTTPSESSIONVtbl = {
4196     HTTPSESSION_Destroy,
4197     NULL,
4198     HTTPSESSION_QueryOption,
4199     HTTPSESSION_SetOption,
4200     NULL,
4201     NULL,
4202     NULL,
4203     NULL,
4204     NULL
4205 };
4206
4207
4208 /***********************************************************************
4209  *           HTTP_Connect  (internal)
4210  *
4211  * Create http session handle
4212  *
4213  * RETURNS
4214  *   HINTERNET a session handle on success
4215  *   NULL on failure
4216  *
4217  */
4218 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4219         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4220         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4221         DWORD dwInternalFlags, HINTERNET *ret)
4222 {
4223     http_session_t *lpwhs = NULL;
4224     HINTERNET handle = NULL;
4225     DWORD res = ERROR_SUCCESS;
4226
4227     TRACE("-->\n");
4228
4229     if (!lpszServerName || !lpszServerName[0])
4230         return ERROR_INVALID_PARAMETER;
4231
4232     assert( hIC->hdr.htype == WH_HINIT );
4233
4234     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4235     if (!lpwhs)
4236         return ERROR_OUTOFMEMORY;
4237
4238    /*
4239     * According to my tests. The name is not resolved until a request is sent
4240     */
4241
4242     lpwhs->hdr.htype = WH_HHTTPSESSION;
4243     lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4244     lpwhs->hdr.dwFlags = dwFlags;
4245     lpwhs->hdr.dwContext = dwContext;
4246     lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4247     lpwhs->hdr.refs = 1;
4248     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4249
4250     WININET_AddRef( &hIC->hdr );
4251     lpwhs->lpAppInfo = hIC;
4252     list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4253
4254     handle = WININET_AllocHandle( &lpwhs->hdr );
4255     if (NULL == handle)
4256     {
4257         ERR("Failed to alloc handle\n");
4258         res = ERROR_OUTOFMEMORY;
4259         goto lerror;
4260     }
4261
4262     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4263         if(hIC->lpszProxyBypass)
4264             FIXME("Proxy bypass is ignored.\n");
4265     }
4266     lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4267     lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4268     if (lpszUserName && lpszUserName[0])
4269         lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4270     if (lpszPassword && lpszPassword[0])
4271         lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4272     lpwhs->nServerPort = nServerPort;
4273     lpwhs->nHostPort = nServerPort;
4274
4275     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4276     if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4277     {
4278         INTERNET_SendCallback(&hIC->hdr, dwContext,
4279                               INTERNET_STATUS_HANDLE_CREATED, &handle,
4280                               sizeof(handle));
4281     }
4282
4283 lerror:
4284     if( lpwhs )
4285         WININET_Release( &lpwhs->hdr );
4286
4287 /*
4288  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4289  * windows
4290  */
4291
4292     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4293
4294     if(res == ERROR_SUCCESS)
4295         *ret = handle;
4296     return res;
4297 }
4298
4299
4300 /***********************************************************************
4301  *           HTTP_OpenConnection (internal)
4302  *
4303  * Connect to a web server
4304  *
4305  * RETURNS
4306  *
4307  *   TRUE  on success
4308  *   FALSE on failure
4309  */
4310 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4311 {
4312     http_session_t *lpwhs;
4313     appinfo_t *hIC = NULL;
4314     char szaddr[INET6_ADDRSTRLEN];
4315     const void *addr;
4316     DWORD res = ERROR_SUCCESS;
4317
4318     TRACE("-->\n");
4319
4320
4321     if (lpwhr->hdr.htype != WH_HHTTPREQ)
4322     {
4323         res = ERROR_INVALID_PARAMETER;
4324         goto lend;
4325     }
4326
4327     if (NETCON_connected(&lpwhr->netConnection))
4328         goto lend;
4329     if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4330
4331     lpwhs = lpwhr->lpHttpSession;
4332
4333     hIC = lpwhs->lpAppInfo;
4334     switch (lpwhs->socketAddress.ss_family)
4335     {
4336     case AF_INET:
4337         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4338         break;
4339     case AF_INET6:
4340         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4341         break;
4342     default:
4343         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4344         return ERROR_INTERNET_NAME_NOT_RESOLVED;
4345     }
4346     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4347     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4348                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4349                           szaddr,
4350                           strlen(szaddr)+1);
4351
4352     res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4353     if (res != ERROR_SUCCESS)
4354     {
4355         WARN("Socket creation failed: %u\n", res);
4356         goto lend;
4357     }
4358
4359     res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4360                          lpwhs->sa_len);
4361     if(res != ERROR_SUCCESS)
4362        goto lend;
4363
4364     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4365             INTERNET_STATUS_CONNECTED_TO_SERVER,
4366             szaddr, strlen(szaddr)+1);
4367
4368     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4369     {
4370         /* Note: we differ from Microsoft's WinINet here. they seem to have
4371          * a bug that causes no status callbacks to be sent when starting
4372          * a tunnel to a proxy server using the CONNECT verb. i believe our
4373          * behaviour to be more correct and to not cause any incompatibilities
4374          * because using a secure connection through a proxy server is a rare
4375          * case that would be hard for anyone to depend on */
4376         if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4377             HTTPREQ_CloseConnection(&lpwhr->hdr);
4378             goto lend;
4379         }
4380
4381         res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4382         if(res != ERROR_SUCCESS)
4383         {
4384             WARN("Couldn't connect securely to host\n");
4385
4386             if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4387                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4388                     || res == ERROR_INTERNET_INVALID_CA
4389                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
4390                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4391                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
4392                     || res == ERROR_INTERNET_SEC_INVALID_CERT
4393                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4394                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4395
4396             HTTPREQ_CloseConnection(&lpwhr->hdr);
4397             goto lend;
4398         }
4399     }
4400
4401
4402 lend:
4403     lpwhr->read_pos = lpwhr->read_size = 0;
4404     lpwhr->read_chunked = FALSE;
4405
4406     TRACE("%d <--\n", res);
4407     return res;
4408 }
4409
4410
4411 /***********************************************************************
4412  *           HTTP_clear_response_headers (internal)
4413  *
4414  * clear out any old response headers
4415  */
4416 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4417 {
4418     DWORD i;
4419
4420     for( i=0; i<lpwhr->nCustHeaders; i++)
4421     {
4422         if( !lpwhr->pCustHeaders[i].lpszField )
4423             continue;
4424         if( !lpwhr->pCustHeaders[i].lpszValue )
4425             continue;
4426         if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4427             continue;
4428         HTTP_DeleteCustomHeader( lpwhr, i );
4429         i--;
4430     }
4431 }
4432
4433 /***********************************************************************
4434  *           HTTP_GetResponseHeaders (internal)
4435  *
4436  * Read server response
4437  *
4438  * RETURNS
4439  *
4440  *   TRUE  on success
4441  *   FALSE on error
4442  */
4443 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4444 {
4445     INT cbreaks = 0;
4446     WCHAR buffer[MAX_REPLY_LEN];
4447     DWORD buflen = MAX_REPLY_LEN;
4448     BOOL bSuccess = FALSE;
4449     INT  rc = 0;
4450     char bufferA[MAX_REPLY_LEN];
4451     LPWSTR status_code = NULL, status_text = NULL;
4452     DWORD cchMaxRawHeaders = 1024;
4453     LPWSTR lpszRawHeaders = NULL;
4454     LPWSTR temp;
4455     DWORD cchRawHeaders = 0;
4456     BOOL codeHundred = FALSE;
4457
4458     TRACE("-->\n");
4459
4460     /* clear old response headers (eg. from a redirect response) */
4461     if (clear) HTTP_clear_response_headers( lpwhr );
4462
4463     if (!NETCON_connected(&lpwhr->netConnection))
4464         goto lend;
4465
4466     do {
4467         static const WCHAR szHundred[] = {'1','0','0',0};
4468         /*
4469          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4470          */
4471         buflen = MAX_REPLY_LEN;
4472         if (!read_line(lpwhr, bufferA, &buflen))
4473             goto lend;
4474         rc += buflen;
4475         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4476         /* check is this a status code line? */
4477         if (!strncmpW(buffer, g_szHttp1_0, 4))
4478         {
4479             /* split the version from the status code */
4480             status_code = strchrW( buffer, ' ' );
4481             if( !status_code )
4482                 goto lend;
4483             *status_code++=0;
4484
4485             /* split the status code from the status text */
4486             status_text = strchrW( status_code, ' ' );
4487             if( !status_text )
4488                 goto lend;
4489             *status_text++=0;
4490
4491             TRACE("version [%s] status code [%s] status text [%s]\n",
4492                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4493
4494             codeHundred = (!strcmpW(status_code, szHundred));
4495         }
4496         else if (!codeHundred)
4497         {
4498             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4499
4500             HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4501             HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4502
4503             lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4504             lpwhr->lpszStatusText = heap_strdupW(szOK);
4505
4506             HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4507             lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4508
4509             bSuccess = TRUE;
4510             goto lend;
4511         }
4512     } while (codeHundred);
4513
4514     /* Add status code */
4515     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4516             HTTP_ADDHDR_FLAG_REPLACE);
4517
4518     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4519     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4520
4521     lpwhr->lpszVersion = heap_strdupW(buffer);
4522     lpwhr->lpszStatusText = heap_strdupW(status_text);
4523
4524     /* Restore the spaces */
4525     *(status_code-1) = ' ';
4526     *(status_text-1) = ' ';
4527
4528     /* regenerate raw headers */
4529     lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4530     if (!lpszRawHeaders) goto lend;
4531
4532     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4533         cchMaxRawHeaders *= 2;
4534     temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4535     if (temp == NULL) goto lend;
4536     lpszRawHeaders = temp;
4537     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4538     cchRawHeaders += (buflen-1);
4539     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4540     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4541     lpszRawHeaders[cchRawHeaders] = '\0';
4542
4543     /* Parse each response line */
4544     do
4545     {
4546         buflen = MAX_REPLY_LEN;
4547         if (read_line(lpwhr, bufferA, &buflen))
4548         {
4549             LPWSTR * pFieldAndValue;
4550
4551             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4552
4553             if (!bufferA[0]) break;
4554             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4555
4556             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4557             if (pFieldAndValue)
4558             {
4559                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4560                     cchMaxRawHeaders *= 2;
4561                 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4562                 if (temp == NULL) goto lend;
4563                 lpszRawHeaders = temp;
4564                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4565                 cchRawHeaders += (buflen-1);
4566                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4567                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4568                 lpszRawHeaders[cchRawHeaders] = '\0';
4569
4570                 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4571                                    HTTP_ADDREQ_FLAG_ADD );
4572
4573                 HTTP_FreeTokens(pFieldAndValue);
4574             }
4575         }
4576         else
4577         {
4578             cbreaks++;
4579             if (cbreaks >= 2)
4580                break;
4581         }
4582     }while(1);
4583
4584     /* make sure the response header is terminated with an empty line.  Some apps really
4585        truly care about that empty line being there for some reason.  Just add it to the
4586        header. */
4587     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4588     {
4589         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4590         temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4591         if (temp == NULL) goto lend;
4592         lpszRawHeaders = temp;
4593     }
4594
4595     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4596
4597     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4598     lpwhr->lpszRawHeaders = lpszRawHeaders;
4599     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4600     bSuccess = TRUE;
4601
4602 lend:
4603
4604     TRACE("<--\n");
4605     if (bSuccess)
4606         return rc;
4607     else
4608     {
4609         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4610         return 0;
4611     }
4612 }
4613
4614 /***********************************************************************
4615  *           HTTP_InterpretHttpHeader (internal)
4616  *
4617  * Parse server response
4618  *
4619  * RETURNS
4620  *
4621  *   Pointer to array of field, value, NULL on success.
4622  *   NULL on error.
4623  */
4624 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4625 {
4626     LPWSTR * pTokenPair;
4627     LPWSTR pszColon;
4628     INT len;
4629
4630     pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4631
4632     pszColon = strchrW(buffer, ':');
4633     /* must have two tokens */
4634     if (!pszColon)
4635     {
4636         HTTP_FreeTokens(pTokenPair);
4637         if (buffer[0])
4638             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4639         return NULL;
4640     }
4641
4642     pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4643     if (!pTokenPair[0])
4644     {
4645         HTTP_FreeTokens(pTokenPair);
4646         return NULL;
4647     }
4648     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4649     pTokenPair[0][pszColon - buffer] = '\0';
4650
4651     /* skip colon */
4652     pszColon++;
4653     len = strlenW(pszColon);
4654     pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4655     if (!pTokenPair[1])
4656     {
4657         HTTP_FreeTokens(pTokenPair);
4658         return NULL;
4659     }
4660     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4661
4662     strip_spaces(pTokenPair[0]);
4663     strip_spaces(pTokenPair[1]);
4664
4665     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4666     return pTokenPair;
4667 }
4668
4669 /***********************************************************************
4670  *           HTTP_ProcessHeader (internal)
4671  *
4672  * Stuff header into header tables according to <dwModifier>
4673  *
4674  */
4675
4676 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4677
4678 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4679 {
4680     LPHTTPHEADERW lphttpHdr = NULL;
4681     INT index = -1;
4682     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4683     DWORD res = ERROR_HTTP_INVALID_HEADER;
4684
4685     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4686
4687     /* REPLACE wins out over ADD */
4688     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4689         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4690     
4691     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4692         index = -1;
4693     else
4694         index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4695
4696     if (index >= 0)
4697     {
4698         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4699             return ERROR_HTTP_INVALID_HEADER;
4700         lphttpHdr = &lpwhr->pCustHeaders[index];
4701     }
4702     else if (value)
4703     {
4704         HTTPHEADERW hdr;
4705
4706         hdr.lpszField = (LPWSTR)field;
4707         hdr.lpszValue = (LPWSTR)value;
4708         hdr.wFlags = hdr.wCount = 0;
4709
4710         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4711             hdr.wFlags |= HDR_ISREQUEST;
4712
4713         return HTTP_InsertCustomHeader(lpwhr, &hdr);
4714     }
4715     /* no value to delete */
4716     else return ERROR_SUCCESS;
4717
4718     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4719             lphttpHdr->wFlags |= HDR_ISREQUEST;
4720     else
4721         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4722
4723     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4724     {
4725         HTTP_DeleteCustomHeader( lpwhr, index );
4726
4727         if (value)
4728         {
4729             HTTPHEADERW hdr;
4730
4731             hdr.lpszField = (LPWSTR)field;
4732             hdr.lpszValue = (LPWSTR)value;
4733             hdr.wFlags = hdr.wCount = 0;
4734
4735             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4736                 hdr.wFlags |= HDR_ISREQUEST;
4737
4738             return HTTP_InsertCustomHeader(lpwhr, &hdr);
4739         }
4740
4741         return ERROR_SUCCESS;
4742     }
4743     else if (dwModifier & COALESCEFLAGS)
4744     {
4745         LPWSTR lpsztmp;
4746         WCHAR ch = 0;
4747         INT len = 0;
4748         INT origlen = strlenW(lphttpHdr->lpszValue);
4749         INT valuelen = strlenW(value);
4750
4751         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4752         {
4753             ch = ',';
4754             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4755         }
4756         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4757         {
4758             ch = ';';
4759             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4760         }
4761
4762         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4763
4764         lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4765         if (lpsztmp)
4766         {
4767             lphttpHdr->lpszValue = lpsztmp;
4768     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4769             if (ch > 0)
4770             {
4771                 lphttpHdr->lpszValue[origlen] = ch;
4772                 origlen++;
4773                 lphttpHdr->lpszValue[origlen] = ' ';
4774                 origlen++;
4775             }
4776
4777             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4778             lphttpHdr->lpszValue[len] = '\0';
4779             res = ERROR_SUCCESS;
4780         }
4781         else
4782         {
4783             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4784             res = ERROR_OUTOFMEMORY;
4785         }
4786     }
4787     TRACE("<-- %d\n", res);
4788     return res;
4789 }
4790
4791
4792 /***********************************************************************
4793  *           HTTP_FinishedReading (internal)
4794  *
4795  * Called when all content from server has been read by client.
4796  *
4797  */
4798 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4799 {
4800     BOOL keepalive = HTTP_KeepAlive(lpwhr);
4801
4802     TRACE("\n");
4803
4804
4805     if (!keepalive)
4806     {
4807         HTTPREQ_CloseConnection(&lpwhr->hdr);
4808     }
4809
4810     /* FIXME: store data in the URL cache here */
4811
4812     return TRUE;
4813 }
4814
4815
4816 /***********************************************************************
4817  *           HTTP_GetCustomHeaderIndex (internal)
4818  *
4819  * Return index of custom header from header array
4820  *
4821  */
4822 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4823                                      int requested_index, BOOL request_only)
4824 {
4825     DWORD index;
4826
4827     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4828
4829     for (index = 0; index < lpwhr->nCustHeaders; index++)
4830     {
4831         if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4832             continue;
4833
4834         if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4835             continue;
4836
4837         if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4838             continue;
4839
4840         if (requested_index == 0)
4841             break;
4842         requested_index --;
4843     }
4844
4845     if (index >= lpwhr->nCustHeaders)
4846         index = -1;
4847
4848     TRACE("Return: %d\n", index);
4849     return index;
4850 }
4851
4852
4853 /***********************************************************************
4854  *           HTTP_InsertCustomHeader (internal)
4855  *
4856  * Insert header into array
4857  *
4858  */
4859 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4860 {
4861     INT count;
4862     LPHTTPHEADERW lph = NULL;
4863
4864     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4865     count = lpwhr->nCustHeaders + 1;
4866     if (count > 1)
4867         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4868     else
4869         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4870
4871     if (!lph)
4872         return ERROR_OUTOFMEMORY;
4873
4874     lpwhr->pCustHeaders = lph;
4875     lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4876     lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4877     lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4878     lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4879     lpwhr->nCustHeaders++;
4880
4881     return ERROR_SUCCESS;
4882 }
4883
4884
4885 /***********************************************************************
4886  *           HTTP_DeleteCustomHeader (internal)
4887  *
4888  * Delete header from array
4889  *  If this function is called, the indexs may change.
4890  */
4891 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4892 {
4893     if( lpwhr->nCustHeaders <= 0 )
4894         return FALSE;
4895     if( index >= lpwhr->nCustHeaders )
4896         return FALSE;
4897     lpwhr->nCustHeaders--;
4898
4899     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4900     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4901
4902     memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4903              (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4904     memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4905
4906     return TRUE;
4907 }
4908
4909
4910 /***********************************************************************
4911  *           HTTP_VerifyValidHeader (internal)
4912  *
4913  * Verify the given header is not invalid for the given http request
4914  *
4915  */
4916 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4917 {
4918     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4919     if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4920         return ERROR_HTTP_INVALID_HEADER;
4921
4922     return ERROR_SUCCESS;
4923 }
4924
4925 /***********************************************************************
4926  *          IsHostInProxyBypassList (@)
4927  *
4928  * Undocumented
4929  *
4930  */
4931 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4932 {
4933    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4934    return FALSE;
4935 }