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