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 szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 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 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 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 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
180 static BOOL HTTP_OpenConnection(http_request_t *req);
181 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
182 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
183 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
184 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
185 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
186 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
187 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
188 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
189 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
190 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
191 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
192 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
193 static void HTTP_DrainContent(http_request_t *req);
194 static BOOL HTTP_FinishedReading(http_request_t *req);
196 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
199 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
200 if (HeaderIndex == -1)
203 return &req->pCustHeaders[HeaderIndex];
208 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
210 return HeapAlloc(GetProcessHeap(), 0, items*size);
213 static void wininet_zfree(voidpf opaque, voidpf address)
215 HeapFree(GetProcessHeap(), 0, address);
218 static void init_gzip_stream(http_request_t *req)
220 gzip_stream_t *gzip_stream;
223 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
224 gzip_stream->zstream.zalloc = wininet_zalloc;
225 gzip_stream->zstream.zfree = wininet_zfree;
226 gzip_stream->zstream.opaque = NULL;
227 gzip_stream->zstream.next_in = NULL;
228 gzip_stream->zstream.avail_in = 0;
229 gzip_stream->zstream.next_out = NULL;
230 gzip_stream->zstream.avail_out = 0;
231 gzip_stream->buf_pos = 0;
232 gzip_stream->buf_size = 0;
233 gzip_stream->end_of_data = FALSE;
235 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
237 ERR("inflateInit failed: %d\n", zres);
238 HeapFree(GetProcessHeap(), 0, gzip_stream);
242 req->gzip_stream = gzip_stream;
244 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
246 HTTP_DeleteCustomHeader(req, index);
251 static void init_gzip_stream(http_request_t *req)
253 ERR("gzip stream not supported, missing zlib.\n");
258 /* set the request content length based on the headers */
259 static DWORD set_content_length( http_request_t *lpwhr )
261 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
265 size = sizeof(lpwhr->dwContentLength);
266 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
267 &lpwhr->dwContentLength, &size, NULL))
268 lpwhr->dwContentLength = ~0u;
270 size = sizeof(encoding);
271 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
272 !strcmpiW(encoding, szChunked))
274 lpwhr->dwContentLength = ~0u;
275 lpwhr->read_chunked = TRUE;
278 if(lpwhr->decoding) {
281 static const WCHAR gzipW[] = {'g','z','i','p',0};
283 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
284 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
285 init_gzip_stream(lpwhr);
288 return lpwhr->dwContentLength;
291 /***********************************************************************
292 * HTTP_Tokenize (internal)
294 * Tokenize a string, allocating memory for the tokens.
296 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
298 LPWSTR * token_array;
305 /* empty string has no tokens */
309 for (i = 0; string[i]; i++)
311 if (!strncmpW(string+i, token_string, strlenW(token_string)))
315 /* we want to skip over separators, but not the null terminator */
316 for (j = 0; j < strlenW(token_string) - 1; j++)
324 /* add 1 for terminating NULL */
325 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
326 token_array[tokens] = NULL;
329 for (i = 0; i < tokens; i++)
332 next_token = strstrW(string, token_string);
333 if (!next_token) next_token = string+strlenW(string);
334 len = next_token - string;
335 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
336 memcpy(token_array[i], string, len*sizeof(WCHAR));
337 token_array[i][len] = '\0';
338 string = next_token+strlenW(token_string);
343 /***********************************************************************
344 * HTTP_FreeTokens (internal)
346 * Frees memory returned from HTTP_Tokenize.
348 static void HTTP_FreeTokens(LPWSTR * token_array)
351 for (i = 0; token_array[i]; i++)
352 HeapFree(GetProcessHeap(), 0, token_array[i]);
353 HeapFree(GetProcessHeap(), 0, token_array);
356 /* **********************************************************************
358 * Helper functions for the HttpSendRequest(Ex) functions
361 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
363 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
364 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
366 TRACE("%p\n", lpwhr);
368 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
369 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
370 req->dwContentLength, req->bEndRequest);
372 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
375 static void HTTP_FixURL(http_request_t *lpwhr)
377 static const WCHAR szSlash[] = { '/',0 };
378 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
380 /* If we don't have a path we set it to root */
381 if (NULL == lpwhr->lpszPath)
382 lpwhr->lpszPath = heap_strdupW(szSlash);
383 else /* remove \r and \n*/
385 int nLen = strlenW(lpwhr->lpszPath);
386 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
389 lpwhr->lpszPath[nLen]='\0';
391 /* Replace '\' with '/' */
394 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
398 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
399 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
400 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
402 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
403 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
405 strcpyW(fixurl + 1, lpwhr->lpszPath);
406 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
407 lpwhr->lpszPath = fixurl;
411 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
413 LPWSTR requestString;
419 static const WCHAR szSpace[] = { ' ',0 };
420 static const WCHAR szColon[] = { ':',' ',0 };
421 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
423 /* allocate space for an array of all the string pointers to be added */
424 len = (lpwhr->nCustHeaders)*4 + 10;
425 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
427 /* add the verb, path and HTTP version string */
435 /* Append custom request headers */
436 for (i = 0; i < lpwhr->nCustHeaders; i++)
438 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
441 req[n++] = lpwhr->pCustHeaders[i].lpszField;
443 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
445 TRACE("Adding custom header %s (%s)\n",
446 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
447 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
452 ERR("oops. buffer overrun\n");
455 requestString = HTTP_build_req( req, 4 );
456 HeapFree( GetProcessHeap(), 0, req );
459 * Set (header) termination string for request
460 * Make sure there's exactly two new lines at the end of the request
462 p = &requestString[strlenW(requestString)-1];
463 while ( (*p == '\n') || (*p == '\r') )
465 strcpyW( p+1, sztwocrlf );
467 return requestString;
470 static void HTTP_ProcessCookies( http_request_t *lpwhr )
474 LPHTTPHEADERW setCookieHeader;
476 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
478 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
480 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
483 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
487 Host = HTTP_GetHeader(lpwhr, hostW);
488 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
489 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
490 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
491 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
493 HeapFree(GetProcessHeap(), 0, buf_url);
499 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
501 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
502 return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
503 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
506 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
508 if (!authinfo) return;
510 if (SecIsValidHandle(&authinfo->ctx))
511 DeleteSecurityContext(&authinfo->ctx);
512 if (SecIsValidHandle(&authinfo->cred))
513 FreeCredentialsHandle(&authinfo->cred);
515 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
516 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
517 HeapFree(GetProcessHeap(), 0, authinfo);
520 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
521 struct HttpAuthInfo **ppAuthInfo,
522 LPWSTR domain_and_username, LPWSTR password )
524 SECURITY_STATUS sec_status;
525 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
528 TRACE("%s\n", debugstr_w(pszAuthValue));
535 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
539 SecInvalidateHandle(&pAuthInfo->cred);
540 SecInvalidateHandle(&pAuthInfo->ctx);
541 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
543 pAuthInfo->auth_data = NULL;
544 pAuthInfo->auth_data_len = 0;
545 pAuthInfo->finished = FALSE;
547 if (is_basic_auth_value(pszAuthValue))
549 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
550 pAuthInfo->scheme = heap_strdupW(szBasic);
551 if (!pAuthInfo->scheme)
553 HeapFree(GetProcessHeap(), 0, pAuthInfo);
560 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
562 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
563 if (!pAuthInfo->scheme)
565 HeapFree(GetProcessHeap(), 0, pAuthInfo);
569 if (domain_and_username)
571 WCHAR *user = strchrW(domain_and_username, '\\');
572 WCHAR *domain = domain_and_username;
574 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
576 pAuthData = &nt_auth_identity;
581 user = domain_and_username;
585 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
586 nt_auth_identity.User = user;
587 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
588 nt_auth_identity.Domain = domain;
589 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
590 nt_auth_identity.Password = password;
591 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
594 /* use default credentials */
597 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
598 SECPKG_CRED_OUTBOUND, NULL,
600 NULL, &pAuthInfo->cred,
602 if (sec_status == SEC_E_OK)
604 PSecPkgInfoW sec_pkg_info;
605 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
606 if (sec_status == SEC_E_OK)
608 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
609 FreeContextBuffer(sec_pkg_info);
612 if (sec_status != SEC_E_OK)
614 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
615 debugstr_w(pAuthInfo->scheme), sec_status);
616 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
617 HeapFree(GetProcessHeap(), 0, pAuthInfo);
621 *ppAuthInfo = pAuthInfo;
623 else if (pAuthInfo->finished)
626 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
627 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
629 ERR("authentication scheme changed from %s to %s\n",
630 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
634 if (is_basic_auth_value(pszAuthValue))
640 TRACE("basic authentication\n");
642 /* we don't cache credentials for basic authentication, so we can't
643 * retrieve them if the application didn't pass us any credentials */
644 if (!domain_and_username) return FALSE;
646 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
647 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
649 /* length includes a nul terminator, which will be re-used for the ':' */
650 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
654 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
655 auth_data[userlen] = ':';
656 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
658 pAuthInfo->auth_data = auth_data;
659 pAuthInfo->auth_data_len = userlen + 1 + passlen;
660 pAuthInfo->finished = TRUE;
667 SecBufferDesc out_desc, in_desc;
669 unsigned char *buffer;
670 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
671 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
673 in.BufferType = SECBUFFER_TOKEN;
677 in_desc.ulVersion = 0;
678 in_desc.cBuffers = 1;
679 in_desc.pBuffers = ∈
681 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
682 if (*pszAuthData == ' ')
685 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
686 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
687 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
690 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
692 out.BufferType = SECBUFFER_TOKEN;
693 out.cbBuffer = pAuthInfo->max_token;
694 out.pvBuffer = buffer;
696 out_desc.ulVersion = 0;
697 out_desc.cBuffers = 1;
698 out_desc.pBuffers = &out;
700 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
701 first ? NULL : &pAuthInfo->ctx,
702 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
703 context_req, 0, SECURITY_NETWORK_DREP,
704 in.pvBuffer ? &in_desc : NULL,
705 0, &pAuthInfo->ctx, &out_desc,
706 &pAuthInfo->attr, &pAuthInfo->exp);
707 if (sec_status == SEC_E_OK)
709 pAuthInfo->finished = TRUE;
710 pAuthInfo->auth_data = out.pvBuffer;
711 pAuthInfo->auth_data_len = out.cbBuffer;
712 TRACE("sending last auth packet\n");
714 else if (sec_status == SEC_I_CONTINUE_NEEDED)
716 pAuthInfo->auth_data = out.pvBuffer;
717 pAuthInfo->auth_data_len = out.cbBuffer;
718 TRACE("sending next auth packet\n");
722 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
723 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
724 destroy_authinfo(pAuthInfo);
733 /***********************************************************************
734 * HTTP_HttpAddRequestHeadersW (internal)
736 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
737 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
742 BOOL bSuccess = FALSE;
745 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
747 if( dwHeaderLength == ~0U )
748 len = strlenW(lpszHeader);
750 len = dwHeaderLength;
751 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
752 lstrcpynW( buffer, lpszHeader, len + 1);
758 LPWSTR * pFieldAndValue;
762 while (*lpszEnd != '\0')
764 if (*lpszEnd == '\r' || *lpszEnd == '\n')
769 if (*lpszStart == '\0')
772 if (*lpszEnd == '\r' || *lpszEnd == '\n')
775 lpszEnd++; /* Jump over newline */
777 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
778 if (*lpszStart == '\0')
780 /* Skip 0-length headers */
785 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
788 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
790 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
791 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
792 HTTP_FreeTokens(pFieldAndValue);
798 HeapFree(GetProcessHeap(), 0, buffer);
803 /***********************************************************************
804 * HttpAddRequestHeadersW (WININET.@)
806 * Adds one or more HTTP header to the request handler
809 * On Windows if dwHeaderLength includes the trailing '\0', then
810 * HttpAddRequestHeadersW() adds it too. However this results in an
811 * invalid Http header which is rejected by some servers so we probably
812 * don't need to match Windows on that point.
819 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
820 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
822 BOOL bSuccess = FALSE;
823 http_request_t *lpwhr;
825 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
830 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
831 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
833 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
836 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
839 WININET_Release( &lpwhr->hdr );
844 /***********************************************************************
845 * HttpAddRequestHeadersA (WININET.@)
847 * Adds one or more HTTP header to the request handler
854 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
855 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
861 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
863 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
864 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
865 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
866 if( dwHeaderLength != ~0U )
867 dwHeaderLength = len;
869 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
871 HeapFree( GetProcessHeap(), 0, hdr );
876 /***********************************************************************
877 * HttpEndRequestA (WININET.@)
879 * Ends an HTTP request that was started by HttpSendRequestEx
886 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
887 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
889 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
893 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
897 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
900 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
905 INTERNET_ASYNC_RESULT iar;
907 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
908 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
910 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
914 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
915 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
917 /* process cookies here. Is this right? */
918 HTTP_ProcessCookies(lpwhr);
920 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
922 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
924 DWORD dwCode,dwCodeLength = sizeof(DWORD);
925 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
926 (dwCode == 302 || dwCode == 301 || dwCode == 303))
928 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
929 dwBufferSize=sizeof(szNewLocation);
930 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
932 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
934 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
935 lpwhr->lpszVerb = heap_strdupW(szGET);
937 HTTP_DrainContent(lpwhr);
938 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
940 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
941 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
942 rc = HTTP_HandleRedirect(lpwhr, new_url);
944 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
945 HeapFree( GetProcessHeap(), 0, new_url );
951 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
952 iar.dwError = rc ? 0 : INTERNET_GetLastError();
954 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
955 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
956 sizeof(INTERNET_ASYNC_RESULT));
960 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
962 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
963 http_request_t *lpwhr = (http_request_t*)work->hdr;
965 TRACE("%p\n", lpwhr);
967 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
970 /***********************************************************************
971 * HttpEndRequestW (WININET.@)
973 * Ends an HTTP request that was started by HttpSendRequestEx
980 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
981 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
984 http_request_t *lpwhr;
990 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
994 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
996 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
998 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1000 WININET_Release( &lpwhr->hdr );
1003 lpwhr->hdr.dwFlags |= dwFlags;
1005 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1008 struct WORKREQ_HTTPENDREQUESTW *request;
1010 work.asyncproc = AsyncHttpEndRequestProc;
1011 work.hdr = WININET_AddRef( &lpwhr->hdr );
1013 request = &work.u.HttpEndRequestW;
1014 request->dwFlags = dwFlags;
1015 request->dwContext = dwContext;
1017 INTERNET_AsyncCall(&work);
1018 INTERNET_SetLastError(ERROR_IO_PENDING);
1021 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1023 WININET_Release( &lpwhr->hdr );
1024 TRACE("%i <--\n",rc);
1028 /***********************************************************************
1029 * HttpOpenRequestW (WININET.@)
1031 * Open a HTTP request handle
1034 * HINTERNET a HTTP request handle on success
1038 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1039 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1040 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1041 DWORD dwFlags, DWORD_PTR dwContext)
1043 http_session_t *lpwhs;
1044 HINTERNET handle = NULL;
1046 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1047 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1048 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1049 dwFlags, dwContext);
1050 if(lpszAcceptTypes!=NULL)
1053 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1054 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1057 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1058 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1060 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1065 * My tests seem to show that the windows version does not
1066 * become asynchronous until after this point. And anyhow
1067 * if this call was asynchronous then how would you get the
1068 * necessary HINTERNET pointer returned by this function.
1071 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1072 lpszVersion, lpszReferrer, lpszAcceptTypes,
1073 dwFlags, dwContext);
1076 WININET_Release( &lpwhs->hdr );
1077 TRACE("returning %p\n", handle);
1082 /***********************************************************************
1083 * HttpOpenRequestA (WININET.@)
1085 * Open a HTTP request handle
1088 * HINTERNET a HTTP request handle on success
1092 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1093 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1094 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1095 DWORD dwFlags, DWORD_PTR dwContext)
1097 LPWSTR szVerb = NULL, szObjectName = NULL;
1098 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1099 INT acceptTypesCount;
1100 HINTERNET rc = FALSE;
1103 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1104 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1105 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1106 dwFlags, dwContext);
1110 szVerb = heap_strdupAtoW(lpszVerb);
1117 szObjectName = heap_strdupAtoW(lpszObjectName);
1118 if ( !szObjectName )
1124 szVersion = heap_strdupAtoW(lpszVersion);
1131 szReferrer = heap_strdupAtoW(lpszReferrer);
1136 if (lpszAcceptTypes)
1138 acceptTypesCount = 0;
1139 types = lpszAcceptTypes;
1144 /* find out how many there are */
1145 if (*types && **types)
1147 TRACE("accept type: %s\n", debugstr_a(*types));
1153 WARN("invalid accept type pointer\n");
1158 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1159 if (!szAcceptTypes) goto end;
1161 acceptTypesCount = 0;
1162 types = lpszAcceptTypes;
1167 if (*types && **types)
1168 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1172 /* ignore invalid pointer */
1177 szAcceptTypes[acceptTypesCount] = NULL;
1180 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1181 szVersion, szReferrer,
1182 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1187 acceptTypesCount = 0;
1188 while (szAcceptTypes[acceptTypesCount])
1190 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1193 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1195 HeapFree(GetProcessHeap(), 0, szReferrer);
1196 HeapFree(GetProcessHeap(), 0, szVersion);
1197 HeapFree(GetProcessHeap(), 0, szObjectName);
1198 HeapFree(GetProcessHeap(), 0, szVerb);
1203 /***********************************************************************
1206 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1209 static const CHAR HTTP_Base64Enc[] =
1210 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1214 /* first 6 bits, all from bin[0] */
1215 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1216 x = (bin[0] & 3) << 4;
1218 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1221 base64[n++] = HTTP_Base64Enc[x];
1226 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1227 x = ( bin[1] & 0x0f ) << 2;
1229 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1232 base64[n++] = HTTP_Base64Enc[x];
1236 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1238 /* last 6 bits, all from bin [2] */
1239 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1247 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1248 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1249 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1250 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1251 static const signed char HTTP_Base64Dec[256] =
1253 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1254 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1255 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1256 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1257 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1258 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1259 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1260 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1261 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1262 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1263 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1264 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1265 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1266 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1267 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1268 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1269 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1270 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1271 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1272 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1273 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1274 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1275 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1276 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1277 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1278 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1282 /***********************************************************************
1285 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1293 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1294 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1295 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1296 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1298 WARN("invalid base64: %s\n", debugstr_w(base64));
1302 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1305 if ((base64[2] == '=') && (base64[3] == '='))
1307 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1308 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1310 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1314 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1317 if (base64[3] == '=')
1319 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1320 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1322 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1326 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1335 /***********************************************************************
1336 * HTTP_InsertAuthorization
1338 * Insert or delete the authorization field in the request header.
1340 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1344 static const WCHAR wszSpace[] = {' ',0};
1345 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1347 WCHAR *authorization = NULL;
1349 if (pAuthInfo->auth_data_len)
1351 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1352 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1353 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1357 strcpyW(authorization, pAuthInfo->scheme);
1358 strcatW(authorization, wszSpace);
1359 HTTP_EncodeBase64(pAuthInfo->auth_data,
1360 pAuthInfo->auth_data_len,
1361 authorization+strlenW(authorization));
1363 /* clear the data as it isn't valid now that it has been sent to the
1364 * server, unless it's Basic authentication which doesn't do
1365 * connection tracking */
1366 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1368 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1369 pAuthInfo->auth_data = NULL;
1370 pAuthInfo->auth_data_len = 0;
1374 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1376 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1378 HeapFree(GetProcessHeap(), 0, authorization);
1383 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1385 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1388 size = sizeof(new_location);
1389 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1391 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1392 strcpyW( url, new_location );
1396 static const WCHAR slash[] = { '/',0 };
1397 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1398 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1399 http_session_t *session = req->lpHttpSession;
1401 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1402 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1404 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1406 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1407 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1409 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1410 if (req->lpszPath[0] != '/') strcatW( url, slash );
1411 strcatW( url, req->lpszPath );
1413 TRACE("url=%s\n", debugstr_w(url));
1417 /***********************************************************************
1418 * HTTP_DealWithProxy
1420 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1422 WCHAR buf[MAXHOSTNAME];
1423 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1424 static WCHAR szNul[] = { 0 };
1425 URL_COMPONENTSW UrlComponents;
1426 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1427 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1429 memset( &UrlComponents, 0, sizeof UrlComponents );
1430 UrlComponents.dwStructSize = sizeof UrlComponents;
1431 UrlComponents.lpszHostName = buf;
1432 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1434 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1435 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1436 sprintfW(proxy, szFormat, hIC->lpszProxy);
1438 strcpyW(proxy, hIC->lpszProxy);
1439 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1441 if( UrlComponents.dwHostNameLength == 0 )
1444 if( !lpwhr->lpszPath )
1445 lpwhr->lpszPath = szNul;
1447 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1448 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1450 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1451 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1452 lpwhs->nServerPort = UrlComponents.nPort;
1454 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1458 #ifndef INET6_ADDRSTRLEN
1459 #define INET6_ADDRSTRLEN 46
1462 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1464 char szaddr[INET6_ADDRSTRLEN];
1465 http_session_t *lpwhs = lpwhr->lpHttpSession;
1468 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1469 INTERNET_STATUS_RESOLVING_NAME,
1470 lpwhs->lpszServerName,
1471 strlenW(lpwhs->lpszServerName)+1);
1473 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1474 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1475 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1477 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1481 switch (lpwhs->socketAddress.ss_family)
1484 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1487 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1490 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1491 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1494 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1495 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1496 INTERNET_STATUS_NAME_RESOLVED,
1497 szaddr, strlen(szaddr)+1);
1499 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1504 /***********************************************************************
1505 * HTTPREQ_Destroy (internal)
1507 * Deallocate request handle
1510 static void HTTPREQ_Destroy(object_header_t *hdr)
1512 http_request_t *lpwhr = (http_request_t*) hdr;
1517 if(lpwhr->hCacheFile)
1518 CloseHandle(lpwhr->hCacheFile);
1520 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1522 DeleteCriticalSection( &lpwhr->read_section );
1523 WININET_Release(&lpwhr->lpHttpSession->hdr);
1525 destroy_authinfo(lpwhr->pAuthInfo);
1526 destroy_authinfo(lpwhr->pProxyAuthInfo);
1528 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1529 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1530 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1531 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1532 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1534 for (i = 0; i < lpwhr->nCustHeaders; i++)
1536 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1537 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1541 if(lpwhr->gzip_stream) {
1542 if(!lpwhr->gzip_stream->end_of_data)
1543 inflateEnd(&lpwhr->gzip_stream->zstream);
1544 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1548 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1549 HeapFree(GetProcessHeap(), 0, lpwhr);
1552 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1554 http_request_t *lpwhr = (http_request_t*) hdr;
1556 TRACE("%p\n",lpwhr);
1558 if (!NETCON_connected(&lpwhr->netConnection))
1561 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1562 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1564 NETCON_close(&lpwhr->netConnection);
1566 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1567 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1570 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1572 LPHTTPHEADERW host_header;
1574 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1576 host_header = HTTP_GetHeader(req, hostW);
1580 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1584 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1586 WCHAR szVersion[10];
1587 WCHAR szConnectionResponse[20];
1588 DWORD dwBufferSize = sizeof(szVersion);
1589 BOOL keepalive = FALSE;
1591 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1592 * the connection is keep-alive by default */
1593 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1594 &dwBufferSize, NULL) &&
1595 !strcmpiW(szVersion, g_szHttp1_1))
1600 dwBufferSize = sizeof(szConnectionResponse);
1601 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1602 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1604 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1610 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1612 http_request_t *req = (http_request_t*)hdr;
1615 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1617 http_session_t *lpwhs = req->lpHttpSession;
1618 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1620 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1622 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1623 return ERROR_INSUFFICIENT_BUFFER;
1624 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1625 /* FIXME: can't get a SOCKET from our connection since we don't use
1629 /* FIXME: get source port from req->netConnection */
1630 info->SourcePort = 0;
1631 info->DestPort = lpwhs->nHostPort;
1633 if (HTTP_KeepAlive(req))
1634 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1635 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1636 info->Flags |= IDSI_FLAG_PROXY;
1637 if (req->netConnection.useSSL)
1638 info->Flags |= IDSI_FLAG_SECURE;
1640 return ERROR_SUCCESS;
1643 case INTERNET_OPTION_SECURITY_FLAGS:
1645 http_session_t *lpwhs;
1646 lpwhs = req->lpHttpSession;
1648 if (*size < sizeof(ULONG))
1649 return ERROR_INSUFFICIENT_BUFFER;
1651 *size = sizeof(DWORD);
1652 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1653 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1655 *(DWORD*)buffer = 0;
1656 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1657 return ERROR_SUCCESS;
1660 case INTERNET_OPTION_HANDLE_TYPE:
1661 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1663 if (*size < sizeof(ULONG))
1664 return ERROR_INSUFFICIENT_BUFFER;
1666 *size = sizeof(DWORD);
1667 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1668 return ERROR_SUCCESS;
1670 case INTERNET_OPTION_URL: {
1671 WCHAR url[INTERNET_MAX_URL_LENGTH];
1676 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1678 TRACE("INTERNET_OPTION_URL\n");
1680 host = HTTP_GetHeader(req, hostW);
1681 strcpyW(url, httpW);
1682 strcatW(url, host->lpszValue);
1683 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1685 strcatW(url, req->lpszPath);
1687 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1690 len = (strlenW(url)+1) * sizeof(WCHAR);
1692 return ERROR_INSUFFICIENT_BUFFER;
1695 strcpyW(buffer, url);
1696 return ERROR_SUCCESS;
1698 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1700 return ERROR_INSUFFICIENT_BUFFER;
1703 return ERROR_SUCCESS;
1707 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1708 INTERNET_CACHE_ENTRY_INFOW *info;
1709 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1710 WCHAR url[INTERNET_MAX_URL_LENGTH];
1711 DWORD nbytes, error;
1714 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1716 if (*size < sizeof(*ts))
1718 *size = sizeof(*ts);
1719 return ERROR_INSUFFICIENT_BUFFER;
1722 HTTP_GetRequestURL(req, url);
1723 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1724 error = GetLastError();
1725 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1727 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1728 return ERROR_OUTOFMEMORY;
1730 GetUrlCacheEntryInfoW(url, info, &nbytes);
1732 ts->ftExpires = info->ExpireTime;
1733 ts->ftLastModified = info->LastModifiedTime;
1735 HeapFree(GetProcessHeap(), 0, info);
1736 *size = sizeof(*ts);
1737 return ERROR_SUCCESS;
1742 case INTERNET_OPTION_DATAFILE_NAME: {
1745 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1747 if(!req->lpszCacheFile) {
1749 return ERROR_INTERNET_ITEM_NOT_FOUND;
1753 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1754 if(*size < req_size)
1755 return ERROR_INSUFFICIENT_BUFFER;
1758 memcpy(buffer, req->lpszCacheFile, *size);
1759 return ERROR_SUCCESS;
1761 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1762 if (req_size > *size)
1763 return ERROR_INSUFFICIENT_BUFFER;
1765 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1766 -1, buffer, *size, NULL, NULL);
1767 return ERROR_SUCCESS;
1771 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1772 PCCERT_CONTEXT context;
1774 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1775 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1776 return ERROR_INSUFFICIENT_BUFFER;
1779 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1781 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1784 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1785 info->ftExpiry = context->pCertInfo->NotAfter;
1786 info->ftStart = context->pCertInfo->NotBefore;
1788 len = CertNameToStrW(context->dwCertEncodingType,
1789 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1790 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1791 if(info->lpszSubjectInfo)
1792 CertNameToStrW(context->dwCertEncodingType,
1793 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1794 info->lpszSubjectInfo, len);
1795 len = CertNameToStrW(context->dwCertEncodingType,
1796 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1797 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1798 if (info->lpszIssuerInfo)
1799 CertNameToStrW(context->dwCertEncodingType,
1800 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1801 info->lpszIssuerInfo, len);
1803 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1805 len = CertNameToStrA(context->dwCertEncodingType,
1806 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1807 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1808 if(infoA->lpszSubjectInfo)
1809 CertNameToStrA(context->dwCertEncodingType,
1810 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1811 infoA->lpszSubjectInfo, len);
1812 len = CertNameToStrA(context->dwCertEncodingType,
1813 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1814 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1815 if(infoA->lpszIssuerInfo)
1816 CertNameToStrA(context->dwCertEncodingType,
1817 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1818 infoA->lpszIssuerInfo, len);
1822 * Contrary to MSDN, these do not appear to be set.
1824 * lpszSignatureAlgName
1825 * lpszEncryptionAlgName
1828 CertFreeCertificateContext(context);
1829 return ERROR_SUCCESS;
1834 return INET_QueryOption(option, buffer, size, unicode);
1837 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1839 http_request_t *req = (http_request_t*)hdr;
1842 case INTERNET_OPTION_SEND_TIMEOUT:
1843 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1844 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1846 if (size != sizeof(DWORD))
1847 return ERROR_INVALID_PARAMETER;
1849 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1852 case INTERNET_OPTION_USERNAME:
1853 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1854 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1855 return ERROR_SUCCESS;
1857 case INTERNET_OPTION_PASSWORD:
1858 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1859 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1860 return ERROR_SUCCESS;
1861 case INTERNET_OPTION_HTTP_DECODING:
1862 if(size != sizeof(BOOL))
1863 return ERROR_INVALID_PARAMETER;
1864 req->decoding = *(BOOL*)buffer;
1865 return ERROR_SUCCESS;
1868 return ERROR_INTERNET_INVALID_OPTION;
1871 /* read some more data into the read buffer (the read section must be held) */
1872 static BOOL read_more_data( http_request_t *req, int maxlen )
1878 /* move existing data to the start of the buffer */
1880 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1884 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1886 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1887 maxlen - req->read_size, 0, &len ))
1890 req->read_size += len;
1894 /* remove some amount of data from the read buffer (the read section must be held) */
1895 static void remove_data( http_request_t *req, int count )
1897 if (!(req->read_size -= count)) req->read_pos = 0;
1898 else req->read_pos += count;
1901 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1903 int count, bytes_read, pos = 0;
1905 EnterCriticalSection( &req->read_section );
1908 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1912 count = eol - (req->read_buf + req->read_pos);
1913 bytes_read = count + 1;
1915 else count = bytes_read = req->read_size;
1917 count = min( count, *len - pos );
1918 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1920 remove_data( req, bytes_read );
1923 if (!read_more_data( req, -1 ) || !req->read_size)
1926 TRACE( "returning empty string\n" );
1927 LeaveCriticalSection( &req->read_section );
1931 LeaveCriticalSection( &req->read_section );
1935 if (pos && buffer[pos - 1] == '\r') pos--;
1938 buffer[*len - 1] = 0;
1939 TRACE( "returning %s\n", debugstr_a(buffer));
1943 /* discard data contents until we reach end of line (the read section must be held) */
1944 static BOOL discard_eol( http_request_t *req )
1948 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1951 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1954 req->read_pos = req->read_size = 0; /* discard everything */
1955 if (!read_more_data( req, -1 )) return FALSE;
1956 } while (req->read_size);
1960 /* read the size of the next chunk (the read section must be held) */
1961 static BOOL start_next_chunk( http_request_t *req )
1963 DWORD chunk_size = 0;
1965 if (!req->dwContentLength) return TRUE;
1966 if (req->dwContentLength == req->dwContentRead)
1968 /* read terminator for the previous chunk */
1969 if (!discard_eol( req )) return FALSE;
1970 req->dwContentLength = ~0u;
1971 req->dwContentRead = 0;
1975 while (req->read_size)
1977 char ch = req->read_buf[req->read_pos];
1978 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1979 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1980 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1981 else if (ch == ';' || ch == '\r' || ch == '\n')
1983 TRACE( "reading %u byte chunk\n", chunk_size );
1984 req->dwContentLength = chunk_size;
1985 req->dwContentRead = 0;
1986 if (!discard_eol( req )) return FALSE;
1989 remove_data( req, 1 );
1991 if (!read_more_data( req, -1 )) return FALSE;
1992 if (!req->read_size)
1994 req->dwContentLength = req->dwContentRead = 0;
2000 /* check if we have reached the end of the data to read (the read section must be held) */
2001 static BOOL end_of_read_data( http_request_t *req )
2003 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2004 if (req->read_chunked) return (req->dwContentLength == 0);
2005 if (req->dwContentLength == ~0u) return FALSE;
2006 return (req->dwContentLength == req->dwContentRead);
2009 /* fetch some more data into the read buffer (the read section must be held) */
2010 static BOOL refill_buffer( http_request_t *req )
2012 int len = sizeof(req->read_buf);
2014 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2016 if (!start_next_chunk( req )) return FALSE;
2019 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2020 if (len <= req->read_size) return TRUE;
2022 if (!read_more_data( req, len )) return FALSE;
2023 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2027 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2029 DWORD ret = ERROR_SUCCESS;
2033 z_stream *zstream = &req->gzip_stream->zstream;
2036 while(read < size && !req->gzip_stream->end_of_data) {
2037 if(!req->read_size) {
2038 if(!sync || !refill_buffer(req))
2042 zstream->next_in = req->read_buf+req->read_pos;
2043 zstream->avail_in = req->read_size;
2044 zstream->next_out = buf+read;
2045 zstream->avail_out = size-read;
2046 zres = inflate(zstream, Z_FULL_FLUSH);
2047 read = size - zstream->avail_out;
2048 remove_data(req, req->read_size-zstream->avail_in);
2049 if(zres == Z_STREAM_END) {
2050 TRACE("end of data\n");
2051 req->gzip_stream->end_of_data = TRUE;
2052 inflateEnd(&req->gzip_stream->zstream);
2053 }else if(zres != Z_OK) {
2054 WARN("inflate failed %d\n", zres);
2056 ret = ERROR_INTERNET_DECODING_FAILED;
2066 static void refill_gzip_buffer(http_request_t *req)
2071 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2074 if(req->gzip_stream->buf_pos) {
2075 if(req->gzip_stream->buf_size)
2076 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2077 req->gzip_stream->buf_pos = 0;
2080 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2081 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2082 if(res == ERROR_SUCCESS)
2083 req->gzip_stream->buf_size += len;
2086 /* return the size of data available to be read immediately (the read section must be held) */
2087 static DWORD get_avail_data( http_request_t *req )
2089 if (req->gzip_stream) {
2090 refill_gzip_buffer(req);
2091 return req->gzip_stream->buf_size;
2093 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2095 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2098 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2100 INTERNET_ASYNC_RESULT iar;
2104 EnterCriticalSection( &req->read_section );
2105 if (refill_buffer( req )) {
2106 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2107 iar.dwError = first_notif ? 0 : get_avail_data(req);
2110 iar.dwError = INTERNET_GetLastError();
2112 LeaveCriticalSection( &req->read_section );
2114 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2115 sizeof(INTERNET_ASYNC_RESULT));
2118 /* read data from the http connection (the read section must be held) */
2119 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2121 BOOL finished_reading = FALSE;
2122 int len, bytes_read = 0;
2123 DWORD ret = ERROR_SUCCESS;
2125 EnterCriticalSection( &req->read_section );
2127 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2129 if (!start_next_chunk( req )) goto done;
2132 if(req->gzip_stream) {
2133 if(req->gzip_stream->buf_size) {
2134 bytes_read = min(req->gzip_stream->buf_size, size);
2135 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2136 req->gzip_stream->buf_pos += bytes_read;
2137 req->gzip_stream->buf_size -= bytes_read;
2138 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2142 if(size > bytes_read) {
2143 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2144 if(ret == ERROR_SUCCESS)
2148 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2150 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2152 if (req->read_size) {
2153 bytes_read = min( req->read_size, size );
2154 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2155 remove_data( req, bytes_read );
2158 if (size > bytes_read && (!bytes_read || sync)) {
2159 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2160 sync ? MSG_WAITALL : 0, &len))
2162 /* always return success, even if the network layer returns an error */
2165 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2168 req->dwContentRead += bytes_read;
2171 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2172 LeaveCriticalSection( &req->read_section );
2174 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2176 DWORD dwBytesWritten;
2178 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2180 WARN("WriteFile failed: %u\n", GetLastError());
2183 if(finished_reading)
2184 HTTP_FinishedReading(req);
2190 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2192 http_request_t *req = (http_request_t*)hdr;
2193 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2196 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2198 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2199 http_request_t *req = (http_request_t*)workRequest->hdr;
2200 INTERNET_ASYNC_RESULT iar;
2203 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2205 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2206 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2208 iar.dwResult = res == ERROR_SUCCESS;
2211 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2212 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2213 sizeof(INTERNET_ASYNC_RESULT));
2216 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2217 DWORD flags, DWORD_PTR context)
2219 http_request_t *req = (http_request_t*)hdr;
2222 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2223 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2225 if (buffers->dwStructSize != sizeof(*buffers))
2226 return ERROR_INVALID_PARAMETER;
2228 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2230 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2232 WORKREQUEST workRequest;
2234 if (TryEnterCriticalSection( &req->read_section ))
2236 if (get_avail_data(req))
2238 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2239 &buffers->dwBufferLength, FALSE);
2240 LeaveCriticalSection( &req->read_section );
2243 LeaveCriticalSection( &req->read_section );
2246 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2247 workRequest.hdr = WININET_AddRef(&req->hdr);
2248 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2250 INTERNET_AsyncCall(&workRequest);
2252 return ERROR_IO_PENDING;
2255 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2256 !(flags & IRF_NO_WAIT));
2259 if (res == ERROR_SUCCESS) {
2260 DWORD size = buffers->dwBufferLength;
2261 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2262 &size, sizeof(size));
2268 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2270 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2271 http_request_t *req = (http_request_t*)workRequest->hdr;
2272 INTERNET_ASYNC_RESULT iar;
2275 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2277 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2278 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2280 iar.dwResult = res == ERROR_SUCCESS;
2283 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2284 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2285 sizeof(INTERNET_ASYNC_RESULT));
2288 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2289 DWORD flags, DWORD_PTR context)
2292 http_request_t *req = (http_request_t*)hdr;
2295 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2296 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2298 if (buffers->dwStructSize != sizeof(*buffers))
2299 return ERROR_INVALID_PARAMETER;
2301 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2303 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2305 WORKREQUEST workRequest;
2307 if (TryEnterCriticalSection( &req->read_section ))
2309 if (get_avail_data(req))
2311 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2312 &buffers->dwBufferLength, FALSE);
2313 LeaveCriticalSection( &req->read_section );
2316 LeaveCriticalSection( &req->read_section );
2319 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2320 workRequest.hdr = WININET_AddRef(&req->hdr);
2321 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2323 INTERNET_AsyncCall(&workRequest);
2325 return ERROR_IO_PENDING;
2328 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2329 !(flags & IRF_NO_WAIT));
2332 if (res == ERROR_SUCCESS) {
2333 DWORD size = buffers->dwBufferLength;
2334 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2335 &size, sizeof(size));
2341 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2344 http_request_t *lpwhr = (http_request_t*)hdr;
2346 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2349 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2350 lpwhr->dwBytesWritten += *written;
2352 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2356 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2358 http_request_t *req = (http_request_t*)workRequest->hdr;
2360 HTTP_ReceiveRequestData(req, FALSE);
2363 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2365 http_request_t *req = (http_request_t*)hdr;
2367 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2369 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2371 WORKREQUEST workRequest;
2373 /* never wait, if we can't enter the section we queue an async request right away */
2374 if (TryEnterCriticalSection( &req->read_section ))
2376 if ((*available = get_avail_data( req ))) goto done;
2377 if (end_of_read_data( req )) goto done;
2378 LeaveCriticalSection( &req->read_section );
2381 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2382 workRequest.hdr = WININET_AddRef( &req->hdr );
2384 INTERNET_AsyncCall(&workRequest);
2386 return ERROR_IO_PENDING;
2389 EnterCriticalSection( &req->read_section );
2391 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2393 refill_buffer( req );
2394 *available = get_avail_data( req );
2398 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2401 if (NETCON_query_data_available(&req->netConnection, &extra))
2402 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2404 LeaveCriticalSection( &req->read_section );
2406 TRACE( "returning %u\n", *available );
2407 return ERROR_SUCCESS;
2410 static const object_vtbl_t HTTPREQVtbl = {
2412 HTTPREQ_CloseConnection,
2413 HTTPREQ_QueryOption,
2416 HTTPREQ_ReadFileExA,
2417 HTTPREQ_ReadFileExW,
2419 HTTPREQ_QueryDataAvailable,
2423 /***********************************************************************
2424 * HTTP_HttpOpenRequestW (internal)
2426 * Open a HTTP request handle
2429 * HINTERNET a HTTP request handle on success
2433 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2434 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2435 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2436 DWORD dwFlags, DWORD_PTR dwContext)
2438 appinfo_t *hIC = NULL;
2439 http_request_t *lpwhr;
2440 LPWSTR lpszHostName = NULL;
2441 HINTERNET handle = NULL;
2442 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2447 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2448 hIC = lpwhs->lpAppInfo;
2450 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2453 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2456 lpwhr->hdr.htype = WH_HHTTPREQ;
2457 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2458 lpwhr->hdr.dwFlags = dwFlags;
2459 lpwhr->hdr.dwContext = dwContext;
2460 lpwhr->hdr.refs = 1;
2461 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2462 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2463 lpwhr->dwContentLength = ~0u;
2464 InitializeCriticalSection( &lpwhr->read_section );
2466 WININET_AddRef( &lpwhs->hdr );
2467 lpwhr->lpHttpSession = lpwhs;
2468 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2470 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2471 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2472 if (NULL == lpszHostName)
2474 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2478 handle = WININET_AllocHandle( &lpwhr->hdr );
2481 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2485 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2487 InternetCloseHandle( handle );
2492 if (lpszObjectName && *lpszObjectName) {
2496 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2497 if (rc != E_POINTER)
2498 len = strlenW(lpszObjectName)+1;
2499 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2500 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2501 URL_ESCAPE_SPACES_ONLY);
2504 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2505 strcpyW(lpwhr->lpszPath,lpszObjectName);
2508 static const WCHAR slashW[] = {'/',0};
2510 lpwhr->lpszPath = heap_strdupW(slashW);
2513 if (lpszReferrer && *lpszReferrer)
2514 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2516 if (lpszAcceptTypes)
2519 for (i = 0; lpszAcceptTypes[i]; i++)
2521 if (!*lpszAcceptTypes[i]) continue;
2522 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2523 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2524 HTTP_ADDHDR_FLAG_REQ |
2525 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2529 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2530 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2532 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2533 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2534 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2536 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2537 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2538 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2541 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2542 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2544 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2545 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2546 INTERNET_DEFAULT_HTTPS_PORT :
2547 INTERNET_DEFAULT_HTTP_PORT);
2549 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2550 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2551 INTERNET_DEFAULT_HTTPS_PORT :
2552 INTERNET_DEFAULT_HTTP_PORT);
2554 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2555 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2557 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2558 INTERNET_STATUS_HANDLE_CREATED, &handle,
2562 HeapFree(GetProcessHeap(), 0, lpszHostName);
2564 WININET_Release( &lpwhr->hdr );
2566 TRACE("<-- %p (%p)\n", handle, lpwhr);
2570 /* read any content returned by the server so that the connection can be
2572 static void HTTP_DrainContent(http_request_t *req)
2576 if (!NETCON_connected(&req->netConnection)) return;
2578 if (req->dwContentLength == -1)
2580 NETCON_close(&req->netConnection);
2583 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2588 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2590 } while (bytes_read);
2593 static const LPCWSTR header_lookup[] = {
2594 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2595 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2596 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2597 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2598 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2599 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2600 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2601 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2602 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2603 szDate, /* HTTP_QUERY_DATE = 9 */
2604 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2605 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2606 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2607 szURI, /* HTTP_QUERY_URI = 13 */
2608 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2609 NULL, /* HTTP_QUERY_COST = 15 */
2610 NULL, /* HTTP_QUERY_LINK = 16 */
2611 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2612 NULL, /* HTTP_QUERY_VERSION = 18 */
2613 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2614 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2615 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2616 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2617 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2618 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2619 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2620 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2621 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2622 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2623 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2624 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2625 NULL, /* HTTP_QUERY_FROM = 31 */
2626 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2627 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2628 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2629 szReferer, /* HTTP_QUERY_REFERER = 35 */
2630 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2631 szServer, /* HTTP_QUERY_SERVER = 37 */
2632 NULL, /* HTTP_TITLE = 38 */
2633 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2634 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2635 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2636 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2637 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2638 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2639 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2640 NULL, /* HTTP_QUERY_REFRESH = 46 */
2641 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2642 szAge, /* HTTP_QUERY_AGE = 48 */
2643 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2644 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2645 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2646 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2647 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2648 szETag, /* HTTP_QUERY_ETAG = 54 */
2649 hostW, /* HTTP_QUERY_HOST = 55 */
2650 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2651 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2652 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2653 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2654 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2655 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2656 szRange, /* HTTP_QUERY_RANGE = 62 */
2657 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2658 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2659 szVary, /* HTTP_QUERY_VARY = 65 */
2660 szVia, /* HTTP_QUERY_VIA = 66 */
2661 szWarning, /* HTTP_QUERY_WARNING = 67 */
2662 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2663 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2664 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2667 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2669 /***********************************************************************
2670 * HTTP_HttpQueryInfoW (internal)
2672 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2673 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2675 LPHTTPHEADERW lphttpHdr = NULL;
2676 BOOL bSuccess = FALSE;
2677 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2678 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2679 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2682 /* Find requested header structure */
2685 case HTTP_QUERY_CUSTOM:
2686 if (!lpBuffer) return FALSE;
2687 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2689 case HTTP_QUERY_RAW_HEADERS_CRLF:
2696 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2698 headers = lpwhr->lpszRawHeaders;
2701 len = strlenW(headers) * sizeof(WCHAR);
2703 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2705 len += sizeof(WCHAR);
2706 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2712 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2715 len = strlenW(szCrLf) * sizeof(WCHAR);
2716 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2718 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2721 *lpdwBufferLength = len;
2724 HeapFree(GetProcessHeap(), 0, headers);
2727 case HTTP_QUERY_RAW_HEADERS:
2729 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2731 LPWSTR pszString = lpBuffer;
2733 for (i = 0; ppszRawHeaderLines[i]; i++)
2734 size += strlenW(ppszRawHeaderLines[i]) + 1;
2736 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2738 HTTP_FreeTokens(ppszRawHeaderLines);
2739 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2740 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2745 for (i = 0; ppszRawHeaderLines[i]; i++)
2747 DWORD len = strlenW(ppszRawHeaderLines[i]);
2748 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2752 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2754 *lpdwBufferLength = size * sizeof(WCHAR);
2755 HTTP_FreeTokens(ppszRawHeaderLines);
2759 case HTTP_QUERY_STATUS_TEXT:
2760 if (lpwhr->lpszStatusText)
2762 DWORD len = strlenW(lpwhr->lpszStatusText);
2763 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2765 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2766 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2771 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2772 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2774 *lpdwBufferLength = len * sizeof(WCHAR);
2778 case HTTP_QUERY_VERSION:
2779 if (lpwhr->lpszVersion)
2781 DWORD len = strlenW(lpwhr->lpszVersion);
2782 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2784 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2785 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2790 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2791 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2793 *lpdwBufferLength = len * sizeof(WCHAR);
2797 case HTTP_QUERY_CONTENT_ENCODING:
2798 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2799 requested_index,request_only);
2802 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2804 if (level < LAST_TABLE_HEADER && header_lookup[level])
2805 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2806 requested_index,request_only);
2810 lphttpHdr = &lpwhr->pCustHeaders[index];
2812 /* Ensure header satisfies requested attributes */
2814 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2815 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2817 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2821 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2823 /* coalesce value to requested type */
2824 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2826 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2827 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2830 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2836 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2838 tmpTM = *gmtime(&tmpTime);
2839 STHook = (SYSTEMTIME *)lpBuffer;
2840 STHook->wDay = tmpTM.tm_mday;
2841 STHook->wHour = tmpTM.tm_hour;
2842 STHook->wMilliseconds = 0;
2843 STHook->wMinute = tmpTM.tm_min;
2844 STHook->wDayOfWeek = tmpTM.tm_wday;
2845 STHook->wMonth = tmpTM.tm_mon + 1;
2846 STHook->wSecond = tmpTM.tm_sec;
2847 STHook->wYear = tmpTM.tm_year;
2850 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2851 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2852 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2854 else if (lphttpHdr->lpszValue)
2856 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2858 if (len > *lpdwBufferLength)
2860 *lpdwBufferLength = len;
2861 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2866 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2867 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2869 *lpdwBufferLength = len - sizeof(WCHAR);
2875 /***********************************************************************
2876 * HttpQueryInfoW (WININET.@)
2878 * Queries for information about an HTTP request
2885 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2886 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2888 BOOL bSuccess = FALSE;
2889 http_request_t *lpwhr;
2891 if (TRACE_ON(wininet)) {
2892 #define FE(x) { x, #x }
2893 static const wininet_flag_info query_flags[] = {
2894 FE(HTTP_QUERY_MIME_VERSION),
2895 FE(HTTP_QUERY_CONTENT_TYPE),
2896 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2897 FE(HTTP_QUERY_CONTENT_ID),
2898 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2899 FE(HTTP_QUERY_CONTENT_LENGTH),
2900 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2901 FE(HTTP_QUERY_ALLOW),
2902 FE(HTTP_QUERY_PUBLIC),
2903 FE(HTTP_QUERY_DATE),
2904 FE(HTTP_QUERY_EXPIRES),
2905 FE(HTTP_QUERY_LAST_MODIFIED),
2906 FE(HTTP_QUERY_MESSAGE_ID),
2908 FE(HTTP_QUERY_DERIVED_FROM),
2909 FE(HTTP_QUERY_COST),
2910 FE(HTTP_QUERY_LINK),
2911 FE(HTTP_QUERY_PRAGMA),
2912 FE(HTTP_QUERY_VERSION),
2913 FE(HTTP_QUERY_STATUS_CODE),
2914 FE(HTTP_QUERY_STATUS_TEXT),
2915 FE(HTTP_QUERY_RAW_HEADERS),
2916 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2917 FE(HTTP_QUERY_CONNECTION),
2918 FE(HTTP_QUERY_ACCEPT),
2919 FE(HTTP_QUERY_ACCEPT_CHARSET),
2920 FE(HTTP_QUERY_ACCEPT_ENCODING),
2921 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2922 FE(HTTP_QUERY_AUTHORIZATION),
2923 FE(HTTP_QUERY_CONTENT_ENCODING),
2924 FE(HTTP_QUERY_FORWARDED),
2925 FE(HTTP_QUERY_FROM),
2926 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2927 FE(HTTP_QUERY_LOCATION),
2928 FE(HTTP_QUERY_ORIG_URI),
2929 FE(HTTP_QUERY_REFERER),
2930 FE(HTTP_QUERY_RETRY_AFTER),
2931 FE(HTTP_QUERY_SERVER),
2932 FE(HTTP_QUERY_TITLE),
2933 FE(HTTP_QUERY_USER_AGENT),
2934 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2935 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2936 FE(HTTP_QUERY_ACCEPT_RANGES),
2937 FE(HTTP_QUERY_SET_COOKIE),
2938 FE(HTTP_QUERY_COOKIE),
2939 FE(HTTP_QUERY_REQUEST_METHOD),
2940 FE(HTTP_QUERY_REFRESH),
2941 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2943 FE(HTTP_QUERY_CACHE_CONTROL),
2944 FE(HTTP_QUERY_CONTENT_BASE),
2945 FE(HTTP_QUERY_CONTENT_LOCATION),
2946 FE(HTTP_QUERY_CONTENT_MD5),
2947 FE(HTTP_QUERY_CONTENT_RANGE),
2948 FE(HTTP_QUERY_ETAG),
2949 FE(HTTP_QUERY_HOST),
2950 FE(HTTP_QUERY_IF_MATCH),
2951 FE(HTTP_QUERY_IF_NONE_MATCH),
2952 FE(HTTP_QUERY_IF_RANGE),
2953 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2954 FE(HTTP_QUERY_MAX_FORWARDS),
2955 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2956 FE(HTTP_QUERY_RANGE),
2957 FE(HTTP_QUERY_TRANSFER_ENCODING),
2958 FE(HTTP_QUERY_UPGRADE),
2959 FE(HTTP_QUERY_VARY),
2961 FE(HTTP_QUERY_WARNING),
2962 FE(HTTP_QUERY_CUSTOM)
2964 static const wininet_flag_info modifier_flags[] = {
2965 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2966 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2967 FE(HTTP_QUERY_FLAG_NUMBER),
2968 FE(HTTP_QUERY_FLAG_COALESCE)
2971 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2972 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2975 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2976 TRACE(" Attribute:");
2977 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2978 if (query_flags[i].val == info) {
2979 TRACE(" %s", query_flags[i].name);
2983 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2984 TRACE(" Unknown (%08x)", info);
2987 TRACE(" Modifier:");
2988 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2989 if (modifier_flags[i].val & info_mod) {
2990 TRACE(" %s", modifier_flags[i].name);
2991 info_mod &= ~ modifier_flags[i].val;
2996 TRACE(" Unknown (%08x)", info_mod);
3001 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3002 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3004 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3008 if (lpBuffer == NULL)
3009 *lpdwBufferLength = 0;
3010 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3011 lpBuffer, lpdwBufferLength, lpdwIndex);
3015 WININET_Release( &lpwhr->hdr );
3017 TRACE("%d <--\n", bSuccess);
3021 /***********************************************************************
3022 * HttpQueryInfoA (WININET.@)
3024 * Queries for information about an HTTP request
3031 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3032 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3038 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3039 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3041 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3042 lpdwBufferLength, lpdwIndex );
3048 len = (*lpdwBufferLength)*sizeof(WCHAR);
3049 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3051 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3057 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3058 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3059 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3060 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3067 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3071 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3072 lpBuffer, *lpdwBufferLength, NULL, NULL );
3073 *lpdwBufferLength = len - 1;
3075 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3078 /* since the strings being returned from HttpQueryInfoW should be
3079 * only ASCII characters, it is reasonable to assume that all of
3080 * the Unicode characters can be reduced to a single byte */
3081 *lpdwBufferLength = len / sizeof(WCHAR);
3083 HeapFree(GetProcessHeap(), 0, bufferW );
3088 /***********************************************************************
3089 * HttpSendRequestExA (WININET.@)
3091 * Sends the specified request to the HTTP server and allows chunked
3096 * Failure: FALSE, call GetLastError() for more information.
3098 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3099 LPINTERNET_BUFFERSA lpBuffersIn,
3100 LPINTERNET_BUFFERSA lpBuffersOut,
3101 DWORD dwFlags, DWORD_PTR dwContext)
3103 INTERNET_BUFFERSW BuffersInW;
3106 LPWSTR header = NULL;
3108 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3109 lpBuffersOut, dwFlags, dwContext);
3113 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3114 if (lpBuffersIn->lpcszHeader)
3116 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3117 lpBuffersIn->dwHeadersLength,0,0);
3118 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3119 if (!(BuffersInW.lpcszHeader = header))
3121 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3124 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3125 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3129 BuffersInW.lpcszHeader = NULL;
3130 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3131 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3132 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3133 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3134 BuffersInW.Next = NULL;
3137 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3139 HeapFree(GetProcessHeap(),0,header);
3144 /***********************************************************************
3145 * HttpSendRequestExW (WININET.@)
3147 * Sends the specified request to the HTTP server and allows chunked
3152 * Failure: FALSE, call GetLastError() for more information.
3154 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3155 LPINTERNET_BUFFERSW lpBuffersIn,
3156 LPINTERNET_BUFFERSW lpBuffersOut,
3157 DWORD dwFlags, DWORD_PTR dwContext)
3160 http_request_t *lpwhr;
3161 http_session_t *lpwhs;
3164 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3165 lpBuffersOut, dwFlags, dwContext);
3167 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3169 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3171 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3175 lpwhs = lpwhr->lpHttpSession;
3176 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3177 hIC = lpwhs->lpAppInfo;
3178 assert(hIC->hdr.htype == WH_HINIT);
3180 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3182 WORKREQUEST workRequest;
3183 struct WORKREQ_HTTPSENDREQUESTW *req;
3185 workRequest.asyncproc = AsyncHttpSendRequestProc;
3186 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3187 req = &workRequest.u.HttpSendRequestW;
3190 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3191 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3192 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3193 req->lpOptional = lpBuffersIn->lpvBuffer;
3194 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3195 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3199 req->lpszHeader = NULL;
3200 req->dwHeaderLength = 0;
3201 req->lpOptional = NULL;
3202 req->dwOptionalLength = 0;
3203 req->dwContentLength = 0;
3206 req->bEndRequest = FALSE;
3208 INTERNET_AsyncCall(&workRequest);
3210 * This is from windows.
3212 INTERNET_SetLastError(ERROR_IO_PENDING);
3217 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3218 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3219 lpBuffersIn->dwBufferTotal, FALSE);
3221 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3226 WININET_Release( &lpwhr->hdr );
3232 /***********************************************************************
3233 * HttpSendRequestW (WININET.@)
3235 * Sends the specified request to the HTTP server
3242 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3243 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3245 http_request_t *lpwhr;
3246 http_session_t *lpwhs = NULL;
3247 appinfo_t *hIC = NULL;
3250 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3251 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3253 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3254 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3256 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3261 lpwhs = lpwhr->lpHttpSession;
3262 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3264 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3269 hIC = lpwhs->lpAppInfo;
3270 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3272 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3277 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3279 WORKREQUEST workRequest;
3280 struct WORKREQ_HTTPSENDREQUESTW *req;
3282 workRequest.asyncproc = AsyncHttpSendRequestProc;
3283 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3284 req = &workRequest.u.HttpSendRequestW;
3289 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3290 else size = dwHeaderLength * sizeof(WCHAR);
3292 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3293 memcpy(req->lpszHeader, lpszHeaders, size);
3296 req->lpszHeader = 0;
3297 req->dwHeaderLength = dwHeaderLength;
3298 req->lpOptional = lpOptional;
3299 req->dwOptionalLength = dwOptionalLength;
3300 req->dwContentLength = dwOptionalLength;
3301 req->bEndRequest = TRUE;
3303 INTERNET_AsyncCall(&workRequest);
3305 * This is from windows.
3307 INTERNET_SetLastError(ERROR_IO_PENDING);
3312 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3313 dwHeaderLength, lpOptional, dwOptionalLength,
3314 dwOptionalLength, TRUE);
3318 WININET_Release( &lpwhr->hdr );
3322 /***********************************************************************
3323 * HttpSendRequestA (WININET.@)
3325 * Sends the specified request to the HTTP server
3332 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3333 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3336 LPWSTR szHeaders=NULL;
3337 DWORD nLen=dwHeaderLength;
3338 if(lpszHeaders!=NULL)
3340 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3341 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3342 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3344 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3345 HeapFree(GetProcessHeap(),0,szHeaders);
3349 /***********************************************************************
3350 * HTTP_GetRedirectURL (internal)
3352 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3354 static WCHAR szHttp[] = {'h','t','t','p',0};
3355 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3356 http_session_t *lpwhs = lpwhr->lpHttpSession;
3357 URL_COMPONENTSW urlComponents;
3358 DWORD url_length = 0;
3360 LPWSTR combined_url;
3362 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3363 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3364 urlComponents.dwSchemeLength = 0;
3365 urlComponents.lpszHostName = lpwhs->lpszHostName;
3366 urlComponents.dwHostNameLength = 0;
3367 urlComponents.nPort = lpwhs->nHostPort;
3368 urlComponents.lpszUserName = lpwhs->lpszUserName;
3369 urlComponents.dwUserNameLength = 0;
3370 urlComponents.lpszPassword = NULL;
3371 urlComponents.dwPasswordLength = 0;
3372 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3373 urlComponents.dwUrlPathLength = 0;
3374 urlComponents.lpszExtraInfo = NULL;
3375 urlComponents.dwExtraInfoLength = 0;
3377 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3378 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3381 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3383 /* convert from bytes to characters */
3384 url_length = url_length / sizeof(WCHAR) - 1;
3385 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3387 HeapFree(GetProcessHeap(), 0, orig_url);
3392 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3393 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3395 HeapFree(GetProcessHeap(), 0, orig_url);
3398 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3400 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3402 HeapFree(GetProcessHeap(), 0, orig_url);
3403 HeapFree(GetProcessHeap(), 0, combined_url);
3406 HeapFree(GetProcessHeap(), 0, orig_url);
3407 return combined_url;
3411 /***********************************************************************
3412 * HTTP_HandleRedirect (internal)
3414 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3416 http_session_t *lpwhs = lpwhr->lpHttpSession;
3417 appinfo_t *hIC = lpwhs->lpAppInfo;
3418 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3419 WCHAR path[INTERNET_MAX_URL_LENGTH];
3424 /* if it's an absolute path, keep the same session info */
3425 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3429 URL_COMPONENTSW urlComponents;
3430 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3431 static WCHAR szHttp[] = {'h','t','t','p',0};
3432 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3438 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3439 urlComponents.lpszScheme = protocol;
3440 urlComponents.dwSchemeLength = 32;
3441 urlComponents.lpszHostName = hostName;
3442 urlComponents.dwHostNameLength = MAXHOSTNAME;
3443 urlComponents.lpszUserName = userName;
3444 urlComponents.dwUserNameLength = 1024;
3445 urlComponents.lpszPassword = NULL;
3446 urlComponents.dwPasswordLength = 0;
3447 urlComponents.lpszUrlPath = path;
3448 urlComponents.dwUrlPathLength = 2048;
3449 urlComponents.lpszExtraInfo = NULL;
3450 urlComponents.dwExtraInfoLength = 0;
3451 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3454 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3455 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3457 TRACE("redirect from secure page to non-secure page\n");
3458 /* FIXME: warn about from secure redirect to non-secure page */
3459 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3461 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3462 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3464 TRACE("redirect from non-secure page to secure page\n");
3465 /* FIXME: notify about redirect to secure page */
3466 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3469 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3471 if (lstrlenW(protocol)>4) /*https*/
3472 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3474 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3479 * This upsets redirects to binary files on sourceforge.net
3480 * and gives an html page instead of the target file
3481 * Examination of the HTTP request sent by native wininet.dll
3482 * reveals that it doesn't send a referrer in that case.
3483 * Maybe there's a flag that enables this, or maybe a referrer
3484 * shouldn't be added in case of a redirect.
3487 /* consider the current host as the referrer */
3488 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3489 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3490 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3491 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3494 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3495 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3496 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3499 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3500 len = lstrlenW(hostName);
3501 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3502 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3503 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3506 lpwhs->lpszHostName = heap_strdupW(hostName);
3508 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3510 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3511 lpwhs->lpszUserName = NULL;
3513 lpwhs->lpszUserName = heap_strdupW(userName);
3517 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3519 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3520 lpwhs->lpszServerName = heap_strdupW(hostName);
3521 lpwhs->nServerPort = urlComponents.nPort;
3523 NETCON_close(&lpwhr->netConnection);
3524 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3525 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3526 lpwhr->read_pos = lpwhr->read_size = 0;
3527 lpwhr->read_chunked = FALSE;
3531 TRACE("Redirect through proxy\n");
3534 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3535 lpwhr->lpszPath=NULL;
3541 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3542 if (rc != E_POINTER)
3543 needed = strlenW(path)+1;
3544 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3545 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3546 URL_ESCAPE_SPACES_ONLY);
3549 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3550 strcpyW(lpwhr->lpszPath,path);
3554 /* Remove custom content-type/length headers on redirects. */
3555 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3557 HTTP_DeleteCustomHeader(lpwhr, index);
3558 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3560 HTTP_DeleteCustomHeader(lpwhr, index);
3565 /***********************************************************************
3566 * HTTP_build_req (internal)
3568 * concatenate all the strings in the request together
3570 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3575 for( t = list; *t ; t++ )
3576 len += strlenW( *t );
3579 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3582 for( t = list; *t ; t++ )
3588 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3591 LPWSTR requestString;
3597 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3598 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3599 http_session_t *lpwhs = lpwhr->lpHttpSession;
3603 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3604 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3605 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3606 HeapFree( GetProcessHeap(), 0, lpszPath );
3608 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3609 NULL, 0, NULL, NULL );
3610 len--; /* the nul terminator isn't needed */
3611 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3612 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3613 ascii_req, len, NULL, NULL );
3614 HeapFree( GetProcessHeap(), 0, requestString );
3616 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3618 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3619 HeapFree( GetProcessHeap(), 0, ascii_req );
3620 if (!ret || cnt < 0)
3623 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3630 static void HTTP_InsertCookies(http_request_t *lpwhr)
3632 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3633 LPWSTR lpszCookies, lpszUrl = NULL;
3634 DWORD nCookieSize, size;
3635 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3637 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3638 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3639 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3641 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3644 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3646 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3647 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3649 cnt += sprintfW(lpszCookies, szCookie);
3650 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3651 strcatW(lpszCookies, szCrLf);
3653 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3654 HeapFree(GetProcessHeap(), 0, lpszCookies);
3657 HeapFree(GetProcessHeap(), 0, lpszUrl);
3660 /***********************************************************************
3661 * HTTP_HttpSendRequestW (internal)
3663 * Sends the specified request to the HTTP server
3670 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3671 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3672 DWORD dwContentLength, BOOL bEndRequest)
3675 BOOL bSuccess = FALSE, redirected = FALSE;
3676 LPWSTR requestString = NULL;
3679 INTERNET_ASYNC_RESULT iar;
3680 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3681 static const WCHAR szContentLength[] =
3682 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3683 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3685 TRACE("--> %p\n", lpwhr);
3687 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3689 /* if the verb is NULL default to GET */
3690 if (!lpwhr->lpszVerb)
3691 lpwhr->lpszVerb = heap_strdupW(szGET);
3693 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3695 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3696 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3697 lpwhr->dwBytesToWrite = dwContentLength;
3699 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3701 WCHAR *agent_header;
3702 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3705 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3706 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3707 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3709 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3710 HeapFree(GetProcessHeap(), 0, agent_header);
3712 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3714 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3715 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3717 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3719 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3720 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3721 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3731 /* like native, just in case the caller forgot to call InternetReadFile
3732 * for all the data */
3733 HTTP_DrainContent(lpwhr);
3734 lpwhr->dwContentRead = 0;
3736 if (TRACE_ON(wininet))
3738 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3739 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3743 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3745 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3747 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3748 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3750 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3751 HTTP_InsertCookies(lpwhr);
3753 /* add the headers the caller supplied */
3754 if( lpszHeaders && dwHeaderLength )
3756 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3757 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3760 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3762 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3763 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3764 HeapFree(GetProcessHeap(), 0, url);
3767 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3770 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3772 /* Send the request and store the results */
3773 if (!HTTP_OpenConnection(lpwhr))
3776 /* send the request as ASCII, tack on the optional data */
3777 if (!lpOptional || redirected)
3778 dwOptionalLength = 0;
3779 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3780 NULL, 0, NULL, NULL );
3781 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3782 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3783 ascii_req, len, NULL, NULL );
3785 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3786 len = (len + dwOptionalLength - 1);
3788 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3790 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3791 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3793 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3794 HeapFree( GetProcessHeap(), 0, ascii_req );
3796 lpwhr->dwBytesWritten = dwOptionalLength;
3798 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3799 INTERNET_STATUS_REQUEST_SENT,
3800 &len, sizeof(DWORD));
3807 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3808 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3813 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3817 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3818 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3821 HTTP_ProcessCookies(lpwhr);
3823 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3825 dwBufferSize = sizeof(dwStatusCode);
3826 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3827 &dwStatusCode,&dwBufferSize,NULL))
3830 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3832 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3833 dwBufferSize=sizeof(szNewLocation);
3834 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3835 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3837 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3839 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3840 lpwhr->lpszVerb = heap_strdupW(szGET);
3842 HTTP_DrainContent(lpwhr);
3843 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3845 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3846 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3847 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3850 HeapFree(GetProcessHeap(), 0, requestString);
3853 HeapFree( GetProcessHeap(), 0, new_url );
3858 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3860 WCHAR szAuthValue[2048];
3862 if (dwStatusCode == HTTP_STATUS_DENIED)
3865 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3867 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3869 lpwhr->lpHttpSession->lpszUserName,
3870 lpwhr->lpHttpSession->lpszPassword))
3877 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3880 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3882 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3883 &lpwhr->pProxyAuthInfo,
3884 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3885 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3900 WCHAR url[INTERNET_MAX_URL_LENGTH];
3901 WCHAR cacheFileName[MAX_PATH+1];
3904 b = HTTP_GetRequestURL(lpwhr, url);
3906 WARN("Could not get URL\n");
3910 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3912 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3913 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3914 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3915 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3916 WARN("Could not create file: %u\n", GetLastError());
3917 lpwhr->hCacheFile = NULL;
3920 WARN("Could not create cache entry: %08x\n", GetLastError());
3926 HeapFree(GetProcessHeap(), 0, requestString);
3928 /* TODO: send notification for P3P header */
3930 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3934 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3937 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3940 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3941 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3942 sizeof(INTERNET_ASYNC_RESULT));
3947 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3948 iar.dwError = INTERNET_GetLastError();
3950 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3951 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3952 sizeof(INTERNET_ASYNC_RESULT));
3957 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3961 /***********************************************************************
3962 * HTTPSESSION_Destroy (internal)
3964 * Deallocate session handle
3967 static void HTTPSESSION_Destroy(object_header_t *hdr)
3969 http_session_t *lpwhs = (http_session_t*) hdr;
3971 TRACE("%p\n", lpwhs);
3973 WININET_Release(&lpwhs->lpAppInfo->hdr);
3975 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3976 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3977 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3978 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3979 HeapFree(GetProcessHeap(), 0, lpwhs);
3982 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3985 case INTERNET_OPTION_HANDLE_TYPE:
3986 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3988 if (*size < sizeof(ULONG))
3989 return ERROR_INSUFFICIENT_BUFFER;
3991 *size = sizeof(DWORD);
3992 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
3993 return ERROR_SUCCESS;
3996 return INET_QueryOption(option, buffer, size, unicode);
3999 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4001 http_session_t *ses = (http_session_t*)hdr;
4004 case INTERNET_OPTION_USERNAME:
4006 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4007 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4008 return ERROR_SUCCESS;
4010 case INTERNET_OPTION_PASSWORD:
4012 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4013 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4014 return ERROR_SUCCESS;
4019 return ERROR_INTERNET_INVALID_OPTION;
4022 static const object_vtbl_t HTTPSESSIONVtbl = {
4023 HTTPSESSION_Destroy,
4025 HTTPSESSION_QueryOption,
4026 HTTPSESSION_SetOption,
4035 /***********************************************************************
4036 * HTTP_Connect (internal)
4038 * Create http session handle
4041 * HINTERNET a session handle on success
4045 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4046 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4047 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4048 DWORD dwInternalFlags)
4050 http_session_t *lpwhs = NULL;
4051 HINTERNET handle = NULL;
4055 if (!lpszServerName || !lpszServerName[0])
4057 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4061 assert( hIC->hdr.htype == WH_HINIT );
4063 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4066 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4071 * According to my tests. The name is not resolved until a request is sent
4074 lpwhs->hdr.htype = WH_HHTTPSESSION;
4075 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4076 lpwhs->hdr.dwFlags = dwFlags;
4077 lpwhs->hdr.dwContext = dwContext;
4078 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4079 lpwhs->hdr.refs = 1;
4080 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4082 WININET_AddRef( &hIC->hdr );
4083 lpwhs->lpAppInfo = hIC;
4084 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4086 handle = WININET_AllocHandle( &lpwhs->hdr );
4089 ERR("Failed to alloc handle\n");
4090 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4094 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4095 if(strchrW(hIC->lpszProxy, ' '))
4096 FIXME("Several proxies not implemented.\n");
4097 if(hIC->lpszProxyBypass)
4098 FIXME("Proxy bypass is ignored.\n");
4100 if (lpszServerName && lpszServerName[0])
4102 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4103 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4105 if (lpszUserName && lpszUserName[0])
4106 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4107 if (lpszPassword && lpszPassword[0])
4108 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4109 lpwhs->nServerPort = nServerPort;
4110 lpwhs->nHostPort = nServerPort;
4112 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4113 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4115 INTERNET_SendCallback(&hIC->hdr, dwContext,
4116 INTERNET_STATUS_HANDLE_CREATED, &handle,
4122 WININET_Release( &lpwhs->hdr );
4125 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4129 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4134 /***********************************************************************
4135 * HTTP_OpenConnection (internal)
4137 * Connect to a web server
4144 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4146 BOOL bSuccess = FALSE;
4147 http_session_t *lpwhs;
4148 appinfo_t *hIC = NULL;
4149 char szaddr[INET6_ADDRSTRLEN];
4155 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4157 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4161 if (NETCON_connected(&lpwhr->netConnection))
4166 if (!HTTP_ResolveName(lpwhr)) goto lend;
4168 lpwhs = lpwhr->lpHttpSession;
4170 hIC = lpwhs->lpAppInfo;
4171 switch (lpwhs->socketAddress.ss_family)
4174 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4177 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4180 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4181 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4184 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4185 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4186 INTERNET_STATUS_CONNECTING_TO_SERVER,
4190 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4193 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4197 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4201 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4203 /* Note: we differ from Microsoft's WinINet here. they seem to have
4204 * a bug that causes no status callbacks to be sent when starting
4205 * a tunnel to a proxy server using the CONNECT verb. i believe our
4206 * behaviour to be more correct and to not cause any incompatibilities
4207 * because using a secure connection through a proxy server is a rare
4208 * case that would be hard for anyone to depend on */
4209 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4212 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4214 WARN("Couldn't connect securely to host\n");
4219 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4220 INTERNET_STATUS_CONNECTED_TO_SERVER,
4221 szaddr, strlen(szaddr)+1);
4226 lpwhr->read_pos = lpwhr->read_size = 0;
4227 lpwhr->read_chunked = FALSE;
4229 TRACE("%d <--\n", bSuccess);
4234 /***********************************************************************
4235 * HTTP_clear_response_headers (internal)
4237 * clear out any old response headers
4239 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4243 for( i=0; i<lpwhr->nCustHeaders; i++)
4245 if( !lpwhr->pCustHeaders[i].lpszField )
4247 if( !lpwhr->pCustHeaders[i].lpszValue )
4249 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4251 HTTP_DeleteCustomHeader( lpwhr, i );
4256 /***********************************************************************
4257 * HTTP_GetResponseHeaders (internal)
4259 * Read server response
4266 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4269 WCHAR buffer[MAX_REPLY_LEN];
4270 DWORD buflen = MAX_REPLY_LEN;
4271 BOOL bSuccess = FALSE;
4273 char bufferA[MAX_REPLY_LEN];
4274 LPWSTR status_code = NULL, status_text = NULL;
4275 DWORD cchMaxRawHeaders = 1024;
4276 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4278 DWORD cchRawHeaders = 0;
4279 BOOL codeHundred = FALSE;
4283 /* clear old response headers (eg. from a redirect response) */
4284 if (clear) HTTP_clear_response_headers( lpwhr );
4286 if (!NETCON_connected(&lpwhr->netConnection))
4290 static const WCHAR szHundred[] = {'1','0','0',0};
4292 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4294 buflen = MAX_REPLY_LEN;
4295 if (!read_line(lpwhr, bufferA, &buflen))
4298 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4299 /* check is this a status code line? */
4300 if (!strncmpW(buffer, g_szHttp1_0, 4))
4302 /* split the version from the status code */
4303 status_code = strchrW( buffer, ' ' );
4308 /* split the status code from the status text */
4309 status_text = strchrW( status_code, ' ' );
4314 TRACE("version [%s] status code [%s] status text [%s]\n",
4315 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4317 codeHundred = (!strcmpW(status_code, szHundred));
4319 else if (!codeHundred)
4321 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4324 } while (codeHundred);
4326 /* Add status code */
4327 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4328 HTTP_ADDHDR_FLAG_REPLACE);
4330 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4331 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4333 lpwhr->lpszVersion = heap_strdupW(buffer);
4334 lpwhr->lpszStatusText = heap_strdupW(status_text);
4336 /* Restore the spaces */
4337 *(status_code-1) = ' ';
4338 *(status_text-1) = ' ';
4340 /* regenerate raw headers */
4341 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4342 cchMaxRawHeaders *= 2;
4343 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4344 if (temp == NULL) goto lend;
4345 lpszRawHeaders = temp;
4346 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4347 cchRawHeaders += (buflen-1);
4348 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4349 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4350 lpszRawHeaders[cchRawHeaders] = '\0';
4352 /* Parse each response line */
4355 buflen = MAX_REPLY_LEN;
4356 if (read_line(lpwhr, bufferA, &buflen))
4358 LPWSTR * pFieldAndValue;
4360 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4362 if (!bufferA[0]) break;
4363 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4365 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4368 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4369 cchMaxRawHeaders *= 2;
4370 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4371 if (temp == NULL) goto lend;
4372 lpszRawHeaders = temp;
4373 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4374 cchRawHeaders += (buflen-1);
4375 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4376 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4377 lpszRawHeaders[cchRawHeaders] = '\0';
4379 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4380 HTTP_ADDREQ_FLAG_ADD );
4382 HTTP_FreeTokens(pFieldAndValue);
4393 /* make sure the response header is terminated with an empty line. Some apps really
4394 truly care about that empty line being there for some reason. Just add it to the
4396 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4398 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4399 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4400 if (temp == NULL) goto lend;
4401 lpszRawHeaders = temp;
4404 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4406 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4407 lpwhr->lpszRawHeaders = lpszRawHeaders;
4408 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4418 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4424 static void strip_spaces(LPWSTR start)
4429 while (*str == ' ' && *str != '\0')
4433 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4435 end = start + strlenW(start) - 1;
4436 while (end >= start && *end == ' ')
4444 /***********************************************************************
4445 * HTTP_InterpretHttpHeader (internal)
4447 * Parse server response
4451 * Pointer to array of field, value, NULL on success.
4454 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4456 LPWSTR * pTokenPair;
4460 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4462 pszColon = strchrW(buffer, ':');
4463 /* must have two tokens */
4466 HTTP_FreeTokens(pTokenPair);
4468 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4472 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4475 HTTP_FreeTokens(pTokenPair);
4478 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4479 pTokenPair[0][pszColon - buffer] = '\0';
4483 len = strlenW(pszColon);
4484 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4487 HTTP_FreeTokens(pTokenPair);
4490 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4492 strip_spaces(pTokenPair[0]);
4493 strip_spaces(pTokenPair[1]);
4495 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4499 /***********************************************************************
4500 * HTTP_ProcessHeader (internal)
4502 * Stuff header into header tables according to <dwModifier>
4506 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4508 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4510 LPHTTPHEADERW lphttpHdr = NULL;
4511 BOOL bSuccess = FALSE;
4513 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4515 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4517 /* REPLACE wins out over ADD */
4518 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4519 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4521 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4524 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4528 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4532 lphttpHdr = &lpwhr->pCustHeaders[index];
4538 hdr.lpszField = (LPWSTR)field;
4539 hdr.lpszValue = (LPWSTR)value;
4540 hdr.wFlags = hdr.wCount = 0;
4542 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4543 hdr.wFlags |= HDR_ISREQUEST;
4545 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4547 /* no value to delete */
4550 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4551 lphttpHdr->wFlags |= HDR_ISREQUEST;
4553 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4555 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4557 HTTP_DeleteCustomHeader( lpwhr, index );
4563 hdr.lpszField = (LPWSTR)field;
4564 hdr.lpszValue = (LPWSTR)value;
4565 hdr.wFlags = hdr.wCount = 0;
4567 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4568 hdr.wFlags |= HDR_ISREQUEST;
4570 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4575 else if (dwModifier & COALESCEFLAGS)
4580 INT origlen = strlenW(lphttpHdr->lpszValue);
4581 INT valuelen = strlenW(value);
4583 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4586 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4588 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4591 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4594 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4596 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4599 lphttpHdr->lpszValue = lpsztmp;
4600 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4603 lphttpHdr->lpszValue[origlen] = ch;
4605 lphttpHdr->lpszValue[origlen] = ' ';
4609 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4610 lphttpHdr->lpszValue[len] = '\0';
4615 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4616 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4619 TRACE("<-- %d\n",bSuccess);
4624 /***********************************************************************
4625 * HTTP_FinishedReading (internal)
4627 * Called when all content from server has been read by client.
4630 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4632 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4639 HTTPREQ_CloseConnection(&lpwhr->hdr);
4642 /* FIXME: store data in the URL cache here */
4648 /***********************************************************************
4649 * HTTP_GetCustomHeaderIndex (internal)
4651 * Return index of custom header from header array
4654 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4655 int requested_index, BOOL request_only)
4659 TRACE("%s\n", debugstr_w(lpszField));
4661 for (index = 0; index < lpwhr->nCustHeaders; index++)
4663 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4666 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4669 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4672 if (requested_index == 0)
4677 if (index >= lpwhr->nCustHeaders)
4680 TRACE("Return: %d\n", index);
4685 /***********************************************************************
4686 * HTTP_InsertCustomHeader (internal)
4688 * Insert header into array
4691 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4694 LPHTTPHEADERW lph = NULL;
4697 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4698 count = lpwhr->nCustHeaders + 1;
4700 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4702 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4706 lpwhr->pCustHeaders = lph;
4707 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4708 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4709 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4710 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4711 lpwhr->nCustHeaders++;
4716 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4723 /***********************************************************************
4724 * HTTP_DeleteCustomHeader (internal)
4726 * Delete header from array
4727 * If this function is called, the indexs may change.
4729 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4731 if( lpwhr->nCustHeaders <= 0 )
4733 if( index >= lpwhr->nCustHeaders )
4735 lpwhr->nCustHeaders--;
4737 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4738 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4740 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4741 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4742 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4748 /***********************************************************************
4749 * HTTP_VerifyValidHeader (internal)
4751 * Verify the given header is not invalid for the given http request
4754 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4756 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4757 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4763 /***********************************************************************
4764 * IsHostInProxyBypassList (@)
4769 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4771 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);