~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/wininet/http.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Wininet - Http Implementation
  3  *
  4  * Copyright 1999 Corel Corporation
  5  * Copyright 2002 CodeWeavers Inc.
  6  * Copyright 2002 TransGaming Technologies Inc.
  7  * Copyright 2004 Mike McCormack for CodeWeavers
  8  * Copyright 2005 Aric Stewart for CodeWeavers
  9  * Copyright 2006 Robert Shearman for CodeWeavers
 10  *
 11  * Ulrich Czekalla
 12  * David Hammerton
 13  *
 14  * This library is free software; you can redistribute it and/or
 15  * modify it under the terms of the GNU Lesser General Public
 16  * License as published by the Free Software Foundation; either
 17  * version 2.1 of the License, or (at your option) any later version.
 18  *
 19  * This library is distributed in the hope that it will be useful,
 20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 22  * Lesser General Public License for more details.
 23  *
 24  * You should have received a copy of the GNU Lesser General Public
 25  * License along with this library; if not, write to the Free Software
 26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 27  */
 28 
 29 #include "config.h"
 30 #include "wine/port.h"
 31 
 32 #if defined(__MINGW32__) || defined (_MSC_VER)
 33 #include <ws2tcpip.h>
 34 #endif
 35 
 36 #include <sys/types.h>
 37 #ifdef HAVE_SYS_SOCKET_H
 38 # include <sys/socket.h>
 39 #endif
 40 #ifdef HAVE_ARPA_INET_H
 41 # include <arpa/inet.h>
 42 #endif
 43 #include <stdarg.h>
 44 #include <stdio.h>
 45 #include <stdlib.h>
 46 #ifdef HAVE_UNISTD_H
 47 # include <unistd.h>
 48 #endif
 49 #include <time.h>
 50 #include <assert.h>
 51 #ifdef HAVE_ZLIB
 52 #  include <zlib.h>
 53 #endif
 54 
 55 #include "windef.h"
 56 #include "winbase.h"
 57 #include "wininet.h"
 58 #include "winerror.h"
 59 #define NO_SHLWAPI_STREAM
 60 #define NO_SHLWAPI_REG
 61 #define NO_SHLWAPI_STRFCNS
 62 #define NO_SHLWAPI_GDI
 63 #include "shlwapi.h"
 64 #include "sspi.h"
 65 #include "wincrypt.h"
 66 
 67 #include "internet.h"
 68 #include "wine/debug.h"
 69 #include "wine/exception.h"
 70 #include "wine/unicode.h"
 71 
 72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 73 
 74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','',0};
 75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
 76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
 77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
 80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
 81 static const WCHAR szGET[] = { 'G','E','T', 0 };
 82 static const WCHAR szCrLf[] = {'\r','\n', 0};
 83 
 84 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
 85 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
 86 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
 87 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
 88 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
 89 static const WCHAR szAge[] = { 'A','g','e',0 };
 90 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
 91 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
 92 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
 93 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
 94 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
 95 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
 96 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
 97 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
 98 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
 99 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
100 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
101 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 };
102 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
103 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
104 static const WCHAR szDate[] = { 'D','a','t','e',0 };
105 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
106 static const WCHAR szETag[] = { 'E','T','a','g',0 };
107 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
108 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
109 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
110 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
111 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
112 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
113 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
115 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
116 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
117 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
118 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
119 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
120 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
121 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
122 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
123 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
124 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
125 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
126 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
127 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
128 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 };
129 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
130 static const WCHAR szURI[] = { 'U','R','I',0 };
131 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
132 static const WCHAR szVary[] = { 'V','a','r','y',0 };
133 static const WCHAR szVia[] = { 'V','i','a',0 };
134 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
135 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
136 
137 #define MAXHOSTNAME 100
138 #define MAX_FIELD_VALUE_LEN 256
139 #define MAX_FIELD_LEN 256
140 
141 #define HTTP_REFERER    szReferer
142 #define HTTP_ACCEPT     szAccept
143 #define HTTP_USERAGENT  szUser_Agent
144 
145 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
146 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
147 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
148 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
150 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
151 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
152 
153 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
154 
155 struct HttpAuthInfo
156 {
157     LPWSTR scheme;
158     CredHandle cred;
159     CtxtHandle ctx;
160     TimeStamp exp;
161     ULONG attr;
162     ULONG max_token;
163     void *auth_data;
164     unsigned int auth_data_len;
165     BOOL finished; /* finished authenticating */
166 };
167 
168 
169 struct gzip_stream_t {
170 #ifdef HAVE_ZLIB
171     z_stream zstream;
172 #endif
173     BYTE buf[8192];
174     DWORD buf_size;
175     DWORD buf_pos;
176     BOOL end_of_data;
177 };
178 
179 static BOOL HTTP_OpenConnection(http_request_t *req);
180 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
181 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
182 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
183 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
184 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
185 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
186 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
187 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
188 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
189 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
190 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
191 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
192 static void HTTP_DrainContent(http_request_t *req);
193 static BOOL HTTP_FinishedReading(http_request_t *req);
194 
195 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
196 {
197     int HeaderIndex = 0;
198     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
199     if (HeaderIndex == -1)
200         return NULL;
201     else
202         return &req->pCustHeaders[HeaderIndex];
203 }
204 
205 #ifdef HAVE_ZLIB
206 
207 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
208 {
209     return HeapAlloc(GetProcessHeap(), 0, items*size);
210 }
211 
212 static void wininet_zfree(voidpf opaque, voidpf address)
213 {
214     HeapFree(GetProcessHeap(), 0, address);
215 }
216 
217 static void init_gzip_stream(http_request_t *req)
218 {
219     gzip_stream_t *gzip_stream;
220     int zres;
221 
222     gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
223     gzip_stream->zstream.zalloc = wininet_zalloc;
224     gzip_stream->zstream.zfree = wininet_zfree;
225     gzip_stream->zstream.opaque = NULL;
226     gzip_stream->zstream.next_in = NULL;
227     gzip_stream->zstream.avail_in = 0;
228     gzip_stream->zstream.next_out = NULL;
229     gzip_stream->zstream.avail_out = 0;
230     gzip_stream->buf_pos = 0;
231     gzip_stream->buf_size = 0;
232     gzip_stream->end_of_data = FALSE;
233 
234     zres = inflateInit2(&gzip_stream->zstream, 0x1f);
235     if(zres != Z_OK) {
236         ERR("inflateInit failed: %d\n", zres);
237         HeapFree(GetProcessHeap(), 0, gzip_stream);
238         return;
239     }
240 
241     req->gzip_stream = gzip_stream;
242 }
243 
244 #else
245 
246 static void init_gzip_stream(http_request_t *req)
247 {
248     ERR("gzip stream not supported, missing zlib.\n");
249 }
250 
251 #endif
252 
253 /* set the request content length based on the headers */
254 static DWORD set_content_length( http_request_t *lpwhr )
255 {
256     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
257     WCHAR encoding[20];
258     DWORD size;
259 
260     size = sizeof(lpwhr->dwContentLength);
261     if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
262                              &lpwhr->dwContentLength, &size, NULL))
263         lpwhr->dwContentLength = ~0u;
264 
265     size = sizeof(encoding);
266     if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
267         !strcmpiW(encoding, szChunked))
268     {
269         lpwhr->dwContentLength = ~0u;
270         lpwhr->read_chunked = TRUE;
271     }
272 
273     if(lpwhr->decoding) {
274         int encoding_idx;
275 
276         static const WCHAR gzipW[] = {'g','z','i','p',0};
277 
278         encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
279         if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
280             init_gzip_stream(lpwhr);
281     }
282 
283     return lpwhr->dwContentLength;
284 }
285 
286 /***********************************************************************
287  *           HTTP_Tokenize (internal)
288  *
289  *  Tokenize a string, allocating memory for the tokens.
290  */
291 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
292 {
293     LPWSTR * token_array;
294     int tokens = 0;
295     int i;
296     LPCWSTR next_token;
297 
298     if (string)
299     {
300         /* empty string has no tokens */
301         if (*string)
302             tokens++;
303         /* count tokens */
304         for (i = 0; string[i]; i++)
305         {
306             if (!strncmpW(string+i, token_string, strlenW(token_string)))
307             {
308                 DWORD j;
309                 tokens++;
310                 /* we want to skip over separators, but not the null terminator */
311                 for (j = 0; j < strlenW(token_string) - 1; j++)
312                     if (!string[i+j])
313                         break;
314                 i += j;
315             }
316         }
317     }
318 
319     /* add 1 for terminating NULL */
320     token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
321     token_array[tokens] = NULL;
322     if (!tokens)
323         return token_array;
324     for (i = 0; i < tokens; i++)
325     {
326         int len;
327         next_token = strstrW(string, token_string);
328         if (!next_token) next_token = string+strlenW(string);
329         len = next_token - string;
330         token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
331         memcpy(token_array[i], string, len*sizeof(WCHAR));
332         token_array[i][len] = '\0';
333         string = next_token+strlenW(token_string);
334     }
335     return token_array;
336 }
337 
338 /***********************************************************************
339  *           HTTP_FreeTokens (internal)
340  *
341  *  Frees memory returned from HTTP_Tokenize.
342  */
343 static void HTTP_FreeTokens(LPWSTR * token_array)
344 {
345     int i;
346     for (i = 0; token_array[i]; i++)
347         HeapFree(GetProcessHeap(), 0, token_array[i]);
348     HeapFree(GetProcessHeap(), 0, token_array);
349 }
350 
351 /* **********************************************************************
352  * 
353  * Helper functions for the HttpSendRequest(Ex) functions
354  * 
355  */
356 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
357 {
358     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
359     http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
360 
361     TRACE("%p\n", lpwhr);
362 
363     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
364             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
365             req->dwContentLength, req->bEndRequest);
366 
367     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
368 }
369 
370 static void HTTP_FixURL(http_request_t *lpwhr)
371 {
372     static const WCHAR szSlash[] = { '/',0 };
373     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
374 
375     /* If we don't have a path we set it to root */
376     if (NULL == lpwhr->lpszPath)
377         lpwhr->lpszPath = heap_strdupW(szSlash);
378     else /* remove \r and \n*/
379     {
380         int nLen = strlenW(lpwhr->lpszPath);
381         while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
382         {
383             nLen--;
384             lpwhr->lpszPath[nLen]='\0';
385         }
386         /* Replace '\' with '/' */
387         while (nLen>0) {
388             nLen--;
389             if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
390         }
391     }
392 
393     if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
394                        lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
395        && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
396     {
397         WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0, 
398                              (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
399         *fixurl = '/';
400         strcpyW(fixurl + 1, lpwhr->lpszPath);
401         HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
402         lpwhr->lpszPath = fixurl;
403     }
404 }
405 
406 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
407 {
408     LPWSTR requestString;
409     DWORD len, n;
410     LPCWSTR *req;
411     UINT i;
412     LPWSTR p;
413 
414     static const WCHAR szSpace[] = { ' ',0 };
415     static const WCHAR szColon[] = { ':',' ',0 };
416     static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
417 
418     /* allocate space for an array of all the string pointers to be added */
419     len = (lpwhr->nCustHeaders)*4 + 10;
420     req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
421 
422     /* add the verb, path and HTTP version string */
423     n = 0;
424     req[n++] = verb;
425     req[n++] = szSpace;
426     req[n++] = path;
427     req[n++] = szSpace;
428     req[n++] = version;
429 
430     /* Append custom request headers */
431     for (i = 0; i < lpwhr->nCustHeaders; i++)
432     {
433         if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
434         {
435             req[n++] = szCrLf;
436             req[n++] = lpwhr->pCustHeaders[i].lpszField;
437             req[n++] = szColon;
438             req[n++] = lpwhr->pCustHeaders[i].lpszValue;
439 
440             TRACE("Adding custom header %s (%s)\n",
441                    debugstr_w(lpwhr->pCustHeaders[i].lpszField),
442                    debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
443         }
444     }
445 
446     if( n >= len )
447         ERR("oops. buffer overrun\n");
448 
449     req[n] = NULL;
450     requestString = HTTP_build_req( req, 4 );
451     HeapFree( GetProcessHeap(), 0, req );
452 
453     /*
454      * Set (header) termination string for request
455      * Make sure there's exactly two new lines at the end of the request
456      */
457     p = &requestString[strlenW(requestString)-1];
458     while ( (*p == '\n') || (*p == '\r') )
459        p--;
460     strcpyW( p+1, sztwocrlf );
461     
462     return requestString;
463 }
464 
465 static void HTTP_ProcessCookies( http_request_t *lpwhr )
466 {
467     int HeaderIndex;
468     int numCookies = 0;
469     LPHTTPHEADERW setCookieHeader;
470 
471     while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
472     {
473         setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
474 
475         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
476         {
477             int len;
478             static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
479             LPWSTR buf_url;
480             LPHTTPHEADERW Host;
481 
482             Host = HTTP_GetHeader(lpwhr, hostW);
483             len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
484             buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
485             sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
486             InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
487 
488             HeapFree(GetProcessHeap(), 0, buf_url);
489         }
490         numCookies++;
491     }
492 }
493 
494 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
495 {
496     static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
497     return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
498         ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
499 }
500 
501 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
502                                   struct HttpAuthInfo **ppAuthInfo,
503                                   LPWSTR domain_and_username, LPWSTR password )
504 {
505     SECURITY_STATUS sec_status;
506     struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
507     BOOL first = FALSE;
508 
509     TRACE("%s\n", debugstr_w(pszAuthValue));
510 
511     if (!pAuthInfo)
512     {
513         TimeStamp exp;
514 
515         first = TRUE;
516         pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
517         if (!pAuthInfo)
518             return FALSE;
519 
520         SecInvalidateHandle(&pAuthInfo->cred);
521         SecInvalidateHandle(&pAuthInfo->ctx);
522         memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
523         pAuthInfo->attr = 0;
524         pAuthInfo->auth_data = NULL;
525         pAuthInfo->auth_data_len = 0;
526         pAuthInfo->finished = FALSE;
527 
528         if (is_basic_auth_value(pszAuthValue))
529         {
530             static const WCHAR szBasic[] = {'B','a','s','i','c',0};
531             pAuthInfo->scheme = heap_strdupW(szBasic);
532             if (!pAuthInfo->scheme)
533             {
534                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
535                 return FALSE;
536             }
537         }
538         else
539         {
540             PVOID pAuthData;
541             SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
542 
543             pAuthInfo->scheme = heap_strdupW(pszAuthValue);
544             if (!pAuthInfo->scheme)
545             {
546                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
547                 return FALSE;
548             }
549 
550             if (domain_and_username)
551             {
552                 WCHAR *user = strchrW(domain_and_username, '\\');
553                 WCHAR *domain = domain_and_username;
554 
555                 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
556 
557                 pAuthData = &nt_auth_identity;
558 
559                 if (user) user++;
560                 else
561                 {
562                     user = domain_and_username;
563                     domain = NULL;
564                 }
565 
566                 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
567                 nt_auth_identity.User = user;
568                 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
569                 nt_auth_identity.Domain = domain;
570                 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
571                 nt_auth_identity.Password = password;
572                 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
573             }
574             else
575                 /* use default credentials */
576                 pAuthData = NULL;
577 
578             sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
579                                                    SECPKG_CRED_OUTBOUND, NULL,
580                                                    pAuthData, NULL,
581                                                    NULL, &pAuthInfo->cred,
582                                                    &exp);
583             if (sec_status == SEC_E_OK)
584             {
585                 PSecPkgInfoW sec_pkg_info;
586                 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
587                 if (sec_status == SEC_E_OK)
588                 {
589                     pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
590                     FreeContextBuffer(sec_pkg_info);
591                 }
592             }
593             if (sec_status != SEC_E_OK)
594             {
595                 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
596                      debugstr_w(pAuthInfo->scheme), sec_status);
597                 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
598                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
599                 return FALSE;
600             }
601         }
602         *ppAuthInfo = pAuthInfo;
603     }
604     else if (pAuthInfo->finished)
605         return FALSE;
606 
607     if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
608         strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
609     {
610         ERR("authentication scheme changed from %s to %s\n",
611             debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
612         return FALSE;
613     }
614 
615     if (is_basic_auth_value(pszAuthValue))
616     {
617         int userlen;
618         int passlen;
619         char *auth_data;
620 
621         TRACE("basic authentication\n");
622 
623         /* we don't cache credentials for basic authentication, so we can't
624          * retrieve them if the application didn't pass us any credentials */
625         if (!domain_and_username) return FALSE;
626 
627         userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
628         passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
629 
630         /* length includes a nul terminator, which will be re-used for the ':' */
631         auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
632         if (!auth_data)
633             return FALSE;
634 
635         WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
636         auth_data[userlen] = ':';
637         WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
638 
639         pAuthInfo->auth_data = auth_data;
640         pAuthInfo->auth_data_len = userlen + 1 + passlen;
641         pAuthInfo->finished = TRUE;
642 
643         return TRUE;
644     }
645     else
646     {
647         LPCWSTR pszAuthData;
648         SecBufferDesc out_desc, in_desc;
649         SecBuffer out, in;
650         unsigned char *buffer;
651         ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
652             ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
653 
654         in.BufferType = SECBUFFER_TOKEN;
655         in.cbBuffer = 0;
656         in.pvBuffer = NULL;
657 
658         in_desc.ulVersion = 0;
659         in_desc.cBuffers = 1;
660         in_desc.pBuffers = &in;
661 
662         pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
663         if (*pszAuthData == ' ')
664         {
665             pszAuthData++;
666             in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
667             in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
668             HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
669         }
670 
671         buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
672 
673         out.BufferType = SECBUFFER_TOKEN;
674         out.cbBuffer = pAuthInfo->max_token;
675         out.pvBuffer = buffer;
676 
677         out_desc.ulVersion = 0;
678         out_desc.cBuffers = 1;
679         out_desc.pBuffers = &out;
680 
681         sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
682                                                 first ? NULL : &pAuthInfo->ctx,
683                                                 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
684                                                 context_req, 0, SECURITY_NETWORK_DREP,
685                                                 in.pvBuffer ? &in_desc : NULL,
686                                                 0, &pAuthInfo->ctx, &out_desc,
687                                                 &pAuthInfo->attr, &pAuthInfo->exp);
688         if (sec_status == SEC_E_OK)
689         {
690             pAuthInfo->finished = TRUE;
691             pAuthInfo->auth_data = out.pvBuffer;
692             pAuthInfo->auth_data_len = out.cbBuffer;
693             TRACE("sending last auth packet\n");
694         }
695         else if (sec_status == SEC_I_CONTINUE_NEEDED)
696         {
697             pAuthInfo->auth_data = out.pvBuffer;
698             pAuthInfo->auth_data_len = out.cbBuffer;
699             TRACE("sending next auth packet\n");
700         }
701         else
702         {
703             ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
704             pAuthInfo->finished = TRUE;
705             HeapFree(GetProcessHeap(), 0, out.pvBuffer);
706             return FALSE;
707         }
708     }
709 
710     return TRUE;
711 }
712 
713 /***********************************************************************
714  *           HTTP_HttpAddRequestHeadersW (internal)
715  */
716 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
717         LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
718 {
719     LPWSTR lpszStart;
720     LPWSTR lpszEnd;
721     LPWSTR buffer;
722     BOOL bSuccess = FALSE;
723     DWORD len;
724 
725     TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
726 
727     if( dwHeaderLength == ~0U )
728         len = strlenW(lpszHeader);
729     else
730         len = dwHeaderLength;
731     buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
732     lstrcpynW( buffer, lpszHeader, len + 1);
733 
734     lpszStart = buffer;
735 
736     do
737     {
738         LPWSTR * pFieldAndValue;
739 
740         lpszEnd = lpszStart;
741 
742         while (*lpszEnd != '\0')
743         {
744             if (*lpszEnd == '\r' || *lpszEnd == '\n')
745                  break;
746             lpszEnd++;
747         }
748 
749         if (*lpszStart == '\0')
750             break;
751 
752         if (*lpszEnd == '\r' || *lpszEnd == '\n')
753         {
754             *lpszEnd = '\0';
755             lpszEnd++; /* Jump over newline */
756         }
757         TRACE("interpreting header %s\n", debugstr_w(lpszStart));
758         if (*lpszStart == '\0')
759         {
760             /* Skip 0-length headers */
761             lpszStart = lpszEnd;
762             bSuccess = TRUE;
763             continue;
764         }
765         pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
766         if (pFieldAndValue)
767         {
768             bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
769             if (bSuccess)
770                 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
771                     pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
772             HTTP_FreeTokens(pFieldAndValue);
773         }
774 
775         lpszStart = lpszEnd;
776     } while (bSuccess);
777 
778     HeapFree(GetProcessHeap(), 0, buffer);
779 
780     return bSuccess;
781 }
782 
783 /***********************************************************************
784  *           HttpAddRequestHeadersW (WININET.@)
785  *
786  * Adds one or more HTTP header to the request handler
787  *
788  * NOTE
789  * On Windows if dwHeaderLength includes the trailing '\0', then
790  * HttpAddRequestHeadersW() adds it too. However this results in an
791  * invalid Http header which is rejected by some servers so we probably
792  * don't need to match Windows on that point.
793  *
794  * RETURNS
795  *    TRUE  on success
796  *    FALSE on failure
797  *
798  */
799 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
800         LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
801 {
802     BOOL bSuccess = FALSE;
803     http_request_t *lpwhr;
804 
805     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
806 
807     if (!lpszHeader) 
808       return TRUE;
809 
810     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
811     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
812     {
813         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
814         goto lend;
815     }
816     bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
817 lend:
818     if( lpwhr )
819         WININET_Release( &lpwhr->hdr );
820 
821     return bSuccess;
822 }
823 
824 /***********************************************************************
825  *           HttpAddRequestHeadersA (WININET.@)
826  *
827  * Adds one or more HTTP header to the request handler
828  *
829  * RETURNS
830  *    TRUE  on success
831  *    FALSE on failure
832  *
833  */
834 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
835         LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
836 {
837     DWORD len;
838     LPWSTR hdr;
839     BOOL r;
840 
841     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
842 
843     len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
844     hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
845     MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
846     if( dwHeaderLength != ~0U )
847         dwHeaderLength = len;
848 
849     r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
850 
851     HeapFree( GetProcessHeap(), 0, hdr );
852 
853     return r;
854 }
855 
856 /***********************************************************************
857  *           HttpEndRequestA (WININET.@)
858  *
859  * Ends an HTTP request that was started by HttpSendRequestEx
860  *
861  * RETURNS
862  *    TRUE      if successful
863  *    FALSE     on failure
864  *
865  */
866 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest, 
867         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
868 {
869     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
870 
871     if (lpBuffersOut)
872     {
873         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
874         return FALSE;
875     }
876 
877     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
878 }
879 
880 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
881 {
882     BOOL rc = FALSE;
883     INT responseLen;
884     DWORD dwBufferSize;
885     INTERNET_ASYNC_RESULT iar;
886 
887     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
888                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
889 
890     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
891     if (responseLen)
892         rc = TRUE;
893 
894     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
895                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
896 
897     /* process cookies here. Is this right? */
898     HTTP_ProcessCookies(lpwhr);
899 
900     if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
901 
902     if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
903     {
904         DWORD dwCode,dwCodeLength = sizeof(DWORD);
905         if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
906             (dwCode == 302 || dwCode == 301 || dwCode == 303))
907         {
908             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
909             dwBufferSize=sizeof(szNewLocation);
910             if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
911             {
912                 /* redirects are always GETs */
913                 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
914                 lpwhr->lpszVerb = heap_strdupW(szGET);
915                 HTTP_DrainContent(lpwhr);
916                 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
917                 {
918                     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
919                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
920                     rc = HTTP_HandleRedirect(lpwhr, new_url);
921                     if (rc)
922                         rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
923                     HeapFree( GetProcessHeap(), 0, new_url );
924                 }
925             }
926         }
927     }
928 
929     iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
930     iar.dwError = rc ? 0 : INTERNET_GetLastError();
931 
932     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
933                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
934                           sizeof(INTERNET_ASYNC_RESULT));
935     return rc;
936 }
937 
938 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
939 {
940     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
941     http_request_t *lpwhr = (http_request_t*)work->hdr;
942 
943     TRACE("%p\n", lpwhr);
944 
945     HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
946 }
947 
948 /***********************************************************************
949  *           HttpEndRequestW (WININET.@)
950  *
951  * Ends an HTTP request that was started by HttpSendRequestEx
952  *
953  * RETURNS
954  *    TRUE      if successful
955  *    FALSE     on failure
956  *
957  */
958 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest, 
959         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
960 {
961     BOOL rc = FALSE;
962     http_request_t *lpwhr;
963 
964     TRACE("-->\n");
965 
966     if (lpBuffersOut)
967     {
968         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
969         return FALSE;
970     }
971 
972     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
973 
974     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
975     {
976         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977         if (lpwhr)
978             WININET_Release( &lpwhr->hdr );
979         return FALSE;
980     }
981     lpwhr->hdr.dwFlags |= dwFlags;
982 
983     if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
984     {
985         WORKREQUEST work;
986         struct WORKREQ_HTTPENDREQUESTW *request;
987 
988         work.asyncproc = AsyncHttpEndRequestProc;
989         work.hdr = WININET_AddRef( &lpwhr->hdr );
990 
991         request = &work.u.HttpEndRequestW;
992         request->dwFlags = dwFlags;
993         request->dwContext = dwContext;
994 
995         INTERNET_AsyncCall(&work);
996         INTERNET_SetLastError(ERROR_IO_PENDING);
997     }
998     else
999         rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1000 
1001     WININET_Release( &lpwhr->hdr );
1002     TRACE("%i <--\n",rc);
1003     return rc;
1004 }
1005 
1006 /***********************************************************************
1007  *           HttpOpenRequestW (WININET.@)
1008  *
1009  * Open a HTTP request handle
1010  *
1011  * RETURNS
1012  *    HINTERNET  a HTTP request handle on success
1013  *    NULL       on failure
1014  *
1015  */
1016 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1017         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1018         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1019         DWORD dwFlags, DWORD_PTR dwContext)
1020 {
1021     http_session_t *lpwhs;
1022     HINTERNET handle = NULL;
1023 
1024     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1025           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1026           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1027           dwFlags, dwContext);
1028     if(lpszAcceptTypes!=NULL)
1029     {
1030         int i;
1031         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1032             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1033     }    
1034 
1035     lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1036     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
1037     {
1038         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1039         goto lend;
1040     }
1041 
1042     /*
1043      * My tests seem to show that the windows version does not
1044      * become asynchronous until after this point. And anyhow
1045      * if this call was asynchronous then how would you get the
1046      * necessary HINTERNET pointer returned by this function.
1047      *
1048      */
1049     handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1050                                    lpszVersion, lpszReferrer, lpszAcceptTypes,
1051                                    dwFlags, dwContext);
1052 lend:
1053     if( lpwhs )
1054         WININET_Release( &lpwhs->hdr );
1055     TRACE("returning %p\n", handle);
1056     return handle;
1057 }
1058 
1059 
1060 /***********************************************************************
1061  *           HttpOpenRequestA (WININET.@)
1062  *
1063  * Open a HTTP request handle
1064  *
1065  * RETURNS
1066  *    HINTERNET  a HTTP request handle on success
1067  *    NULL       on failure
1068  *
1069  */
1070 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1071         LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1072         LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1073         DWORD dwFlags, DWORD_PTR dwContext)
1074 {
1075     LPWSTR szVerb = NULL, szObjectName = NULL;
1076     LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1077     INT acceptTypesCount;
1078     HINTERNET rc = FALSE;
1079     LPCSTR *types;
1080 
1081     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1082           debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1083           debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1084           dwFlags, dwContext);
1085 
1086     if (lpszVerb)
1087     {
1088         szVerb = heap_strdupAtoW(lpszVerb);
1089         if ( !szVerb )
1090             goto end;
1091     }
1092 
1093     if (lpszObjectName)
1094     {
1095         szObjectName = heap_strdupAtoW(lpszObjectName);
1096         if ( !szObjectName )
1097             goto end;
1098     }
1099 
1100     if (lpszVersion)
1101     {
1102         szVersion = heap_strdupAtoW(lpszVersion);
1103         if ( !szVersion )
1104             goto end;
1105     }
1106 
1107     if (lpszReferrer)
1108     {
1109         szReferrer = heap_strdupAtoW(lpszReferrer);
1110         if ( !szReferrer )
1111             goto end;
1112     }
1113 
1114     if (lpszAcceptTypes)
1115     {
1116         acceptTypesCount = 0;
1117         types = lpszAcceptTypes;
1118         while (*types)
1119         {
1120             __TRY
1121             {
1122                 /* find out how many there are */
1123                 if (*types && **types)
1124                 {
1125                     TRACE("accept type: %s\n", debugstr_a(*types));
1126                     acceptTypesCount++;
1127                 }
1128             }
1129             __EXCEPT_PAGE_FAULT
1130             {
1131                 WARN("invalid accept type pointer\n");
1132             }
1133             __ENDTRY;
1134             types++;
1135         }
1136         szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1137         if (!szAcceptTypes) goto end;
1138 
1139         acceptTypesCount = 0;
1140         types = lpszAcceptTypes;
1141         while (*types)
1142         {
1143             __TRY
1144             {
1145                 if (*types && **types)
1146                     szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1147             }
1148             __EXCEPT_PAGE_FAULT
1149             {
1150                 /* ignore invalid pointer */
1151             }
1152             __ENDTRY;
1153             types++;
1154         }
1155         szAcceptTypes[acceptTypesCount] = NULL;
1156     }
1157 
1158     rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1159                           szVersion, szReferrer,
1160                           (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1161 
1162 end:
1163     if (szAcceptTypes)
1164     {
1165         acceptTypesCount = 0;
1166         while (szAcceptTypes[acceptTypesCount])
1167         {
1168             HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1169             acceptTypesCount++;
1170         }
1171         HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1172     }
1173     HeapFree(GetProcessHeap(), 0, szReferrer);
1174     HeapFree(GetProcessHeap(), 0, szVersion);
1175     HeapFree(GetProcessHeap(), 0, szObjectName);
1176     HeapFree(GetProcessHeap(), 0, szVerb);
1177 
1178     return rc;
1179 }
1180 
1181 /***********************************************************************
1182  *  HTTP_EncodeBase64
1183  */
1184 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1185 {
1186     UINT n = 0, x;
1187     static const CHAR HTTP_Base64Enc[] =
1188         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1189 
1190     while( len > 0 )
1191     {
1192         /* first 6 bits, all from bin[0] */
1193         base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1194         x = (bin[0] & 3) << 4;
1195 
1196         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1197         if( len == 1 )
1198         {
1199             base64[n++] = HTTP_Base64Enc[x];
1200             base64[n++] = '=';
1201             base64[n++] = '=';
1202             break;
1203         }
1204         base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1205         x = ( bin[1] & 0x0f ) << 2;
1206 
1207         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1208         if( len == 2 )
1209         {
1210             base64[n++] = HTTP_Base64Enc[x];
1211             base64[n++] = '=';
1212             break;
1213         }
1214         base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1215 
1216         /* last 6 bits, all from bin [2] */
1217         base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1218         bin += 3;
1219         len -= 3;
1220     }
1221     base64[n] = 0;
1222     return n;
1223 }
1224 
1225 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1226                ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1227                ((x) >= '' && (x) <= '9') ? (x) - '' + 52 : \
1228                ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1229 static const signed char HTTP_Base64Dec[256] =
1230 {
1231     CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1232     CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1233     CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1234     CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1235     CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1236     CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1237     CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1238     CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1239     CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1240     CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1241     CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1242     CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1243     CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1244     CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1245     CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1246     CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1247     CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1248     CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1249     CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1250     CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1251     CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1252     CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1253     CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1254     CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1255     CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1256     CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1257 };
1258 #undef CH
1259 
1260 /***********************************************************************
1261  *  HTTP_DecodeBase64
1262  */
1263 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1264 {
1265     unsigned int n = 0;
1266 
1267     while(*base64)
1268     {
1269         signed char in[4];
1270 
1271         if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1272             ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1273             base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1274             ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1275         {
1276             WARN("invalid base64: %s\n", debugstr_w(base64));
1277             return 0;
1278         }
1279         if (bin)
1280             bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1281         n++;
1282 
1283         if ((base64[2] == '=') && (base64[3] == '='))
1284             break;
1285         if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1286             ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1287         {
1288             WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1289             return 0;
1290         }
1291         if (bin)
1292             bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1293         n++;
1294 
1295         if (base64[3] == '=')
1296             break;
1297         if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1298             ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1299         {
1300             WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1301             return 0;
1302         }
1303         if (bin)
1304             bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1305         n++;
1306 
1307         base64 += 4;
1308     }
1309 
1310     return n;
1311 }
1312 
1313 /***********************************************************************
1314  *  HTTP_InsertAuthorization
1315  *
1316  *   Insert or delete the authorization field in the request header.
1317  */
1318 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1319 {
1320     if (pAuthInfo)
1321     {
1322         static const WCHAR wszSpace[] = {' ',0};
1323         static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1324         unsigned int len;
1325         WCHAR *authorization = NULL;
1326 
1327         if (pAuthInfo->auth_data_len)
1328         {
1329             /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1330             len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1331             authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1332             if (!authorization)
1333                 return FALSE;
1334 
1335             strcpyW(authorization, pAuthInfo->scheme);
1336             strcatW(authorization, wszSpace);
1337             HTTP_EncodeBase64(pAuthInfo->auth_data,
1338                               pAuthInfo->auth_data_len,
1339                               authorization+strlenW(authorization));
1340 
1341             /* clear the data as it isn't valid now that it has been sent to the
1342              * server, unless it's Basic authentication which doesn't do
1343              * connection tracking */
1344             if (strcmpiW(pAuthInfo->scheme, wszBasic))
1345             {
1346                 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1347                 pAuthInfo->auth_data = NULL;
1348                 pAuthInfo->auth_data_len = 0;
1349             }
1350         }
1351 
1352         TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1353 
1354         HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1355 
1356         HeapFree(GetProcessHeap(), 0, authorization);
1357     }
1358     return TRUE;
1359 }
1360 
1361 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1362 {
1363     WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1364     DWORD size;
1365 
1366     size = sizeof(new_location);
1367     if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1368     {
1369         if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1370         strcpyW( url, new_location );
1371     }
1372     else
1373     {
1374         static const WCHAR slash[] = { '/',0 };
1375         static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1376         static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1377         http_session_t *session = req->lpHttpSession;
1378 
1379         size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1380         size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1381 
1382         if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1383 
1384         if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1385             sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1386         else
1387             sprintfW( url, format, session->lpszHostName, session->nHostPort );
1388         if (req->lpszPath[0] != '/') strcatW( url, slash );
1389         strcatW( url, req->lpszPath );
1390     }
1391     TRACE("url=%s\n", debugstr_w(url));
1392     return url;
1393 }
1394 
1395 /***********************************************************************
1396  *           HTTP_DealWithProxy
1397  */
1398 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1399 {
1400     WCHAR buf[MAXHOSTNAME];
1401     WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1402     static WCHAR szNul[] = { 0 };
1403     URL_COMPONENTSW UrlComponents;
1404     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1405     static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1406 
1407     memset( &UrlComponents, 0, sizeof UrlComponents );
1408     UrlComponents.dwStructSize = sizeof UrlComponents;
1409     UrlComponents.lpszHostName = buf;
1410     UrlComponents.dwHostNameLength = MAXHOSTNAME;
1411 
1412     if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1413                                  hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1414         sprintfW(proxy, szFormat, hIC->lpszProxy);
1415     else
1416         strcpyW(proxy, hIC->lpszProxy);
1417     if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1418         return FALSE;
1419     if( UrlComponents.dwHostNameLength == 0 )
1420         return FALSE;
1421 
1422     if( !lpwhr->lpszPath )
1423         lpwhr->lpszPath = szNul;
1424 
1425     if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1426         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1427 
1428     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1429     lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1430     lpwhs->nServerPort = UrlComponents.nPort;
1431 
1432     TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1433     return TRUE;
1434 }
1435 
1436 #ifndef INET6_ADDRSTRLEN
1437 #define INET6_ADDRSTRLEN 46
1438 #endif
1439 
1440 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1441 {
1442     char szaddr[INET6_ADDRSTRLEN];
1443     http_session_t *lpwhs = lpwhr->lpHttpSession;
1444     const void *addr;
1445 
1446     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1447                           INTERNET_STATUS_RESOLVING_NAME,
1448                           lpwhs->lpszServerName,
1449                           strlenW(lpwhs->lpszServerName)+1);
1450 
1451     lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1452     if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1453                     (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1454     {
1455         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1456         return FALSE;
1457     }
1458 
1459     switch (lpwhs->socketAddress.ss_family)
1460     {
1461     case AF_INET:
1462         addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1463         break;
1464     case AF_INET6:
1465         addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1466         break;
1467     default:
1468         WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1469         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1470         return FALSE;
1471     }
1472     inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1473     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1474                           INTERNET_STATUS_NAME_RESOLVED,
1475                           szaddr, strlen(szaddr)+1);
1476 
1477     TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1478     return TRUE;
1479 }
1480 
1481 
1482 /***********************************************************************
1483  *           HTTPREQ_Destroy (internal)
1484  *
1485  * Deallocate request handle
1486  *
1487  */
1488 static void HTTPREQ_Destroy(object_header_t *hdr)
1489 {
1490     http_request_t *lpwhr = (http_request_t*) hdr;
1491     DWORD i;
1492 
1493     TRACE("\n");
1494 
1495     if(lpwhr->hCacheFile)
1496         CloseHandle(lpwhr->hCacheFile);
1497 
1498     HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1499 
1500     DeleteCriticalSection( &lpwhr->read_section );
1501     WININET_Release(&lpwhr->lpHttpSession->hdr);
1502 
1503     if (lpwhr->pAuthInfo)
1504     {
1505         if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1506             DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1507         if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1508             FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1509 
1510         HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1511         HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1512         HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1513         lpwhr->pAuthInfo = NULL;
1514     }
1515 
1516     if (lpwhr->pProxyAuthInfo)
1517     {
1518         if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1519             DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1520         if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1521             FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1522 
1523         HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1524         HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1525         HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1526         lpwhr->pProxyAuthInfo = NULL;
1527     }
1528 
1529     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1530     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1531     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1532     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1533     HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1534 
1535     for (i = 0; i < lpwhr->nCustHeaders; i++)
1536     {
1537         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1538         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1539     }
1540 
1541     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1542     HeapFree(GetProcessHeap(), 0, lpwhr);
1543 }
1544 
1545 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1546 {
1547     http_request_t *lpwhr = (http_request_t*) hdr;
1548 
1549     TRACE("%p\n",lpwhr);
1550 
1551 #ifdef HAVE_ZLIB
1552     if(lpwhr->gzip_stream) {
1553         inflateEnd(&lpwhr->gzip_stream->zstream);
1554         HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1555         lpwhr->gzip_stream = NULL;
1556     }
1557 #endif
1558 
1559     if (!NETCON_connected(&lpwhr->netConnection))
1560         return;
1561 
1562     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1563                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1564 
1565     NETCON_close(&lpwhr->netConnection);
1566 
1567     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1568                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1569 }
1570 
1571 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1572 {
1573     LPHTTPHEADERW host_header;
1574 
1575     static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1576 
1577     host_header = HTTP_GetHeader(req, hostW);
1578     if(!host_header)
1579         return FALSE;
1580 
1581     sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1582     return TRUE;
1583 }
1584 
1585 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1586 {
1587     http_request_t *req = (http_request_t*)hdr;
1588 
1589     switch(option) {
1590     case INTERNET_OPTION_SECURITY_FLAGS:
1591     {
1592         http_session_t *lpwhs;
1593         lpwhs = req->lpHttpSession;
1594 
1595         if (*size < sizeof(ULONG))
1596             return ERROR_INSUFFICIENT_BUFFER;
1597 
1598         *size = sizeof(DWORD);
1599         if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1600             *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1601         else
1602             *(DWORD*)buffer = 0;
1603         FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1604         return ERROR_SUCCESS;
1605     }
1606 
1607     case INTERNET_OPTION_HANDLE_TYPE:
1608         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1609 
1610         if (*size < sizeof(ULONG))
1611             return ERROR_INSUFFICIENT_BUFFER;
1612 
1613         *size = sizeof(DWORD);
1614         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1615         return ERROR_SUCCESS;
1616 
1617     case INTERNET_OPTION_URL: {
1618         WCHAR url[INTERNET_MAX_URL_LENGTH];
1619         HTTPHEADERW *host;
1620         DWORD len;
1621         WCHAR *pch;
1622 
1623         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1624 
1625         TRACE("INTERNET_OPTION_URL\n");
1626 
1627         host = HTTP_GetHeader(req, hostW);
1628         strcpyW(url, httpW);
1629         strcatW(url, host->lpszValue);
1630         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1631             *pch = 0;
1632         strcatW(url, req->lpszPath);
1633 
1634         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1635 
1636         if(unicode) {
1637             len = (strlenW(url)+1) * sizeof(WCHAR);
1638             if(*size < len)
1639                 return ERROR_INSUFFICIENT_BUFFER;
1640 
1641             *size = len;
1642             strcpyW(buffer, url);
1643             return ERROR_SUCCESS;
1644         }else {
1645             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1646             if(len > *size)
1647                 return ERROR_INSUFFICIENT_BUFFER;
1648 
1649             *size = len;
1650             return ERROR_SUCCESS;
1651         }
1652     }
1653 
1654     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1655         INTERNET_CACHE_ENTRY_INFOW *info;
1656         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1657         WCHAR url[INTERNET_MAX_URL_LENGTH];
1658         DWORD nbytes, error;
1659         BOOL ret;
1660 
1661         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1662 
1663         if (*size < sizeof(*ts))
1664         {
1665             *size = sizeof(*ts);
1666             return ERROR_INSUFFICIENT_BUFFER;
1667         }
1668         nbytes = 0;
1669         HTTP_GetRequestURL(req, url);
1670         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1671         error = GetLastError();
1672         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1673         {
1674             if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1675                 return ERROR_OUTOFMEMORY;
1676 
1677             GetUrlCacheEntryInfoW(url, info, &nbytes);
1678 
1679             ts->ftExpires = info->ExpireTime;
1680             ts->ftLastModified = info->LastModifiedTime;
1681 
1682             HeapFree(GetProcessHeap(), 0, info);
1683             *size = sizeof(*ts);
1684             return ERROR_SUCCESS;
1685         }
1686         return error;
1687     }
1688 
1689     case INTERNET_OPTION_DATAFILE_NAME: {
1690         DWORD req_size;
1691 
1692         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1693 
1694         if(!req->lpszCacheFile) {
1695             *size = 0;
1696             return ERROR_INTERNET_ITEM_NOT_FOUND;
1697         }
1698 
1699         if(unicode) {
1700             req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1701             if(*size < req_size)
1702                 return ERROR_INSUFFICIENT_BUFFER;
1703 
1704             *size = req_size;
1705             memcpy(buffer, req->lpszCacheFile, *size);
1706             return ERROR_SUCCESS;
1707         }else {
1708             req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1709             if (req_size > *size)
1710                 return ERROR_INSUFFICIENT_BUFFER;
1711 
1712             *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1713                     -1, buffer, *size, NULL, NULL);
1714             return ERROR_SUCCESS;
1715         }
1716     }
1717 
1718     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1719         PCCERT_CONTEXT context;
1720 
1721         if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1722             *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1723             return ERROR_INSUFFICIENT_BUFFER;
1724         }
1725 
1726         context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1727         if(context) {
1728             INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1729             DWORD len;
1730 
1731             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1732             info->ftExpiry = context->pCertInfo->NotAfter;
1733             info->ftStart = context->pCertInfo->NotBefore;
1734             if(unicode) {
1735                 len = CertNameToStrW(context->dwCertEncodingType,
1736                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1737                 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1738                 if(info->lpszSubjectInfo)
1739                     CertNameToStrW(context->dwCertEncodingType,
1740                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1741                              info->lpszSubjectInfo, len);
1742                 len = CertNameToStrW(context->dwCertEncodingType,
1743                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1744                 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1745                 if (info->lpszIssuerInfo)
1746                     CertNameToStrW(context->dwCertEncodingType,
1747                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1748                              info->lpszIssuerInfo, len);
1749             }else {
1750                 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1751 
1752                 len = CertNameToStrA(context->dwCertEncodingType,
1753                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1754                 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1755                 if(infoA->lpszSubjectInfo)
1756                     CertNameToStrA(context->dwCertEncodingType,
1757                              &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1758                              infoA->lpszSubjectInfo, len);
1759                 len = CertNameToStrA(context->dwCertEncodingType,
1760                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1761                 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1762                 if(infoA->lpszIssuerInfo)
1763                     CertNameToStrA(context->dwCertEncodingType,
1764                              &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1765                              infoA->lpszIssuerInfo, len);
1766             }
1767 
1768             /*
1769              * Contrary to MSDN, these do not appear to be set.
1770              * lpszProtocolName
1771              * lpszSignatureAlgName
1772              * lpszEncryptionAlgName
1773              * dwKeySize
1774              */
1775             CertFreeCertificateContext(context);
1776             return ERROR_SUCCESS;
1777         }
1778     }
1779     }
1780 
1781     return INET_QueryOption(option, buffer, size, unicode);
1782 }
1783 
1784 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1785 {
1786     http_request_t *req = (http_request_t*)hdr;
1787 
1788     switch(option) {
1789     case INTERNET_OPTION_SEND_TIMEOUT:
1790     case INTERNET_OPTION_RECEIVE_TIMEOUT:
1791         TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1792 
1793         if (size != sizeof(DWORD))
1794             return ERROR_INVALID_PARAMETER;
1795 
1796         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1797                     *(DWORD*)buffer);
1798 
1799     case INTERNET_OPTION_USERNAME:
1800         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1801         if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1802         return ERROR_SUCCESS;
1803 
1804     case INTERNET_OPTION_PASSWORD:
1805         HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1806         if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1807         return ERROR_SUCCESS;
1808     case INTERNET_OPTION_HTTP_DECODING:
1809         if(size != sizeof(BOOL))
1810             return ERROR_INVALID_PARAMETER;
1811         req->decoding = *(BOOL*)buffer;
1812         return ERROR_SUCCESS;
1813     }
1814 
1815     return ERROR_INTERNET_INVALID_OPTION;
1816 }
1817 
1818 /* read some more data into the read buffer (the read section must be held) */
1819 static BOOL read_more_data( http_request_t *req, int maxlen )
1820 {
1821     int len;
1822 
1823     if (req->read_pos)
1824     {
1825         /* move existing data to the start of the buffer */
1826         if(req->read_size)
1827             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1828         req->read_pos = 0;
1829     }
1830 
1831     if (maxlen == -1) maxlen = sizeof(req->read_buf);
1832 
1833     if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1834                      maxlen - req->read_size, 0, &len ))
1835         return FALSE;
1836 
1837     req->read_size += len;
1838     return TRUE;
1839 }
1840 
1841 /* remove some amount of data from the read buffer (the read section must be held) */
1842 static void remove_data( http_request_t *req, int count )
1843 {
1844     if (!(req->read_size -= count)) req->read_pos = 0;
1845     else req->read_pos += count;
1846 }
1847 
1848 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1849 {
1850     int count, bytes_read, pos = 0;
1851 
1852     EnterCriticalSection( &req->read_section );
1853     for (;;)
1854     {
1855         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1856 
1857         if (eol)
1858         {
1859             count = eol - (req->read_buf + req->read_pos);
1860             bytes_read = count + 1;
1861         }
1862         else count = bytes_read = req->read_size;
1863 
1864         count = min( count, *len - pos );
1865         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1866         pos += count;
1867         remove_data( req, bytes_read );
1868         if (eol) break;
1869 
1870         if (!read_more_data( req, -1 ) || !req->read_size)
1871         {
1872             *len = 0;
1873             TRACE( "returning empty string\n" );
1874             LeaveCriticalSection( &req->read_section );
1875             return FALSE;
1876         }
1877     }
1878     LeaveCriticalSection( &req->read_section );
1879 
1880     if (pos < *len)
1881     {
1882         if (pos && buffer[pos - 1] == '\r') pos--;
1883         *len = pos + 1;
1884     }
1885     buffer[*len - 1] = 0;
1886     TRACE( "returning %s\n", debugstr_a(buffer));
1887     return TRUE;
1888 }
1889 
1890 /* discard data contents until we reach end of line (the read section must be held) */
1891 static BOOL discard_eol( http_request_t *req )
1892 {
1893     do
1894     {
1895         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1896         if (eol)
1897         {
1898             remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1899             break;
1900         }
1901         req->read_pos = req->read_size = 0;  /* discard everything */
1902         if (!read_more_data( req, -1 )) return FALSE;
1903     } while (req->read_size);
1904     return TRUE;
1905 }
1906 
1907 /* read the size of the next chunk (the read section must be held) */
1908 static BOOL start_next_chunk( http_request_t *req )
1909 {
1910     DWORD chunk_size = 0;
1911 
1912     if (!req->dwContentLength) return TRUE;
1913     if (req->dwContentLength == req->dwContentRead)
1914     {
1915         /* read terminator for the previous chunk */
1916         if (!discard_eol( req )) return FALSE;
1917         req->dwContentLength = ~0u;
1918         req->dwContentRead = 0;
1919     }
1920     for (;;)
1921     {
1922         while (req->read_size)
1923         {
1924             char ch = req->read_buf[req->read_pos];
1925             if (ch >= '' && ch <= '9') chunk_size = chunk_size * 16 + ch - '';
1926             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1927             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1928             else if (ch == ';' || ch == '\r' || ch == '\n')
1929             {
1930                 TRACE( "reading %u byte chunk\n", chunk_size );
1931                 req->dwContentLength = chunk_size;
1932                 req->dwContentRead = 0;
1933                 if (!discard_eol( req )) return FALSE;
1934                 return TRUE;
1935             }
1936             remove_data( req, 1 );
1937         }
1938         if (!read_more_data( req, -1 )) return FALSE;
1939         if (!req->read_size)
1940         {
1941             req->dwContentLength = req->dwContentRead = 0;
1942             return TRUE;
1943         }
1944     }
1945 }
1946 
1947 /* check if we have reached the end of the data to read (the read section must be held) */
1948 static BOOL end_of_read_data( http_request_t *req )
1949 {
1950     if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1951     if (req->read_chunked) return (req->dwContentLength == 0);
1952     if (req->dwContentLength == ~0u) return FALSE;
1953     return (req->dwContentLength == req->dwContentRead);
1954 }
1955 
1956 /* fetch some more data into the read buffer (the read section must be held) */
1957 static BOOL refill_buffer( http_request_t *req )
1958 {
1959     int len = sizeof(req->read_buf);
1960 
1961     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1962     {
1963         if (!start_next_chunk( req )) return FALSE;
1964     }
1965 
1966     if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1967     if (len <= req->read_size) return TRUE;
1968 
1969     if (!read_more_data( req, len )) return FALSE;
1970     if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1971     return TRUE;
1972 }
1973 
1974 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1975 {
1976     DWORD ret = ERROR_SUCCESS;
1977     int read = 0;
1978 
1979 #ifdef HAVE_ZLIB
1980     z_stream *zstream = &req->gzip_stream->zstream;
1981     int zres;
1982 
1983     while(read < size && !req->gzip_stream->end_of_data) {
1984         if(!req->read_size) {
1985             if(!sync || !refill_buffer(req))
1986                 break;
1987         }
1988 
1989         zstream->next_in = req->read_buf+req->read_pos;
1990         zstream->avail_in = req->read_size;
1991         zstream->next_out = buf+read;
1992         zstream->avail_out = size-read;
1993         zres = inflate(zstream, Z_FULL_FLUSH);
1994         read = size - zstream->avail_out;
1995         remove_data(req, req->read_size-zstream->avail_in);
1996         if(zres == Z_STREAM_END) {
1997             TRACE("end of data\n");
1998             req->gzip_stream->end_of_data = TRUE;
1999         }else if(zres != Z_OK) {
2000             WARN("inflate failed %d\n", zres);
2001             if(!read)
2002                 ret = ERROR_INTERNET_DECODING_FAILED;
2003             break;
2004         }
2005     }
2006 #endif
2007 
2008     *read_ret = read;
2009     return ret;
2010 }
2011 
2012 static void refill_gzip_buffer(http_request_t *req)
2013 {
2014     DWORD res;
2015     int len;
2016 
2017     if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2018         return;
2019 
2020     if(req->gzip_stream->buf_pos) {
2021         if(req->gzip_stream->buf_size)
2022             memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2023         req->gzip_stream->buf_pos = 0;
2024     }
2025 
2026     res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2027             sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2028     if(res == ERROR_SUCCESS)
2029         req->gzip_stream->buf_size += len;
2030 }
2031 
2032 /* return the size of data available to be read immediately (the read section must be held) */
2033 static DWORD get_avail_data( http_request_t *req )
2034 {
2035     if (req->gzip_stream) {
2036         refill_gzip_buffer(req);
2037         return req->gzip_stream->buf_size;
2038     }
2039     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2040         return 0;
2041     return min( req->read_size, req->dwContentLength - req->dwContentRead );
2042 }
2043 
2044 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2045 {
2046     INTERNET_ASYNC_RESULT iar;
2047 
2048     TRACE("%p\n", req);
2049 
2050     EnterCriticalSection( &req->read_section );
2051     if (refill_buffer( req )) {
2052         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2053         iar.dwError = first_notif ? 0 : get_avail_data(req);
2054     }else {
2055         iar.dwResult = 0;
2056         iar.dwError = INTERNET_GetLastError();
2057     }
2058     LeaveCriticalSection( &req->read_section );
2059 
2060     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2061                           sizeof(INTERNET_ASYNC_RESULT));
2062 }
2063 
2064 /* read data from the http connection (the read section must be held) */
2065 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2066 {
2067     BOOL finished_reading = FALSE;
2068     int len, bytes_read = 0;
2069     DWORD ret = ERROR_SUCCESS;
2070 
2071     EnterCriticalSection( &req->read_section );
2072 
2073     if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2074     {
2075         if (!start_next_chunk( req )) goto done;
2076     }
2077 
2078     if(req->gzip_stream) {
2079         if(req->gzip_stream->buf_size) {
2080             bytes_read = min(req->gzip_stream->buf_size, size);
2081             memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2082             req->gzip_stream->buf_pos += bytes_read;
2083             req->gzip_stream->buf_size -= bytes_read;
2084         }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2085             refill_buffer(req);
2086         }
2087 
2088         if(size > bytes_read) {
2089             ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2090             if(ret == ERROR_SUCCESS)
2091                 bytes_read += len;
2092         }
2093 
2094         finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2095     }else {
2096         if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2097 
2098         if (req->read_size) {
2099             bytes_read = min( req->read_size, size );
2100             memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2101             remove_data( req, bytes_read );
2102         }
2103 
2104         if (size > bytes_read && (!bytes_read || sync)) {
2105             if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2106                              sync ? MSG_WAITALL : 0, &len))
2107                 bytes_read += len;
2108             /* always return success, even if the network layer returns an error */
2109         }
2110 
2111         finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2112     }
2113 done:
2114     req->dwContentRead += bytes_read;
2115     *read = bytes_read;
2116 
2117     TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2118     LeaveCriticalSection( &req->read_section );
2119 
2120     if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2121         BOOL res;
2122         DWORD dwBytesWritten;
2123 
2124         res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2125         if(!res)
2126             WARN("WriteFile failed: %u\n", GetLastError());
2127     }
2128 
2129     if(finished_reading)
2130         HTTP_FinishedReading(req);
2131 
2132     return ret;
2133 }
2134 
2135 
2136 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2137 {
2138     http_request_t *req = (http_request_t*)hdr;
2139     return HTTPREQ_Read(req, buffer, size, read, TRUE);
2140 }
2141 
2142 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2143 {
2144     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2145     http_request_t *req = (http_request_t*)workRequest->hdr;
2146     INTERNET_ASYNC_RESULT iar;
2147     DWORD res;
2148 
2149     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2150 
2151     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2152             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2153 
2154     iar.dwResult = res == ERROR_SUCCESS;
2155     iar.dwError = res;
2156 
2157     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2158                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2159                           sizeof(INTERNET_ASYNC_RESULT));
2160 }
2161 
2162 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2163         DWORD flags, DWORD_PTR context)
2164 {
2165     http_request_t *req = (http_request_t*)hdr;
2166     DWORD res;
2167 
2168     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2169         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2170 
2171     if (buffers->dwStructSize != sizeof(*buffers))
2172         return ERROR_INVALID_PARAMETER;
2173 
2174     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2175 
2176     if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2177     {
2178         WORKREQUEST workRequest;
2179 
2180         if (TryEnterCriticalSection( &req->read_section ))
2181         {
2182             if (get_avail_data(req))
2183             {
2184                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2185                                    &buffers->dwBufferLength, FALSE);
2186                 LeaveCriticalSection( &req->read_section );
2187                 goto done;
2188             }
2189             LeaveCriticalSection( &req->read_section );
2190         }
2191 
2192         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2193         workRequest.hdr = WININET_AddRef(&req->hdr);
2194         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2195 
2196         INTERNET_AsyncCall(&workRequest);
2197 
2198         return ERROR_IO_PENDING;
2199     }
2200 
2201     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2202             !(flags & IRF_NO_WAIT));
2203 
2204 done:
2205     if (res == ERROR_SUCCESS) {
2206         DWORD size = buffers->dwBufferLength;
2207         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2208                 &size, sizeof(size));
2209     }
2210 
2211     return res;
2212 }
2213 
2214 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2215 {
2216     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2217     http_request_t *req = (http_request_t*)workRequest->hdr;
2218     INTERNET_ASYNC_RESULT iar;
2219     DWORD res;
2220 
2221     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2222 
2223     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2224             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2225 
2226     iar.dwResult = res == ERROR_SUCCESS;
2227     iar.dwError = res;
2228 
2229     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2230                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2231                           sizeof(INTERNET_ASYNC_RESULT));
2232 }
2233 
2234 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2235         DWORD flags, DWORD_PTR context)
2236 {
2237 
2238     http_request_t *req = (http_request_t*)hdr;
2239     DWORD res;
2240 
2241     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2242         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2243 
2244     if (buffers->dwStructSize != sizeof(*buffers))
2245         return ERROR_INVALID_PARAMETER;
2246 
2247     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2248 
2249     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2250     {
2251         WORKREQUEST workRequest;
2252 
2253         if (TryEnterCriticalSection( &req->read_section ))
2254         {
2255             if (get_avail_data(req))
2256             {
2257                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2258                                    &buffers->dwBufferLength, FALSE);
2259                 LeaveCriticalSection( &req->read_section );
2260                 goto done;
2261             }
2262             LeaveCriticalSection( &req->read_section );
2263         }
2264 
2265         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2266         workRequest.hdr = WININET_AddRef(&req->hdr);
2267         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2268 
2269         INTERNET_AsyncCall(&workRequest);
2270 
2271         return ERROR_IO_PENDING;
2272     }
2273 
2274     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2275             !(flags & IRF_NO_WAIT));
2276 
2277 done:
2278     if (res == ERROR_SUCCESS) {
2279         DWORD size = buffers->dwBufferLength;
2280         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2281                 &size, sizeof(size));
2282     }
2283 
2284     return res;
2285 }
2286 
2287 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2288 {
2289     BOOL ret;
2290     http_request_t *lpwhr = (http_request_t*)hdr;
2291 
2292     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2293 
2294     *written = 0;
2295     if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2296         lpwhr->dwBytesWritten += *written;
2297 
2298     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2299     return ret;
2300 }
2301 
2302 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2303 {
2304     http_request_t *req = (http_request_t*)workRequest->hdr;
2305 
2306     HTTP_ReceiveRequestData(req, FALSE);
2307 }
2308 
2309 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2310 {
2311     http_request_t *req = (http_request_t*)hdr;
2312 
2313     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2314 
2315     if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2316     {
2317         WORKREQUEST workRequest;
2318 
2319         /* never wait, if we can't enter the section we queue an async request right away */
2320         if (TryEnterCriticalSection( &req->read_section ))
2321         {
2322             if ((*available = get_avail_data( req ))) goto done;
2323             if (end_of_read_data( req )) goto done;
2324             LeaveCriticalSection( &req->read_section );
2325         }
2326 
2327         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2328         workRequest.hdr = WININET_AddRef( &req->hdr );
2329 
2330         INTERNET_AsyncCall(&workRequest);
2331 
2332         return ERROR_IO_PENDING;
2333     }
2334 
2335     EnterCriticalSection( &req->read_section );
2336 
2337     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2338     {
2339         refill_buffer( req );
2340         *available = get_avail_data( req );
2341     }
2342 
2343 done:
2344     if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
2345     {
2346         DWORD extra;
2347         if (NETCON_query_data_available(&req->netConnection, &extra))
2348             *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2349     }
2350     LeaveCriticalSection( &req->read_section );
2351 
2352     TRACE( "returning %u\n", *available );
2353     return ERROR_SUCCESS;
2354 }
2355 
2356 static const object_vtbl_t HTTPREQVtbl = {
2357     HTTPREQ_Destroy,
2358     HTTPREQ_CloseConnection,
2359     HTTPREQ_QueryOption,
2360     HTTPREQ_SetOption,
2361     HTTPREQ_ReadFile,
2362     HTTPREQ_ReadFileExA,
2363     HTTPREQ_ReadFileExW,
2364     HTTPREQ_WriteFile,
2365     HTTPREQ_QueryDataAvailable,
2366     NULL
2367 };
2368 
2369 /***********************************************************************
2370  *           HTTP_HttpOpenRequestW (internal)
2371  *
2372  * Open a HTTP request handle
2373  *
2374  * RETURNS
2375  *    HINTERNET  a HTTP request handle on success
2376  *    NULL       on failure
2377  *
2378  */
2379 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2380         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2381         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2382         DWORD dwFlags, DWORD_PTR dwContext)
2383 {
2384     appinfo_t *hIC = NULL;
2385     http_request_t *lpwhr;
2386     LPWSTR lpszHostName = NULL;
2387     HINTERNET handle = NULL;
2388     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2389     DWORD len;
2390 
2391     TRACE("-->\n");
2392 
2393     assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2394     hIC = lpwhs->lpAppInfo;
2395 
2396     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2397     if (NULL == lpwhr)
2398     {
2399         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2400         goto lend;
2401     }
2402     lpwhr->hdr.htype = WH_HHTTPREQ;
2403     lpwhr->hdr.vtbl = &HTTPREQVtbl;
2404     lpwhr->hdr.dwFlags = dwFlags;
2405     lpwhr->hdr.dwContext = dwContext;
2406     lpwhr->hdr.refs = 1;
2407     lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2408     lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2409     lpwhr->dwContentLength = ~0u;
2410     InitializeCriticalSection( &lpwhr->read_section );
2411 
2412     WININET_AddRef( &lpwhs->hdr );
2413     lpwhr->lpHttpSession = lpwhs;
2414     list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2415 
2416     lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2417             (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2418     if (NULL == lpszHostName)
2419     {
2420         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2421         goto lend;
2422     }
2423 
2424     handle = WININET_AllocHandle( &lpwhr->hdr );
2425     if (NULL == handle)
2426     {
2427         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2428         goto lend;
2429     }
2430 
2431     if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2432     {
2433         InternetCloseHandle( handle );
2434         handle = NULL;
2435         goto lend;
2436     }
2437 
2438     if (lpszObjectName && *lpszObjectName) {
2439         HRESULT rc;
2440 
2441         len = 0;
2442         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2443         if (rc != E_POINTER)
2444             len = strlenW(lpszObjectName)+1;
2445         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2446         rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2447                    URL_ESCAPE_SPACES_ONLY);
2448         if (rc != S_OK)
2449         {
2450             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2451             strcpyW(lpwhr->lpszPath,lpszObjectName);
2452         }
2453     }else {
2454         static const WCHAR slashW[] = {'/',0};
2455 
2456         lpwhr->lpszPath = heap_strdupW(slashW);
2457     }
2458 
2459     if (lpszReferrer && *lpszReferrer)
2460         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2461 
2462     if (lpszAcceptTypes)
2463     {
2464         int i;
2465         for (i = 0; lpszAcceptTypes[i]; i++)
2466         {
2467             if (!*lpszAcceptTypes[i]) continue;
2468             HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2469                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2470                                HTTP_ADDHDR_FLAG_REQ |
2471                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2472         }
2473     }
2474 
2475     lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2476     lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2477 
2478     if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2479         lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2480         lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2481     {
2482         sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2483         HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2484                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2485     }
2486     else
2487         HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2488                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2489 
2490     if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2491         lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2492                         INTERNET_DEFAULT_HTTPS_PORT :
2493                         INTERNET_DEFAULT_HTTP_PORT);
2494 
2495     if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2496         lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2497                         INTERNET_DEFAULT_HTTPS_PORT :
2498                         INTERNET_DEFAULT_HTTP_PORT);
2499 
2500     if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2501         HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2502 
2503     INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2504                           INTERNET_STATUS_HANDLE_CREATED, &handle,
2505                           sizeof(handle));
2506 
2507 lend:
2508     HeapFree(GetProcessHeap(), 0, lpszHostName);
2509     if( lpwhr )
2510         WININET_Release( &lpwhr->hdr );
2511 
2512     TRACE("<-- %p (%p)\n", handle, lpwhr);
2513     return handle;
2514 }
2515 
2516 /* read any content returned by the server so that the connection can be
2517  * reused */
2518 static void HTTP_DrainContent(http_request_t *req)
2519 {
2520     DWORD bytes_read;
2521 
2522     if (!NETCON_connected(&req->netConnection)) return;
2523 
2524     if (req->dwContentLength == -1)
2525     {
2526         NETCON_close(&req->netConnection);
2527         return;
2528     }
2529 
2530     do
2531     {
2532         char buffer[2048];
2533         if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2534             return;
2535     } while (bytes_read);
2536 }
2537 
2538 static const LPCWSTR header_lookup[] = {
2539     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
2540     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
2541     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2542     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
2543     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2544     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
2545     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
2546     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
2547     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
2548     szDate,                     /* HTTP_QUERY_DATE = 9 */
2549     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
2550     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
2551     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
2552     szURI,                      /* HTTP_QUERY_URI = 13 */
2553     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
2554     NULL,                       /* HTTP_QUERY_COST = 15 */
2555     NULL,                       /* HTTP_QUERY_LINK = 16 */
2556     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
2557     NULL,                       /* HTTP_QUERY_VERSION = 18 */
2558     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
2559     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
2560     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
2561     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2562     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
2563     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
2564     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2565     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2566     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2567     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
2568     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2569     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
2570     NULL,                       /* HTTP_QUERY_FROM = 31 */
2571     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2572     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
2573     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
2574     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
2575     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
2576     szServer,                   /* HTTP_QUERY_SERVER = 37 */
2577     NULL,                       /* HTTP_TITLE = 38 */
2578     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
2579     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2580     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2581     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2582     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
2583     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
2584     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
2585     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
2586     NULL,                       /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2587     szAge,                      /* HTTP_QUERY_AGE = 48 */
2588     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
2589     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
2590     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2591     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
2592     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
2593     szETag,                     /* HTTP_QUERY_ETAG = 54 */
2594     hostW,                      /* HTTP_QUERY_HOST = 55 */
2595     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
2596     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2597     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
2598     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2599     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
2600     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2601     szRange,                    /* HTTP_QUERY_RANGE = 62 */
2602     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2603     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
2604     szVary,                     /* HTTP_QUERY_VARY = 65 */
2605     szVia,                      /* HTTP_QUERY_VIA = 66 */
2606     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
2607     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
2608     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2609     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2610 };
2611 
2612 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2613 
2614 /***********************************************************************
2615  *           HTTP_HttpQueryInfoW (internal)
2616  */
2617 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2618         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2619 {
2620     LPHTTPHEADERW lphttpHdr = NULL;
2621     BOOL bSuccess = FALSE;
2622     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2623     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2624     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2625     INT index = -1;
2626 
2627     /* Find requested header structure */
2628     switch (level)
2629     {
2630     case HTTP_QUERY_CUSTOM:
2631         if (!lpBuffer) return FALSE;
2632         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2633         break;
2634 
2635     case HTTP_QUERY_CONTENT_LENGTH:
2636         if(lpwhr->gzip_stream) {
2637             INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2638             return FALSE;
2639         }
2640 
2641         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2642                                           requested_index,request_only);
2643         break;
2644 
2645     case HTTP_QUERY_RAW_HEADERS_CRLF:
2646         {
2647             LPWSTR headers;
2648             DWORD len = 0;
2649             BOOL ret = FALSE;
2650 
2651             if (request_only)
2652                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2653             else
2654                 headers = lpwhr->lpszRawHeaders;
2655 
2656             if (headers)
2657                 len = strlenW(headers) * sizeof(WCHAR);
2658 
2659             if (len + sizeof(WCHAR) > *lpdwBufferLength)
2660             {
2661                 len += sizeof(WCHAR);
2662                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2663                 ret = FALSE;
2664             }
2665             else if (lpBuffer)
2666             {
2667                 if (headers)
2668                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2669                 else
2670                 {
2671                     len = strlenW(szCrLf) * sizeof(WCHAR);
2672                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2673                 }
2674                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2675                 ret = TRUE;
2676             }
2677             *lpdwBufferLength = len;
2678 
2679             if (request_only)
2680                 HeapFree(GetProcessHeap(), 0, headers);
2681             return ret;
2682         }
2683     case HTTP_QUERY_RAW_HEADERS:
2684         {
2685             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2686             DWORD i, size = 0;
2687             LPWSTR pszString = lpBuffer;
2688 
2689             for (i = 0; ppszRawHeaderLines[i]; i++)
2690                 size += strlenW(ppszRawHeaderLines[i]) + 1;
2691 
2692             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2693             {
2694                 HTTP_FreeTokens(ppszRawHeaderLines);
2695                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2696                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2697                 return FALSE;
2698             }
2699             if (pszString)
2700             {
2701                 for (i = 0; ppszRawHeaderLines[i]; i++)
2702                 {
2703                     DWORD len = strlenW(ppszRawHeaderLines[i]);
2704                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2705                     pszString += len+1;
2706                 }
2707                 *pszString = '\0';
2708                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2709             }
2710             *lpdwBufferLength = size * sizeof(WCHAR);
2711             HTTP_FreeTokens(ppszRawHeaderLines);
2712 
2713             return TRUE;
2714         }
2715     case HTTP_QUERY_STATUS_TEXT:
2716         if (lpwhr->lpszStatusText)
2717         {
2718             DWORD len = strlenW(lpwhr->lpszStatusText);
2719             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2720             {
2721                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2722                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2723                 return FALSE;
2724             }
2725             if (lpBuffer)
2726             {
2727                 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2728                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2729             }
2730             *lpdwBufferLength = len * sizeof(WCHAR);
2731             return TRUE;
2732         }
2733         break;
2734     case HTTP_QUERY_VERSION:
2735         if (lpwhr->lpszVersion)
2736         {
2737             DWORD len = strlenW(lpwhr->lpszVersion);
2738             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2739             {
2740                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2741                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2742                 return FALSE;
2743             }
2744             if (lpBuffer)
2745             {
2746                 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2747                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2748             }
2749             *lpdwBufferLength = len * sizeof(WCHAR);
2750             return TRUE;
2751         }
2752         break;
2753     case HTTP_QUERY_CONTENT_ENCODING:
2754         index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2755                 requested_index,request_only);
2756         break;
2757     default:
2758         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2759 
2760         if (level < LAST_TABLE_HEADER && header_lookup[level])
2761             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2762                                               requested_index,request_only);
2763     }
2764 
2765     if (index >= 0)
2766         lphttpHdr = &lpwhr->pCustHeaders[index];
2767 
2768     /* Ensure header satisfies requested attributes */
2769     if (!lphttpHdr ||
2770         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2771          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2772     {
2773         INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2774         return bSuccess;
2775     }
2776 
2777     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2778 
2779     /* coalesce value to requested type */
2780     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2781     {
2782         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2783         TRACE(" returning number: %d\n", *(int *)lpBuffer);
2784         bSuccess = TRUE;
2785     }
2786     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2787     {
2788         time_t tmpTime;
2789         struct tm tmpTM;
2790         SYSTEMTIME *STHook;
2791 
2792         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2793 
2794         tmpTM = *gmtime(&tmpTime);
2795         STHook = (SYSTEMTIME *)lpBuffer;
2796         STHook->wDay = tmpTM.tm_mday;
2797         STHook->wHour = tmpTM.tm_hour;
2798         STHook->wMilliseconds = 0;
2799         STHook->wMinute = tmpTM.tm_min;
2800         STHook->wDayOfWeek = tmpTM.tm_wday;
2801         STHook->wMonth = tmpTM.tm_mon + 1;
2802         STHook->wSecond = tmpTM.tm_sec;
2803         STHook->wYear = tmpTM.tm_year;
2804         bSuccess = TRUE;
2805         
2806         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2807               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2808               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2809     }
2810     else if (lphttpHdr->lpszValue)
2811     {
2812         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2813 
2814         if (len > *lpdwBufferLength)
2815         {
2816             *lpdwBufferLength = len;
2817             INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2818             return bSuccess;
2819         }
2820         if (lpBuffer)
2821         {
2822             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2823             TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2824         }
2825         *lpdwBufferLength = len - sizeof(WCHAR);
2826         bSuccess = TRUE;
2827     }
2828     return bSuccess;
2829 }
2830 
2831 /***********************************************************************
2832  *           HttpQueryInfoW (WININET.@)
2833  *
2834  * Queries for information about an HTTP request
2835  *
2836  * RETURNS
2837  *    TRUE  on success
2838  *    FALSE on failure
2839  *
2840  */
2841 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2842         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2843 {
2844     BOOL bSuccess = FALSE;
2845     http_request_t *lpwhr;
2846 
2847     if (TRACE_ON(wininet)) {
2848 #define FE(x) { x, #x }
2849         static const wininet_flag_info query_flags[] = {
2850             FE(HTTP_QUERY_MIME_VERSION),
2851             FE(HTTP_QUERY_CONTENT_TYPE),
2852             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2853             FE(HTTP_QUERY_CONTENT_ID),
2854             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2855             FE(HTTP_QUERY_CONTENT_LENGTH),
2856             FE(HTTP_QUERY_CONTENT_LANGUAGE),
2857             FE(HTTP_QUERY_ALLOW),
2858             FE(HTTP_QUERY_PUBLIC),
2859             FE(HTTP_QUERY_DATE),
2860             FE(HTTP_QUERY_EXPIRES),
2861             FE(HTTP_QUERY_LAST_MODIFIED),
2862             FE(HTTP_QUERY_MESSAGE_ID),
2863             FE(HTTP_QUERY_URI),
2864             FE(HTTP_QUERY_DERIVED_FROM),
2865             FE(HTTP_QUERY_COST),
2866             FE(HTTP_QUERY_LINK),
2867             FE(HTTP_QUERY_PRAGMA),
2868             FE(HTTP_QUERY_VERSION),
2869             FE(HTTP_QUERY_STATUS_CODE),
2870             FE(HTTP_QUERY_STATUS_TEXT),
2871             FE(HTTP_QUERY_RAW_HEADERS),
2872             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2873             FE(HTTP_QUERY_CONNECTION),
2874             FE(HTTP_QUERY_ACCEPT),
2875             FE(HTTP_QUERY_ACCEPT_CHARSET),
2876             FE(HTTP_QUERY_ACCEPT_ENCODING),
2877             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2878             FE(HTTP_QUERY_AUTHORIZATION),
2879             FE(HTTP_QUERY_CONTENT_ENCODING),
2880             FE(HTTP_QUERY_FORWARDED),
2881             FE(HTTP_QUERY_FROM),
2882             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2883             FE(HTTP_QUERY_LOCATION),
2884             FE(HTTP_QUERY_ORIG_URI),
2885             FE(HTTP_QUERY_REFERER),
2886             FE(HTTP_QUERY_RETRY_AFTER),
2887             FE(HTTP_QUERY_SERVER),
2888             FE(HTTP_QUERY_TITLE),
2889             FE(HTTP_QUERY_USER_AGENT),
2890             FE(HTTP_QUERY_WWW_AUTHENTICATE),
2891             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2892             FE(HTTP_QUERY_ACCEPT_RANGES),
2893         FE(HTTP_QUERY_SET_COOKIE),
2894         FE(HTTP_QUERY_COOKIE),
2895             FE(HTTP_QUERY_REQUEST_METHOD),
2896             FE(HTTP_QUERY_REFRESH),
2897             FE(HTTP_QUERY_CONTENT_DISPOSITION),
2898             FE(HTTP_QUERY_AGE),
2899             FE(HTTP_QUERY_CACHE_CONTROL),
2900             FE(HTTP_QUERY_CONTENT_BASE),
2901             FE(HTTP_QUERY_CONTENT_LOCATION),
2902             FE(HTTP_QUERY_CONTENT_MD5),
2903             FE(HTTP_QUERY_CONTENT_RANGE),
2904             FE(HTTP_QUERY_ETAG),
2905             FE(HTTP_QUERY_HOST),
2906             FE(HTTP_QUERY_IF_MATCH),
2907             FE(HTTP_QUERY_IF_NONE_MATCH),
2908             FE(HTTP_QUERY_IF_RANGE),
2909             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2910             FE(HTTP_QUERY_MAX_FORWARDS),
2911             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2912             FE(HTTP_QUERY_RANGE),
2913             FE(HTTP_QUERY_TRANSFER_ENCODING),
2914             FE(HTTP_QUERY_UPGRADE),
2915             FE(HTTP_QUERY_VARY),
2916             FE(HTTP_QUERY_VIA),
2917             FE(HTTP_QUERY_WARNING),
2918             FE(HTTP_QUERY_CUSTOM)
2919         };
2920         static const wininet_flag_info modifier_flags[] = {
2921             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2922             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2923             FE(HTTP_QUERY_FLAG_NUMBER),
2924             FE(HTTP_QUERY_FLAG_COALESCE)
2925         };
2926 #undef FE
2927         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2928         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2929         DWORD i;
2930 
2931         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2932         TRACE("  Attribute:");
2933         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2934             if (query_flags[i].val == info) {
2935                 TRACE(" %s", query_flags[i].name);
2936                 break;
2937             }
2938         }
2939         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2940             TRACE(" Unknown (%08x)", info);
2941         }
2942 
2943         TRACE(" Modifier:");
2944         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2945             if (modifier_flags[i].val & info_mod) {
2946                 TRACE(" %s", modifier_flags[i].name);
2947                 info_mod &= ~ modifier_flags[i].val;
2948             }
2949         }
2950         
2951         if (info_mod) {
2952             TRACE(" Unknown (%08x)", info_mod);
2953         }
2954         TRACE("\n");
2955     }
2956     
2957     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2958     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
2959     {
2960         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2961         goto lend;
2962     }
2963 
2964     if (lpBuffer == NULL)
2965         *lpdwBufferLength = 0;
2966     bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2967                                     lpBuffer, lpdwBufferLength, lpdwIndex);
2968 
2969 lend:
2970     if( lpwhr )
2971          WININET_Release( &lpwhr->hdr );
2972 
2973     TRACE("%d <--\n", bSuccess);
2974     return bSuccess;
2975 }
2976 
2977 /***********************************************************************
2978  *           HttpQueryInfoA (WININET.@)
2979  *
2980  * Queries for information about an HTTP request
2981  *
2982  * RETURNS
2983  *    TRUE  on success
2984  *    FALSE on failure
2985  *
2986  */
2987 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2988         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2989 {
2990     BOOL result;
2991     DWORD len;
2992     WCHAR* bufferW;
2993 
2994     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
2995        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
2996     {
2997         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
2998                                lpdwBufferLength, lpdwIndex );
2999     }
3000 
3001     if (lpBuffer)
3002     {
3003         DWORD alloclen;
3004         len = (*lpdwBufferLength)*sizeof(WCHAR);
3005         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3006         {
3007             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3008             if (alloclen < len)
3009                 alloclen = len;
3010         }
3011         else
3012             alloclen = len;
3013         bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3014         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3015         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3016             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3017     } else
3018     {
3019         bufferW = NULL;
3020         len = 0;
3021     }
3022 
3023     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3024                            &len, lpdwIndex );
3025     if( result )
3026     {
3027         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3028                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3029         *lpdwBufferLength = len - 1;
3030 
3031         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3032     }
3033     else
3034         /* since the strings being returned from HttpQueryInfoW should be
3035          * only ASCII characters, it is reasonable to assume that all of
3036          * the Unicode characters can be reduced to a single byte */
3037         *lpdwBufferLength = len / sizeof(WCHAR);
3038 
3039     HeapFree(GetProcessHeap(), 0, bufferW );
3040 
3041     return result;
3042 }
3043 
3044 /***********************************************************************
3045  *           HttpSendRequestExA (WININET.@)
3046  *
3047  * Sends the specified request to the HTTP server and allows chunked
3048  * transfers.
3049  *
3050  * RETURNS
3051  *  Success: TRUE
3052  *  Failure: FALSE, call GetLastError() for more information.
3053  */
3054 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3055                                LPINTERNET_BUFFERSA lpBuffersIn,
3056                                LPINTERNET_BUFFERSA lpBuffersOut,
3057                                DWORD dwFlags, DWORD_PTR dwContext)
3058 {
3059     INTERNET_BUFFERSW BuffersInW;
3060     BOOL rc = FALSE;
3061     DWORD headerlen;
3062     LPWSTR header = NULL;
3063 
3064     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3065             lpBuffersOut, dwFlags, dwContext);
3066 
3067     if (lpBuffersIn)
3068     {
3069         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3070         if (lpBuffersIn->lpcszHeader)
3071         {
3072             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3073                     lpBuffersIn->dwHeadersLength,0,0);
3074             header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3075             if (!(BuffersInW.lpcszHeader = header))
3076             {
3077                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3078                 return FALSE;
3079             }
3080             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3081                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3082                     header, headerlen);
3083         }
3084         else
3085             BuffersInW.lpcszHeader = NULL;
3086         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3087         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3088         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3089         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3090         BuffersInW.Next = NULL;
3091     }
3092 
3093     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3094 
3095     HeapFree(GetProcessHeap(),0,header);
3096 
3097     return rc;
3098 }
3099 
3100 /***********************************************************************
3101  *           HttpSendRequestExW (WININET.@)
3102  *
3103  * Sends the specified request to the HTTP server and allows chunked
3104  * transfers
3105  *
3106  * RETURNS
3107  *  Success: TRUE
3108  *  Failure: FALSE, call GetLastError() for more information.
3109  */
3110 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3111                    LPINTERNET_BUFFERSW lpBuffersIn,
3112                    LPINTERNET_BUFFERSW lpBuffersOut,
3113                    DWORD dwFlags, DWORD_PTR dwContext)
3114 {
3115     BOOL ret = FALSE;
3116     http_request_t *lpwhr;
3117     http_session_t *lpwhs;
3118     appinfo_t *hIC;
3119 
3120     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3121             lpBuffersOut, dwFlags, dwContext);
3122 
3123     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3124 
3125     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3126     {
3127         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3128         goto lend;
3129     }
3130 
3131     lpwhs = lpwhr->lpHttpSession;
3132     assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3133     hIC = lpwhs->lpAppInfo;
3134     assert(hIC->hdr.htype == WH_HINIT);
3135 
3136     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3137     {
3138         WORKREQUEST workRequest;
3139         struct WORKREQ_HTTPSENDREQUESTW *req;
3140 
3141         workRequest.asyncproc = AsyncHttpSendRequestProc;
3142         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3143         req = &workRequest.u.HttpSendRequestW;
3144         if (lpBuffersIn)
3145         {
3146             /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3147             req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3148             req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3149             req->lpOptional = lpBuffersIn->lpvBuffer;
3150             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3151             req->dwContentLength = lpBuffersIn->dwBufferTotal;
3152         }
3153         else
3154         {
3155             req->lpszHeader = NULL;
3156             req->dwHeaderLength = 0;
3157             req->lpOptional = NULL;
3158             req->dwOptionalLength = 0;
3159             req->dwContentLength = 0;
3160         }
3161 
3162         req->bEndRequest = FALSE;
3163 
3164         INTERNET_AsyncCall(&workRequest);
3165         /*
3166          * This is from windows.
3167          */
3168         INTERNET_SetLastError(ERROR_IO_PENDING);
3169     }
3170     else
3171     {
3172         if (lpBuffersIn)
3173             ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3174                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3175                                         lpBuffersIn->dwBufferTotal, FALSE);
3176         else
3177             ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3178     }
3179 
3180 lend:
3181     if ( lpwhr )
3182         WININET_Release( &lpwhr->hdr );
3183 
3184     TRACE("<---\n");
3185     return ret;
3186 }
3187 
3188 /***********************************************************************
3189  *           HttpSendRequestW (WININET.@)
3190  *
3191  * Sends the specified request to the HTTP server
3192  *
3193  * RETURNS
3194  *    TRUE  on success
3195  *    FALSE on failure
3196  *
3197  */
3198 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3199         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3200 {
3201     http_request_t *lpwhr;
3202     http_session_t *lpwhs = NULL;
3203     appinfo_t *hIC = NULL;
3204     BOOL r;
3205 
3206     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3207             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3208 
3209     lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3210     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3211     {
3212         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3213         r = FALSE;
3214         goto lend;
3215     }
3216 
3217     lpwhs = lpwhr->lpHttpSession;
3218     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
3219     {
3220         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3221         r = FALSE;
3222         goto lend;
3223     }
3224 
3225     hIC = lpwhs->lpAppInfo;
3226     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
3227     {
3228         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3229         r = FALSE;
3230         goto lend;
3231     }
3232 
3233     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3234     {
3235         WORKREQUEST workRequest;
3236         struct WORKREQ_HTTPSENDREQUESTW *req;
3237 
3238         workRequest.asyncproc = AsyncHttpSendRequestProc;
3239         workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3240         req = &workRequest.u.HttpSendRequestW;
3241         if (lpszHeaders)
3242         {
3243             DWORD size;
3244 
3245             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3246             else size = dwHeaderLength * sizeof(WCHAR);
3247 
3248             req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3249             memcpy(req->lpszHeader, lpszHeaders, size);
3250         }
3251         else
3252             req->lpszHeader = 0;
3253         req->dwHeaderLength = dwHeaderLength;
3254         req->lpOptional = lpOptional;
3255         req->dwOptionalLength = dwOptionalLength;
3256         req->dwContentLength = dwOptionalLength;
3257         req->bEndRequest = TRUE;
3258 
3259         INTERNET_AsyncCall(&workRequest);
3260         /*
3261          * This is from windows.
3262          */
3263         INTERNET_SetLastError(ERROR_IO_PENDING);
3264         r = FALSE;
3265     }
3266     else
3267     {
3268         r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3269                 dwHeaderLength, lpOptional, dwOptionalLength,
3270                 dwOptionalLength, TRUE);
3271     }
3272 lend:
3273     if( lpwhr )
3274         WININET_Release( &lpwhr->hdr );
3275     return r;
3276 }
3277 
3278 /***********************************************************************
3279  *           HttpSendRequestA (WININET.@)
3280  *
3281  * Sends the specified request to the HTTP server
3282  *
3283  * RETURNS
3284  *    TRUE  on success
3285  *    FALSE on failure
3286  *
3287  */
3288 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,