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(http_request_t *req);
180 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
181 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
182 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
183 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
184 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
185 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
186 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
187 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
188 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
189 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
190 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
191 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
192 static void HTTP_DrainContent(http_request_t *req);
193 static BOOL HTTP_FinishedReading(http_request_t *req);
195 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *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(http_request_t *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(http_request_t *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( http_request_t *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 http_request_t *lpwhr = (http_request_t*) 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(http_request_t *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( http_request_t *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( http_request_t *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( http_request_t *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(http_request_t *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 http_request_t *lpwhr;
805 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
810 lpwhr = (http_request_t*) 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(http_request_t *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 http_request_t *lpwhr = (http_request_t*)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 http_request_t *lpwhr;
968 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
972 lpwhr = (http_request_t*) 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 http_session_t *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 = (http_session_t*) 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( http_request_t *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(http_request_t *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 http_session_t *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(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1414 WCHAR buf[MAXHOSTNAME];
1415 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1416 static WCHAR szNul[] = { 0 };
1417 URL_COMPONENTSW UrlComponents;
1418 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1419 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1421 memset( &UrlComponents, 0, sizeof UrlComponents );
1422 UrlComponents.dwStructSize = sizeof UrlComponents;
1423 UrlComponents.lpszHostName = buf;
1424 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1426 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1427 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1428 sprintfW(proxy, szFormat, hIC->lpszProxy);
1430 strcpyW(proxy, hIC->lpszProxy);
1431 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1433 if( UrlComponents.dwHostNameLength == 0 )
1436 if( !lpwhr->lpszPath )
1437 lpwhr->lpszPath = szNul;
1439 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1440 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1442 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1443 lpwhs->lpszServerName = WININET_strdupW(UrlComponents.lpszHostName);
1444 lpwhs->nServerPort = UrlComponents.nPort;
1446 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1450 #ifndef INET6_ADDRSTRLEN
1451 #define INET6_ADDRSTRLEN 46
1454 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1456 char szaddr[INET6_ADDRSTRLEN];
1457 http_session_t *lpwhs = lpwhr->lpHttpSession;
1460 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1461 INTERNET_STATUS_RESOLVING_NAME,
1462 lpwhs->lpszServerName,
1463 strlenW(lpwhs->lpszServerName)+1);
1465 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1466 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1467 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1469 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1473 switch (lpwhs->socketAddress.ss_family)
1476 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1479 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1482 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1483 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1486 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1487 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1488 INTERNET_STATUS_NAME_RESOLVED,
1489 szaddr, strlen(szaddr)+1);
1491 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1496 /***********************************************************************
1497 * HTTPREQ_Destroy (internal)
1499 * Deallocate request handle
1502 static void HTTPREQ_Destroy(object_header_t *hdr)
1504 http_request_t *lpwhr = (http_request_t*) hdr;
1509 if(lpwhr->hCacheFile)
1510 CloseHandle(lpwhr->hCacheFile);
1512 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1514 DeleteCriticalSection( &lpwhr->read_section );
1515 WININET_Release(&lpwhr->lpHttpSession->hdr);
1517 if (lpwhr->pAuthInfo)
1519 if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1520 DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1521 if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1522 FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1524 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1525 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1526 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1527 lpwhr->pAuthInfo = NULL;
1530 if (lpwhr->pProxyAuthInfo)
1532 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1533 DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1534 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1535 FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1537 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1538 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1539 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1540 lpwhr->pProxyAuthInfo = NULL;
1543 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1544 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1545 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1546 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1547 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1549 for (i = 0; i < lpwhr->nCustHeaders; i++)
1551 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1552 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1555 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1556 HeapFree(GetProcessHeap(), 0, lpwhr);
1559 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1561 http_request_t *lpwhr = (http_request_t*) hdr;
1563 TRACE("%p\n",lpwhr);
1566 if(lpwhr->gzip_stream) {
1567 inflateEnd(&lpwhr->gzip_stream->zstream);
1568 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1569 lpwhr->gzip_stream = NULL;
1573 if (!NETCON_connected(&lpwhr->netConnection))
1576 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1577 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1579 NETCON_close(&lpwhr->netConnection);
1581 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1582 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1585 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1587 LPHTTPHEADERW host_header;
1589 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1591 host_header = HTTP_GetHeader(req, hostW);
1595 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1599 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1601 http_request_t *req = (http_request_t*)hdr;
1604 case INTERNET_OPTION_SECURITY_FLAGS:
1606 http_session_t *lpwhs;
1607 lpwhs = req->lpHttpSession;
1609 if (*size < sizeof(ULONG))
1610 return ERROR_INSUFFICIENT_BUFFER;
1612 *size = sizeof(DWORD);
1613 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1614 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1616 *(DWORD*)buffer = 0;
1617 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1618 return ERROR_SUCCESS;
1621 case INTERNET_OPTION_HANDLE_TYPE:
1622 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1624 if (*size < sizeof(ULONG))
1625 return ERROR_INSUFFICIENT_BUFFER;
1627 *size = sizeof(DWORD);
1628 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1629 return ERROR_SUCCESS;
1631 case INTERNET_OPTION_URL: {
1632 WCHAR url[INTERNET_MAX_URL_LENGTH];
1637 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1639 TRACE("INTERNET_OPTION_URL\n");
1641 host = HTTP_GetHeader(req, hostW);
1642 strcpyW(url, httpW);
1643 strcatW(url, host->lpszValue);
1644 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1646 strcatW(url, req->lpszPath);
1648 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1651 len = (strlenW(url)+1) * sizeof(WCHAR);
1653 return ERROR_INSUFFICIENT_BUFFER;
1656 strcpyW(buffer, url);
1657 return ERROR_SUCCESS;
1659 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1661 return ERROR_INSUFFICIENT_BUFFER;
1664 return ERROR_SUCCESS;
1668 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1669 INTERNET_CACHE_ENTRY_INFOW *info;
1670 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1671 WCHAR url[INTERNET_MAX_URL_LENGTH];
1672 DWORD nbytes, error;
1675 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1677 if (*size < sizeof(*ts))
1679 *size = sizeof(*ts);
1680 return ERROR_INSUFFICIENT_BUFFER;
1683 HTTP_GetRequestURL(req, url);
1684 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1685 error = GetLastError();
1686 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1688 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1689 return ERROR_OUTOFMEMORY;
1691 GetUrlCacheEntryInfoW(url, info, &nbytes);
1693 ts->ftExpires = info->ExpireTime;
1694 ts->ftLastModified = info->LastModifiedTime;
1696 HeapFree(GetProcessHeap(), 0, info);
1697 *size = sizeof(*ts);
1698 return ERROR_SUCCESS;
1703 case INTERNET_OPTION_DATAFILE_NAME: {
1706 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1708 if(!req->lpszCacheFile) {
1710 return ERROR_INTERNET_ITEM_NOT_FOUND;
1714 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1715 if(*size < req_size)
1716 return ERROR_INSUFFICIENT_BUFFER;
1719 memcpy(buffer, req->lpszCacheFile, *size);
1720 return ERROR_SUCCESS;
1722 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1723 if (req_size > *size)
1724 return ERROR_INSUFFICIENT_BUFFER;
1726 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1727 -1, buffer, *size, NULL, NULL);
1728 return ERROR_SUCCESS;
1732 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1733 PCCERT_CONTEXT context;
1735 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1736 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1737 return ERROR_INSUFFICIENT_BUFFER;
1740 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1742 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1745 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1746 info->ftExpiry = context->pCertInfo->NotAfter;
1747 info->ftStart = context->pCertInfo->NotBefore;
1749 len = CertNameToStrW(context->dwCertEncodingType,
1750 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1751 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1752 if(info->lpszSubjectInfo)
1753 CertNameToStrW(context->dwCertEncodingType,
1754 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1755 info->lpszSubjectInfo, len);
1756 len = CertNameToStrW(context->dwCertEncodingType,
1757 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1758 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1759 if (info->lpszIssuerInfo)
1760 CertNameToStrW(context->dwCertEncodingType,
1761 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1762 info->lpszIssuerInfo, len);
1764 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1766 len = CertNameToStrA(context->dwCertEncodingType,
1767 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1768 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1769 if(infoA->lpszSubjectInfo)
1770 CertNameToStrA(context->dwCertEncodingType,
1771 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1772 infoA->lpszSubjectInfo, len);
1773 len = CertNameToStrA(context->dwCertEncodingType,
1774 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1775 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1776 if(infoA->lpszIssuerInfo)
1777 CertNameToStrA(context->dwCertEncodingType,
1778 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1779 infoA->lpszIssuerInfo, len);
1783 * Contrary to MSDN, these do not appear to be set.
1785 * lpszSignatureAlgName
1786 * lpszEncryptionAlgName
1789 CertFreeCertificateContext(context);
1790 return ERROR_SUCCESS;
1795 return INET_QueryOption(option, buffer, size, unicode);
1798 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1800 http_request_t *req = (http_request_t*)hdr;
1803 case INTERNET_OPTION_SEND_TIMEOUT:
1804 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1805 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1807 if (size != sizeof(DWORD))
1808 return ERROR_INVALID_PARAMETER;
1810 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1813 case INTERNET_OPTION_USERNAME:
1814 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1815 if (!(req->lpHttpSession->lpszUserName = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1816 return ERROR_SUCCESS;
1818 case INTERNET_OPTION_PASSWORD:
1819 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1820 if (!(req->lpHttpSession->lpszPassword = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1821 return ERROR_SUCCESS;
1822 case INTERNET_OPTION_HTTP_DECODING:
1823 if(size != sizeof(BOOL))
1824 return ERROR_INVALID_PARAMETER;
1825 req->decoding = *(BOOL*)buffer;
1826 return ERROR_SUCCESS;
1829 return ERROR_INTERNET_INVALID_OPTION;
1832 /* read some more data into the read buffer (the read section must be held) */
1833 static BOOL read_more_data( http_request_t *req, int maxlen )
1839 /* move existing data to the start of the buffer */
1841 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1845 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1847 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1848 maxlen - req->read_size, 0, &len ))
1851 req->read_size += len;
1855 /* remove some amount of data from the read buffer (the read section must be held) */
1856 static void remove_data( http_request_t *req, int count )
1858 if (!(req->read_size -= count)) req->read_pos = 0;
1859 else req->read_pos += count;
1862 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1864 int count, bytes_read, pos = 0;
1866 EnterCriticalSection( &req->read_section );
1869 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1873 count = eol - (req->read_buf + req->read_pos);
1874 bytes_read = count + 1;
1876 else count = bytes_read = req->read_size;
1878 count = min( count, *len - pos );
1879 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1881 remove_data( req, bytes_read );
1884 if (!read_more_data( req, -1 ) || !req->read_size)
1887 TRACE( "returning empty string\n" );
1888 LeaveCriticalSection( &req->read_section );
1892 LeaveCriticalSection( &req->read_section );
1896 if (pos && buffer[pos - 1] == '\r') pos--;
1899 buffer[*len - 1] = 0;
1900 TRACE( "returning %s\n", debugstr_a(buffer));
1904 /* discard data contents until we reach end of line (the read section must be held) */
1905 static BOOL discard_eol( http_request_t *req )
1909 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1912 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1915 req->read_pos = req->read_size = 0; /* discard everything */
1916 if (!read_more_data( req, -1 )) return FALSE;
1917 } while (req->read_size);
1921 /* read the size of the next chunk (the read section must be held) */
1922 static BOOL start_next_chunk( http_request_t *req )
1924 DWORD chunk_size = 0;
1926 if (!req->dwContentLength) return TRUE;
1927 if (req->dwContentLength == req->dwContentRead)
1929 /* read terminator for the previous chunk */
1930 if (!discard_eol( req )) return FALSE;
1931 req->dwContentLength = ~0u;
1932 req->dwContentRead = 0;
1936 while (req->read_size)
1938 char ch = req->read_buf[req->read_pos];
1939 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1940 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1941 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1942 else if (ch == ';' || ch == '\r' || ch == '\n')
1944 TRACE( "reading %u byte chunk\n", chunk_size );
1945 req->dwContentLength = chunk_size;
1946 req->dwContentRead = 0;
1947 if (!discard_eol( req )) return FALSE;
1950 remove_data( req, 1 );
1952 if (!read_more_data( req, -1 )) return FALSE;
1953 if (!req->read_size)
1955 req->dwContentLength = req->dwContentRead = 0;
1961 /* check if we have reached the end of the data to read (the read section must be held) */
1962 static BOOL end_of_read_data( http_request_t *req )
1964 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1965 if (req->read_chunked) return (req->dwContentLength == 0);
1966 if (req->dwContentLength == ~0u) return FALSE;
1967 return (req->dwContentLength == req->dwContentRead);
1970 /* fetch some more data into the read buffer (the read section must be held) */
1971 static BOOL refill_buffer( http_request_t *req )
1973 int len = sizeof(req->read_buf);
1975 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1977 if (!start_next_chunk( req )) return FALSE;
1980 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1981 if (len <= req->read_size) return TRUE;
1983 if (!read_more_data( req, len )) return FALSE;
1984 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1988 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1990 DWORD ret = ERROR_SUCCESS;
1994 z_stream *zstream = &req->gzip_stream->zstream;
1997 while(read < size && !req->gzip_stream->end_of_data) {
1998 if(!req->read_size) {
1999 if(!sync || !refill_buffer(req))
2003 zstream->next_in = req->read_buf+req->read_pos;
2004 zstream->avail_in = req->read_size;
2005 zstream->next_out = buf+read;
2006 zstream->avail_out = size-read;
2007 zres = inflate(zstream, Z_FULL_FLUSH);
2008 read = size - zstream->avail_out;
2009 remove_data(req, req->read_size-zstream->avail_in);
2010 if(zres == Z_STREAM_END) {
2011 TRACE("end of data\n");
2012 req->gzip_stream->end_of_data = TRUE;
2013 }else if(zres != Z_OK) {
2014 WARN("inflate failed %d\n", zres);
2016 ret = ERROR_INTERNET_DECODING_FAILED;
2026 static void refill_gzip_buffer(http_request_t *req)
2031 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2034 if(req->gzip_stream->buf_pos) {
2035 if(req->gzip_stream->buf_size)
2036 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2037 req->gzip_stream->buf_pos = 0;
2040 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2041 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2042 if(res == ERROR_SUCCESS)
2043 req->gzip_stream->buf_size += len;
2046 /* return the size of data available to be read immediately (the read section must be held) */
2047 static DWORD get_avail_data( http_request_t *req )
2049 if (req->gzip_stream) {
2050 refill_gzip_buffer(req);
2051 return req->gzip_stream->buf_size;
2053 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2055 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2058 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2060 INTERNET_ASYNC_RESULT iar;
2064 EnterCriticalSection( &req->read_section );
2065 if (refill_buffer( req )) {
2066 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2067 iar.dwError = first_notif ? 0 : get_avail_data(req);
2070 iar.dwError = INTERNET_GetLastError();
2072 LeaveCriticalSection( &req->read_section );
2074 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2075 sizeof(INTERNET_ASYNC_RESULT));
2078 /* read data from the http connection (the read section must be held) */
2079 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2081 BOOL finished_reading = FALSE;
2082 int len, bytes_read = 0;
2083 DWORD ret = ERROR_SUCCESS;
2085 EnterCriticalSection( &req->read_section );
2087 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2089 if (!start_next_chunk( req )) goto done;
2092 if(req->gzip_stream) {
2093 if(req->gzip_stream->buf_size) {
2094 bytes_read = min(req->gzip_stream->buf_size, size);
2095 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2096 req->gzip_stream->buf_pos += bytes_read;
2097 req->gzip_stream->buf_size -= bytes_read;
2098 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2102 if(size > bytes_read) {
2103 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2104 if(ret == ERROR_SUCCESS)
2108 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2110 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2112 if (req->read_size) {
2113 bytes_read = min( req->read_size, size );
2114 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2115 remove_data( req, bytes_read );
2118 if (size > bytes_read && (!bytes_read || sync)) {
2119 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2120 sync ? MSG_WAITALL : 0, &len))
2122 /* always return success, even if the network layer returns an error */
2125 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2128 req->dwContentRead += bytes_read;
2131 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2132 LeaveCriticalSection( &req->read_section );
2134 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2136 DWORD dwBytesWritten;
2138 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2140 WARN("WriteFile failed: %u\n", GetLastError());
2143 if(finished_reading)
2144 HTTP_FinishedReading(req);
2150 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2152 http_request_t *req = (http_request_t*)hdr;
2153 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2156 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2158 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2159 http_request_t *req = (http_request_t*)workRequest->hdr;
2160 INTERNET_ASYNC_RESULT iar;
2163 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2165 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2166 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2168 iar.dwResult = res == ERROR_SUCCESS;
2171 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2172 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2173 sizeof(INTERNET_ASYNC_RESULT));
2176 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2177 DWORD flags, DWORD_PTR context)
2179 http_request_t *req = (http_request_t*)hdr;
2182 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2183 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2185 if (buffers->dwStructSize != sizeof(*buffers))
2186 return ERROR_INVALID_PARAMETER;
2188 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2190 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2192 WORKREQUEST workRequest;
2194 if (TryEnterCriticalSection( &req->read_section ))
2196 if (get_avail_data(req))
2198 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2199 &buffers->dwBufferLength, FALSE);
2200 LeaveCriticalSection( &req->read_section );
2203 LeaveCriticalSection( &req->read_section );
2206 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2207 workRequest.hdr = WININET_AddRef(&req->hdr);
2208 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2210 INTERNET_AsyncCall(&workRequest);
2212 return ERROR_IO_PENDING;
2215 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2216 !(flags & IRF_NO_WAIT));
2219 if (res == ERROR_SUCCESS) {
2220 DWORD size = buffers->dwBufferLength;
2221 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2222 &size, sizeof(size));
2228 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2230 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2231 http_request_t *req = (http_request_t*)workRequest->hdr;
2232 INTERNET_ASYNC_RESULT iar;
2235 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2237 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2238 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2240 iar.dwResult = res == ERROR_SUCCESS;
2243 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2244 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2245 sizeof(INTERNET_ASYNC_RESULT));
2248 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2249 DWORD flags, DWORD_PTR context)
2252 http_request_t *req = (http_request_t*)hdr;
2255 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2256 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2258 if (buffers->dwStructSize != sizeof(*buffers))
2259 return ERROR_INVALID_PARAMETER;
2261 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2263 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2265 WORKREQUEST workRequest;
2267 if (TryEnterCriticalSection( &req->read_section ))
2269 if (get_avail_data(req))
2271 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2272 &buffers->dwBufferLength, FALSE);
2273 LeaveCriticalSection( &req->read_section );
2276 LeaveCriticalSection( &req->read_section );
2279 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2280 workRequest.hdr = WININET_AddRef(&req->hdr);
2281 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2283 INTERNET_AsyncCall(&workRequest);
2285 return ERROR_IO_PENDING;
2288 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2289 !(flags & IRF_NO_WAIT));
2292 if (res == ERROR_SUCCESS) {
2293 DWORD size = buffers->dwBufferLength;
2294 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2295 &size, sizeof(size));
2301 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2304 http_request_t *lpwhr = (http_request_t*)hdr;
2306 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2309 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2310 lpwhr->dwBytesWritten += *written;
2312 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2316 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2318 http_request_t *req = (http_request_t*)workRequest->hdr;
2320 HTTP_ReceiveRequestData(req, FALSE);
2323 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2325 http_request_t *req = (http_request_t*)hdr;
2327 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2329 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2331 WORKREQUEST workRequest;
2333 /* never wait, if we can't enter the section we queue an async request right away */
2334 if (TryEnterCriticalSection( &req->read_section ))
2336 if ((*available = get_avail_data( req ))) goto done;
2337 if (end_of_read_data( req )) goto done;
2338 LeaveCriticalSection( &req->read_section );
2341 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2342 workRequest.hdr = WININET_AddRef( &req->hdr );
2344 INTERNET_AsyncCall(&workRequest);
2346 return ERROR_IO_PENDING;
2349 EnterCriticalSection( &req->read_section );
2351 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2353 refill_buffer( req );
2354 *available = get_avail_data( req );
2358 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2361 if (NETCON_query_data_available(&req->netConnection, &extra))
2362 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2364 LeaveCriticalSection( &req->read_section );
2366 TRACE( "returning %u\n", *available );
2367 return ERROR_SUCCESS;
2370 static const object_vtbl_t HTTPREQVtbl = {
2372 HTTPREQ_CloseConnection,
2373 HTTPREQ_QueryOption,
2376 HTTPREQ_ReadFileExA,
2377 HTTPREQ_ReadFileExW,
2379 HTTPREQ_QueryDataAvailable,
2383 /***********************************************************************
2384 * HTTP_HttpOpenRequestW (internal)
2386 * Open a HTTP request handle
2389 * HINTERNET a HTTP request handle on success
2393 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2394 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2395 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2396 DWORD dwFlags, DWORD_PTR dwContext)
2398 appinfo_t *hIC = NULL;
2399 http_request_t *lpwhr;
2400 LPWSTR lpszHostName = NULL;
2401 HINTERNET handle = NULL;
2402 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2407 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2408 hIC = lpwhs->lpAppInfo;
2410 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2413 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2416 lpwhr->hdr.htype = WH_HHTTPREQ;
2417 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2418 lpwhr->hdr.dwFlags = dwFlags;
2419 lpwhr->hdr.dwContext = dwContext;
2420 lpwhr->hdr.refs = 1;
2421 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2422 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2423 lpwhr->dwContentLength = ~0u;
2424 InitializeCriticalSection( &lpwhr->read_section );
2426 WININET_AddRef( &lpwhs->hdr );
2427 lpwhr->lpHttpSession = lpwhs;
2428 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2430 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2431 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2432 if (NULL == lpszHostName)
2434 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2438 handle = WININET_AllocHandle( &lpwhr->hdr );
2441 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2445 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2447 InternetCloseHandle( handle );
2452 if (lpszObjectName && *lpszObjectName) {
2456 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2457 if (rc != E_POINTER)
2458 len = strlenW(lpszObjectName)+1;
2459 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2460 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2461 URL_ESCAPE_SPACES_ONLY);
2464 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2465 strcpyW(lpwhr->lpszPath,lpszObjectName);
2468 static const WCHAR slashW[] = {'/',0};
2470 lpwhr->lpszPath = WININET_strdupW(slashW);
2473 if (lpszReferrer && *lpszReferrer)
2474 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2476 if (lpszAcceptTypes)
2479 for (i = 0; lpszAcceptTypes[i]; i++)
2481 if (!*lpszAcceptTypes[i]) continue;
2482 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2483 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2484 HTTP_ADDHDR_FLAG_REQ |
2485 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2489 lpwhr->lpszVerb = WININET_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2492 lpwhr->lpszVersion = WININET_strdupW(lpszVersion);
2494 lpwhr->lpszVersion = WININET_strdupW(g_szHttp1_1);
2496 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2497 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2498 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2500 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2501 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2502 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2505 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2506 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2508 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2509 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2510 INTERNET_DEFAULT_HTTPS_PORT :
2511 INTERNET_DEFAULT_HTTP_PORT);
2513 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2514 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2515 INTERNET_DEFAULT_HTTPS_PORT :
2516 INTERNET_DEFAULT_HTTP_PORT);
2518 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2519 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2521 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2522 INTERNET_STATUS_HANDLE_CREATED, &handle,
2526 HeapFree(GetProcessHeap(), 0, lpszHostName);
2528 WININET_Release( &lpwhr->hdr );
2530 TRACE("<-- %p (%p)\n", handle, lpwhr);
2534 /* read any content returned by the server so that the connection can be
2536 static void HTTP_DrainContent(http_request_t *req)
2540 if (!NETCON_connected(&req->netConnection)) return;
2542 if (req->dwContentLength == -1)
2544 NETCON_close(&req->netConnection);
2551 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2553 } while (bytes_read);
2556 static const LPCWSTR header_lookup[] = {
2557 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2558 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2559 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2560 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2561 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2562 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2563 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2564 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2565 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2566 szDate, /* HTTP_QUERY_DATE = 9 */
2567 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2568 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2569 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2570 szURI, /* HTTP_QUERY_URI = 13 */
2571 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2572 NULL, /* HTTP_QUERY_COST = 15 */
2573 NULL, /* HTTP_QUERY_LINK = 16 */
2574 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2575 NULL, /* HTTP_QUERY_VERSION = 18 */
2576 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2577 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2578 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2579 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2580 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2581 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2582 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2583 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2584 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2585 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2586 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2587 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2588 NULL, /* HTTP_QUERY_FROM = 31 */
2589 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2590 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2591 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2592 szReferer, /* HTTP_QUERY_REFERER = 35 */
2593 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2594 szServer, /* HTTP_QUERY_SERVER = 37 */
2595 NULL, /* HTTP_TITLE = 38 */
2596 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2597 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2598 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2599 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2600 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2601 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2602 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2603 NULL, /* HTTP_QUERY_REFRESH = 46 */
2604 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2605 szAge, /* HTTP_QUERY_AGE = 48 */
2606 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2607 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2608 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2609 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2610 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2611 szETag, /* HTTP_QUERY_ETAG = 54 */
2612 hostW, /* HTTP_QUERY_HOST = 55 */
2613 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2614 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2615 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2616 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2617 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2618 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2619 szRange, /* HTTP_QUERY_RANGE = 62 */
2620 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2621 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2622 szVary, /* HTTP_QUERY_VARY = 65 */
2623 szVia, /* HTTP_QUERY_VIA = 66 */
2624 szWarning, /* HTTP_QUERY_WARNING = 67 */
2625 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2626 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2627 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2630 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2632 /***********************************************************************
2633 * HTTP_HttpQueryInfoW (internal)
2635 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2636 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2638 LPHTTPHEADERW lphttpHdr = NULL;
2639 BOOL bSuccess = FALSE;
2640 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2641 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2642 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2645 /* Find requested header structure */
2648 case HTTP_QUERY_CUSTOM:
2649 if (!lpBuffer) return FALSE;
2650 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2653 case HTTP_QUERY_CONTENT_LENGTH:
2654 if(lpwhr->gzip_stream) {
2655 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2659 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2660 requested_index,request_only);
2663 case HTTP_QUERY_RAW_HEADERS_CRLF:
2670 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2672 headers = lpwhr->lpszRawHeaders;
2675 len = strlenW(headers) * sizeof(WCHAR);
2677 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2679 len += sizeof(WCHAR);
2680 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2686 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2689 len = strlenW(szCrLf) * sizeof(WCHAR);
2690 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2692 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2695 *lpdwBufferLength = len;
2698 HeapFree(GetProcessHeap(), 0, headers);
2701 case HTTP_QUERY_RAW_HEADERS:
2703 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2705 LPWSTR pszString = lpBuffer;
2707 for (i = 0; ppszRawHeaderLines[i]; i++)
2708 size += strlenW(ppszRawHeaderLines[i]) + 1;
2710 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2712 HTTP_FreeTokens(ppszRawHeaderLines);
2713 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2714 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2719 for (i = 0; ppszRawHeaderLines[i]; i++)
2721 DWORD len = strlenW(ppszRawHeaderLines[i]);
2722 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2726 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2728 *lpdwBufferLength = size * sizeof(WCHAR);
2729 HTTP_FreeTokens(ppszRawHeaderLines);
2733 case HTTP_QUERY_STATUS_TEXT:
2734 if (lpwhr->lpszStatusText)
2736 DWORD len = strlenW(lpwhr->lpszStatusText);
2737 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2739 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2740 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2745 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2746 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2748 *lpdwBufferLength = len * sizeof(WCHAR);
2752 case HTTP_QUERY_VERSION:
2753 if (lpwhr->lpszVersion)
2755 DWORD len = strlenW(lpwhr->lpszVersion);
2756 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2758 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2759 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2764 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2765 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2767 *lpdwBufferLength = len * sizeof(WCHAR);
2771 case HTTP_QUERY_CONTENT_ENCODING:
2772 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2773 requested_index,request_only);
2776 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2778 if (level < LAST_TABLE_HEADER && header_lookup[level])
2779 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2780 requested_index,request_only);
2784 lphttpHdr = &lpwhr->pCustHeaders[index];
2786 /* Ensure header satisfies requested attributes */
2788 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2789 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2791 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2795 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2797 /* coalesce value to requested type */
2798 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2800 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2801 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2804 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2810 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2812 tmpTM = *gmtime(&tmpTime);
2813 STHook = (SYSTEMTIME *)lpBuffer;
2814 STHook->wDay = tmpTM.tm_mday;
2815 STHook->wHour = tmpTM.tm_hour;
2816 STHook->wMilliseconds = 0;
2817 STHook->wMinute = tmpTM.tm_min;
2818 STHook->wDayOfWeek = tmpTM.tm_wday;
2819 STHook->wMonth = tmpTM.tm_mon + 1;
2820 STHook->wSecond = tmpTM.tm_sec;
2821 STHook->wYear = tmpTM.tm_year;
2824 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2825 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2826 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2828 else if (lphttpHdr->lpszValue)
2830 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2832 if (len > *lpdwBufferLength)
2834 *lpdwBufferLength = len;
2835 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2840 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2841 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2843 *lpdwBufferLength = len - sizeof(WCHAR);
2849 /***********************************************************************
2850 * HttpQueryInfoW (WININET.@)
2852 * Queries for information about an HTTP request
2859 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2860 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2862 BOOL bSuccess = FALSE;
2863 http_request_t *lpwhr;
2865 if (TRACE_ON(wininet)) {
2866 #define FE(x) { x, #x }
2867 static const wininet_flag_info query_flags[] = {
2868 FE(HTTP_QUERY_MIME_VERSION),
2869 FE(HTTP_QUERY_CONTENT_TYPE),
2870 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2871 FE(HTTP_QUERY_CONTENT_ID),
2872 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2873 FE(HTTP_QUERY_CONTENT_LENGTH),
2874 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2875 FE(HTTP_QUERY_ALLOW),
2876 FE(HTTP_QUERY_PUBLIC),
2877 FE(HTTP_QUERY_DATE),
2878 FE(HTTP_QUERY_EXPIRES),
2879 FE(HTTP_QUERY_LAST_MODIFIED),
2880 FE(HTTP_QUERY_MESSAGE_ID),
2882 FE(HTTP_QUERY_DERIVED_FROM),
2883 FE(HTTP_QUERY_COST),
2884 FE(HTTP_QUERY_LINK),
2885 FE(HTTP_QUERY_PRAGMA),
2886 FE(HTTP_QUERY_VERSION),
2887 FE(HTTP_QUERY_STATUS_CODE),
2888 FE(HTTP_QUERY_STATUS_TEXT),
2889 FE(HTTP_QUERY_RAW_HEADERS),
2890 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2891 FE(HTTP_QUERY_CONNECTION),
2892 FE(HTTP_QUERY_ACCEPT),
2893 FE(HTTP_QUERY_ACCEPT_CHARSET),
2894 FE(HTTP_QUERY_ACCEPT_ENCODING),
2895 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2896 FE(HTTP_QUERY_AUTHORIZATION),
2897 FE(HTTP_QUERY_CONTENT_ENCODING),
2898 FE(HTTP_QUERY_FORWARDED),
2899 FE(HTTP_QUERY_FROM),
2900 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2901 FE(HTTP_QUERY_LOCATION),
2902 FE(HTTP_QUERY_ORIG_URI),
2903 FE(HTTP_QUERY_REFERER),
2904 FE(HTTP_QUERY_RETRY_AFTER),
2905 FE(HTTP_QUERY_SERVER),
2906 FE(HTTP_QUERY_TITLE),
2907 FE(HTTP_QUERY_USER_AGENT),
2908 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2909 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2910 FE(HTTP_QUERY_ACCEPT_RANGES),
2911 FE(HTTP_QUERY_SET_COOKIE),
2912 FE(HTTP_QUERY_COOKIE),
2913 FE(HTTP_QUERY_REQUEST_METHOD),
2914 FE(HTTP_QUERY_REFRESH),
2915 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2917 FE(HTTP_QUERY_CACHE_CONTROL),
2918 FE(HTTP_QUERY_CONTENT_BASE),
2919 FE(HTTP_QUERY_CONTENT_LOCATION),
2920 FE(HTTP_QUERY_CONTENT_MD5),
2921 FE(HTTP_QUERY_CONTENT_RANGE),
2922 FE(HTTP_QUERY_ETAG),
2923 FE(HTTP_QUERY_HOST),
2924 FE(HTTP_QUERY_IF_MATCH),
2925 FE(HTTP_QUERY_IF_NONE_MATCH),
2926 FE(HTTP_QUERY_IF_RANGE),
2927 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2928 FE(HTTP_QUERY_MAX_FORWARDS),
2929 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2930 FE(HTTP_QUERY_RANGE),
2931 FE(HTTP_QUERY_TRANSFER_ENCODING),
2932 FE(HTTP_QUERY_UPGRADE),
2933 FE(HTTP_QUERY_VARY),
2935 FE(HTTP_QUERY_WARNING),
2936 FE(HTTP_QUERY_CUSTOM)
2938 static const wininet_flag_info modifier_flags[] = {
2939 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2940 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2941 FE(HTTP_QUERY_FLAG_NUMBER),
2942 FE(HTTP_QUERY_FLAG_COALESCE)
2945 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2946 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2949 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2950 TRACE(" Attribute:");
2951 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2952 if (query_flags[i].val == info) {
2953 TRACE(" %s", query_flags[i].name);
2957 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2958 TRACE(" Unknown (%08x)", info);
2961 TRACE(" Modifier:");
2962 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2963 if (modifier_flags[i].val & info_mod) {
2964 TRACE(" %s", modifier_flags[i].name);
2965 info_mod &= ~ modifier_flags[i].val;
2970 TRACE(" Unknown (%08x)", info_mod);
2975 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2976 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2978 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2982 if (lpBuffer == NULL)
2983 *lpdwBufferLength = 0;
2984 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2985 lpBuffer, lpdwBufferLength, lpdwIndex);
2989 WININET_Release( &lpwhr->hdr );
2991 TRACE("%d <--\n", bSuccess);
2995 /***********************************************************************
2996 * HttpQueryInfoA (WININET.@)
2998 * Queries for information about an HTTP request
3005 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3006 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3012 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3013 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3015 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3016 lpdwBufferLength, lpdwIndex );
3022 len = (*lpdwBufferLength)*sizeof(WCHAR);
3023 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3025 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3031 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3032 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3033 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3034 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3041 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3045 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3046 lpBuffer, *lpdwBufferLength, NULL, NULL );
3047 *lpdwBufferLength = len - 1;
3049 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3052 /* since the strings being returned from HttpQueryInfoW should be
3053 * only ASCII characters, it is reasonable to assume that all of
3054 * the Unicode characters can be reduced to a single byte */
3055 *lpdwBufferLength = len / sizeof(WCHAR);
3057 HeapFree(GetProcessHeap(), 0, bufferW );
3062 /***********************************************************************
3063 * HttpSendRequestExA (WININET.@)
3065 * Sends the specified request to the HTTP server and allows chunked
3070 * Failure: FALSE, call GetLastError() for more information.
3072 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3073 LPINTERNET_BUFFERSA lpBuffersIn,
3074 LPINTERNET_BUFFERSA lpBuffersOut,
3075 DWORD dwFlags, DWORD_PTR dwContext)
3077 INTERNET_BUFFERSW BuffersInW;
3080 LPWSTR header = NULL;
3082 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3083 lpBuffersOut, dwFlags, dwContext);
3087 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3088 if (lpBuffersIn->lpcszHeader)
3090 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3091 lpBuffersIn->dwHeadersLength,0,0);
3092 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3093 if (!(BuffersInW.lpcszHeader = header))
3095 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3098 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3099 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3103 BuffersInW.lpcszHeader = NULL;
3104 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3105 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3106 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3107 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3108 BuffersInW.Next = NULL;
3111 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3113 HeapFree(GetProcessHeap(),0,header);
3118 /***********************************************************************
3119 * HttpSendRequestExW (WININET.@)
3121 * Sends the specified request to the HTTP server and allows chunked
3126 * Failure: FALSE, call GetLastError() for more information.
3128 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3129 LPINTERNET_BUFFERSW lpBuffersIn,
3130 LPINTERNET_BUFFERSW lpBuffersOut,
3131 DWORD dwFlags, DWORD_PTR dwContext)
3134 http_request_t *lpwhr;
3135 http_session_t *lpwhs;
3138 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3139 lpBuffersOut, dwFlags, dwContext);
3141 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3143 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3145 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3149 lpwhs = lpwhr->lpHttpSession;
3150 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3151 hIC = lpwhs->lpAppInfo;
3152 assert(hIC->hdr.htype == WH_HINIT);
3154 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3156 WORKREQUEST workRequest;
3157 struct WORKREQ_HTTPSENDREQUESTW *req;
3159 workRequest.asyncproc = AsyncHttpSendRequestProc;
3160 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3161 req = &workRequest.u.HttpSendRequestW;
3164 if (lpBuffersIn->lpcszHeader)
3165 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3166 req->lpszHeader = WININET_strdupW(lpBuffersIn->lpcszHeader);
3168 req->lpszHeader = NULL;
3169 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3170 req->lpOptional = lpBuffersIn->lpvBuffer;
3171 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3172 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3176 req->lpszHeader = NULL;
3177 req->dwHeaderLength = 0;
3178 req->lpOptional = NULL;
3179 req->dwOptionalLength = 0;
3180 req->dwContentLength = 0;
3183 req->bEndRequest = FALSE;
3185 INTERNET_AsyncCall(&workRequest);
3187 * This is from windows.
3189 INTERNET_SetLastError(ERROR_IO_PENDING);
3194 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3195 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3196 lpBuffersIn->dwBufferTotal, FALSE);
3198 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3203 WININET_Release( &lpwhr->hdr );
3209 /***********************************************************************
3210 * HttpSendRequestW (WININET.@)
3212 * Sends the specified request to the HTTP server
3219 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3220 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3222 http_request_t *lpwhr;
3223 http_session_t *lpwhs = NULL;
3224 appinfo_t *hIC = NULL;
3227 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3228 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3230 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3231 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3233 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3238 lpwhs = lpwhr->lpHttpSession;
3239 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3241 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3246 hIC = lpwhs->lpAppInfo;
3247 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3249 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3254 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3256 WORKREQUEST workRequest;
3257 struct WORKREQ_HTTPSENDREQUESTW *req;
3259 workRequest.asyncproc = AsyncHttpSendRequestProc;
3260 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3261 req = &workRequest.u.HttpSendRequestW;
3266 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3267 else size = dwHeaderLength * sizeof(WCHAR);
3269 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3270 memcpy(req->lpszHeader, lpszHeaders, size);
3273 req->lpszHeader = 0;
3274 req->dwHeaderLength = dwHeaderLength;
3275 req->lpOptional = lpOptional;
3276 req->dwOptionalLength = dwOptionalLength;
3277 req->dwContentLength = dwOptionalLength;
3278 req->bEndRequest = TRUE;
3280 INTERNET_AsyncCall(&workRequest);
3282 * This is from windows.
3284 INTERNET_SetLastError(ERROR_IO_PENDING);
3289 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3290 dwHeaderLength, lpOptional, dwOptionalLength,
3291 dwOptionalLength, TRUE);
3295 WININET_Release( &lpwhr->hdr );
3299 /***********************************************************************
3300 * HttpSendRequestA (WININET.@)
3302 * Sends the specified request to the HTTP server
3309 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3310 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3313 LPWSTR szHeaders=NULL;
3314 DWORD nLen=dwHeaderLength;
3315 if(lpszHeaders!=NULL)
3317 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3318 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3319 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3321 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3322 HeapFree(GetProcessHeap(),0,szHeaders);
3326 /***********************************************************************
3327 * HTTP_GetRedirectURL (internal)
3329 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3331 static WCHAR szHttp[] = {'h','t','t','p',0};
3332 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3333 http_session_t *lpwhs = lpwhr->lpHttpSession;
3334 URL_COMPONENTSW urlComponents;
3335 DWORD url_length = 0;
3337 LPWSTR combined_url;
3339 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3340 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3341 urlComponents.dwSchemeLength = 0;
3342 urlComponents.lpszHostName = lpwhs->lpszHostName;
3343 urlComponents.dwHostNameLength = 0;
3344 urlComponents.nPort = lpwhs->nHostPort;
3345 urlComponents.lpszUserName = lpwhs->lpszUserName;
3346 urlComponents.dwUserNameLength = 0;
3347 urlComponents.lpszPassword = NULL;
3348 urlComponents.dwPasswordLength = 0;
3349 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3350 urlComponents.dwUrlPathLength = 0;
3351 urlComponents.lpszExtraInfo = NULL;
3352 urlComponents.dwExtraInfoLength = 0;
3354 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3355 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3358 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3360 /* convert from bytes to characters */
3361 url_length = url_length / sizeof(WCHAR) - 1;
3362 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3364 HeapFree(GetProcessHeap(), 0, orig_url);
3369 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3370 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3372 HeapFree(GetProcessHeap(), 0, orig_url);
3375 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3377 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3379 HeapFree(GetProcessHeap(), 0, orig_url);
3380 HeapFree(GetProcessHeap(), 0, combined_url);
3383 HeapFree(GetProcessHeap(), 0, orig_url);
3384 return combined_url;
3388 /***********************************************************************
3389 * HTTP_HandleRedirect (internal)
3391 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3393 http_session_t *lpwhs = lpwhr->lpHttpSession;
3394 appinfo_t *hIC = lpwhs->lpAppInfo;
3395 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3396 WCHAR path[INTERNET_MAX_URL_LENGTH];
3401 /* if it's an absolute path, keep the same session info */
3402 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3406 URL_COMPONENTSW urlComponents;
3407 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3408 static WCHAR szHttp[] = {'h','t','t','p',0};
3409 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3415 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3416 urlComponents.lpszScheme = protocol;
3417 urlComponents.dwSchemeLength = 32;
3418 urlComponents.lpszHostName = hostName;
3419 urlComponents.dwHostNameLength = MAXHOSTNAME;
3420 urlComponents.lpszUserName = userName;
3421 urlComponents.dwUserNameLength = 1024;
3422 urlComponents.lpszPassword = NULL;
3423 urlComponents.dwPasswordLength = 0;
3424 urlComponents.lpszUrlPath = path;
3425 urlComponents.dwUrlPathLength = 2048;
3426 urlComponents.lpszExtraInfo = NULL;
3427 urlComponents.dwExtraInfoLength = 0;
3428 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3431 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3432 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3434 TRACE("redirect from secure page to non-secure page\n");
3435 /* FIXME: warn about from secure redirect to non-secure page */
3436 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3438 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3439 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3441 TRACE("redirect from non-secure page to secure page\n");
3442 /* FIXME: notify about redirect to secure page */
3443 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3446 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3448 if (lstrlenW(protocol)>4) /*https*/
3449 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3451 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3456 * This upsets redirects to binary files on sourceforge.net
3457 * and gives an html page instead of the target file
3458 * Examination of the HTTP request sent by native wininet.dll
3459 * reveals that it doesn't send a referrer in that case.
3460 * Maybe there's a flag that enables this, or maybe a referrer
3461 * shouldn't be added in case of a redirect.
3464 /* consider the current host as the referrer */
3465 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3466 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3467 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3468 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3471 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3472 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3473 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3476 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3477 len = lstrlenW(hostName);
3478 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3479 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3480 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3483 lpwhs->lpszHostName = WININET_strdupW(hostName);
3485 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3487 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3488 lpwhs->lpszUserName = NULL;
3490 lpwhs->lpszUserName = WININET_strdupW(userName);
3494 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3496 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3497 lpwhs->lpszServerName = WININET_strdupW(hostName);
3498 lpwhs->nServerPort = urlComponents.nPort;
3500 NETCON_close(&lpwhr->netConnection);
3501 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3502 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3503 lpwhr->read_pos = lpwhr->read_size = 0;
3504 lpwhr->read_chunked = FALSE;
3508 TRACE("Redirect through proxy\n");
3511 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3512 lpwhr->lpszPath=NULL;
3518 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3519 if (rc != E_POINTER)
3520 needed = strlenW(path)+1;
3521 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3522 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3523 URL_ESCAPE_SPACES_ONLY);
3526 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3527 strcpyW(lpwhr->lpszPath,path);
3531 /* Remove custom content-type/length headers on redirects. */
3532 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3534 HTTP_DeleteCustomHeader(lpwhr, index);
3535 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3537 HTTP_DeleteCustomHeader(lpwhr, index);
3542 /***********************************************************************
3543 * HTTP_build_req (internal)
3545 * concatenate all the strings in the request together
3547 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3552 for( t = list; *t ; t++ )
3553 len += strlenW( *t );
3556 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3559 for( t = list; *t ; t++ )
3565 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3568 LPWSTR requestString;
3574 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3575 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3576 http_session_t *lpwhs = lpwhr->lpHttpSession;
3580 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3581 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3582 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3583 HeapFree( GetProcessHeap(), 0, lpszPath );
3585 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3586 NULL, 0, NULL, NULL );
3587 len--; /* the nul terminator isn't needed */
3588 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3589 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3590 ascii_req, len, NULL, NULL );
3591 HeapFree( GetProcessHeap(), 0, requestString );
3593 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3595 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3596 HeapFree( GetProcessHeap(), 0, ascii_req );
3597 if (!ret || cnt < 0)
3600 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3607 static void HTTP_InsertCookies(http_request_t *lpwhr)
3609 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3610 LPWSTR lpszCookies, lpszUrl = NULL;
3611 DWORD nCookieSize, size;
3612 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3614 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3615 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3616 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3618 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3621 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3623 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3624 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3626 cnt += sprintfW(lpszCookies, szCookie);
3627 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3628 strcatW(lpszCookies, szCrLf);
3630 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3631 HeapFree(GetProcessHeap(), 0, lpszCookies);
3634 HeapFree(GetProcessHeap(), 0, lpszUrl);
3637 /***********************************************************************
3638 * HTTP_HttpSendRequestW (internal)
3640 * Sends the specified request to the HTTP server
3647 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3648 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3649 DWORD dwContentLength, BOOL bEndRequest)
3652 BOOL bSuccess = FALSE, redirected = FALSE;
3653 LPWSTR requestString = NULL;
3656 INTERNET_ASYNC_RESULT iar;
3657 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3658 static const WCHAR szContentLength[] =
3659 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3660 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3662 TRACE("--> %p\n", lpwhr);
3664 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3666 /* if the verb is NULL default to GET */
3667 if (!lpwhr->lpszVerb)
3668 lpwhr->lpszVerb = WININET_strdupW(szGET);
3670 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3672 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3673 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3674 lpwhr->dwBytesToWrite = dwContentLength;
3676 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3678 WCHAR *agent_header;
3679 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3682 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3683 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3684 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3686 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3687 HeapFree(GetProcessHeap(), 0, agent_header);
3689 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3691 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3692 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3694 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3696 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3697 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3698 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3708 /* like native, just in case the caller forgot to call InternetReadFile
3709 * for all the data */
3710 HTTP_DrainContent(lpwhr);
3711 lpwhr->dwContentRead = 0;
3713 if (TRACE_ON(wininet))
3715 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3716 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3720 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3722 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3724 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3725 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3727 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3728 HTTP_InsertCookies(lpwhr);
3730 /* add the headers the caller supplied */
3731 if( lpszHeaders && dwHeaderLength )
3733 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3734 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3737 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3739 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3740 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3741 HeapFree(GetProcessHeap(), 0, url);
3744 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3747 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3749 /* Send the request and store the results */
3750 if (!HTTP_OpenConnection(lpwhr))
3753 /* send the request as ASCII, tack on the optional data */
3754 if (!lpOptional || redirected)
3755 dwOptionalLength = 0;
3756 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3757 NULL, 0, NULL, NULL );
3758 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3759 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3760 ascii_req, len, NULL, NULL );
3762 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3763 len = (len + dwOptionalLength - 1);
3765 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3767 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3768 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3770 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3771 HeapFree( GetProcessHeap(), 0, ascii_req );
3773 lpwhr->dwBytesWritten = dwOptionalLength;
3775 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3776 INTERNET_STATUS_REQUEST_SENT,
3777 &len, sizeof(DWORD));
3784 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3785 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3790 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3794 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3795 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3798 HTTP_ProcessCookies(lpwhr);
3800 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3802 dwBufferSize = sizeof(dwStatusCode);
3803 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3804 &dwStatusCode,&dwBufferSize,NULL))
3807 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3809 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3810 dwBufferSize=sizeof(szNewLocation);
3811 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3812 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3814 /* redirects are always GETs */
3815 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3816 lpwhr->lpszVerb = WININET_strdupW(szGET);
3818 HTTP_DrainContent(lpwhr);
3819 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3821 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3822 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3823 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3826 HeapFree(GetProcessHeap(), 0, requestString);
3829 HeapFree( GetProcessHeap(), 0, new_url );
3834 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3836 WCHAR szAuthValue[2048];
3838 if (dwStatusCode == HTTP_STATUS_DENIED)
3841 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3843 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3845 lpwhr->lpHttpSession->lpszUserName,
3846 lpwhr->lpHttpSession->lpszPassword))
3853 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3856 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3858 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3859 &lpwhr->pProxyAuthInfo,
3860 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3861 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3876 WCHAR url[INTERNET_MAX_URL_LENGTH];
3877 WCHAR cacheFileName[MAX_PATH+1];
3880 b = HTTP_GetRequestURL(lpwhr, url);
3882 WARN("Could not get URL\n");
3886 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3888 lpwhr->lpszCacheFile = WININET_strdupW(cacheFileName);
3889 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3890 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3891 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3892 WARN("Could not create file: %u\n", GetLastError());
3893 lpwhr->hCacheFile = NULL;
3896 WARN("Could not create cache entry: %08x\n", GetLastError());
3902 HeapFree(GetProcessHeap(), 0, requestString);
3904 /* TODO: send notification for P3P header */
3906 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3910 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3913 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3916 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3917 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3918 sizeof(INTERNET_ASYNC_RESULT));
3923 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3924 iar.dwError = INTERNET_GetLastError();
3926 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3927 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3928 sizeof(INTERNET_ASYNC_RESULT));
3933 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3937 /***********************************************************************
3938 * HTTPSESSION_Destroy (internal)
3940 * Deallocate session handle
3943 static void HTTPSESSION_Destroy(object_header_t *hdr)
3945 http_session_t *lpwhs = (http_session_t*) hdr;
3947 TRACE("%p\n", lpwhs);
3949 WININET_Release(&lpwhs->lpAppInfo->hdr);
3951 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3952 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3953 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3954 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3955 HeapFree(GetProcessHeap(), 0, lpwhs);
3958 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3961 case INTERNET_OPTION_HANDLE_TYPE:
3962 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3964 if (*size < sizeof(ULONG))
3965 return ERROR_INSUFFICIENT_BUFFER;
3967 *size = sizeof(DWORD);
3968 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
3969 return ERROR_SUCCESS;
3972 return INET_QueryOption(option, buffer, size, unicode);
3975 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
3977 http_session_t *ses = (http_session_t*)hdr;
3980 case INTERNET_OPTION_USERNAME:
3982 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
3983 if (!(ses->lpszUserName = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3984 return ERROR_SUCCESS;
3986 case INTERNET_OPTION_PASSWORD:
3988 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
3989 if (!(ses->lpszPassword = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3990 return ERROR_SUCCESS;
3995 return ERROR_INTERNET_INVALID_OPTION;
3998 static const object_vtbl_t HTTPSESSIONVtbl = {
3999 HTTPSESSION_Destroy,
4001 HTTPSESSION_QueryOption,
4002 HTTPSESSION_SetOption,
4011 /***********************************************************************
4012 * HTTP_Connect (internal)
4014 * Create http session handle
4017 * HINTERNET a session handle on success
4021 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4022 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4023 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4024 DWORD dwInternalFlags)
4026 http_session_t *lpwhs = NULL;
4027 HINTERNET handle = NULL;
4031 if (!lpszServerName || !lpszServerName[0])
4033 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4037 assert( hIC->hdr.htype == WH_HINIT );
4039 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4042 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4047 * According to my tests. The name is not resolved until a request is sent
4050 lpwhs->hdr.htype = WH_HHTTPSESSION;
4051 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4052 lpwhs->hdr.dwFlags = dwFlags;
4053 lpwhs->hdr.dwContext = dwContext;
4054 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4055 lpwhs->hdr.refs = 1;
4056 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4058 WININET_AddRef( &hIC->hdr );
4059 lpwhs->lpAppInfo = hIC;
4060 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4062 handle = WININET_AllocHandle( &lpwhs->hdr );
4065 ERR("Failed to alloc handle\n");
4066 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4070 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4071 if(strchrW(hIC->lpszProxy, ' '))
4072 FIXME("Several proxies not implemented.\n");
4073 if(hIC->lpszProxyBypass)
4074 FIXME("Proxy bypass is ignored.\n");
4076 if (lpszServerName && lpszServerName[0])
4078 lpwhs->lpszServerName = WININET_strdupW(lpszServerName);
4079 lpwhs->lpszHostName = WININET_strdupW(lpszServerName);
4081 if (lpszUserName && lpszUserName[0])
4082 lpwhs->lpszUserName = WININET_strdupW(lpszUserName);
4083 if (lpszPassword && lpszPassword[0])
4084 lpwhs->lpszPassword = WININET_strdupW(lpszPassword);
4085 lpwhs->nServerPort = nServerPort;
4086 lpwhs->nHostPort = nServerPort;
4088 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4089 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4091 INTERNET_SendCallback(&hIC->hdr, dwContext,
4092 INTERNET_STATUS_HANDLE_CREATED, &handle,
4098 WININET_Release( &lpwhs->hdr );
4101 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4105 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4110 /***********************************************************************
4111 * HTTP_OpenConnection (internal)
4113 * Connect to a web server
4120 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4122 BOOL bSuccess = FALSE;
4123 http_session_t *lpwhs;
4124 appinfo_t *hIC = NULL;
4125 char szaddr[INET6_ADDRSTRLEN];
4131 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4133 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4137 if (NETCON_connected(&lpwhr->netConnection))
4142 if (!HTTP_ResolveName(lpwhr)) goto lend;
4144 lpwhs = lpwhr->lpHttpSession;
4146 hIC = lpwhs->lpAppInfo;
4147 switch (lpwhs->socketAddress.ss_family)
4150 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4153 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4156 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4157 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4160 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4161 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4162 INTERNET_STATUS_CONNECTING_TO_SERVER,
4166 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4169 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4173 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4177 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4179 /* Note: we differ from Microsoft's WinINet here. they seem to have
4180 * a bug that causes no status callbacks to be sent when starting
4181 * a tunnel to a proxy server using the CONNECT verb. i believe our
4182 * behaviour to be more correct and to not cause any incompatibilities
4183 * because using a secure connection through a proxy server is a rare
4184 * case that would be hard for anyone to depend on */
4185 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4188 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4190 WARN("Couldn't connect securely to host\n");
4195 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4196 INTERNET_STATUS_CONNECTED_TO_SERVER,
4197 szaddr, strlen(szaddr)+1);
4202 lpwhr->read_pos = lpwhr->read_size = 0;
4203 lpwhr->read_chunked = FALSE;
4205 TRACE("%d <--\n", bSuccess);
4210 /***********************************************************************
4211 * HTTP_clear_response_headers (internal)
4213 * clear out any old response headers
4215 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4219 for( i=0; i<lpwhr->nCustHeaders; i++)
4221 if( !lpwhr->pCustHeaders[i].lpszField )
4223 if( !lpwhr->pCustHeaders[i].lpszValue )
4225 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4227 HTTP_DeleteCustomHeader( lpwhr, i );
4232 /***********************************************************************
4233 * HTTP_GetResponseHeaders (internal)
4235 * Read server response
4242 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4245 WCHAR buffer[MAX_REPLY_LEN];
4246 DWORD buflen = MAX_REPLY_LEN;
4247 BOOL bSuccess = FALSE;
4249 char bufferA[MAX_REPLY_LEN];
4250 LPWSTR status_code = NULL, status_text = NULL;
4251 DWORD cchMaxRawHeaders = 1024;
4252 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4254 DWORD cchRawHeaders = 0;
4255 BOOL codeHundred = FALSE;
4259 /* clear old response headers (eg. from a redirect response) */
4260 if (clear) HTTP_clear_response_headers( lpwhr );
4262 if (!NETCON_connected(&lpwhr->netConnection))
4266 static const WCHAR szHundred[] = {'1','0','0',0};
4268 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4270 buflen = MAX_REPLY_LEN;
4271 if (!read_line(lpwhr, bufferA, &buflen))
4274 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4275 /* check is this a status code line? */
4276 if (!strncmpW(buffer, g_szHttp1_0, 4))
4278 /* split the version from the status code */
4279 status_code = strchrW( buffer, ' ' );
4284 /* split the status code from the status text */
4285 status_text = strchrW( status_code, ' ' );
4290 TRACE("version [%s] status code [%s] status text [%s]\n",
4291 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4293 codeHundred = (!strcmpW(status_code, szHundred));
4295 else if (!codeHundred)
4297 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4300 } while (codeHundred);
4302 /* Add status code */
4303 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4304 HTTP_ADDHDR_FLAG_REPLACE);
4306 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4307 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4309 lpwhr->lpszVersion= WININET_strdupW(buffer);
4310 lpwhr->lpszStatusText = WININET_strdupW(status_text);
4312 /* Restore the spaces */
4313 *(status_code-1) = ' ';
4314 *(status_text-1) = ' ';
4316 /* regenerate raw headers */
4317 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4318 cchMaxRawHeaders *= 2;
4319 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4320 if (temp == NULL) goto lend;
4321 lpszRawHeaders = temp;
4322 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4323 cchRawHeaders += (buflen-1);
4324 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4325 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4326 lpszRawHeaders[cchRawHeaders] = '\0';
4328 /* Parse each response line */
4331 buflen = MAX_REPLY_LEN;
4332 if (read_line(lpwhr, bufferA, &buflen))
4334 LPWSTR * pFieldAndValue;
4336 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4338 if (!bufferA[0]) break;
4339 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4341 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4344 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4345 cchMaxRawHeaders *= 2;
4346 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4347 if (temp == NULL) goto lend;
4348 lpszRawHeaders = temp;
4349 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4350 cchRawHeaders += (buflen-1);
4351 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4352 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4353 lpszRawHeaders[cchRawHeaders] = '\0';
4355 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4356 HTTP_ADDREQ_FLAG_ADD );
4358 HTTP_FreeTokens(pFieldAndValue);
4369 /* make sure the response header is terminated with an empty line. Some apps really
4370 truly care about that empty line being there for some reason. Just add it to the
4372 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4374 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4375 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4376 if (temp == NULL) goto lend;
4377 lpszRawHeaders = temp;
4380 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4382 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4383 lpwhr->lpszRawHeaders = lpszRawHeaders;
4384 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4394 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4400 static void strip_spaces(LPWSTR start)
4405 while (*str == ' ' && *str != '\0')
4409 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4411 end = start + strlenW(start) - 1;
4412 while (end >= start && *end == ' ')
4420 /***********************************************************************
4421 * HTTP_InterpretHttpHeader (internal)
4423 * Parse server response
4427 * Pointer to array of field, value, NULL on success.
4430 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4432 LPWSTR * pTokenPair;
4436 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4438 pszColon = strchrW(buffer, ':');
4439 /* must have two tokens */
4442 HTTP_FreeTokens(pTokenPair);
4444 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4448 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4451 HTTP_FreeTokens(pTokenPair);
4454 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4455 pTokenPair[0][pszColon - buffer] = '\0';
4459 len = strlenW(pszColon);
4460 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4463 HTTP_FreeTokens(pTokenPair);
4466 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4468 strip_spaces(pTokenPair[0]);
4469 strip_spaces(pTokenPair[1]);
4471 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4475 /***********************************************************************
4476 * HTTP_ProcessHeader (internal)
4478 * Stuff header into header tables according to <dwModifier>
4482 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4484 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4486 LPHTTPHEADERW lphttpHdr = NULL;
4487 BOOL bSuccess = FALSE;
4489 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4491 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4493 /* REPLACE wins out over ADD */
4494 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4495 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4497 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4500 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4504 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4508 lphttpHdr = &lpwhr->pCustHeaders[index];
4514 hdr.lpszField = (LPWSTR)field;
4515 hdr.lpszValue = (LPWSTR)value;
4516 hdr.wFlags = hdr.wCount = 0;
4518 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4519 hdr.wFlags |= HDR_ISREQUEST;
4521 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4523 /* no value to delete */
4526 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4527 lphttpHdr->wFlags |= HDR_ISREQUEST;
4529 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4531 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4533 HTTP_DeleteCustomHeader( lpwhr, index );
4539 hdr.lpszField = (LPWSTR)field;
4540 hdr.lpszValue = (LPWSTR)value;
4541 hdr.wFlags = hdr.wCount = 0;
4543 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4544 hdr.wFlags |= HDR_ISREQUEST;
4546 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4551 else if (dwModifier & COALESCEFLAGS)
4556 INT origlen = strlenW(lphttpHdr->lpszValue);
4557 INT valuelen = strlenW(value);
4559 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4562 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4564 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4567 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4570 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4572 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4575 lphttpHdr->lpszValue = lpsztmp;
4576 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4579 lphttpHdr->lpszValue[origlen] = ch;
4581 lphttpHdr->lpszValue[origlen] = ' ';
4585 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4586 lphttpHdr->lpszValue[len] = '\0';
4591 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4592 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4595 TRACE("<-- %d\n",bSuccess);
4600 /***********************************************************************
4601 * HTTP_FinishedReading (internal)
4603 * Called when all content from server has been read by client.
4606 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4608 WCHAR szVersion[10];
4609 WCHAR szConnectionResponse[20];
4610 DWORD dwBufferSize = sizeof(szVersion);
4611 BOOL keepalive = FALSE;
4615 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4616 * the connection is keep-alive by default */
4617 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
4618 &dwBufferSize, NULL) &&
4619 !strcmpiW(szVersion, g_szHttp1_1))
4624 dwBufferSize = sizeof(szConnectionResponse);
4625 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
4626 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
4628 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
4633 HTTPREQ_CloseConnection(&lpwhr->hdr);
4636 /* FIXME: store data in the URL cache here */
4642 /***********************************************************************
4643 * HTTP_GetCustomHeaderIndex (internal)
4645 * Return index of custom header from header array
4648 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4649 int requested_index, BOOL request_only)
4653 TRACE("%s\n", debugstr_w(lpszField));
4655 for (index = 0; index < lpwhr->nCustHeaders; index++)
4657 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4660 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4663 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4666 if (requested_index == 0)
4671 if (index >= lpwhr->nCustHeaders)
4674 TRACE("Return: %d\n", index);
4679 /***********************************************************************
4680 * HTTP_InsertCustomHeader (internal)
4682 * Insert header into array
4685 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4688 LPHTTPHEADERW lph = NULL;
4691 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4692 count = lpwhr->nCustHeaders + 1;
4694 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4696 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4700 lpwhr->pCustHeaders = lph;
4701 lpwhr->pCustHeaders[count-1].lpszField = WININET_strdupW(lpHdr->lpszField);
4702 lpwhr->pCustHeaders[count-1].lpszValue = WININET_strdupW(lpHdr->lpszValue);
4703 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4704 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4705 lpwhr->nCustHeaders++;
4710 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4717 /***********************************************************************
4718 * HTTP_DeleteCustomHeader (internal)
4720 * Delete header from array
4721 * If this function is called, the indexs may change.
4723 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4725 if( lpwhr->nCustHeaders <= 0 )
4727 if( index >= lpwhr->nCustHeaders )
4729 lpwhr->nCustHeaders--;
4731 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4732 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4734 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4735 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4736 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4742 /***********************************************************************
4743 * HTTP_VerifyValidHeader (internal)
4745 * Verify the given header is not invalid for the given http request
4748 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4750 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4751 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4757 /***********************************************************************
4758 * IsHostInProxyBypassList (@)
4763 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4765 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);