2 * Wininet - Http Implementation
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
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.
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.
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
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',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};
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 };
137 #define MAXHOSTNAME 100
138 #define MAX_FIELD_VALUE_LEN 256
139 #define MAX_FIELD_LEN 256
141 #define HTTP_REFERER szReferer
142 #define HTTP_ACCEPT szAccept
143 #define HTTP_USERAGENT szUser_Agent
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
153 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
164 unsigned int auth_data_len;
165 BOOL finished; /* finished authenticating */
169 struct gzip_stream_t {
179 static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
180 static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear);
181 static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
182 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
183 static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr);
184 static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField, INT index, BOOL Request);
185 static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index);
186 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
187 static BOOL HTTP_HttpQueryInfoW(LPWININETHTTPREQW, DWORD, LPVOID, LPDWORD, LPDWORD);
188 static LPWSTR HTTP_GetRedirectURL(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl);
189 static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl);
190 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
191 static BOOL HTTP_VerifyValidHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field);
192 static void HTTP_DrainContent(WININETHTTPREQW *req);
193 static BOOL HTTP_FinishedReading(LPWININETHTTPREQW lpwhr);
195 static LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
198 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
199 if (HeaderIndex == -1)
202 return &req->pCustHeaders[HeaderIndex];
207 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
209 return HeapAlloc(GetProcessHeap(), 0, items*size);
212 static void wininet_zfree(voidpf opaque, voidpf address)
214 HeapFree(GetProcessHeap(), 0, address);
217 static void init_gzip_stream(WININETHTTPREQW *req)
219 gzip_stream_t *gzip_stream;
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;
234 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
236 ERR("inflateInit failed: %d\n", zres);
237 HeapFree(GetProcessHeap(), 0, gzip_stream);
241 req->gzip_stream = gzip_stream;
246 static void init_gzip_stream(WININETHTTPREQW *req)
248 ERR("gzip stream not supported, missing zlib.\n");
253 /* set the request content length based on the headers */
254 static DWORD set_content_length( LPWININETHTTPREQW lpwhr )
256 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
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;
265 size = sizeof(encoding);
266 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
267 !strcmpiW(encoding, szChunked))
269 lpwhr->dwContentLength = ~0u;
270 lpwhr->read_chunked = TRUE;
273 if(lpwhr->decoding) {
276 static const WCHAR gzipW[] = {'g','z','i','p',0};
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);
283 return lpwhr->dwContentLength;
286 /***********************************************************************
287 * HTTP_Tokenize (internal)
289 * Tokenize a string, allocating memory for the tokens.
291 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
293 LPWSTR * token_array;
300 /* empty string has no tokens */
304 for (i = 0; string[i]; i++)
306 if (!strncmpW(string+i, token_string, strlenW(token_string)))
310 /* we want to skip over separators, but not the null terminator */
311 for (j = 0; j < strlenW(token_string) - 1; j++)
319 /* add 1 for terminating NULL */
320 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
321 token_array[tokens] = NULL;
324 for (i = 0; i < tokens; i++)
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);
338 /***********************************************************************
339 * HTTP_FreeTokens (internal)
341 * Frees memory returned from HTTP_Tokenize.
343 static void HTTP_FreeTokens(LPWSTR * token_array)
346 for (i = 0; token_array[i]; i++)
347 HeapFree(GetProcessHeap(), 0, token_array[i]);
348 HeapFree(GetProcessHeap(), 0, token_array);
351 /* **********************************************************************
353 * Helper functions for the HttpSendRequest(Ex) functions
356 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
358 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
359 LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) workRequest->hdr;
361 TRACE("%p\n", lpwhr);
363 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
364 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
365 req->dwContentLength, req->bEndRequest);
367 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
370 static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
372 static const WCHAR szSlash[] = { '/',0 };
373 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
375 /* If we don't have a path we set it to root */
376 if (NULL == lpwhr->lpszPath)
377 lpwhr->lpszPath = WININET_strdupW(szSlash);
378 else /* remove \r and \n*/
380 int nLen = strlenW(lpwhr->lpszPath);
381 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
384 lpwhr->lpszPath[nLen]='\0';
386 /* Replace '\' with '/' */
389 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
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 !! */
397 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
398 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
400 strcpyW(fixurl + 1, lpwhr->lpszPath);
401 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
402 lpwhr->lpszPath = fixurl;
406 static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
408 LPWSTR requestString;
414 static const WCHAR szSpace[] = { ' ',0 };
415 static const WCHAR szColon[] = { ':',' ',0 };
416 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
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) );
422 /* add the verb, path and HTTP version string */
430 /* Append custom request headers */
431 for (i = 0; i < lpwhr->nCustHeaders; i++)
433 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
436 req[n++] = lpwhr->pCustHeaders[i].lpszField;
438 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
440 TRACE("Adding custom header %s (%s)\n",
441 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
442 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
447 ERR("oops. buffer overrun\n");
450 requestString = HTTP_build_req( req, 4 );
451 HeapFree( GetProcessHeap(), 0, req );
454 * Set (header) termination string for request
455 * Make sure there's exactly two new lines at the end of the request
457 p = &requestString[strlenW(requestString)-1];
458 while ( (*p == '\n') || (*p == '\r') )
460 strcpyW( p+1, sztwocrlf );
462 return requestString;
465 static void HTTP_ProcessCookies( LPWININETHTTPREQW lpwhr )
469 LPHTTPHEADERW setCookieHeader;
471 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
473 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
475 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
478 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
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);
488 HeapFree(GetProcessHeap(), 0, buf_url);
494 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
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)]);
501 static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
502 struct HttpAuthInfo **ppAuthInfo,
503 LPWSTR domain_and_username, LPWSTR password )
505 SECURITY_STATUS sec_status;
506 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
509 TRACE("%s\n", debugstr_w(pszAuthValue));
516 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
520 SecInvalidateHandle(&pAuthInfo->cred);
521 SecInvalidateHandle(&pAuthInfo->ctx);
522 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
524 pAuthInfo->auth_data = NULL;
525 pAuthInfo->auth_data_len = 0;
526 pAuthInfo->finished = FALSE;
528 if (is_basic_auth_value(pszAuthValue))
530 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
531 pAuthInfo->scheme = WININET_strdupW(szBasic);
532 if (!pAuthInfo->scheme)
534 HeapFree(GetProcessHeap(), 0, pAuthInfo);
541 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
543 pAuthInfo->scheme = WININET_strdupW(pszAuthValue);
544 if (!pAuthInfo->scheme)
546 HeapFree(GetProcessHeap(), 0, pAuthInfo);
550 if (domain_and_username)
552 WCHAR *user = strchrW(domain_and_username, '\\');
553 WCHAR *domain = domain_and_username;
555 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
557 pAuthData = &nt_auth_identity;
562 user = domain_and_username;
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);
575 /* use default credentials */
578 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
579 SECPKG_CRED_OUTBOUND, NULL,
581 NULL, &pAuthInfo->cred,
583 if (sec_status == SEC_E_OK)
585 PSecPkgInfoW sec_pkg_info;
586 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
587 if (sec_status == SEC_E_OK)
589 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
590 FreeContextBuffer(sec_pkg_info);
593 if (sec_status != SEC_E_OK)
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);
602 *ppAuthInfo = pAuthInfo;
604 else if (pAuthInfo->finished)
607 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
608 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
610 ERR("authentication scheme changed from %s to %s\n",
611 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
615 if (is_basic_auth_value(pszAuthValue))
621 TRACE("basic authentication\n");
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;
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);
630 /* length includes a nul terminator, which will be re-used for the ':' */
631 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
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);
639 pAuthInfo->auth_data = auth_data;
640 pAuthInfo->auth_data_len = userlen + 1 + passlen;
641 pAuthInfo->finished = TRUE;
648 SecBufferDesc out_desc, in_desc;
650 unsigned char *buffer;
651 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
652 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
654 in.BufferType = SECBUFFER_TOKEN;
658 in_desc.ulVersion = 0;
659 in_desc.cBuffers = 1;
660 in_desc.pBuffers = ∈
662 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
663 if (*pszAuthData == ' ')
666 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
667 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
668 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
671 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
673 out.BufferType = SECBUFFER_TOKEN;
674 out.cbBuffer = pAuthInfo->max_token;
675 out.pvBuffer = buffer;
677 out_desc.ulVersion = 0;
678 out_desc.cBuffers = 1;
679 out_desc.pBuffers = &out;
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)
690 pAuthInfo->finished = TRUE;
691 pAuthInfo->auth_data = out.pvBuffer;
692 pAuthInfo->auth_data_len = out.cbBuffer;
693 TRACE("sending last auth packet\n");
695 else if (sec_status == SEC_I_CONTINUE_NEEDED)
697 pAuthInfo->auth_data = out.pvBuffer;
698 pAuthInfo->auth_data_len = out.cbBuffer;
699 TRACE("sending next auth packet\n");
703 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
704 pAuthInfo->finished = TRUE;
705 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
713 /***********************************************************************
714 * HTTP_HttpAddRequestHeadersW (internal)
716 static BOOL HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
717 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
722 BOOL bSuccess = FALSE;
725 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
727 if( dwHeaderLength == ~0U )
728 len = strlenW(lpszHeader);
730 len = dwHeaderLength;
731 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
732 lstrcpynW( buffer, lpszHeader, len + 1);
738 LPWSTR * pFieldAndValue;
742 while (*lpszEnd != '\0')
744 if (*lpszEnd == '\r' || *lpszEnd == '\n')
749 if (*lpszStart == '\0')
752 if (*lpszEnd == '\r' || *lpszEnd == '\n')
755 lpszEnd++; /* Jump over newline */
757 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
758 if (*lpszStart == '\0')
760 /* Skip 0-length headers */
765 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
768 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
770 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
771 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
772 HTTP_FreeTokens(pFieldAndValue);
778 HeapFree(GetProcessHeap(), 0, buffer);
783 /***********************************************************************
784 * HttpAddRequestHeadersW (WININET.@)
786 * Adds one or more HTTP header to the request handler
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.
799 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
800 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
802 BOOL bSuccess = FALSE;
803 LPWININETHTTPREQW lpwhr;
805 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
810 lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
811 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
813 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
816 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
819 WININET_Release( &lpwhr->hdr );
824 /***********************************************************************
825 * HttpAddRequestHeadersA (WININET.@)
827 * Adds one or more HTTP header to the request handler
834 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
835 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
841 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
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;
849 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
851 HeapFree( GetProcessHeap(), 0, hdr );
856 /***********************************************************************
857 * HttpEndRequestA (WININET.@)
859 * Ends an HTTP request that was started by HttpSendRequestEx
866 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
867 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
869 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
873 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
877 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
880 static BOOL HTTP_HttpEndRequestW(LPWININETHTTPREQW lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
885 INTERNET_ASYNC_RESULT iar;
887 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
888 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
890 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
894 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
895 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
897 /* process cookies here. Is this right? */
898 HTTP_ProcessCookies(lpwhr);
900 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
902 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
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))
908 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
909 dwBufferSize=sizeof(szNewLocation);
910 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
912 /* redirects are always GETs */
913 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
914 lpwhr->lpszVerb = WININET_strdupW(szGET);
915 HTTP_DrainContent(lpwhr);
916 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
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);
922 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
923 HeapFree( GetProcessHeap(), 0, new_url );
929 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
930 iar.dwError = rc ? 0 : INTERNET_GetLastError();
932 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
933 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
934 sizeof(INTERNET_ASYNC_RESULT));
938 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
940 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
941 LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW)work->hdr;
943 TRACE("%p\n", lpwhr);
945 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
948 /***********************************************************************
949 * HttpEndRequestW (WININET.@)
951 * Ends an HTTP request that was started by HttpSendRequestEx
958 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
959 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
962 LPWININETHTTPREQW lpwhr;
968 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
972 lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
974 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
978 WININET_Release( &lpwhr->hdr );
981 lpwhr->hdr.dwFlags |= dwFlags;
983 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
986 struct WORKREQ_HTTPENDREQUESTW *request;
988 work.asyncproc = AsyncHttpEndRequestProc;
989 work.hdr = WININET_AddRef( &lpwhr->hdr );
991 request = &work.u.HttpEndRequestW;
992 request->dwFlags = dwFlags;
993 request->dwContext = dwContext;
995 INTERNET_AsyncCall(&work);
996 INTERNET_SetLastError(ERROR_IO_PENDING);
999 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1001 WININET_Release( &lpwhr->hdr );
1002 TRACE("%i <--\n",rc);
1006 /***********************************************************************
1007 * HttpOpenRequestW (WININET.@)
1009 * Open a HTTP request handle
1012 * HINTERNET a HTTP request handle on success
1016 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1017 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1018 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1019 DWORD dwFlags, DWORD_PTR dwContext)
1021 LPWININETHTTPSESSIONW lpwhs;
1022 HINTERNET handle = NULL;
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)
1031 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1032 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1035 lpwhs = (LPWININETHTTPSESSIONW) WININET_GetObject( hHttpSession );
1036 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1038 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
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.
1049 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1050 lpszVersion, lpszReferrer, lpszAcceptTypes,
1051 dwFlags, dwContext);
1054 WININET_Release( &lpwhs->hdr );
1055 TRACE("returning %p\n", handle);
1060 /***********************************************************************
1061 * HttpOpenRequestA (WININET.@)
1063 * Open a HTTP request handle
1066 * HINTERNET a HTTP request handle on success
1070 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1071 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1072 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1073 DWORD dwFlags, DWORD_PTR dwContext)
1075 LPWSTR szVerb = NULL, szObjectName = NULL;
1076 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1077 INT len, acceptTypesCount;
1078 HINTERNET rc = FALSE;
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);
1088 len = MultiByteToWideChar(CP_ACP, 0, lpszVerb, -1, NULL, 0 );
1089 szVerb = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR) );
1092 MultiByteToWideChar(CP_ACP, 0, lpszVerb, -1, szVerb, len);
1097 len = MultiByteToWideChar(CP_ACP, 0, lpszObjectName, -1, NULL, 0 );
1098 szObjectName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR) );
1099 if ( !szObjectName )
1101 MultiByteToWideChar(CP_ACP, 0, lpszObjectName, -1, szObjectName, len );
1106 len = MultiByteToWideChar(CP_ACP, 0, lpszVersion, -1, NULL, 0 );
1107 szVersion = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1110 MultiByteToWideChar(CP_ACP, 0, lpszVersion, -1, szVersion, len );
1115 len = MultiByteToWideChar(CP_ACP, 0, lpszReferrer, -1, NULL, 0 );
1116 szReferrer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1119 MultiByteToWideChar(CP_ACP, 0, lpszReferrer, -1, szReferrer, len );
1122 if (lpszAcceptTypes)
1124 acceptTypesCount = 0;
1125 types = lpszAcceptTypes;
1130 /* find out how many there are */
1131 if (*types && **types)
1133 TRACE("accept type: %s\n", debugstr_a(*types));
1139 WARN("invalid accept type pointer\n");
1144 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1145 if (!szAcceptTypes) goto end;
1147 acceptTypesCount = 0;
1148 types = lpszAcceptTypes;
1153 if (*types && **types)
1155 len = MultiByteToWideChar(CP_ACP, 0, *types, -1, NULL, 0 );
1156 szAcceptTypes[acceptTypesCount] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1158 MultiByteToWideChar(CP_ACP, 0, *types, -1, szAcceptTypes[acceptTypesCount], len);
1164 /* ignore invalid pointer */
1169 szAcceptTypes[acceptTypesCount] = NULL;
1172 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1173 szVersion, szReferrer,
1174 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1179 acceptTypesCount = 0;
1180 while (szAcceptTypes[acceptTypesCount])
1182 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1185 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1187 HeapFree(GetProcessHeap(), 0, szReferrer);
1188 HeapFree(GetProcessHeap(), 0, szVersion);
1189 HeapFree(GetProcessHeap(), 0, szObjectName);
1190 HeapFree(GetProcessHeap(), 0, szVerb);
1195 /***********************************************************************
1198 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1201 static const CHAR HTTP_Base64Enc[] =
1202 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1206 /* first 6 bits, all from bin[0] */
1207 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1208 x = (bin[0] & 3) << 4;
1210 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1213 base64[n++] = HTTP_Base64Enc[x];
1218 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1219 x = ( bin[1] & 0x0f ) << 2;
1221 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1224 base64[n++] = HTTP_Base64Enc[x];
1228 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1230 /* last 6 bits, all from bin [2] */
1231 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1239 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1240 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1241 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1242 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1243 static const signed char HTTP_Base64Dec[256] =
1245 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1246 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1247 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1248 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1249 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1250 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1251 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1252 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1253 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1254 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1255 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1256 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1257 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1258 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1259 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1260 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1261 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1262 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1263 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1264 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1265 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1266 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1267 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1268 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1269 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1270 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1274 /***********************************************************************
1277 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1285 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1286 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1287 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1288 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1290 WARN("invalid base64: %s\n", debugstr_w(base64));
1294 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1297 if ((base64[2] == '=') && (base64[3] == '='))
1299 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1300 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1302 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1306 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1309 if (base64[3] == '=')
1311 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1312 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1314 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1318 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1327 /***********************************************************************
1328 * HTTP_InsertAuthorization
1330 * Insert or delete the authorization field in the request header.
1332 static BOOL HTTP_InsertAuthorization( LPWININETHTTPREQW lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1336 static const WCHAR wszSpace[] = {' ',0};
1337 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1339 WCHAR *authorization = NULL;
1341 if (pAuthInfo->auth_data_len)
1343 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1344 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1345 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1349 strcpyW(authorization, pAuthInfo->scheme);
1350 strcatW(authorization, wszSpace);
1351 HTTP_EncodeBase64(pAuthInfo->auth_data,
1352 pAuthInfo->auth_data_len,
1353 authorization+strlenW(authorization));
1355 /* clear the data as it isn't valid now that it has been sent to the
1356 * server, unless it's Basic authentication which doesn't do
1357 * connection tracking */
1358 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1360 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1361 pAuthInfo->auth_data = NULL;
1362 pAuthInfo->auth_data_len = 0;
1366 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1368 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1370 HeapFree(GetProcessHeap(), 0, authorization);
1375 static WCHAR *HTTP_BuildProxyRequestUrl(WININETHTTPREQW *req)
1377 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1380 size = sizeof(new_location);
1381 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1383 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1384 strcpyW( url, new_location );
1388 static const WCHAR slash[] = { '/',0 };
1389 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1390 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1391 WININETHTTPSESSIONW *session = req->lpHttpSession;
1393 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1394 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1396 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1398 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1399 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1401 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1402 if (req->lpszPath[0] != '/') strcatW( url, slash );
1403 strcatW( url, req->lpszPath );
1405 TRACE("url=%s\n", debugstr_w(url));
1409 /***********************************************************************
1410 * HTTP_DealWithProxy
1412 static BOOL HTTP_DealWithProxy( LPWININETAPPINFOW hIC,
1413 LPWININETHTTPSESSIONW lpwhs, LPWININETHTTPREQW lpwhr)
1415 WCHAR buf[MAXHOSTNAME];
1416 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1417 static WCHAR szNul[] = { 0 };
1418 URL_COMPONENTSW UrlComponents;
1419 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1420 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1422 memset( &UrlComponents, 0, sizeof UrlComponents );
1423 UrlComponents.dwStructSize = sizeof UrlComponents;
1424 UrlComponents.lpszHostName = buf;
1425 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1427 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1428 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1429 sprintfW(proxy, szFormat, hIC->lpszProxy);
1431 strcpyW(proxy, hIC->lpszProxy);
1432 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1434 if( UrlComponents.dwHostNameLength == 0 )
1437 if( !lpwhr->lpszPath )
1438 lpwhr->lpszPath = szNul;
1440 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1441 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1443 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1444 lpwhs->lpszServerName = WININET_strdupW(UrlComponents.lpszHostName);
1445 lpwhs->nServerPort = UrlComponents.nPort;
1447 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1451 static BOOL HTTP_ResolveName(LPWININETHTTPREQW lpwhr)
1454 LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
1456 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1457 INTERNET_STATUS_RESOLVING_NAME,
1458 lpwhs->lpszServerName,
1459 strlenW(lpwhs->lpszServerName)+1);
1461 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1462 &lpwhs->socketAddress))
1464 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1468 inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
1469 szaddr, sizeof(szaddr));
1470 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1471 INTERNET_STATUS_NAME_RESOLVED,
1472 szaddr, strlen(szaddr)+1);
1474 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1479 /***********************************************************************
1480 * HTTPREQ_Destroy (internal)
1482 * Deallocate request handle
1485 static void HTTPREQ_Destroy(WININETHANDLEHEADER *hdr)
1487 LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) hdr;
1492 if(lpwhr->hCacheFile)
1493 CloseHandle(lpwhr->hCacheFile);
1495 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1497 DeleteCriticalSection( &lpwhr->read_section );
1498 WININET_Release(&lpwhr->lpHttpSession->hdr);
1500 if (lpwhr->pAuthInfo)
1502 if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1503 DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1504 if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1505 FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1507 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1508 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1509 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1510 lpwhr->pAuthInfo = NULL;
1513 if (lpwhr->pProxyAuthInfo)
1515 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1516 DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1517 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1518 FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1520 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1521 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1522 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1523 lpwhr->pProxyAuthInfo = NULL;
1526 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1527 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1528 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1529 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1530 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1532 for (i = 0; i < lpwhr->nCustHeaders; i++)
1534 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1535 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1538 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1539 HeapFree(GetProcessHeap(), 0, lpwhr);
1542 static void HTTPREQ_CloseConnection(WININETHANDLEHEADER *hdr)
1544 LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) hdr;
1546 TRACE("%p\n",lpwhr);
1549 if(lpwhr->gzip_stream) {
1550 inflateEnd(&lpwhr->gzip_stream->zstream);
1551 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1552 lpwhr->gzip_stream = NULL;
1556 if (!NETCON_connected(&lpwhr->netConnection))
1559 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1560 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1562 NETCON_close(&lpwhr->netConnection);
1564 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1565 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1568 static BOOL HTTP_GetRequestURL(WININETHTTPREQW *req, LPWSTR buf)
1570 LPHTTPHEADERW host_header;
1572 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1574 host_header = HTTP_GetHeader(req, hostW);
1578 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1582 static DWORD HTTPREQ_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1584 WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
1587 case INTERNET_OPTION_SECURITY_FLAGS:
1589 LPWININETHTTPSESSIONW lpwhs;
1590 lpwhs = req->lpHttpSession;
1592 if (*size < sizeof(ULONG))
1593 return ERROR_INSUFFICIENT_BUFFER;
1595 *size = sizeof(DWORD);
1596 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1597 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1599 *(DWORD*)buffer = 0;
1600 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1601 return ERROR_SUCCESS;
1604 case INTERNET_OPTION_HANDLE_TYPE:
1605 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1607 if (*size < sizeof(ULONG))
1608 return ERROR_INSUFFICIENT_BUFFER;
1610 *size = sizeof(DWORD);
1611 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1612 return ERROR_SUCCESS;
1614 case INTERNET_OPTION_URL: {
1615 WCHAR url[INTERNET_MAX_URL_LENGTH];
1620 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1622 TRACE("INTERNET_OPTION_URL\n");
1624 host = HTTP_GetHeader(req, hostW);
1625 strcpyW(url, httpW);
1626 strcatW(url, host->lpszValue);
1627 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1629 strcatW(url, req->lpszPath);
1631 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1634 len = (strlenW(url)+1) * sizeof(WCHAR);
1636 return ERROR_INSUFFICIENT_BUFFER;
1639 strcpyW(buffer, url);
1640 return ERROR_SUCCESS;
1642 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1644 return ERROR_INSUFFICIENT_BUFFER;
1647 return ERROR_SUCCESS;
1651 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1652 INTERNET_CACHE_ENTRY_INFOW *info;
1653 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1654 WCHAR url[INTERNET_MAX_URL_LENGTH];
1655 DWORD nbytes, error;
1658 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1660 if (*size < sizeof(*ts))
1662 *size = sizeof(*ts);
1663 return ERROR_INSUFFICIENT_BUFFER;
1666 HTTP_GetRequestURL(req, url);
1667 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1668 error = GetLastError();
1669 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1671 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1672 return ERROR_OUTOFMEMORY;
1674 GetUrlCacheEntryInfoW(url, info, &nbytes);
1676 ts->ftExpires = info->ExpireTime;
1677 ts->ftLastModified = info->LastModifiedTime;
1679 HeapFree(GetProcessHeap(), 0, info);
1680 *size = sizeof(*ts);
1681 return ERROR_SUCCESS;
1686 case INTERNET_OPTION_DATAFILE_NAME: {
1689 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1691 if(!req->lpszCacheFile) {
1693 return ERROR_INTERNET_ITEM_NOT_FOUND;
1697 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1698 if(*size < req_size)
1699 return ERROR_INSUFFICIENT_BUFFER;
1702 memcpy(buffer, req->lpszCacheFile, *size);
1703 return ERROR_SUCCESS;
1705 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1706 if (req_size > *size)
1707 return ERROR_INSUFFICIENT_BUFFER;
1709 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1710 -1, buffer, *size, NULL, NULL);
1711 return ERROR_SUCCESS;
1715 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1716 PCCERT_CONTEXT context;
1718 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1719 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1720 return ERROR_INSUFFICIENT_BUFFER;
1723 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1725 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1728 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1729 info->ftExpiry = context->pCertInfo->NotAfter;
1730 info->ftStart = context->pCertInfo->NotBefore;
1732 len = CertNameToStrW(context->dwCertEncodingType,
1733 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1734 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1735 if(info->lpszSubjectInfo)
1736 CertNameToStrW(context->dwCertEncodingType,
1737 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1738 info->lpszSubjectInfo, len);
1739 len = CertNameToStrW(context->dwCertEncodingType,
1740 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1741 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1742 if (info->lpszIssuerInfo)
1743 CertNameToStrW(context->dwCertEncodingType,
1744 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1745 info->lpszIssuerInfo, len);
1747 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1749 len = CertNameToStrA(context->dwCertEncodingType,
1750 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1751 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1752 if(infoA->lpszSubjectInfo)
1753 CertNameToStrA(context->dwCertEncodingType,
1754 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1755 infoA->lpszSubjectInfo, len);
1756 len = CertNameToStrA(context->dwCertEncodingType,
1757 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1758 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1759 if(infoA->lpszIssuerInfo)
1760 CertNameToStrA(context->dwCertEncodingType,
1761 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1762 infoA->lpszIssuerInfo, len);
1766 * Contrary to MSDN, these do not appear to be set.
1768 * lpszSignatureAlgName
1769 * lpszEncryptionAlgName
1772 CertFreeCertificateContext(context);
1773 return ERROR_SUCCESS;
1778 return INET_QueryOption(option, buffer, size, unicode);
1781 static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD size)
1783 WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
1786 case INTERNET_OPTION_SEND_TIMEOUT:
1787 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1788 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1790 if (size != sizeof(DWORD))
1791 return ERROR_INVALID_PARAMETER;
1793 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1796 case INTERNET_OPTION_USERNAME:
1797 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1798 if (!(req->lpHttpSession->lpszUserName = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1799 return ERROR_SUCCESS;
1801 case INTERNET_OPTION_PASSWORD:
1802 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1803 if (!(req->lpHttpSession->lpszPassword = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1804 return ERROR_SUCCESS;
1805 case INTERNET_OPTION_HTTP_DECODING:
1806 if(size != sizeof(BOOL))
1807 return ERROR_INVALID_PARAMETER;
1808 req->decoding = *(BOOL*)buffer;
1809 return ERROR_SUCCESS;
1812 return ERROR_INTERNET_INVALID_OPTION;
1815 /* read some more data into the read buffer (the read section must be held) */
1816 static BOOL read_more_data( WININETHTTPREQW *req, int maxlen )
1822 /* move existing data to the start of the buffer */
1824 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1828 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1830 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1831 maxlen - req->read_size, 0, &len ))
1834 req->read_size += len;
1838 /* remove some amount of data from the read buffer (the read section must be held) */
1839 static void remove_data( WININETHTTPREQW *req, int count )
1841 if (!(req->read_size -= count)) req->read_pos = 0;
1842 else req->read_pos += count;
1845 static BOOL read_line( WININETHTTPREQW *req, LPSTR buffer, DWORD *len )
1847 int count, bytes_read, pos = 0;
1849 EnterCriticalSection( &req->read_section );
1852 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1856 count = eol - (req->read_buf + req->read_pos);
1857 bytes_read = count + 1;
1859 else count = bytes_read = req->read_size;
1861 count = min( count, *len - pos );
1862 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1864 remove_data( req, bytes_read );
1867 if (!read_more_data( req, -1 ) || !req->read_size)
1870 TRACE( "returning empty string\n" );
1871 LeaveCriticalSection( &req->read_section );
1875 LeaveCriticalSection( &req->read_section );
1879 if (pos && buffer[pos - 1] == '\r') pos--;
1882 buffer[*len - 1] = 0;
1883 TRACE( "returning %s\n", debugstr_a(buffer));
1887 /* discard data contents until we reach end of line (the read section must be held) */
1888 static BOOL discard_eol( WININETHTTPREQW *req )
1892 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1895 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1898 req->read_pos = req->read_size = 0; /* discard everything */
1899 if (!read_more_data( req, -1 )) return FALSE;
1900 } while (req->read_size);
1904 /* read the size of the next chunk (the read section must be held) */
1905 static BOOL start_next_chunk( WININETHTTPREQW *req )
1907 DWORD chunk_size = 0;
1909 if (!req->dwContentLength) return TRUE;
1910 if (req->dwContentLength == req->dwContentRead)
1912 /* read terminator for the previous chunk */
1913 if (!discard_eol( req )) return FALSE;
1914 req->dwContentLength = ~0u;
1915 req->dwContentRead = 0;
1919 while (req->read_size)
1921 char ch = req->read_buf[req->read_pos];
1922 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1923 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1924 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1925 else if (ch == ';' || ch == '\r' || ch == '\n')
1927 TRACE( "reading %u byte chunk\n", chunk_size );
1928 req->dwContentLength = chunk_size;
1929 req->dwContentRead = 0;
1930 if (!discard_eol( req )) return FALSE;
1933 remove_data( req, 1 );
1935 if (!read_more_data( req, -1 )) return FALSE;
1936 if (!req->read_size)
1938 req->dwContentLength = req->dwContentRead = 0;
1944 /* check if we have reached the end of the data to read (the read section must be held) */
1945 static BOOL end_of_read_data( WININETHTTPREQW *req )
1947 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1948 if (req->read_chunked) return (req->dwContentLength == 0);
1949 if (req->dwContentLength == ~0u) return FALSE;
1950 return (req->dwContentLength == req->dwContentRead);
1953 /* fetch some more data into the read buffer (the read section must be held) */
1954 static BOOL refill_buffer( WININETHTTPREQW *req )
1956 int len = sizeof(req->read_buf);
1958 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1960 if (!start_next_chunk( req )) return FALSE;
1963 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1964 if (len <= req->read_size) return TRUE;
1966 if (!read_more_data( req, len )) return FALSE;
1967 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1971 static DWORD read_gzip_data(WININETHTTPREQW *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1973 DWORD ret = ERROR_SUCCESS;
1977 z_stream *zstream = &req->gzip_stream->zstream;
1980 while(read < size && !req->gzip_stream->end_of_data) {
1981 if(!req->read_size) {
1982 if(!sync || !refill_buffer(req))
1986 zstream->next_in = req->read_buf+req->read_pos;
1987 zstream->avail_in = req->read_size;
1988 zstream->next_out = buf+read;
1989 zstream->avail_out = size-read;
1990 zres = inflate(zstream, Z_FULL_FLUSH);
1991 read = size - zstream->avail_out;
1992 remove_data(req, req->read_size-zstream->avail_in);
1993 if(zres == Z_STREAM_END) {
1994 TRACE("end of data\n");
1995 req->gzip_stream->end_of_data = TRUE;
1996 }else if(zres != Z_OK) {
1997 WARN("inflate failed %d\n", zres);
1999 ret = ERROR_INTERNET_DECODING_FAILED;
2009 static void refill_gzip_buffer(WININETHTTPREQW *req)
2014 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2017 if(req->gzip_stream->buf_pos) {
2018 if(req->gzip_stream->buf_size)
2019 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2020 req->gzip_stream->buf_pos = 0;
2023 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2024 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2025 if(res == ERROR_SUCCESS)
2026 req->gzip_stream->buf_size += len;
2029 /* return the size of data available to be read immediately (the read section must be held) */
2030 static DWORD get_avail_data( WININETHTTPREQW *req )
2032 if (req->gzip_stream) {
2033 refill_gzip_buffer(req);
2034 return req->gzip_stream->buf_size;
2036 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2038 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2041 static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif)
2043 INTERNET_ASYNC_RESULT iar;
2047 EnterCriticalSection( &req->read_section );
2048 if (refill_buffer( req )) {
2049 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2050 iar.dwError = first_notif ? 0 : get_avail_data(req);
2053 iar.dwError = INTERNET_GetLastError();
2055 LeaveCriticalSection( &req->read_section );
2057 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2058 sizeof(INTERNET_ASYNC_RESULT));
2061 /* read data from the http connection (the read section must be held) */
2062 static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2064 BOOL finished_reading = FALSE;
2065 int len, bytes_read = 0;
2066 DWORD ret = ERROR_SUCCESS;
2068 EnterCriticalSection( &req->read_section );
2070 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2072 if (!start_next_chunk( req )) goto done;
2075 if(req->gzip_stream) {
2076 if(req->gzip_stream->buf_size) {
2077 bytes_read = min(req->gzip_stream->buf_size, size);
2078 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2079 req->gzip_stream->buf_pos += bytes_read;
2080 req->gzip_stream->buf_size -= bytes_read;
2081 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2085 if(size > bytes_read) {
2086 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2087 if(ret == ERROR_SUCCESS)
2091 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2093 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2095 if (req->read_size) {
2096 bytes_read = min( req->read_size, size );
2097 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2098 remove_data( req, bytes_read );
2101 if (size > bytes_read && (!bytes_read || sync)) {
2102 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2103 sync ? MSG_WAITALL : 0, &len))
2105 /* always return success, even if the network layer returns an error */
2108 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2111 req->dwContentRead += bytes_read;
2114 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2115 LeaveCriticalSection( &req->read_section );
2117 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2119 DWORD dwBytesWritten;
2121 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2123 WARN("WriteFile failed: %u\n", GetLastError());
2126 if(finished_reading)
2127 HTTP_FinishedReading(req);
2133 static DWORD HTTPREQ_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
2135 WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
2136 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2139 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2141 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2142 WININETHTTPREQW *req = (WININETHTTPREQW*)workRequest->hdr;
2143 INTERNET_ASYNC_RESULT iar;
2146 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2148 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2149 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2151 iar.dwResult = res == ERROR_SUCCESS;
2154 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2155 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2156 sizeof(INTERNET_ASYNC_RESULT));
2159 static DWORD HTTPREQ_ReadFileExA(WININETHANDLEHEADER *hdr, INTERNET_BUFFERSA *buffers,
2160 DWORD flags, DWORD_PTR context)
2163 WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
2166 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2167 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2169 if (buffers->dwStructSize != sizeof(*buffers))
2170 return ERROR_INVALID_PARAMETER;
2172 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2174 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2176 WORKREQUEST workRequest;
2178 if (TryEnterCriticalSection( &req->read_section ))
2180 if (get_avail_data(req))
2182 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2183 &buffers->dwBufferLength, FALSE);
2184 LeaveCriticalSection( &req->read_section );
2187 LeaveCriticalSection( &req->read_section );
2190 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2191 workRequest.hdr = WININET_AddRef(&req->hdr);
2192 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2194 INTERNET_AsyncCall(&workRequest);
2196 return ERROR_IO_PENDING;
2199 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2200 !(flags & IRF_NO_WAIT));
2203 if (res == ERROR_SUCCESS) {
2204 DWORD size = buffers->dwBufferLength;
2205 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2206 &size, sizeof(size));
2212 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2214 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2215 WININETHTTPREQW *req = (WININETHTTPREQW*)workRequest->hdr;
2216 INTERNET_ASYNC_RESULT iar;
2219 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2221 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2222 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2224 iar.dwResult = res == ERROR_SUCCESS;
2227 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2228 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2229 sizeof(INTERNET_ASYNC_RESULT));
2232 static DWORD HTTPREQ_ReadFileExW(WININETHANDLEHEADER *hdr, INTERNET_BUFFERSW *buffers,
2233 DWORD flags, DWORD_PTR context)
2236 WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
2239 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2240 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2242 if (buffers->dwStructSize != sizeof(*buffers))
2243 return ERROR_INVALID_PARAMETER;
2245 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2247 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2249 WORKREQUEST workRequest;
2251 if (TryEnterCriticalSection( &req->read_section ))
2253 if (get_avail_data(req))
2255 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2256 &buffers->dwBufferLength, FALSE);
2257 LeaveCriticalSection( &req->read_section );
2260 LeaveCriticalSection( &req->read_section );
2263 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2264 workRequest.hdr = WININET_AddRef(&req->hdr);
2265 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2267 INTERNET_AsyncCall(&workRequest);
2269 return ERROR_IO_PENDING;
2272 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2273 !(flags & IRF_NO_WAIT));
2276 if (res == ERROR_SUCCESS) {
2277 DWORD size = buffers->dwBufferLength;
2278 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2279 &size, sizeof(size));
2285 static BOOL HTTPREQ_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
2288 LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW)hdr;
2290 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2293 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2294 lpwhr->dwBytesWritten += *written;
2296 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2300 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2302 WININETHTTPREQW *req = (WININETHTTPREQW*)workRequest->hdr;
2304 HTTP_ReceiveRequestData(req, FALSE);
2307 static DWORD HTTPREQ_QueryDataAvailable(WININETHANDLEHEADER *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2309 WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
2311 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2313 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2315 WORKREQUEST workRequest;
2317 /* never wait, if we can't enter the section we queue an async request right away */
2318 if (TryEnterCriticalSection( &req->read_section ))
2320 if ((*available = get_avail_data( req ))) goto done;
2321 if (end_of_read_data( req )) goto done;
2322 LeaveCriticalSection( &req->read_section );
2325 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2326 workRequest.hdr = WININET_AddRef( &req->hdr );
2328 INTERNET_AsyncCall(&workRequest);
2330 return ERROR_IO_PENDING;
2333 EnterCriticalSection( &req->read_section );
2335 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2337 refill_buffer( req );
2338 *available = get_avail_data( req );
2342 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2345 if (NETCON_query_data_available(&req->netConnection, &extra))
2346 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2348 LeaveCriticalSection( &req->read_section );
2350 TRACE( "returning %u\n", *available );
2351 return ERROR_SUCCESS;
2354 static const HANDLEHEADERVtbl HTTPREQVtbl = {
2356 HTTPREQ_CloseConnection,
2357 HTTPREQ_QueryOption,
2360 HTTPREQ_ReadFileExA,
2361 HTTPREQ_ReadFileExW,
2363 HTTPREQ_QueryDataAvailable,
2367 /***********************************************************************
2368 * HTTP_HttpOpenRequestW (internal)
2370 * Open a HTTP request handle
2373 * HINTERNET a HTTP request handle on success
2377 HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
2378 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2379 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2380 DWORD dwFlags, DWORD_PTR dwContext)
2382 LPWININETAPPINFOW hIC = NULL;
2383 LPWININETHTTPREQW lpwhr;
2384 LPWSTR lpszHostName = NULL;
2385 HINTERNET handle = NULL;
2386 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2391 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2392 hIC = lpwhs->lpAppInfo;
2394 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPREQW));
2397 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2400 lpwhr->hdr.htype = WH_HHTTPREQ;
2401 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2402 lpwhr->hdr.dwFlags = dwFlags;
2403 lpwhr->hdr.dwContext = dwContext;
2404 lpwhr->hdr.refs = 1;
2405 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2406 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2407 lpwhr->dwContentLength = ~0u;
2408 InitializeCriticalSection( &lpwhr->read_section );
2410 WININET_AddRef( &lpwhs->hdr );
2411 lpwhr->lpHttpSession = lpwhs;
2412 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2414 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2415 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2416 if (NULL == lpszHostName)
2418 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2422 handle = WININET_AllocHandle( &lpwhr->hdr );
2425 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2429 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2431 InternetCloseHandle( handle );
2436 if (lpszObjectName && *lpszObjectName) {
2440 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2441 if (rc != E_POINTER)
2442 len = strlenW(lpszObjectName)+1;
2443 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2444 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2445 URL_ESCAPE_SPACES_ONLY);
2448 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2449 strcpyW(lpwhr->lpszPath,lpszObjectName);
2452 static const WCHAR slashW[] = {'/',0};
2454 lpwhr->lpszPath = WININET_strdupW(slashW);
2457 if (lpszReferrer && *lpszReferrer)
2458 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2460 if (lpszAcceptTypes)
2463 for (i = 0; lpszAcceptTypes[i]; i++)
2465 if (!*lpszAcceptTypes[i]) continue;
2466 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2467 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2468 HTTP_ADDHDR_FLAG_REQ |
2469 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2473 lpwhr->lpszVerb = WININET_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2476 lpwhr->lpszVersion = WININET_strdupW(lpszVersion);
2478 lpwhr->lpszVersion = WININET_strdupW(g_szHttp1_1);
2480 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2481 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2482 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2484 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2485 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2486 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2489 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2490 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2492 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2493 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2494 INTERNET_DEFAULT_HTTPS_PORT :
2495 INTERNET_DEFAULT_HTTP_PORT);
2497 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2498 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2499 INTERNET_DEFAULT_HTTPS_PORT :
2500 INTERNET_DEFAULT_HTTP_PORT);
2502 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2503 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2505 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2506 INTERNET_STATUS_HANDLE_CREATED, &handle,
2510 HeapFree(GetProcessHeap(), 0, lpszHostName);
2512 WININET_Release( &lpwhr->hdr );
2514 TRACE("<-- %p (%p)\n", handle, lpwhr);
2518 /* read any content returned by the server so that the connection can be
2520 static void HTTP_DrainContent(WININETHTTPREQW *req)
2524 if (!NETCON_connected(&req->netConnection)) return;
2526 if (req->dwContentLength == -1)
2528 NETCON_close(&req->netConnection);
2535 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2537 } while (bytes_read);
2540 static const LPCWSTR header_lookup[] = {
2541 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2542 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2543 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2544 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2545 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2546 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2547 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2548 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2549 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2550 szDate, /* HTTP_QUERY_DATE = 9 */
2551 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2552 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2553 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2554 szURI, /* HTTP_QUERY_URI = 13 */
2555 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2556 NULL, /* HTTP_QUERY_COST = 15 */
2557 NULL, /* HTTP_QUERY_LINK = 16 */
2558 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2559 NULL, /* HTTP_QUERY_VERSION = 18 */
2560 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2561 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2562 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2563 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2564 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2565 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2566 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2567 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2568 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2569 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2570 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2571 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2572 NULL, /* HTTP_QUERY_FROM = 31 */
2573 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2574 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2575 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2576 szReferer, /* HTTP_QUERY_REFERER = 35 */
2577 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2578 szServer, /* HTTP_QUERY_SERVER = 37 */
2579 NULL, /* HTTP_TITLE = 38 */
2580 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2581 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2582 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2583 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2584 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2585 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2586 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2587 NULL, /* HTTP_QUERY_REFRESH = 46 */
2588 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2589 szAge, /* HTTP_QUERY_AGE = 48 */
2590 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2591 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2592 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2593 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2594 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2595 szETag, /* HTTP_QUERY_ETAG = 54 */
2596 hostW, /* HTTP_QUERY_HOST = 55 */
2597 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2598 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2599 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2600 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2601 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2602 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2603 szRange, /* HTTP_QUERY_RANGE = 62 */
2604 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2605 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2606 szVary, /* HTTP_QUERY_VARY = 65 */
2607 szVia, /* HTTP_QUERY_VIA = 66 */
2608 szWarning, /* HTTP_QUERY_WARNING = 67 */
2609 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2610 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2611 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2614 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2616 /***********************************************************************
2617 * HTTP_HttpQueryInfoW (internal)
2619 static BOOL HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel,
2620 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2622 LPHTTPHEADERW lphttpHdr = NULL;
2623 BOOL bSuccess = FALSE;
2624 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2625 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2626 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2629 /* Find requested header structure */
2632 case HTTP_QUERY_CUSTOM:
2633 if (!lpBuffer) return FALSE;
2634 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2637 case HTTP_QUERY_CONTENT_LENGTH:
2638 if(lpwhr->gzip_stream) {
2639 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2643 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2644 requested_index,request_only);
2647 case HTTP_QUERY_RAW_HEADERS_CRLF:
2654 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2656 headers = lpwhr->lpszRawHeaders;
2659 len = strlenW(headers) * sizeof(WCHAR);
2661 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2663 len += sizeof(WCHAR);
2664 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2670 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2673 len = strlenW(szCrLf) * sizeof(WCHAR);
2674 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2676 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2679 *lpdwBufferLength = len;
2682 HeapFree(GetProcessHeap(), 0, headers);
2685 case HTTP_QUERY_RAW_HEADERS:
2687 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2689 LPWSTR pszString = lpBuffer;
2691 for (i = 0; ppszRawHeaderLines[i]; i++)
2692 size += strlenW(ppszRawHeaderLines[i]) + 1;
2694 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2696 HTTP_FreeTokens(ppszRawHeaderLines);
2697 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2698 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2703 for (i = 0; ppszRawHeaderLines[i]; i++)
2705 DWORD len = strlenW(ppszRawHeaderLines[i]);
2706 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2710 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2712 *lpdwBufferLength = size * sizeof(WCHAR);
2713 HTTP_FreeTokens(ppszRawHeaderLines);
2717 case HTTP_QUERY_STATUS_TEXT:
2718 if (lpwhr->lpszStatusText)
2720 DWORD len = strlenW(lpwhr->lpszStatusText);
2721 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2723 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2724 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2729 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2730 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2732 *lpdwBufferLength = len * sizeof(WCHAR);
2736 case HTTP_QUERY_VERSION:
2737 if (lpwhr->lpszVersion)
2739 DWORD len = strlenW(lpwhr->lpszVersion);
2740 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2742 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2743 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2748 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2749 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2751 *lpdwBufferLength = len * sizeof(WCHAR);
2755 case HTTP_QUERY_CONTENT_ENCODING:
2756 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2757 requested_index,request_only);
2760 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2762 if (level < LAST_TABLE_HEADER && header_lookup[level])
2763 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2764 requested_index,request_only);
2768 lphttpHdr = &lpwhr->pCustHeaders[index];
2770 /* Ensure header satisfies requested attributes */
2772 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2773 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2775 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2779 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2781 /* coalesce value to requested type */
2782 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2784 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2785 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2788 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2794 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2796 tmpTM = *gmtime(&tmpTime);
2797 STHook = (SYSTEMTIME *)lpBuffer;
2798 STHook->wDay = tmpTM.tm_mday;
2799 STHook->wHour = tmpTM.tm_hour;
2800 STHook->wMilliseconds = 0;
2801 STHook->wMinute = tmpTM.tm_min;
2802 STHook->wDayOfWeek = tmpTM.tm_wday;
2803 STHook->wMonth = tmpTM.tm_mon + 1;
2804 STHook->wSecond = tmpTM.tm_sec;
2805 STHook->wYear = tmpTM.tm_year;
2808 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2809 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2810 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2812 else if (lphttpHdr->lpszValue)
2814 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2816 if (len > *lpdwBufferLength)
2818 *lpdwBufferLength = len;
2819 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2824 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2825 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2827 *lpdwBufferLength = len - sizeof(WCHAR);
2833 /***********************************************************************
2834 * HttpQueryInfoW (WININET.@)
2836 * Queries for information about an HTTP request
2843 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2844 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2846 BOOL bSuccess = FALSE;
2847 LPWININETHTTPREQW lpwhr;
2849 if (TRACE_ON(wininet)) {
2850 #define FE(x) { x, #x }
2851 static const wininet_flag_info query_flags[] = {
2852 FE(HTTP_QUERY_MIME_VERSION),
2853 FE(HTTP_QUERY_CONTENT_TYPE),
2854 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2855 FE(HTTP_QUERY_CONTENT_ID),
2856 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2857 FE(HTTP_QUERY_CONTENT_LENGTH),
2858 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2859 FE(HTTP_QUERY_ALLOW),
2860 FE(HTTP_QUERY_PUBLIC),
2861 FE(HTTP_QUERY_DATE),
2862 FE(HTTP_QUERY_EXPIRES),
2863 FE(HTTP_QUERY_LAST_MODIFIED),
2864 FE(HTTP_QUERY_MESSAGE_ID),
2866 FE(HTTP_QUERY_DERIVED_FROM),
2867 FE(HTTP_QUERY_COST),
2868 FE(HTTP_QUERY_LINK),
2869 FE(HTTP_QUERY_PRAGMA),
2870 FE(HTTP_QUERY_VERSION),
2871 FE(HTTP_QUERY_STATUS_CODE),
2872 FE(HTTP_QUERY_STATUS_TEXT),
2873 FE(HTTP_QUERY_RAW_HEADERS),
2874 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2875 FE(HTTP_QUERY_CONNECTION),
2876 FE(HTTP_QUERY_ACCEPT),
2877 FE(HTTP_QUERY_ACCEPT_CHARSET),
2878 FE(HTTP_QUERY_ACCEPT_ENCODING),
2879 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2880 FE(HTTP_QUERY_AUTHORIZATION),
2881 FE(HTTP_QUERY_CONTENT_ENCODING),
2882 FE(HTTP_QUERY_FORWARDED),
2883 FE(HTTP_QUERY_FROM),
2884 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2885 FE(HTTP_QUERY_LOCATION),
2886 FE(HTTP_QUERY_ORIG_URI),
2887 FE(HTTP_QUERY_REFERER),
2888 FE(HTTP_QUERY_RETRY_AFTER),
2889 FE(HTTP_QUERY_SERVER),
2890 FE(HTTP_QUERY_TITLE),
2891 FE(HTTP_QUERY_USER_AGENT),
2892 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2893 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2894 FE(HTTP_QUERY_ACCEPT_RANGES),
2895 FE(HTTP_QUERY_SET_COOKIE),
2896 FE(HTTP_QUERY_COOKIE),
2897 FE(HTTP_QUERY_REQUEST_METHOD),
2898 FE(HTTP_QUERY_REFRESH),
2899 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2901 FE(HTTP_QUERY_CACHE_CONTROL),
2902 FE(HTTP_QUERY_CONTENT_BASE),
2903 FE(HTTP_QUERY_CONTENT_LOCATION),
2904 FE(HTTP_QUERY_CONTENT_MD5),
2905 FE(HTTP_QUERY_CONTENT_RANGE),
2906 FE(HTTP_QUERY_ETAG),
2907 FE(HTTP_QUERY_HOST),
2908 FE(HTTP_QUERY_IF_MATCH),
2909 FE(HTTP_QUERY_IF_NONE_MATCH),
2910 FE(HTTP_QUERY_IF_RANGE),
2911 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2912 FE(HTTP_QUERY_MAX_FORWARDS),
2913 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2914 FE(HTTP_QUERY_RANGE),
2915 FE(HTTP_QUERY_TRANSFER_ENCODING),
2916 FE(HTTP_QUERY_UPGRADE),
2917 FE(HTTP_QUERY_VARY),
2919 FE(HTTP_QUERY_WARNING),
2920 FE(HTTP_QUERY_CUSTOM)
2922 static const wininet_flag_info modifier_flags[] = {
2923 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2924 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2925 FE(HTTP_QUERY_FLAG_NUMBER),
2926 FE(HTTP_QUERY_FLAG_COALESCE)
2929 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2930 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2933 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2934 TRACE(" Attribute:");
2935 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2936 if (query_flags[i].val == info) {
2937 TRACE(" %s", query_flags[i].name);
2941 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2942 TRACE(" Unknown (%08x)", info);
2945 TRACE(" Modifier:");
2946 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2947 if (modifier_flags[i].val & info_mod) {
2948 TRACE(" %s", modifier_flags[i].name);
2949 info_mod &= ~ modifier_flags[i].val;
2954 TRACE(" Unknown (%08x)", info_mod);
2959 lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
2960 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2962 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2966 if (lpBuffer == NULL)
2967 *lpdwBufferLength = 0;
2968 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2969 lpBuffer, lpdwBufferLength, lpdwIndex);
2973 WININET_Release( &lpwhr->hdr );
2975 TRACE("%d <--\n", bSuccess);
2979 /***********************************************************************
2980 * HttpQueryInfoA (WININET.@)
2982 * Queries for information about an HTTP request
2989 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2990 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2996 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
2997 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
2999 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3000 lpdwBufferLength, lpdwIndex );
3006 len = (*lpdwBufferLength)*sizeof(WCHAR);
3007 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3009 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3015 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3016 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3017 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3018 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3025 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3029 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3030 lpBuffer, *lpdwBufferLength, NULL, NULL );
3031 *lpdwBufferLength = len - 1;
3033 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3036 /* since the strings being returned from HttpQueryInfoW should be
3037 * only ASCII characters, it is reasonable to assume that all of
3038 * the Unicode characters can be reduced to a single byte */
3039 *lpdwBufferLength = len / sizeof(WCHAR);
3041 HeapFree(GetProcessHeap(), 0, bufferW );
3046 /***********************************************************************
3047 * HttpSendRequestExA (WININET.@)
3049 * Sends the specified request to the HTTP server and allows chunked
3054 * Failure: FALSE, call GetLastError() for more information.
3056 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3057 LPINTERNET_BUFFERSA lpBuffersIn,
3058 LPINTERNET_BUFFERSA lpBuffersOut,
3059 DWORD dwFlags, DWORD_PTR dwContext)
3061 INTERNET_BUFFERSW BuffersInW;
3064 LPWSTR header = NULL;
3066 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3067 lpBuffersOut, dwFlags, dwContext);
3071 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3072 if (lpBuffersIn->lpcszHeader)
3074 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3075 lpBuffersIn->dwHeadersLength,0,0);
3076 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3077 if (!(BuffersInW.lpcszHeader = header))
3079 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3082 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3083 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3087 BuffersInW.lpcszHeader = NULL;
3088 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3089 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3090 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3091 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3092 BuffersInW.Next = NULL;
3095 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3097 HeapFree(GetProcessHeap(),0,header);
3102 /***********************************************************************
3103 * HttpSendRequestExW (WININET.@)
3105 * Sends the specified request to the HTTP server and allows chunked
3110 * Failure: FALSE, call GetLastError() for more information.
3112 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3113 LPINTERNET_BUFFERSW lpBuffersIn,
3114 LPINTERNET_BUFFERSW lpBuffersOut,
3115 DWORD dwFlags, DWORD_PTR dwContext)
3118 LPWININETHTTPREQW lpwhr;
3119 LPWININETHTTPSESSIONW lpwhs;
3120 LPWININETAPPINFOW hIC;
3122 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3123 lpBuffersOut, dwFlags, dwContext);
3125 lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
3127 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3129 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3133 lpwhs = lpwhr->lpHttpSession;
3134 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3135 hIC = lpwhs->lpAppInfo;
3136 assert(hIC->hdr.htype == WH_HINIT);
3138 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3140 WORKREQUEST workRequest;
3141 struct WORKREQ_HTTPSENDREQUESTW *req;
3143 workRequest.asyncproc = AsyncHttpSendRequestProc;
3144 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3145 req = &workRequest.u.HttpSendRequestW;
3148 if (lpBuffersIn->lpcszHeader)
3149 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3150 req->lpszHeader = WININET_strdupW(lpBuffersIn->lpcszHeader);
3152 req->lpszHeader = NULL;
3153 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3154 req->lpOptional = lpBuffersIn->lpvBuffer;
3155 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3156 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3160 req->lpszHeader = NULL;
3161 req->dwHeaderLength = 0;
3162 req->lpOptional = NULL;
3163 req->dwOptionalLength = 0;
3164 req->dwContentLength = 0;
3167 req->bEndRequest = FALSE;
3169 INTERNET_AsyncCall(&workRequest);
3171 * This is from windows.
3173 INTERNET_SetLastError(ERROR_IO_PENDING);
3178 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3179 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3180 lpBuffersIn->dwBufferTotal, FALSE);
3182 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3187 WININET_Release( &lpwhr->hdr );
3193 /***********************************************************************
3194 * HttpSendRequestW (WININET.@)
3196 * Sends the specified request to the HTTP server
3203 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3204 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3206 LPWININETHTTPREQW lpwhr;
3207 LPWININETHTTPSESSIONW lpwhs = NULL;
3208 LPWININETAPPINFOW hIC = NULL;
3211 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3212 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3214 lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
3215 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3217 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3222 lpwhs = lpwhr->lpHttpSession;
3223 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3225 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3230 hIC = lpwhs->lpAppInfo;
3231 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3233 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3238 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3240 WORKREQUEST workRequest;
3241 struct WORKREQ_HTTPSENDREQUESTW *req;
3243 workRequest.asyncproc = AsyncHttpSendRequestProc;
3244 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3245 req = &workRequest.u.HttpSendRequestW;
3250 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3251 else size = dwHeaderLength * sizeof(WCHAR);
3253 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3254 memcpy(req->lpszHeader, lpszHeaders, size);
3257 req->lpszHeader = 0;
3258 req->dwHeaderLength = dwHeaderLength;
3259 req->lpOptional = lpOptional;
3260 req->dwOptionalLength = dwOptionalLength;
3261 req->dwContentLength = dwOptionalLength;
3262 req->bEndRequest = TRUE;
3264 INTERNET_AsyncCall(&workRequest);
3266 * This is from windows.
3268 INTERNET_SetLastError(ERROR_IO_PENDING);
3273 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3274 dwHeaderLength, lpOptional, dwOptionalLength,
3275 dwOptionalLength, TRUE);
3279 WININET_Release( &lpwhr->hdr );
3283 /***********************************************************************
3284 * HttpSendRequestA (WININET.@)
3286 * Sends the specified request to the HTTP server
3293 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3294 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3297 LPWSTR szHeaders=NULL;
3298 DWORD nLen=dwHeaderLength;
3299 if(lpszHeaders!=NULL)
3301 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3302 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3303 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3305 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3306 HeapFree(GetProcessHeap(),0,szHeaders);
3310 /***********************************************************************
3311 * HTTP_GetRedirectURL (internal)
3313 static LPWSTR HTTP_GetRedirectURL(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl)
3315 static WCHAR szHttp[] = {'h','t','t','p',0};
3316 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3317 LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
3318 URL_COMPONENTSW urlComponents;
3319 DWORD url_length = 0;
3321 LPWSTR combined_url;
3323 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3324 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3325 urlComponents.dwSchemeLength = 0;
3326 urlComponents.lpszHostName = lpwhs->lpszHostName;
3327 urlComponents.dwHostNameLength = 0;
3328 urlComponents.nPort = lpwhs->nHostPort;
3329 urlComponents.lpszUserName = lpwhs->lpszUserName;
3330 urlComponents.dwUserNameLength = 0;
3331 urlComponents.lpszPassword = NULL;
3332 urlComponents.dwPasswordLength = 0;
3333 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3334 urlComponents.dwUrlPathLength = 0;
3335 urlComponents.lpszExtraInfo = NULL;
3336 urlComponents.dwExtraInfoLength = 0;
3338 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3339 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3342 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3344 /* convert from bytes to characters */
3345 url_length = url_length / sizeof(WCHAR) - 1;
3346 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3348 HeapFree(GetProcessHeap(), 0, orig_url);
3353 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3354 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3356 HeapFree(GetProcessHeap(), 0, orig_url);
3359 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3361 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3363 HeapFree(GetProcessHeap(), 0, orig_url);
3364 HeapFree(GetProcessHeap(), 0, combined_url);
3367 HeapFree(GetProcessHeap(), 0, orig_url);
3368 return combined_url;
3372 /***********************************************************************
3373 * HTTP_HandleRedirect (internal)
3375 static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl)
3377 LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
3378 LPWININETAPPINFOW hIC = lpwhs->lpAppInfo;
3379 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3380 WCHAR path[INTERNET_MAX_URL_LENGTH];
3385 /* if it's an absolute path, keep the same session info */
3386 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3390 URL_COMPONENTSW urlComponents;
3391 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3392 static WCHAR szHttp[] = {'h','t','t','p',0};
3393 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3399 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3400 urlComponents.lpszScheme = protocol;
3401 urlComponents.dwSchemeLength = 32;
3402 urlComponents.lpszHostName = hostName;
3403 urlComponents.dwHostNameLength = MAXHOSTNAME;
3404 urlComponents.lpszUserName = userName;
3405 urlComponents.dwUserNameLength = 1024;
3406 urlComponents.lpszPassword = NULL;
3407 urlComponents.dwPasswordLength = 0;
3408 urlComponents.lpszUrlPath = path;
3409 urlComponents.dwUrlPathLength = 2048;
3410 urlComponents.lpszExtraInfo = NULL;
3411 urlComponents.dwExtraInfoLength = 0;
3412 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3415 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3416 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3418 TRACE("redirect from secure page to non-secure page\n");
3419 /* FIXME: warn about from secure redirect to non-secure page */
3420 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3422 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3423 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3425 TRACE("redirect from non-secure page to secure page\n");
3426 /* FIXME: notify about redirect to secure page */
3427 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3430 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3432 if (lstrlenW(protocol)>4) /*https*/
3433 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3435 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3440 * This upsets redirects to binary files on sourceforge.net
3441 * and gives an html page instead of the target file
3442 * Examination of the HTTP request sent by native wininet.dll
3443 * reveals that it doesn't send a referrer in that case.
3444 * Maybe there's a flag that enables this, or maybe a referrer
3445 * shouldn't be added in case of a redirect.
3448 /* consider the current host as the referrer */
3449 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3450 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3451 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3452 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3455 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3456 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3457 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3460 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3461 len = lstrlenW(hostName);
3462 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3463 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3464 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3467 lpwhs->lpszHostName = WININET_strdupW(hostName);
3469 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3471 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3472 lpwhs->lpszUserName = NULL;
3474 lpwhs->lpszUserName = WININET_strdupW(userName);
3478 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3480 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3481 lpwhs->lpszServerName = WININET_strdupW(hostName);
3482 lpwhs->nServerPort = urlComponents.nPort;
3484 NETCON_close(&lpwhr->netConnection);
3485 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3486 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3487 lpwhr->read_pos = lpwhr->read_size = 0;
3488 lpwhr->read_chunked = FALSE;
3492 TRACE("Redirect through proxy\n");
3495 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3496 lpwhr->lpszPath=NULL;
3502 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3503 if (rc != E_POINTER)
3504 needed = strlenW(path)+1;
3505 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3506 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3507 URL_ESCAPE_SPACES_ONLY);
3510 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3511 strcpyW(lpwhr->lpszPath,path);
3515 /* Remove custom content-type/length headers on redirects. */
3516 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3518 HTTP_DeleteCustomHeader(lpwhr, index);
3519 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3521 HTTP_DeleteCustomHeader(lpwhr, index);
3526 /***********************************************************************
3527 * HTTP_build_req (internal)
3529 * concatenate all the strings in the request together
3531 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3536 for( t = list; *t ; t++ )
3537 len += strlenW( *t );
3540 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3543 for( t = list; *t ; t++ )
3549 static BOOL HTTP_SecureProxyConnect(LPWININETHTTPREQW lpwhr)
3552 LPWSTR requestString;
3558 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3559 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3560 LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
3564 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3565 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3566 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3567 HeapFree( GetProcessHeap(), 0, lpszPath );
3569 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3570 NULL, 0, NULL, NULL );
3571 len--; /* the nul terminator isn't needed */
3572 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3573 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3574 ascii_req, len, NULL, NULL );
3575 HeapFree( GetProcessHeap(), 0, requestString );
3577 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3579 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3580 HeapFree( GetProcessHeap(), 0, ascii_req );
3581 if (!ret || cnt < 0)
3584 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3591 static void HTTP_InsertCookies(LPWININETHTTPREQW lpwhr)
3593 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3594 LPWSTR lpszCookies, lpszUrl = NULL;
3595 DWORD nCookieSize, size;
3596 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3598 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3599 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3600 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3602 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3605 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3607 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3608 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3610 cnt += sprintfW(lpszCookies, szCookie);
3611 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3612 strcatW(lpszCookies, szCrLf);
3614 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3615 HeapFree(GetProcessHeap(), 0, lpszCookies);
3618 HeapFree(GetProcessHeap(), 0, lpszUrl);
3621 /***********************************************************************
3622 * HTTP_HttpSendRequestW (internal)
3624 * Sends the specified request to the HTTP server
3631 BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
3632 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3633 DWORD dwContentLength, BOOL bEndRequest)
3636 BOOL bSuccess = FALSE, redirected = FALSE;
3637 LPWSTR requestString = NULL;
3640 INTERNET_ASYNC_RESULT iar;
3641 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3642 static const WCHAR szContentLength[] =
3643 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3644 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3646 TRACE("--> %p\n", lpwhr);
3648 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3650 /* if the verb is NULL default to GET */
3651 if (!lpwhr->lpszVerb)
3652 lpwhr->lpszVerb = WININET_strdupW(szGET);
3654 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3656 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3657 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3658 lpwhr->dwBytesToWrite = dwContentLength;
3660 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3662 WCHAR *agent_header;
3663 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3666 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3667 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3668 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3670 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3671 HeapFree(GetProcessHeap(), 0, agent_header);
3673 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3675 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3676 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3678 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3680 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3681 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3682 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3692 /* like native, just in case the caller forgot to call InternetReadFile
3693 * for all the data */
3694 HTTP_DrainContent(lpwhr);
3695 lpwhr->dwContentRead = 0;
3697 if (TRACE_ON(wininet))
3699 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3700 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3704 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3706 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3708 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3709 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3711 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3712 HTTP_InsertCookies(lpwhr);
3714 /* add the headers the caller supplied */
3715 if( lpszHeaders && dwHeaderLength )
3717 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3718 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3721 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3723 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3724 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3725 HeapFree(GetProcessHeap(), 0, url);
3728 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3731 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3733 /* Send the request and store the results */
3734 if (!HTTP_OpenConnection(lpwhr))
3737 /* send the request as ASCII, tack on the optional data */
3738 if (!lpOptional || redirected)
3739 dwOptionalLength = 0;
3740 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3741 NULL, 0, NULL, NULL );
3742 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3743 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3744 ascii_req, len, NULL, NULL );
3746 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3747 len = (len + dwOptionalLength - 1);
3749 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3751 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3752 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3754 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3755 HeapFree( GetProcessHeap(), 0, ascii_req );
3757 lpwhr->dwBytesWritten = dwOptionalLength;
3759 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3760 INTERNET_STATUS_REQUEST_SENT,
3761 &len, sizeof(DWORD));
3768 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3769 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3774 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3778 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3779 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3782 HTTP_ProcessCookies(lpwhr);
3784 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3786 dwBufferSize = sizeof(dwStatusCode);
3787 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3788 &dwStatusCode,&dwBufferSize,NULL))
3791 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3793 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3794 dwBufferSize=sizeof(szNewLocation);
3795 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3796 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3798 /* redirects are always GETs */
3799 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3800 lpwhr->lpszVerb = WININET_strdupW(szGET);
3802 HTTP_DrainContent(lpwhr);
3803 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3805 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3806 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3807 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3810 HeapFree(GetProcessHeap(), 0, requestString);
3813 HeapFree( GetProcessHeap(), 0, new_url );
3818 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3820 WCHAR szAuthValue[2048];
3822 if (dwStatusCode == HTTP_STATUS_DENIED)
3825 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3827 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3829 lpwhr->lpHttpSession->lpszUserName,
3830 lpwhr->lpHttpSession->lpszPassword))
3837 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3840 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3842 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3843 &lpwhr->pProxyAuthInfo,
3844 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3845 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3860 WCHAR url[INTERNET_MAX_URL_LENGTH];
3861 WCHAR cacheFileName[MAX_PATH+1];
3864 b = HTTP_GetRequestURL(lpwhr, url);
3866 WARN("Could not get URL\n");
3870 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3872 lpwhr->lpszCacheFile = WININET_strdupW(cacheFileName);
3873 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3874 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3875 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3876 WARN("Could not create file: %u\n", GetLastError());
3877 lpwhr->hCacheFile = NULL;
3880 WARN("Could not create cache entry: %08x\n", GetLastError());
3886 HeapFree(GetProcessHeap(), 0, requestString);
3888 /* TODO: send notification for P3P header */
3890 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3894 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3897 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3900 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3901 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3902 sizeof(INTERNET_ASYNC_RESULT));
3907 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3908 iar.dwError = INTERNET_GetLastError();
3910 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3911 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3912 sizeof(INTERNET_ASYNC_RESULT));
3917 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3921 /***********************************************************************
3922 * HTTPSESSION_Destroy (internal)
3924 * Deallocate session handle
3927 static void HTTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
3929 LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW) hdr;
3931 TRACE("%p\n", lpwhs);
3933 WININET_Release(&lpwhs->lpAppInfo->hdr);
3935 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3936 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3937 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3938 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3939 HeapFree(GetProcessHeap(), 0, lpwhs);
3942 static DWORD HTTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3945 case INTERNET_OPTION_HANDLE_TYPE:
3946 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3948 if (*size < sizeof(ULONG))
3949 return ERROR_INSUFFICIENT_BUFFER;
3951 *size = sizeof(DWORD);
3952 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
3953 return ERROR_SUCCESS;
3956 return INET_QueryOption(option, buffer, size, unicode);
3959 static DWORD HTTPSESSION_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD size)
3961 WININETHTTPSESSIONW *ses = (WININETHTTPSESSIONW*)hdr;
3964 case INTERNET_OPTION_USERNAME:
3966 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
3967 if (!(ses->lpszUserName = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3968 return ERROR_SUCCESS;
3970 case INTERNET_OPTION_PASSWORD:
3972 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
3973 if (!(ses->lpszPassword = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3974 return ERROR_SUCCESS;
3979 return ERROR_INTERNET_INVALID_OPTION;
3982 static const HANDLEHEADERVtbl HTTPSESSIONVtbl = {
3983 HTTPSESSION_Destroy,
3985 HTTPSESSION_QueryOption,
3986 HTTPSESSION_SetOption,
3995 /***********************************************************************
3996 * HTTP_Connect (internal)
3998 * Create http session handle
4001 * HINTERNET a session handle on success
4005 HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
4006 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4007 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4008 DWORD dwInternalFlags)
4010 LPWININETHTTPSESSIONW lpwhs = NULL;
4011 HINTERNET handle = NULL;
4015 if (!lpszServerName || !lpszServerName[0])
4017 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4021 assert( hIC->hdr.htype == WH_HINIT );
4023 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPSESSIONW));
4026 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4031 * According to my tests. The name is not resolved until a request is sent
4034 lpwhs->hdr.htype = WH_HHTTPSESSION;
4035 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4036 lpwhs->hdr.dwFlags = dwFlags;
4037 lpwhs->hdr.dwContext = dwContext;
4038 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4039 lpwhs->hdr.refs = 1;
4040 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4042 WININET_AddRef( &hIC->hdr );
4043 lpwhs->lpAppInfo = hIC;
4044 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4046 handle = WININET_AllocHandle( &lpwhs->hdr );
4049 ERR("Failed to alloc handle\n");
4050 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4054 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4055 if(strchrW(hIC->lpszProxy, ' '))
4056 FIXME("Several proxies not implemented.\n");
4057 if(hIC->lpszProxyBypass)
4058 FIXME("Proxy bypass is ignored.\n");
4060 if (lpszServerName && lpszServerName[0])
4062 lpwhs->lpszServerName = WININET_strdupW(lpszServerName);
4063 lpwhs->lpszHostName = WININET_strdupW(lpszServerName);
4065 if (lpszUserName && lpszUserName[0])
4066 lpwhs->lpszUserName = WININET_strdupW(lpszUserName);
4067 if (lpszPassword && lpszPassword[0])
4068 lpwhs->lpszPassword = WININET_strdupW(lpszPassword);
4069 lpwhs->nServerPort = nServerPort;
4070 lpwhs->nHostPort = nServerPort;
4072 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4073 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4075 INTERNET_SendCallback(&hIC->hdr, dwContext,
4076 INTERNET_STATUS_HANDLE_CREATED, &handle,
4082 WININET_Release( &lpwhs->hdr );
4085 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4089 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4094 /***********************************************************************
4095 * HTTP_OpenConnection (internal)
4097 * Connect to a web server
4104 static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
4106 BOOL bSuccess = FALSE;
4107 LPWININETHTTPSESSIONW lpwhs;
4108 LPWININETAPPINFOW hIC = NULL;
4114 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4116 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4120 if (NETCON_connected(&lpwhr->netConnection))
4125 if (!HTTP_ResolveName(lpwhr)) goto lend;
4127 lpwhs = lpwhr->lpHttpSession;
4129 hIC = lpwhs->lpAppInfo;
4130 inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
4131 szaddr, sizeof(szaddr));
4132 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4133 INTERNET_STATUS_CONNECTING_TO_SERVER,
4137 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.sin_family,
4140 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4144 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4145 sizeof(lpwhs->socketAddress)))
4148 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4150 /* Note: we differ from Microsoft's WinINet here. they seem to have
4151 * a bug that causes no status callbacks to be sent when starting
4152 * a tunnel to a proxy server using the CONNECT verb. i believe our
4153 * behaviour to be more correct and to not cause any incompatibilities
4154 * because using a secure connection through a proxy server is a rare
4155 * case that would be hard for anyone to depend on */
4156 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4159 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4161 WARN("Couldn't connect securely to host\n");
4166 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4167 INTERNET_STATUS_CONNECTED_TO_SERVER,
4168 szaddr, strlen(szaddr)+1);
4173 lpwhr->read_pos = lpwhr->read_size = 0;
4174 lpwhr->read_chunked = FALSE;
4176 TRACE("%d <--\n", bSuccess);
4181 /***********************************************************************
4182 * HTTP_clear_response_headers (internal)
4184 * clear out any old response headers
4186 static void HTTP_clear_response_headers( LPWININETHTTPREQW lpwhr )
4190 for( i=0; i<lpwhr->nCustHeaders; i++)
4192 if( !lpwhr->pCustHeaders[i].lpszField )
4194 if( !lpwhr->pCustHeaders[i].lpszValue )
4196 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4198 HTTP_DeleteCustomHeader( lpwhr, i );
4203 /***********************************************************************
4204 * HTTP_GetResponseHeaders (internal)
4206 * Read server response
4213 static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear)
4216 WCHAR buffer[MAX_REPLY_LEN];
4217 DWORD buflen = MAX_REPLY_LEN;
4218 BOOL bSuccess = FALSE;
4220 char bufferA[MAX_REPLY_LEN];
4221 LPWSTR status_code = NULL, status_text = NULL;
4222 DWORD cchMaxRawHeaders = 1024;
4223 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4225 DWORD cchRawHeaders = 0;
4226 BOOL codeHundred = FALSE;
4230 /* clear old response headers (eg. from a redirect response) */
4231 if (clear) HTTP_clear_response_headers( lpwhr );
4233 if (!NETCON_connected(&lpwhr->netConnection))
4237 static const WCHAR szHundred[] = {'1','0','0',0};
4239 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4241 buflen = MAX_REPLY_LEN;
4242 if (!read_line(lpwhr, bufferA, &buflen))
4245 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4246 /* check is this a status code line? */
4247 if (!strncmpW(buffer, g_szHttp1_0, 4))
4249 /* split the version from the status code */
4250 status_code = strchrW( buffer, ' ' );
4255 /* split the status code from the status text */
4256 status_text = strchrW( status_code, ' ' );
4261 TRACE("version [%s] status code [%s] status text [%s]\n",
4262 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4264 codeHundred = (!strcmpW(status_code, szHundred));
4266 else if (!codeHundred)
4268 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4271 } while (codeHundred);
4273 /* Add status code */
4274 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4275 HTTP_ADDHDR_FLAG_REPLACE);
4277 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4278 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4280 lpwhr->lpszVersion= WININET_strdupW(buffer);
4281 lpwhr->lpszStatusText = WININET_strdupW(status_text);
4283 /* Restore the spaces */
4284 *(status_code-1) = ' ';
4285 *(status_text-1) = ' ';
4287 /* regenerate raw headers */
4288 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4289 cchMaxRawHeaders *= 2;
4290 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4291 if (temp == NULL) goto lend;
4292 lpszRawHeaders = temp;
4293 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4294 cchRawHeaders += (buflen-1);
4295 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4296 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4297 lpszRawHeaders[cchRawHeaders] = '\0';
4299 /* Parse each response line */
4302 buflen = MAX_REPLY_LEN;
4303 if (read_line(lpwhr, bufferA, &buflen))
4305 LPWSTR * pFieldAndValue;
4307 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4309 if (!bufferA[0]) break;
4310 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4312 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4315 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4316 cchMaxRawHeaders *= 2;
4317 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4318 if (temp == NULL) goto lend;
4319 lpszRawHeaders = temp;
4320 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4321 cchRawHeaders += (buflen-1);
4322 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4323 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4324 lpszRawHeaders[cchRawHeaders] = '\0';
4326 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4327 HTTP_ADDREQ_FLAG_ADD );
4329 HTTP_FreeTokens(pFieldAndValue);
4340 /* make sure the response header is terminated with an empty line. Some apps really
4341 truly care about that empty line being there for some reason. Just add it to the
4343 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4345 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4346 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4347 if (temp == NULL) goto lend;
4348 lpszRawHeaders = temp;
4351 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4353 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4354 lpwhr->lpszRawHeaders = lpszRawHeaders;
4355 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4365 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4371 static void strip_spaces(LPWSTR start)
4376 while (*str == ' ' && *str != '\0')
4380 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4382 end = start + strlenW(start) - 1;
4383 while (end >= start && *end == ' ')
4391 /***********************************************************************
4392 * HTTP_InterpretHttpHeader (internal)
4394 * Parse server response
4398 * Pointer to array of field, value, NULL on success.
4401 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4403 LPWSTR * pTokenPair;
4407 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4409 pszColon = strchrW(buffer, ':');
4410 /* must have two tokens */
4413 HTTP_FreeTokens(pTokenPair);
4415 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4419 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4422 HTTP_FreeTokens(pTokenPair);
4425 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4426 pTokenPair[0][pszColon - buffer] = '\0';
4430 len = strlenW(pszColon);
4431 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4434 HTTP_FreeTokens(pTokenPair);
4437 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4439 strip_spaces(pTokenPair[0]);
4440 strip_spaces(pTokenPair[1]);
4442 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4446 /***********************************************************************
4447 * HTTP_ProcessHeader (internal)
4449 * Stuff header into header tables according to <dwModifier>
4453 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4455 static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4457 LPHTTPHEADERW lphttpHdr = NULL;
4458 BOOL bSuccess = FALSE;
4460 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4462 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4464 /* REPLACE wins out over ADD */
4465 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4466 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4468 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4471 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4475 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4479 lphttpHdr = &lpwhr->pCustHeaders[index];
4485 hdr.lpszField = (LPWSTR)field;
4486 hdr.lpszValue = (LPWSTR)value;
4487 hdr.wFlags = hdr.wCount = 0;
4489 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4490 hdr.wFlags |= HDR_ISREQUEST;
4492 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4494 /* no value to delete */
4497 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4498 lphttpHdr->wFlags |= HDR_ISREQUEST;
4500 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4502 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4504 HTTP_DeleteCustomHeader( lpwhr, index );
4510 hdr.lpszField = (LPWSTR)field;
4511 hdr.lpszValue = (LPWSTR)value;
4512 hdr.wFlags = hdr.wCount = 0;
4514 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4515 hdr.wFlags |= HDR_ISREQUEST;
4517 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4522 else if (dwModifier & COALESCEFLAGS)
4527 INT origlen = strlenW(lphttpHdr->lpszValue);
4528 INT valuelen = strlenW(value);
4530 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4533 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4535 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4538 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4541 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4543 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4546 lphttpHdr->lpszValue = lpsztmp;
4547 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4550 lphttpHdr->lpszValue[origlen] = ch;
4552 lphttpHdr->lpszValue[origlen] = ' ';
4556 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4557 lphttpHdr->lpszValue[len] = '\0';
4562 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4563 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4566 TRACE("<-- %d\n",bSuccess);
4571 /***********************************************************************
4572 * HTTP_FinishedReading (internal)
4574 * Called when all content from server has been read by client.
4577 static BOOL HTTP_FinishedReading(LPWININETHTTPREQW lpwhr)
4579 WCHAR szVersion[10];
4580 WCHAR szConnectionResponse[20];
4581 DWORD dwBufferSize = sizeof(szVersion);
4582 BOOL keepalive = FALSE;
4586 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4587 * the connection is keep-alive by default */
4588 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
4589 &dwBufferSize, NULL) &&
4590 !strcmpiW(szVersion, g_szHttp1_1))
4595 dwBufferSize = sizeof(szConnectionResponse);
4596 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
4597 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
4599 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
4604 HTTPREQ_CloseConnection(&lpwhr->hdr);
4607 /* FIXME: store data in the URL cache here */
4613 /***********************************************************************
4614 * HTTP_GetCustomHeaderIndex (internal)
4616 * Return index of custom header from header array
4619 static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField,
4620 int requested_index, BOOL request_only)
4624 TRACE("%s\n", debugstr_w(lpszField));
4626 for (index = 0; index < lpwhr->nCustHeaders; index++)
4628 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4631 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4634 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4637 if (requested_index == 0)
4642 if (index >= lpwhr->nCustHeaders)
4645 TRACE("Return: %d\n", index);
4650 /***********************************************************************
4651 * HTTP_InsertCustomHeader (internal)
4653 * Insert header into array
4656 static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr)
4659 LPHTTPHEADERW lph = NULL;
4662 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4663 count = lpwhr->nCustHeaders + 1;
4665 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4667 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4671 lpwhr->pCustHeaders = lph;
4672 lpwhr->pCustHeaders[count-1].lpszField = WININET_strdupW(lpHdr->lpszField);
4673 lpwhr->pCustHeaders[count-1].lpszValue = WININET_strdupW(lpHdr->lpszValue);
4674 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4675 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4676 lpwhr->nCustHeaders++;
4681 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4688 /***********************************************************************
4689 * HTTP_DeleteCustomHeader (internal)
4691 * Delete header from array
4692 * If this function is called, the indexs may change.
4694 static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
4696 if( lpwhr->nCustHeaders <= 0 )
4698 if( index >= lpwhr->nCustHeaders )
4700 lpwhr->nCustHeaders--;
4702 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4703 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4705 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4706 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4707 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4713 /***********************************************************************
4714 * HTTP_VerifyValidHeader (internal)
4716 * Verify the given header is not invalid for the given http request
4719 static BOOL HTTP_VerifyValidHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field)
4721 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4722 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4728 /***********************************************************************
4729 * IsHostInProxyBypassList (@)
4734 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4736 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);