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