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