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