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