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