msi/tests: Skip some source tests if a required product key cannot be created.
[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)
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         BOOL reusing_connection;
3458         char *ascii_req;
3459
3460         loop_next = FALSE;
3461
3462         /* like native, just in case the caller forgot to call InternetReadFile
3463          * for all the data */
3464         HTTP_DrainContent(lpwhr);
3465         lpwhr->dwContentRead = 0;
3466         if(redirected) {
3467             lpwhr->dwContentLength = ~0u;
3468             lpwhr->dwBytesToWrite = 0;
3469         }
3470
3471         if (TRACE_ON(wininet))
3472         {
3473             LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3474             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3475         }
3476
3477         HTTP_FixURL(lpwhr);
3478         if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3479         {
3480             HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3481         }
3482         HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3483         HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3484
3485         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3486             HTTP_InsertCookies(lpwhr);
3487
3488         /* add the headers the caller supplied */
3489         if( lpszHeaders && dwHeaderLength )
3490         {
3491             HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3492                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3493         }
3494
3495         if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3496         {
3497             WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3498             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3499             HeapFree(GetProcessHeap(), 0, url);
3500         }
3501         else
3502             requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3503
3504  
3505         TRACE("Request header -> %s\n", debugstr_w(requestString) );
3506
3507         /* Send the request and store the results */
3508         if(NETCON_connected(&lpwhr->netConnection))
3509             reusing_connection = TRUE;
3510         else
3511             reusing_connection = FALSE;
3512
3513         if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3514             goto lend;
3515
3516         /* send the request as ASCII, tack on the optional data */
3517         if (!lpOptional || redirected)
3518             dwOptionalLength = 0;
3519         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3520                                    NULL, 0, NULL, NULL );
3521         ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3522         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3523                              ascii_req, len, NULL, NULL );
3524         if( lpOptional )
3525             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3526         len = (len + dwOptionalLength - 1);
3527         ascii_req[len] = 0;
3528         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3529
3530         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3531                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3532
3533         res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3534         HeapFree( GetProcessHeap(), 0, ascii_req );
3535
3536         lpwhr->dwBytesWritten = dwOptionalLength;
3537
3538         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3539                               INTERNET_STATUS_REQUEST_SENT,
3540                               &len, sizeof(DWORD));
3541
3542         if (bEndRequest)
3543         {
3544             DWORD dwBufferSize;
3545             DWORD dwStatusCode;
3546
3547             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3548                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3549     
3550             if (res != ERROR_SUCCESS)
3551                 goto lend;
3552     
3553             responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3554             /* FIXME: We should know that connection is closed before sending
3555              * headers. Otherwise wrong callbacks are executed */
3556             if(!responseLen && reusing_connection) {
3557                 TRACE("Connection closed by server, reconnecting\n");
3558                 loop_next = TRUE;
3559                 continue;
3560             }
3561     
3562             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3563                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3564                                 sizeof(DWORD));
3565
3566             HTTP_ProcessCookies(lpwhr);
3567
3568             if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3569
3570             dwBufferSize = sizeof(dwStatusCode);
3571             if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3572                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3573                 dwStatusCode = 0;
3574
3575             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3576             {
3577                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3578                 dwBufferSize=sizeof(szNewLocation);
3579                 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3580                     HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3581                 {
3582                     if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3583                     {
3584                         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3585                         lpwhr->lpszVerb = heap_strdupW(szGET);
3586                     }
3587                     HTTP_DrainContent(lpwhr);
3588                     if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3589                     {
3590                         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3591                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3592                         res = HTTP_HandleRedirect(lpwhr, new_url);
3593                         if (res == ERROR_SUCCESS)
3594                         {
3595                             HeapFree(GetProcessHeap(), 0, requestString);
3596                             loop_next = TRUE;
3597                         }
3598                         HeapFree( GetProcessHeap(), 0, new_url );
3599                     }
3600                     redirected = TRUE;
3601                 }
3602             }
3603             if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3604             {
3605                 WCHAR szAuthValue[2048];
3606                 dwBufferSize=2048;
3607                 if (dwStatusCode == HTTP_STATUS_DENIED)
3608                 {
3609                     LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3610                     DWORD dwIndex = 0;
3611                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3612                     {
3613                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3614                                                  &lpwhr->pAuthInfo,
3615                                                  lpwhr->lpHttpSession->lpszUserName,
3616                                                  lpwhr->lpHttpSession->lpszPassword,
3617                                                  Host->lpszValue))
3618                         {
3619                             HeapFree(GetProcessHeap(), 0, requestString);
3620                             loop_next = TRUE;
3621                             break;
3622                         }
3623                     }
3624                 }
3625                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3626                 {
3627                     DWORD dwIndex = 0;
3628                     while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3629                     {
3630                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3631                                                  &lpwhr->pProxyAuthInfo,
3632                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3633                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3634                                                  NULL))
3635                         {
3636                             loop_next = TRUE;
3637                             break;
3638                         }
3639                     }
3640                 }
3641             }
3642         }
3643         else
3644             res = ERROR_SUCCESS;
3645     }
3646     while (loop_next);
3647
3648     if(res == ERROR_SUCCESS) {
3649         WCHAR url[INTERNET_MAX_URL_LENGTH];
3650         WCHAR cacheFileName[MAX_PATH+1];
3651         BOOL b;
3652
3653         b = HTTP_GetRequestURL(lpwhr, url);
3654         if(!b) {
3655             WARN("Could not get URL\n");
3656             goto lend;
3657         }
3658
3659         b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3660         if(b) {
3661             HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3662             CloseHandle(lpwhr->hCacheFile);
3663
3664             lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3665             lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3666                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3667             if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3668                 WARN("Could not create file: %u\n", GetLastError());
3669                 lpwhr->hCacheFile = NULL;
3670             }
3671         }else {
3672             WARN("Could not create cache entry: %08x\n", GetLastError());
3673         }
3674     }
3675
3676 lend:
3677
3678     HeapFree(GetProcessHeap(), 0, requestString);
3679
3680     /* TODO: send notification for P3P header */
3681
3682     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3683     {
3684         if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3685             HTTP_ReceiveRequestData(lpwhr, TRUE);
3686         else
3687         {
3688             iar.dwResult = 0;
3689             iar.dwError = res;
3690
3691             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3692                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3693                                   sizeof(INTERNET_ASYNC_RESULT));
3694         }
3695     }
3696
3697     TRACE("<--\n");
3698     return res;
3699 }
3700
3701 /***********************************************************************
3702  *
3703  * Helper functions for the HttpSendRequest(Ex) functions
3704  *
3705  */
3706 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3707 {
3708     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3709     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3710
3711     TRACE("%p\n", lpwhr);
3712
3713     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3714             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3715             req->dwContentLength, req->bEndRequest);
3716
3717     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3718 }
3719
3720
3721 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3722 {
3723     INT responseLen;
3724     DWORD dwBufferSize;
3725     INTERNET_ASYNC_RESULT iar;
3726     DWORD res = ERROR_SUCCESS;
3727
3728     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3729                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3730
3731     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3732     if (!responseLen)
3733         res = ERROR_HTTP_HEADER_NOT_FOUND;
3734
3735     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3736                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3737
3738     /* process cookies here. Is this right? */
3739     HTTP_ProcessCookies(lpwhr);
3740
3741     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3742
3743     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3744     {
3745         DWORD dwCode,dwCodeLength = sizeof(DWORD);
3746         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3747             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3748         {
3749             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3750             dwBufferSize=sizeof(szNewLocation);
3751             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3752             {
3753                 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3754                 {
3755                     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3756                     lpwhr->lpszVerb = heap_strdupW(szGET);
3757                 }
3758                 HTTP_DrainContent(lpwhr);
3759                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3760                 {
3761                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3762                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3763                     res = HTTP_HandleRedirect(lpwhr, new_url);
3764                     if (res == ERROR_SUCCESS)
3765                         res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3766                     HeapFree( GetProcessHeap(), 0, new_url );
3767                 }
3768             }
3769         }
3770     }
3771
3772     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3773     iar.dwError = res;
3774
3775     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3776                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3777                           sizeof(INTERNET_ASYNC_RESULT));
3778     return res;
3779 }
3780
3781 /***********************************************************************
3782  *           HttpEndRequestA (WININET.@)
3783  *
3784  * Ends an HTTP request that was started by HttpSendRequestEx
3785  *
3786  * RETURNS
3787  *    TRUE      if successful
3788  *    FALSE     on failure
3789  *
3790  */
3791 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3792         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3793 {
3794     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3795
3796     if (lpBuffersOut)
3797     {
3798         SetLastError(ERROR_INVALID_PARAMETER);
3799         return FALSE;
3800     }
3801
3802     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3803 }
3804
3805 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3806 {
3807     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3808     http_request_t *lpwhr = (http_request_t*)work->hdr;
3809
3810     TRACE("%p\n", lpwhr);
3811
3812     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3813 }
3814
3815 /***********************************************************************
3816  *           HttpEndRequestW (WININET.@)
3817  *
3818  * Ends an HTTP request that was started by HttpSendRequestEx
3819  *
3820  * RETURNS
3821  *    TRUE      if successful
3822  *    FALSE     on failure
3823  *
3824  */
3825 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3826         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3827 {
3828     http_request_t *lpwhr;
3829     DWORD res;
3830
3831     TRACE("-->\n");
3832
3833     if (lpBuffersOut)
3834     {
3835         SetLastError(ERROR_INVALID_PARAMETER);
3836         return FALSE;
3837     }
3838
3839     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3840
3841     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3842     {
3843         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3844         if (lpwhr)
3845             WININET_Release( &lpwhr->hdr );
3846         return FALSE;
3847     }
3848     lpwhr->hdr.dwFlags |= dwFlags;
3849
3850     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3851     {
3852         WORKREQUEST work;
3853         struct WORKREQ_HTTPENDREQUESTW *request;
3854
3855         work.asyncproc = AsyncHttpEndRequestProc;
3856         work.hdr = WININET_AddRef( &lpwhr->hdr );
3857
3858         request = &work.u.HttpEndRequestW;
3859         request->dwFlags = dwFlags;
3860         request->dwContext = dwContext;
3861
3862         INTERNET_AsyncCall(&work);
3863         res = ERROR_IO_PENDING;
3864     }
3865     else
3866         res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3867
3868     WININET_Release( &lpwhr->hdr );
3869     TRACE("%u <--\n", res);
3870     if(res != ERROR_SUCCESS)
3871         SetLastError(res);
3872     return res == ERROR_SUCCESS;
3873 }
3874
3875 /***********************************************************************
3876  *           HttpSendRequestExA (WININET.@)
3877  *
3878  * Sends the specified request to the HTTP server and allows chunked
3879  * transfers.
3880  *
3881  * RETURNS
3882  *  Success: TRUE
3883  *  Failure: FALSE, call GetLastError() for more information.
3884  */
3885 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3886                                LPINTERNET_BUFFERSA lpBuffersIn,
3887                                LPINTERNET_BUFFERSA lpBuffersOut,
3888                                DWORD dwFlags, DWORD_PTR dwContext)
3889 {
3890     INTERNET_BUFFERSW BuffersInW;
3891     BOOL rc = FALSE;
3892     DWORD headerlen;
3893     LPWSTR header = NULL;
3894
3895     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3896             lpBuffersOut, dwFlags, dwContext);
3897
3898     if (lpBuffersIn)
3899     {
3900         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3901         if (lpBuffersIn->lpcszHeader)
3902         {
3903             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3904                     lpBuffersIn->dwHeadersLength,0,0);
3905             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3906             if (!(BuffersInW.lpcszHeader = header))
3907             {
3908                 SetLastError(ERROR_OUTOFMEMORY);
3909                 return FALSE;
3910             }
3911             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3912                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3913                     header, headerlen);
3914         }
3915         else
3916             BuffersInW.lpcszHeader = NULL;
3917         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3918         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3919         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3920         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3921         BuffersInW.Next = NULL;
3922     }
3923
3924     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3925
3926     HeapFree(GetProcessHeap(),0,header);
3927
3928     return rc;
3929 }
3930
3931 /***********************************************************************
3932  *           HttpSendRequestExW (WININET.@)
3933  *
3934  * Sends the specified request to the HTTP server and allows chunked
3935  * transfers
3936  *
3937  * RETURNS
3938  *  Success: TRUE
3939  *  Failure: FALSE, call GetLastError() for more information.
3940  */
3941 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3942                    LPINTERNET_BUFFERSW lpBuffersIn,
3943                    LPINTERNET_BUFFERSW lpBuffersOut,
3944                    DWORD dwFlags, DWORD_PTR dwContext)
3945 {
3946     http_request_t *lpwhr;
3947     http_session_t *lpwhs;
3948     appinfo_t *hIC;
3949     DWORD res;
3950
3951     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3952             lpBuffersOut, dwFlags, dwContext);
3953
3954     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3955
3956     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3957     {
3958         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3959         goto lend;
3960     }
3961
3962     lpwhs = lpwhr->lpHttpSession;
3963     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3964     hIC = lpwhs->lpAppInfo;
3965     assert(hIC->hdr.htype == WH_HINIT);
3966
3967     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3968     {
3969         WORKREQUEST workRequest;
3970         struct WORKREQ_HTTPSENDREQUESTW *req;
3971
3972         workRequest.asyncproc = AsyncHttpSendRequestProc;
3973         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3974         req = &workRequest.u.HttpSendRequestW;
3975         if (lpBuffersIn)
3976         {
3977             DWORD size = 0;
3978
3979             if (lpBuffersIn->lpcszHeader)
3980             {
3981                 if (lpBuffersIn->dwHeadersLength == ~0u)
3982                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3983                 else
3984                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3985
3986                 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3987                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3988             }
3989             else req->lpszHeader = NULL;
3990
3991             req->dwHeaderLength = size / sizeof(WCHAR);
3992             req->lpOptional = lpBuffersIn->lpvBuffer;
3993             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3994             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3995         }
3996         else
3997         {
3998             req->lpszHeader = NULL;
3999             req->dwHeaderLength = 0;
4000             req->lpOptional = NULL;
4001             req->dwOptionalLength = 0;
4002             req->dwContentLength = 0;
4003         }
4004
4005         req->bEndRequest = FALSE;
4006
4007         INTERNET_AsyncCall(&workRequest);
4008         /*
4009          * This is from windows.
4010          */
4011         res = ERROR_IO_PENDING;
4012     }
4013     else
4014     {
4015         if (lpBuffersIn)
4016             res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4017                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4018                                         lpBuffersIn->dwBufferTotal, FALSE);
4019         else
4020             res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4021     }
4022
4023 lend:
4024     if ( lpwhr )
4025         WININET_Release( &lpwhr->hdr );
4026
4027     TRACE("<---\n");
4028     SetLastError(res);
4029     return res == ERROR_SUCCESS;
4030 }
4031
4032 /***********************************************************************
4033  *           HttpSendRequestW (WININET.@)
4034  *
4035  * Sends the specified request to the HTTP server
4036  *
4037  * RETURNS
4038  *    TRUE  on success
4039  *    FALSE on failure
4040  *
4041  */
4042 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4043         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4044 {
4045     http_request_t *lpwhr;
4046     http_session_t *lpwhs = NULL;
4047     appinfo_t *hIC = NULL;
4048     DWORD res = ERROR_SUCCESS;
4049
4050     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4051             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4052
4053     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4054     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4055     {
4056         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4057         goto lend;
4058     }
4059
4060     lpwhs = lpwhr->lpHttpSession;
4061     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
4062     {
4063         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4064         goto lend;
4065     }
4066
4067     hIC = lpwhs->lpAppInfo;
4068     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
4069     {
4070         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4071         goto lend;
4072     }
4073
4074     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4075     {
4076         WORKREQUEST workRequest;
4077         struct WORKREQ_HTTPSENDREQUESTW *req;
4078
4079         workRequest.asyncproc = AsyncHttpSendRequestProc;
4080         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4081         req = &workRequest.u.HttpSendRequestW;
4082         if (lpszHeaders)
4083         {
4084             DWORD size;
4085
4086             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4087             else size = dwHeaderLength * sizeof(WCHAR);
4088
4089             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4090             memcpy(req->lpszHeader, lpszHeaders, size);
4091         }
4092         else
4093             req->lpszHeader = 0;
4094         req->dwHeaderLength = dwHeaderLength;
4095         req->lpOptional = lpOptional;
4096         req->dwOptionalLength = dwOptionalLength;
4097         req->dwContentLength = dwOptionalLength;
4098         req->bEndRequest = TRUE;
4099
4100         INTERNET_AsyncCall(&workRequest);
4101         /*
4102          * This is from windows.
4103          */
4104         res = ERROR_IO_PENDING;
4105     }
4106     else
4107     {
4108         res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4109                 dwHeaderLength, lpOptional, dwOptionalLength,
4110                 dwOptionalLength, TRUE);
4111     }
4112 lend:
4113     if( lpwhr )
4114         WININET_Release( &lpwhr->hdr );
4115
4116     SetLastError(res);
4117     return res == ERROR_SUCCESS;
4118 }
4119
4120 /***********************************************************************
4121  *           HttpSendRequestA (WININET.@)
4122  *
4123  * Sends the specified request to the HTTP server
4124  *
4125  * RETURNS
4126  *    TRUE  on success
4127  *    FALSE on failure
4128  *
4129  */
4130 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4131         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4132 {
4133     BOOL result;
4134     LPWSTR szHeaders=NULL;
4135     DWORD nLen=dwHeaderLength;
4136     if(lpszHeaders!=NULL)
4137     {
4138         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4139         szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4140         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4141     }
4142     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4143     HeapFree(GetProcessHeap(),0,szHeaders);
4144     return result;
4145 }
4146
4147 /***********************************************************************
4148  *           HTTPSESSION_Destroy (internal)
4149  *
4150  * Deallocate session handle
4151  *
4152  */
4153 static void HTTPSESSION_Destroy(object_header_t *hdr)
4154 {
4155     http_session_t *lpwhs = (http_session_t*) hdr;
4156
4157     TRACE("%p\n", lpwhs);
4158
4159     WININET_Release(&lpwhs->lpAppInfo->hdr);
4160
4161     HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4162     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4163     HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4164     HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4165     HeapFree(GetProcessHeap(), 0, lpwhs);
4166 }
4167
4168 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4169 {
4170     switch(option) {
4171     case INTERNET_OPTION_HANDLE_TYPE:
4172         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4173
4174         if (*size < sizeof(ULONG))
4175             return ERROR_INSUFFICIENT_BUFFER;
4176
4177         *size = sizeof(DWORD);
4178         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4179         return ERROR_SUCCESS;
4180     }
4181
4182     return INET_QueryOption(hdr, option, buffer, size, unicode);
4183 }
4184
4185 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4186 {
4187     http_session_t *ses = (http_session_t*)hdr;
4188
4189     switch(option) {
4190     case INTERNET_OPTION_USERNAME:
4191     {
4192         HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4193         if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4194         return ERROR_SUCCESS;
4195     }
4196     case INTERNET_OPTION_PASSWORD:
4197     {
4198         HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4199         if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4200         return ERROR_SUCCESS;
4201     }
4202     default: break;
4203     }
4204
4205     return ERROR_INTERNET_INVALID_OPTION;
4206 }
4207
4208 static const object_vtbl_t HTTPSESSIONVtbl = {
4209     HTTPSESSION_Destroy,
4210     NULL,
4211     HTTPSESSION_QueryOption,
4212     HTTPSESSION_SetOption,
4213     NULL,
4214     NULL,
4215     NULL,
4216     NULL,
4217     NULL
4218 };
4219
4220
4221 /***********************************************************************
4222  *           HTTP_Connect  (internal)
4223  *
4224  * Create http session handle
4225  *
4226  * RETURNS
4227  *   HINTERNET a session handle on success
4228  *   NULL on failure
4229  *
4230  */
4231 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4232         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4233         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4234         DWORD dwInternalFlags, HINTERNET *ret)
4235 {
4236     http_session_t *lpwhs = NULL;
4237     HINTERNET handle = NULL;
4238     DWORD res = ERROR_SUCCESS;
4239
4240     TRACE("-->\n");
4241
4242     if (!lpszServerName || !lpszServerName[0])
4243         return ERROR_INVALID_PARAMETER;
4244
4245     assert( hIC->hdr.htype == WH_HINIT );
4246
4247     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4248     if (!lpwhs)
4249         return ERROR_OUTOFMEMORY;
4250
4251    /*
4252     * According to my tests. The name is not resolved until a request is sent
4253     */
4254
4255     lpwhs->hdr.htype = WH_HHTTPSESSION;
4256     lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4257     lpwhs->hdr.dwFlags = dwFlags;
4258     lpwhs->hdr.dwContext = dwContext;
4259     lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4260     lpwhs->hdr.refs = 1;
4261     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4262
4263     WININET_AddRef( &hIC->hdr );
4264     lpwhs->lpAppInfo = hIC;
4265     list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4266
4267     handle = WININET_AllocHandle( &lpwhs->hdr );
4268     if (NULL == handle)
4269     {
4270         ERR("Failed to alloc handle\n");
4271         res = ERROR_OUTOFMEMORY;
4272         goto lerror;
4273     }
4274
4275     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4276         if(hIC->lpszProxyBypass)
4277             FIXME("Proxy bypass is ignored.\n");
4278     }
4279     lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4280     lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4281     if (lpszUserName && lpszUserName[0])
4282         lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4283     if (lpszPassword && lpszPassword[0])
4284         lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4285     lpwhs->nServerPort = nServerPort;
4286     lpwhs->nHostPort = nServerPort;
4287
4288     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4289     if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4290     {
4291         INTERNET_SendCallback(&hIC->hdr, dwContext,
4292                               INTERNET_STATUS_HANDLE_CREATED, &handle,
4293                               sizeof(handle));
4294     }
4295
4296 lerror:
4297     if( lpwhs )
4298         WININET_Release( &lpwhs->hdr );
4299
4300 /*
4301  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4302  * windows
4303  */
4304
4305     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4306
4307     if(res == ERROR_SUCCESS)
4308         *ret = handle;
4309     return res;
4310 }
4311
4312
4313 /***********************************************************************
4314  *           HTTP_OpenConnection (internal)
4315  *
4316  * Connect to a web server
4317  *
4318  * RETURNS
4319  *
4320  *   TRUE  on success
4321  *   FALSE on failure
4322  */
4323 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4324 {
4325     http_session_t *lpwhs;
4326     appinfo_t *hIC = NULL;
4327     char szaddr[INET6_ADDRSTRLEN];
4328     const void *addr;
4329     DWORD res = ERROR_SUCCESS;
4330
4331     TRACE("-->\n");
4332
4333
4334     if (lpwhr->hdr.htype != WH_HHTTPREQ)
4335     {
4336         res = ERROR_INVALID_PARAMETER;
4337         goto lend;
4338     }
4339
4340     if (NETCON_connected(&lpwhr->netConnection))
4341         goto lend;
4342     if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4343
4344     lpwhs = lpwhr->lpHttpSession;
4345
4346     hIC = lpwhs->lpAppInfo;
4347     switch (lpwhs->socketAddress.ss_family)
4348     {
4349     case AF_INET:
4350         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4351         break;
4352     case AF_INET6:
4353         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4354         break;
4355     default:
4356         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4357         return ERROR_INTERNET_NAME_NOT_RESOLVED;
4358     }
4359     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4360     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4361                           INTERNET_STATUS_CONNECTING_TO_SERVER,
4362                           szaddr,
4363                           strlen(szaddr)+1);
4364
4365     res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4366     if (res != ERROR_SUCCESS)
4367     {
4368         WARN("Socket creation failed: %u\n", res);
4369         goto lend;
4370     }
4371
4372     res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4373                          lpwhs->sa_len);
4374     if(res != ERROR_SUCCESS)
4375        goto lend;
4376
4377     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4378             INTERNET_STATUS_CONNECTED_TO_SERVER,
4379             szaddr, strlen(szaddr)+1);
4380
4381     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4382     {
4383         /* Note: we differ from Microsoft's WinINet here. they seem to have
4384          * a bug that causes no status callbacks to be sent when starting
4385          * a tunnel to a proxy server using the CONNECT verb. i believe our
4386          * behaviour to be more correct and to not cause any incompatibilities
4387          * because using a secure connection through a proxy server is a rare
4388          * case that would be hard for anyone to depend on */
4389         if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4390             HTTPREQ_CloseConnection(&lpwhr->hdr);
4391             goto lend;
4392         }
4393
4394         res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4395         if(res != ERROR_SUCCESS)
4396         {
4397             WARN("Couldn't connect securely to host\n");
4398
4399             if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4400                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4401                     || res == ERROR_INTERNET_INVALID_CA
4402                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
4403                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4404                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
4405                     || res == ERROR_INTERNET_SEC_INVALID_CERT
4406                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4407                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4408
4409             HTTPREQ_CloseConnection(&lpwhr->hdr);
4410             goto lend;
4411         }
4412     }
4413
4414
4415 lend:
4416     lpwhr->read_pos = lpwhr->read_size = 0;
4417     lpwhr->read_chunked = FALSE;
4418
4419     TRACE("%d <--\n", res);
4420     return res;
4421 }
4422
4423
4424 /***********************************************************************
4425  *           HTTP_clear_response_headers (internal)
4426  *
4427  * clear out any old response headers
4428  */
4429 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4430 {
4431     DWORD i;
4432
4433     for( i=0; i<lpwhr->nCustHeaders; i++)
4434     {
4435         if( !lpwhr->pCustHeaders[i].lpszField )
4436             continue;
4437         if( !lpwhr->pCustHeaders[i].lpszValue )
4438             continue;
4439         if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4440             continue;
4441         HTTP_DeleteCustomHeader( lpwhr, i );
4442         i--;
4443     }
4444 }
4445
4446 /***********************************************************************
4447  *           HTTP_GetResponseHeaders (internal)
4448  *
4449  * Read server response
4450  *
4451  * RETURNS
4452  *
4453  *   TRUE  on success
4454  *   FALSE on error
4455  */
4456 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4457 {
4458     INT cbreaks = 0;
4459     WCHAR buffer[MAX_REPLY_LEN];
4460     DWORD buflen = MAX_REPLY_LEN;
4461     BOOL bSuccess = FALSE;
4462     INT  rc = 0;
4463     char bufferA[MAX_REPLY_LEN];
4464     LPWSTR status_code = NULL, status_text = NULL;
4465     DWORD cchMaxRawHeaders = 1024;
4466     LPWSTR lpszRawHeaders = NULL;
4467     LPWSTR temp;
4468     DWORD cchRawHeaders = 0;
4469     BOOL codeHundred = FALSE;
4470
4471     TRACE("-->\n");
4472
4473     if (!NETCON_connected(&lpwhr->netConnection))
4474         goto lend;
4475
4476     do {
4477         static const WCHAR szHundred[] = {'1','0','0',0};
4478         /*
4479          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4480          */
4481         buflen = MAX_REPLY_LEN;
4482         if (!read_line(lpwhr, bufferA, &buflen))
4483             goto lend;
4484
4485         /* clear old response headers (eg. from a redirect response) */
4486         if (clear) {
4487             HTTP_clear_response_headers( lpwhr );
4488             clear = FALSE;
4489         }
4490
4491         rc += buflen;
4492         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4493         /* check is this a status code line? */
4494         if (!strncmpW(buffer, g_szHttp1_0, 4))
4495         {
4496             /* split the version from the status code */
4497             status_code = strchrW( buffer, ' ' );
4498             if( !status_code )
4499                 goto lend;
4500             *status_code++=0;
4501
4502             /* split the status code from the status text */
4503             status_text = strchrW( status_code, ' ' );
4504             if( !status_text )
4505                 goto lend;
4506             *status_text++=0;
4507
4508             TRACE("version [%s] status code [%s] status text [%s]\n",
4509                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4510
4511             codeHundred = (!strcmpW(status_code, szHundred));
4512         }
4513         else if (!codeHundred)
4514         {
4515             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4516
4517             HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4518             HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4519
4520             lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4521             lpwhr->lpszStatusText = heap_strdupW(szOK);
4522
4523             HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4524             lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4525
4526             bSuccess = TRUE;
4527             goto lend;
4528         }
4529     } while (codeHundred);
4530
4531     /* Add status code */
4532     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4533             HTTP_ADDHDR_FLAG_REPLACE);
4534
4535     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4536     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4537
4538     lpwhr->lpszVersion = heap_strdupW(buffer);
4539     lpwhr->lpszStatusText = heap_strdupW(status_text);
4540
4541     /* Restore the spaces */
4542     *(status_code-1) = ' ';
4543     *(status_text-1) = ' ';
4544
4545     /* regenerate raw headers */
4546     lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4547     if (!lpszRawHeaders) goto lend;
4548
4549     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4550         cchMaxRawHeaders *= 2;
4551     temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4552     if (temp == NULL) goto lend;
4553     lpszRawHeaders = temp;
4554     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4555     cchRawHeaders += (buflen-1);
4556     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4557     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4558     lpszRawHeaders[cchRawHeaders] = '\0';
4559
4560     /* Parse each response line */
4561     do
4562     {
4563         buflen = MAX_REPLY_LEN;
4564         if (read_line(lpwhr, bufferA, &buflen))
4565         {
4566             LPWSTR * pFieldAndValue;
4567
4568             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4569
4570             if (!bufferA[0]) break;
4571             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4572
4573             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4574             if (pFieldAndValue)
4575             {
4576                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4577                     cchMaxRawHeaders *= 2;
4578                 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4579                 if (temp == NULL) goto lend;
4580                 lpszRawHeaders = temp;
4581                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4582                 cchRawHeaders += (buflen-1);
4583                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4584                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4585                 lpszRawHeaders[cchRawHeaders] = '\0';
4586
4587                 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4588                                    HTTP_ADDREQ_FLAG_ADD );
4589
4590                 HTTP_FreeTokens(pFieldAndValue);
4591             }
4592         }
4593         else
4594         {
4595             cbreaks++;
4596             if (cbreaks >= 2)
4597                break;
4598         }
4599     }while(1);
4600
4601     /* make sure the response header is terminated with an empty line.  Some apps really
4602        truly care about that empty line being there for some reason.  Just add it to the
4603        header. */
4604     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4605     {
4606         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4607         temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4608         if (temp == NULL) goto lend;
4609         lpszRawHeaders = temp;
4610     }
4611
4612     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4613
4614     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4615     lpwhr->lpszRawHeaders = lpszRawHeaders;
4616     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4617     bSuccess = TRUE;
4618
4619 lend:
4620
4621     TRACE("<--\n");
4622     if (bSuccess)
4623         return rc;
4624     else
4625     {
4626         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4627         return 0;
4628     }
4629 }
4630
4631 /***********************************************************************
4632  *           HTTP_InterpretHttpHeader (internal)
4633  *
4634  * Parse server response
4635  *
4636  * RETURNS
4637  *
4638  *   Pointer to array of field, value, NULL on success.
4639  *   NULL on error.
4640  */
4641 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4642 {
4643     LPWSTR * pTokenPair;
4644     LPWSTR pszColon;
4645     INT len;
4646
4647     pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4648
4649     pszColon = strchrW(buffer, ':');
4650     /* must have two tokens */
4651     if (!pszColon)
4652     {
4653         HTTP_FreeTokens(pTokenPair);
4654         if (buffer[0])
4655             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4656         return NULL;
4657     }
4658
4659     pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4660     if (!pTokenPair[0])
4661     {
4662         HTTP_FreeTokens(pTokenPair);
4663         return NULL;
4664     }
4665     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4666     pTokenPair[0][pszColon - buffer] = '\0';
4667
4668     /* skip colon */
4669     pszColon++;
4670     len = strlenW(pszColon);
4671     pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4672     if (!pTokenPair[1])
4673     {
4674         HTTP_FreeTokens(pTokenPair);
4675         return NULL;
4676     }
4677     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4678
4679     strip_spaces(pTokenPair[0]);
4680     strip_spaces(pTokenPair[1]);
4681
4682     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4683     return pTokenPair;
4684 }
4685
4686 /***********************************************************************
4687  *           HTTP_ProcessHeader (internal)
4688  *
4689  * Stuff header into header tables according to <dwModifier>
4690  *
4691  */
4692
4693 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4694
4695 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4696 {
4697     LPHTTPHEADERW lphttpHdr = NULL;
4698     INT index = -1;
4699     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4700     DWORD res = ERROR_HTTP_INVALID_HEADER;
4701
4702     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4703
4704     /* REPLACE wins out over ADD */
4705     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4706         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4707     
4708     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4709         index = -1;
4710     else
4711         index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4712
4713     if (index >= 0)
4714     {
4715         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4716             return ERROR_HTTP_INVALID_HEADER;
4717         lphttpHdr = &lpwhr->pCustHeaders[index];
4718     }
4719     else if (value)
4720     {
4721         HTTPHEADERW hdr;
4722
4723         hdr.lpszField = (LPWSTR)field;
4724         hdr.lpszValue = (LPWSTR)value;
4725         hdr.wFlags = hdr.wCount = 0;
4726
4727         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4728             hdr.wFlags |= HDR_ISREQUEST;
4729
4730         return HTTP_InsertCustomHeader(lpwhr, &hdr);
4731     }
4732     /* no value to delete */
4733     else return ERROR_SUCCESS;
4734
4735     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4736             lphttpHdr->wFlags |= HDR_ISREQUEST;
4737     else
4738         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4739
4740     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4741     {
4742         HTTP_DeleteCustomHeader( lpwhr, index );
4743
4744         if (value)
4745         {
4746             HTTPHEADERW hdr;
4747
4748             hdr.lpszField = (LPWSTR)field;
4749             hdr.lpszValue = (LPWSTR)value;
4750             hdr.wFlags = hdr.wCount = 0;
4751
4752             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4753                 hdr.wFlags |= HDR_ISREQUEST;
4754
4755             return HTTP_InsertCustomHeader(lpwhr, &hdr);
4756         }
4757
4758         return ERROR_SUCCESS;
4759     }
4760     else if (dwModifier & COALESCEFLAGS)
4761     {
4762         LPWSTR lpsztmp;
4763         WCHAR ch = 0;
4764         INT len = 0;
4765         INT origlen = strlenW(lphttpHdr->lpszValue);
4766         INT valuelen = strlenW(value);
4767
4768         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4769         {
4770             ch = ',';
4771             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4772         }
4773         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4774         {
4775             ch = ';';
4776             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4777         }
4778
4779         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4780
4781         lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4782         if (lpsztmp)
4783         {
4784             lphttpHdr->lpszValue = lpsztmp;
4785     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4786             if (ch > 0)
4787             {
4788                 lphttpHdr->lpszValue[origlen] = ch;
4789                 origlen++;
4790                 lphttpHdr->lpszValue[origlen] = ' ';
4791                 origlen++;
4792             }
4793
4794             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4795             lphttpHdr->lpszValue[len] = '\0';
4796             res = ERROR_SUCCESS;
4797         }
4798         else
4799         {
4800             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4801             res = ERROR_OUTOFMEMORY;
4802         }
4803     }
4804     TRACE("<-- %d\n", res);
4805     return res;
4806 }
4807
4808
4809 /***********************************************************************
4810  *           HTTP_FinishedReading (internal)
4811  *
4812  * Called when all content from server has been read by client.
4813  *
4814  */
4815 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4816 {
4817     BOOL keepalive = HTTP_KeepAlive(lpwhr);
4818
4819     TRACE("\n");
4820
4821
4822     if (!keepalive)
4823     {
4824         HTTPREQ_CloseConnection(&lpwhr->hdr);
4825     }
4826
4827     /* FIXME: store data in the URL cache here */
4828
4829     return TRUE;
4830 }
4831
4832
4833 /***********************************************************************
4834  *           HTTP_GetCustomHeaderIndex (internal)
4835  *
4836  * Return index of custom header from header array
4837  *
4838  */
4839 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4840                                      int requested_index, BOOL request_only)
4841 {
4842     DWORD index;
4843
4844     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4845
4846     for (index = 0; index < lpwhr->nCustHeaders; index++)
4847     {
4848         if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4849             continue;
4850
4851         if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4852             continue;
4853
4854         if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4855             continue;
4856
4857         if (requested_index == 0)
4858             break;
4859         requested_index --;
4860     }
4861
4862     if (index >= lpwhr->nCustHeaders)
4863         index = -1;
4864
4865     TRACE("Return: %d\n", index);
4866     return index;
4867 }
4868
4869
4870 /***********************************************************************
4871  *           HTTP_InsertCustomHeader (internal)
4872  *
4873  * Insert header into array
4874  *
4875  */
4876 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4877 {
4878     INT count;
4879     LPHTTPHEADERW lph = NULL;
4880
4881     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4882     count = lpwhr->nCustHeaders + 1;
4883     if (count > 1)
4884         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4885     else
4886         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4887
4888     if (!lph)
4889         return ERROR_OUTOFMEMORY;
4890
4891     lpwhr->pCustHeaders = lph;
4892     lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4893     lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4894     lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4895     lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4896     lpwhr->nCustHeaders++;
4897
4898     return ERROR_SUCCESS;
4899 }
4900
4901
4902 /***********************************************************************
4903  *           HTTP_DeleteCustomHeader (internal)
4904  *
4905  * Delete header from array
4906  *  If this function is called, the indexs may change.
4907  */
4908 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4909 {
4910     if( lpwhr->nCustHeaders <= 0 )
4911         return FALSE;
4912     if( index >= lpwhr->nCustHeaders )
4913         return FALSE;
4914     lpwhr->nCustHeaders--;
4915
4916     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4917     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4918
4919     memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4920              (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4921     memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4922
4923     return TRUE;
4924 }
4925
4926
4927 /***********************************************************************
4928  *           HTTP_VerifyValidHeader (internal)
4929  *
4930  * Verify the given header is not invalid for the given http request
4931  *
4932  */
4933 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4934 {
4935     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4936     if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4937         return ERROR_HTTP_INVALID_HEADER;
4938
4939     return ERROR_SUCCESS;
4940 }
4941
4942 /***********************************************************************
4943  *          IsHostInProxyBypassList (@)
4944  *
4945  * Undocumented
4946  *
4947  */
4948 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4949 {
4950    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4951    return FALSE;
4952 }