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

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

Version: ~ [ 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 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  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 #include <sys/types.h>
 33 #ifdef HAVE_SYS_SOCKET_H
 34 # include <sys/socket.h>
 35 #endif
 36 #ifdef HAVE_ARPA_INET_H
 37 # include <arpa/inet.h>
 38 #endif
 39 #include <stdarg.h>
 40 #include <stdio.h>
 41 #include <stdlib.h>
 42 #ifdef HAVE_UNISTD_H
 43 # include <unistd.h>
 44 #endif
 45 #include <time.h>
 46 #include <assert.h>
 47 
 48 #include "windef.h"
 49 #include "winbase.h"
 50 #include "wininet.h"
 51 #include "winerror.h"
 52 #define NO_SHLWAPI_STREAM
 53 #define NO_SHLWAPI_REG
 54 #define NO_SHLWAPI_STRFCNS
 55 #define NO_SHLWAPI_GDI
 56 #include "shlwapi.h"
 57 #include "sspi.h"
 58 #include "wincrypt.h"
 59 
 60 #include "internet.h"
 61 #include "wine/debug.h"
 62 #include "wine/exception.h"
 63 #include "wine/unicode.h"
 64 
 65 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 66 
 67 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','',0};
 68 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
 69 static const WCHAR g_szReferer[] = {'R','e','f','e','r','e','r',0};
 70 static const WCHAR g_szAccept[] = {'A','c','c','e','p','t',0};
 71 static const WCHAR g_szUserAgent[] = {'U','s','e','r','-','A','g','e','n','t',0};
 72 static const WCHAR szHost[] = { 'H','o','s','t',0 };
 73 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 74 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 75 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
 76 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
 77 static const WCHAR szGET[] = { 'G','E','T', 0 };
 78 static const WCHAR szCrLf[] = {'\r','\n', 0};
 79 
 80 #define MAXHOSTNAME 100
 81 #define MAX_FIELD_VALUE_LEN 256
 82 #define MAX_FIELD_LEN 256
 83 
 84 #define HTTP_REFERER    g_szReferer
 85 #define HTTP_ACCEPT     g_szAccept
 86 #define HTTP_USERAGENT  g_szUserAgent
 87 
 88 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
 89 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
 90 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
 91 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
 92 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
 93 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
 94 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
 95 
 96 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
 97 
 98 struct HttpAuthInfo
 99 {
100     LPWSTR scheme;
101     CredHandle cred;
102     CtxtHandle ctx;
103     TimeStamp exp;
104     ULONG attr;
105     ULONG max_token;
106     void *auth_data;
107     unsigned int auth_data_len;
108     BOOL finished; /* finished authenticating */
109 };
110 
111 static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
112 static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear);
113 static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
114 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
115 static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr);
116 static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField, INT index, BOOL Request);
117 static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index);
118 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
119 static BOOL HTTP_HttpQueryInfoW(LPWININETHTTPREQW, DWORD, LPVOID, LPDWORD, LPDWORD);
120 static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl);
121 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
122 static BOOL HTTP_VerifyValidHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field);
123 static void HTTP_DrainContent(WININETHTTPREQW *req);
124 
125 LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
126 {
127     int HeaderIndex = 0;
128     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
129     if (HeaderIndex == -1)
130         return NULL;
131     else
132         return &req->pCustHeaders[HeaderIndex];
133 }
134 
135 /***********************************************************************
136  *           HTTP_Tokenize (internal)
137  *
138  *  Tokenize a string, allocating memory for the tokens.
139  */
140 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
141 {
142     LPWSTR * token_array;
143     int tokens = 0;
144     int i;
145     LPCWSTR next_token;
146 
147     if (string)
148     {
149         /* empty string has no tokens */
150         if (*string)
151             tokens++;
152         /* count tokens */
153         for (i = 0; string[i]; i++)
154         {
155             if (!strncmpW(string+i, token_string, strlenW(token_string)))
156             {
157                 DWORD j;
158                 tokens++;
159                 /* we want to skip over separators, but not the null terminator */
160                 for (j = 0; j < strlenW(token_string) - 1; j++)
161                     if (!string[i+j])
162                         break;
163                 i += j;
164             }
165         }
166     }
167 
168     /* add 1 for terminating NULL */
169     token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
170     token_array[tokens] = NULL;
171     if (!tokens)
172         return token_array;
173     for (i = 0; i < tokens; i++)
174     {
175         int len;
176         next_token = strstrW(string, token_string);
177         if (!next_token) next_token = string+strlenW(string);
178         len = next_token - string;
179         token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
180         memcpy(token_array[i], string, len*sizeof(WCHAR));
181         token_array[i][len] = '\0';
182         string = next_token+strlenW(token_string);
183     }
184     return token_array;
185 }
186 
187 /***********************************************************************
188  *           HTTP_FreeTokens (internal)
189  *
190  *  Frees memory returned from HTTP_Tokenize.
191  */
192 static void HTTP_FreeTokens(LPWSTR * token_array)
193 {
194     int i;
195     for (i = 0; token_array[i]; i++)
196         HeapFree(GetProcessHeap(), 0, token_array[i]);
197     HeapFree(GetProcessHeap(), 0, token_array);
198 }
199 
200 /* **********************************************************************
201  * 
202  * Helper functions for the HttpSendRequest(Ex) functions
203  * 
204  */
205 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
206 {
207     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
208     LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) workRequest->hdr;
209 
210     TRACE("%p\n", lpwhr);
211 
212     HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
213             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
214             req->dwContentLength, req->bEndRequest);
215 
216     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
217 }
218 
219 static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
220 {
221     static const WCHAR szSlash[] = { '/',0 };
222     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
223 
224     /* If we don't have a path we set it to root */
225     if (NULL == lpwhr->lpszPath)
226         lpwhr->lpszPath = WININET_strdupW(szSlash);
227     else /* remove \r and \n*/
228     {
229         int nLen = strlenW(lpwhr->lpszPath);
230         while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
231         {
232             nLen--;
233             lpwhr->lpszPath[nLen]='\0';
234         }
235         /* Replace '\' with '/' */
236         while (nLen>0) {
237             nLen--;
238             if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
239         }
240     }
241 
242     if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
243                        lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
244        && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
245     {
246         WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0, 
247                              (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
248         *fixurl = '/';
249         strcpyW(fixurl + 1, lpwhr->lpszPath);
250         HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
251         lpwhr->lpszPath = fixurl;
252     }
253 }
254 
255 static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
256 {
257     LPWSTR requestString;
258     DWORD len, n;
259     LPCWSTR *req;
260     UINT i;
261     LPWSTR p;
262 
263     static const WCHAR szSpace[] = { ' ',0 };
264     static const WCHAR szColon[] = { ':',' ',0 };
265     static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
266 
267     /* allocate space for an array of all the string pointers to be added */
268     len = (lpwhr->nCustHeaders)*4 + 10;
269     req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
270 
271     /* add the verb, path and HTTP version string */
272     n = 0;
273     req[n++] = verb;
274     req[n++] = szSpace;
275     req[n++] = path;
276     req[n++] = szSpace;
277     req[n++] = version;
278 
279     /* Append custom request headers */
280     for (i = 0; i < lpwhr->nCustHeaders; i++)
281     {
282         if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
283         {
284             req[n++] = szCrLf;
285             req[n++] = lpwhr->pCustHeaders[i].lpszField;
286             req[n++] = szColon;
287             req[n++] = lpwhr->pCustHeaders[i].lpszValue;
288 
289             TRACE("Adding custom header %s (%s)\n",
290                    debugstr_w(lpwhr->pCustHeaders[i].lpszField),
291                    debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
292         }
293     }
294 
295     if( n >= len )
296         ERR("oops. buffer overrun\n");
297 
298     req[n] = NULL;
299     requestString = HTTP_build_req( req, 4 );
300     HeapFree( GetProcessHeap(), 0, req );
301 
302     /*
303      * Set (header) termination string for request
304      * Make sure there's exactly two new lines at the end of the request
305      */
306     p = &requestString[strlenW(requestString)-1];
307     while ( (*p == '\n') || (*p == '\r') )
308        p--;
309     strcpyW( p+1, sztwocrlf );
310     
311     return requestString;
312 }
313 
314 static void HTTP_ProcessCookies( LPWININETHTTPREQW lpwhr )
315 {
316     static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
317     int HeaderIndex;
318     int numCookies = 0;
319     LPHTTPHEADERW setCookieHeader;
320 
321     while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
322     {
323         setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
324 
325         if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
326         {
327             int nPosStart = 0, nPosEnd = 0, len;
328             static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','/',0};
329 
330             while (setCookieHeader->lpszValue[nPosEnd] != '\0')
331             {
332                 LPWSTR buf_cookie, cookie_name, cookie_data;
333                 LPWSTR buf_url;
334                 LPWSTR domain = NULL;
335                 LPHTTPHEADERW Host;
336 
337                 int nEqualPos = 0;
338                 while (setCookieHeader->lpszValue[nPosEnd] != ';' && setCookieHeader->lpszValue[nPosEnd] != ',' &&
339                        setCookieHeader->lpszValue[nPosEnd] != '\0')
340                 {
341                     nPosEnd++;
342                 }
343                 if (setCookieHeader->lpszValue[nPosEnd] == ';')
344                 {
345                     /* fixme: not case sensitive, strcasestr is gnu only */
346                     int nDomainPosEnd = 0;
347                     int nDomainPosStart = 0, nDomainLength = 0;
348                     static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
349                     LPWSTR lpszDomain = strstrW(&setCookieHeader->lpszValue[nPosEnd], szDomain);
350                     if (lpszDomain)
351                     { /* they have specified their own domain, lets use it */
352                         while (lpszDomain[nDomainPosEnd] != ';' && lpszDomain[nDomainPosEnd] != ',' &&
353                                lpszDomain[nDomainPosEnd] != '\0')
354                         {
355                             nDomainPosEnd++;
356                         }
357                         nDomainPosStart = strlenW(szDomain);
358                         nDomainLength = (nDomainPosEnd - nDomainPosStart) + 1;
359                         domain = HeapAlloc(GetProcessHeap(), 0, (nDomainLength + 1)*sizeof(WCHAR));
360                         lstrcpynW(domain, &lpszDomain[nDomainPosStart], nDomainLength + 1);
361                     }
362                 }
363                 if (setCookieHeader->lpszValue[nPosEnd] == '\0') break;
364                 buf_cookie = HeapAlloc(GetProcessHeap(), 0, ((nPosEnd - nPosStart) + 1)*sizeof(WCHAR));
365                 lstrcpynW(buf_cookie, &setCookieHeader->lpszValue[nPosStart], (nPosEnd - nPosStart) + 1);
366                 TRACE("%s\n", debugstr_w(buf_cookie));
367                 while (buf_cookie[nEqualPos] != '=' && buf_cookie[nEqualPos] != '\0')
368                 {
369                     nEqualPos++;
370                 }
371                 if (buf_cookie[nEqualPos] == '\0' || buf_cookie[nEqualPos + 1] == '\0')
372                 {
373                     HeapFree(GetProcessHeap(), 0, buf_cookie);
374                     break;
375                 }
376 
377                 cookie_name = HeapAlloc(GetProcessHeap(), 0, (nEqualPos + 1)*sizeof(WCHAR));
378                 lstrcpynW(cookie_name, buf_cookie, nEqualPos + 1);
379                 cookie_data = &buf_cookie[nEqualPos + 1];
380 
381                 Host = HTTP_GetHeader(lpwhr,szHost);
382                 len = lstrlenW((domain ? domain : (Host?Host->lpszValue:NULL))) +
383                     strlenW(lpwhr->lpszPath) + 9;
384                 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
385                 sprintfW(buf_url, szFmt, (domain ? domain : (Host?Host->lpszValue:NULL))); /* FIXME PATH!!! */
386                 InternetSetCookieW(buf_url, cookie_name, cookie_data);
387 
388                 HeapFree(GetProcessHeap(), 0, buf_url);
389                 HeapFree(GetProcessHeap(), 0, buf_cookie);
390                 HeapFree(GetProcessHeap(), 0, cookie_name);
391                 HeapFree(GetProcessHeap(), 0, domain);
392                 nPosStart = nPosEnd;
393             }
394         }
395         numCookies++;
396     }
397 }
398 
399 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
400 {
401     static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
402     return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
403         ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
404 }
405 
406 static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
407                                   struct HttpAuthInfo **ppAuthInfo,
408                                   LPWSTR domain_and_username, LPWSTR password )
409 {
410     SECURITY_STATUS sec_status;
411     struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
412     BOOL first = FALSE;
413 
414     TRACE("%s\n", debugstr_w(pszAuthValue));
415 
416     if (!pAuthInfo)
417     {
418         TimeStamp exp;
419 
420         first = TRUE;
421         pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
422         if (!pAuthInfo)
423             return FALSE;
424 
425         SecInvalidateHandle(&pAuthInfo->cred);
426         SecInvalidateHandle(&pAuthInfo->ctx);
427         memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
428         pAuthInfo->attr = 0;
429         pAuthInfo->auth_data = NULL;
430         pAuthInfo->auth_data_len = 0;
431         pAuthInfo->finished = FALSE;
432 
433         if (is_basic_auth_value(pszAuthValue))
434         {
435             static const WCHAR szBasic[] = {'B','a','s','i','c',0};
436             pAuthInfo->scheme = WININET_strdupW(szBasic);
437             if (!pAuthInfo->scheme)
438             {
439                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
440                 return FALSE;
441             }
442         }
443         else
444         {
445             PVOID pAuthData;
446             SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
447 
448             pAuthInfo->scheme = WININET_strdupW(pszAuthValue);
449             if (!pAuthInfo->scheme)
450             {
451                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
452                 return FALSE;
453             }
454 
455             if (domain_and_username)
456             {
457                 WCHAR *user = strchrW(domain_and_username, '\\');
458                 WCHAR *domain = domain_and_username;
459 
460                 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
461 
462                 pAuthData = &nt_auth_identity;
463 
464                 if (user) user++;
465                 else
466                 {
467                     user = domain_and_username;
468                     domain = NULL;
469                 }
470 
471                 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
472                 nt_auth_identity.User = user;
473                 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
474                 nt_auth_identity.Domain = domain;
475                 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
476                 nt_auth_identity.Password = password;
477                 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
478             }
479             else
480                 /* use default credentials */
481                 pAuthData = NULL;
482 
483             sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
484                                                    SECPKG_CRED_OUTBOUND, NULL,
485                                                    pAuthData, NULL,
486                                                    NULL, &pAuthInfo->cred,
487                                                    &exp);
488             if (sec_status == SEC_E_OK)
489             {
490                 PSecPkgInfoW sec_pkg_info;
491                 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
492                 if (sec_status == SEC_E_OK)
493                 {
494                     pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
495                     FreeContextBuffer(sec_pkg_info);
496                 }
497             }
498             if (sec_status != SEC_E_OK)
499             {
500                 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
501                      debugstr_w(pAuthInfo->scheme), sec_status);
502                 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
503                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
504                 return FALSE;
505             }
506         }
507         *ppAuthInfo = pAuthInfo;
508     }
509     else if (pAuthInfo->finished)
510         return FALSE;
511 
512     if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
513         strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
514     {
515         ERR("authentication scheme changed from %s to %s\n",
516             debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
517         return FALSE;
518     }
519 
520     if (is_basic_auth_value(pszAuthValue))
521     {
522         int userlen;
523         int passlen;
524         char *auth_data;
525 
526         TRACE("basic authentication\n");
527 
528         /* we don't cache credentials for basic authentication, so we can't
529          * retrieve them if the application didn't pass us any credentials */
530         if (!domain_and_username) return FALSE;
531 
532         userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
533         passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
534 
535         /* length includes a nul terminator, which will be re-used for the ':' */
536         auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
537         if (!auth_data)
538             return FALSE;
539 
540         WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
541         auth_data[userlen] = ':';
542         WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
543 
544         pAuthInfo->auth_data = auth_data;
545         pAuthInfo->auth_data_len = userlen + 1 + passlen;
546         pAuthInfo->finished = TRUE;
547 
548         return TRUE;
549     }
550     else
551     {
552         LPCWSTR pszAuthData;
553         SecBufferDesc out_desc, in_desc;
554         SecBuffer out, in;
555         unsigned char *buffer;
556         ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
557             ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
558 
559         in.BufferType = SECBUFFER_TOKEN;
560         in.cbBuffer = 0;
561         in.pvBuffer = NULL;
562 
563         in_desc.ulVersion = 0;
564         in_desc.cBuffers = 1;
565         in_desc.pBuffers = &in;
566 
567         pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
568         if (*pszAuthData == ' ')
569         {
570             pszAuthData++;
571             in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
572             in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
573             HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
574         }
575 
576         buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
577 
578         out.BufferType = SECBUFFER_TOKEN;
579         out.cbBuffer = pAuthInfo->max_token;
580         out.pvBuffer = buffer;
581 
582         out_desc.ulVersion = 0;
583         out_desc.cBuffers = 1;
584         out_desc.pBuffers = &out;
585 
586         sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
587                                                 first ? NULL : &pAuthInfo->ctx,
588                                                 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
589                                                 context_req, 0, SECURITY_NETWORK_DREP,
590                                                 in.pvBuffer ? &in_desc : NULL,
591                                                 0, &pAuthInfo->ctx, &out_desc,
592                                                 &pAuthInfo->attr, &pAuthInfo->exp);
593         if (sec_status == SEC_E_OK)
594         {
595             pAuthInfo->finished = TRUE;
596             pAuthInfo->auth_data = out.pvBuffer;
597             pAuthInfo->auth_data_len = out.cbBuffer;
598             TRACE("sending last auth packet\n");
599         }
600         else if (sec_status == SEC_I_CONTINUE_NEEDED)
601         {
602             pAuthInfo->auth_data = out.pvBuffer;
603             pAuthInfo->auth_data_len = out.cbBuffer;
604             TRACE("sending next auth packet\n");
605         }
606         else
607         {
608             ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
609             pAuthInfo->finished = TRUE;
610             HeapFree(GetProcessHeap(), 0, out.pvBuffer);
611             return FALSE;
612         }
613     }
614 
615     return TRUE;
616 }
617 
618 /***********************************************************************
619  *           HTTP_HttpAddRequestHeadersW (internal)
620  */
621 static BOOL WINAPI HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
622         LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
623 {
624     LPWSTR lpszStart;
625     LPWSTR lpszEnd;
626     LPWSTR buffer;
627     BOOL bSuccess = FALSE;
628     DWORD len;
629 
630     TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
631 
632     if( dwHeaderLength == ~0U )
633         len = strlenW(lpszHeader);
634     else
635         len = dwHeaderLength;
636     buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
637     lstrcpynW( buffer, lpszHeader, len + 1);
638 
639     lpszStart = buffer;
640 
641     do
642     {
643         LPWSTR * pFieldAndValue;
644 
645         lpszEnd = lpszStart;
646 
647         while (*lpszEnd != '\0')
648         {
649             if (*lpszEnd == '\r' && *(lpszEnd + 1) == '\n')
650                  break;
651             lpszEnd++;
652         }
653 
654         if (*lpszStart == '\0')
655             break;
656 
657         if (*lpszEnd == '\r')
658         {
659             *lpszEnd = '\0';
660             lpszEnd += 2; /* Jump over \r\n */
661         }
662         TRACE("interpreting header %s\n", debugstr_w(lpszStart));
663         pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
664         if (pFieldAndValue)
665         {
666             bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
667             if (bSuccess)
668                 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
669                     pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
670             HTTP_FreeTokens(pFieldAndValue);
671         }
672 
673         lpszStart = lpszEnd;
674     } while (bSuccess);
675 
676     HeapFree(GetProcessHeap(), 0, buffer);
677 
678     return bSuccess;
679 }
680 
681 /***********************************************************************
682  *           HttpAddRequestHeadersW (WININET.@)
683  *
684  * Adds one or more HTTP header to the request handler
685  *
686  * NOTE
687  * On Windows if dwHeaderLength includes the trailing '\0', then
688  * HttpAddRequestHeadersW() adds it too. However this results in an
689  * invalid Http header which is rejected by some servers so we probably
690  * don't need to match Windows on that point.
691  *
692  * RETURNS
693  *    TRUE  on success
694  *    FALSE on failure
695  *
696  */
697 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
698         LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
699 {
700     BOOL bSuccess = FALSE;
701     LPWININETHTTPREQW lpwhr;
702 
703     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
704 
705     if (!lpszHeader) 
706       return TRUE;
707 
708     lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
709     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
710     {
711         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
712         goto lend;
713     }
714     bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
715 lend:
716     if( lpwhr )
717         WININET_Release( &lpwhr->hdr );
718 
719     return bSuccess;
720 }
721 
722 /***********************************************************************
723  *           HttpAddRequestHeadersA (WININET.@)
724  *
725  * Adds one or more HTTP header to the request handler
726  *
727  * RETURNS
728  *    TRUE  on success
729  *    FALSE on failure
730  *
731  */
732 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
733         LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
734 {
735     DWORD len;
736     LPWSTR hdr;
737     BOOL r;
738 
739     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
740 
741     len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
742     hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
743     MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
744     if( dwHeaderLength != ~0U )
745         dwHeaderLength = len;
746 
747     r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
748 
749     HeapFree( GetProcessHeap(), 0, hdr );
750 
751     return r;
752 }
753 
754 /***********************************************************************
755  *           HttpEndRequestA (WININET.@)
756  *
757  * Ends an HTTP request that was started by HttpSendRequestEx
758  *
759  * RETURNS
760  *    TRUE      if successful
761  *    FALSE     on failure
762  *
763  */
764 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest, 
765         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
766 {
767     LPINTERNET_BUFFERSA ptr;
768     LPINTERNET_BUFFERSW lpBuffersOutW,ptrW;
769     BOOL rc = FALSE;
770 
771     TRACE("(%p, %p, %08x, %08lx): stub\n", hRequest, lpBuffersOut, dwFlags,
772             dwContext);
773 
774     ptr = lpBuffersOut;
775     if (ptr)
776         lpBuffersOutW = (LPINTERNET_BUFFERSW)HeapAlloc(GetProcessHeap(),
777                 HEAP_ZERO_MEMORY, sizeof(INTERNET_BUFFERSW));
778     else
779         lpBuffersOutW = NULL;
780 
781     ptrW = lpBuffersOutW;
782     while (ptr)
783     {
784         if (ptr->lpvBuffer && ptr->dwBufferLength)
785             ptrW->lpvBuffer = HeapAlloc(GetProcessHeap(),0,ptr->dwBufferLength);
786         ptrW->dwBufferLength = ptr->dwBufferLength;
787         ptrW->dwBufferTotal= ptr->dwBufferTotal;
788 
789         if (ptr->Next)
790             ptrW->Next = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
791                     sizeof(INTERNET_BUFFERSW));
792 
793         ptr = ptr->Next;
794         ptrW = ptrW->Next;
795     }
796 
797     rc = HttpEndRequestW(hRequest, lpBuffersOutW, dwFlags, dwContext);
798 
799     if (lpBuffersOutW)
800     {
801         ptrW = lpBuffersOutW;
802         while (ptrW)
803         {
804             LPINTERNET_BUFFERSW ptrW2;
805 
806             FIXME("Do we need to translate info out of these buffer?\n");
807 
808             HeapFree(GetProcessHeap(),0,ptrW->lpvBuffer);
809             ptrW2 = ptrW->Next;
810             HeapFree(GetProcessHeap(),0,ptrW);
811             ptrW = ptrW2;
812         }
813     }
814 
815     return rc;
816 }
817 
818 /***********************************************************************
819  *           HttpEndRequestW (WININET.@)
820  *
821  * Ends an HTTP request that was started by HttpSendRequestEx
822  *
823  * RETURNS
824  *    TRUE      if successful
825  *    FALSE     on failure
826  *
827  */
828 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest, 
829         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
830 {
831     BOOL rc = FALSE;
832     LPWININETHTTPREQW lpwhr;
833     INT responseLen;
834     DWORD dwBufferSize;
835 
836     TRACE("-->\n");
837     lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
838 
839     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
840     {
841         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
842         if (lpwhr)
843             WININET_Release( &lpwhr->hdr );
844         return FALSE;
845     }
846 
847     lpwhr->hdr.dwFlags |= dwFlags;
848     lpwhr->hdr.dwContext = dwContext;
849 
850     /* We appear to do nothing with lpBuffersOut.. is that correct? */
851 
852     SendAsyncCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
853             INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
854 
855     responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
856     if (responseLen)
857             rc = TRUE;
858 
859     SendAsyncCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
860             INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
861 
862     /* process cookies here. Is this right? */
863     HTTP_ProcessCookies(lpwhr);
864 
865     dwBufferSize = sizeof(lpwhr->dwContentLength);
866     if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
867                              &lpwhr->dwContentLength,&dwBufferSize,NULL))
868         lpwhr->dwContentLength = -1;
869 
870     if (lpwhr->dwContentLength == 0)
871         HTTP_FinishedReading(lpwhr);
872 
873     if(!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
874     {
875         DWORD dwCode,dwCodeLength=sizeof(DWORD);
876         if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,&dwCode,&dwCodeLength,NULL) &&
877             (dwCode==302 || dwCode==301 || dwCode==303))
878         {
879             WCHAR szNewLocation[INTERNET_MAX_URL_LENGTH];
880             dwBufferSize=sizeof(szNewLocation);
881             if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
882             {
883                 /* redirects are always GETs */
884                 HeapFree(GetProcessHeap(),0,lpwhr->lpszVerb);
885                 lpwhr->lpszVerb = WININET_strdupW(szGET);
886                 HTTP_DrainContent(lpwhr);
887                 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
888                                       INTERNET_STATUS_REDIRECT, szNewLocation,
889                                       dwBufferSize);
890                 rc = HTTP_HandleRedirect(lpwhr, szNewLocation);
891                 if (rc)
892                     rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
893             }
894         }
895     }
896 
897     WININET_Release( &lpwhr->hdr );
898     TRACE("%i <--\n",rc);
899     return rc;
900 }
901 
902 /***********************************************************************
903  *           HttpOpenRequestW (WININET.@)
904  *
905  * Open a HTTP request handle
906  *
907  * RETURNS
908  *    HINTERNET  a HTTP request handle on success
909  *    NULL       on failure
910  *
911  */
912 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
913         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
914         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
915         DWORD dwFlags, DWORD_PTR dwContext)
916 {
917     LPWININETHTTPSESSIONW lpwhs;
918     HINTERNET handle = NULL;
919 
920     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
921           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
922           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
923           dwFlags, dwContext);
924     if(lpszAcceptTypes!=NULL)
925     {
926         int i;
927         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
928             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
929     }    
930 
931     lpwhs = (LPWININETHTTPSESSIONW) WININET_GetObject( hHttpSession );
932     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
933     {
934         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
935         goto lend;
936     }
937 
938     /*
939      * My tests seem to show that the windows version does not
940      * become asynchronous until after this point. And anyhow
941      * if this call was asynchronous then how would you get the
942      * necessary HINTERNET pointer returned by this function.
943      *
944      */
945     handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
946                                    lpszVersion, lpszReferrer, lpszAcceptTypes,
947                                    dwFlags, dwContext);
948 lend:
949     if( lpwhs )
950         WININET_Release( &lpwhs->hdr );
951     TRACE("returning %p\n", handle);
952     return handle;
953 }
954 
955 
956 /**************************************************