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 szOK[] = {'O','K',0};
77 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf[] = {'\r','\n', 0};
87 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge[] = { 'A','g','e',0 };
93 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 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 };
105 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 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 };
132 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia[] = { 'V','i','a',0 };
137 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 struct gzip_stream_t {
182 typedef struct _basicAuthorizationData
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
190 } basicAuthorizationData;
192 typedef struct _authorizationData
206 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
207 static struct list authorizationCache = LIST_INIT(authorizationCache);
209 static CRITICAL_SECTION authcache_cs;
210 static CRITICAL_SECTION_DEBUG critsect_debug =
213 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
214 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
216 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
218 static DWORD HTTP_OpenConnection(http_request_t *req);
219 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
220 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
221 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
222 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
223 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
224 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
225 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
226 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
227 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
228 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
229 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
230 static void HTTP_DrainContent(http_request_t *req);
231 static BOOL HTTP_FinishedReading(http_request_t *req);
233 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
236 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
237 if (HeaderIndex == -1)
240 return &req->pCustHeaders[HeaderIndex];
245 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
247 return HeapAlloc(GetProcessHeap(), 0, items*size);
250 static void wininet_zfree(voidpf opaque, voidpf address)
252 HeapFree(GetProcessHeap(), 0, address);
255 static void init_gzip_stream(http_request_t *req)
257 gzip_stream_t *gzip_stream;
260 gzip_stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(gzip_stream_t));
261 gzip_stream->zstream.zalloc = wininet_zalloc;
262 gzip_stream->zstream.zfree = wininet_zfree;
264 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
266 ERR("inflateInit failed: %d\n", zres);
267 HeapFree(GetProcessHeap(), 0, gzip_stream);
271 req->gzip_stream = gzip_stream;
273 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
275 HTTP_DeleteCustomHeader(req, index);
280 static void init_gzip_stream(http_request_t *req)
282 ERR("gzip stream not supported, missing zlib.\n");
287 /* set the request content length based on the headers */
288 static DWORD set_content_length( http_request_t *lpwhr )
290 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
294 size = sizeof(lpwhr->dwContentLength);
295 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
296 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
297 lpwhr->dwContentLength = ~0u;
299 size = sizeof(encoding);
300 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
301 !strcmpiW(encoding, szChunked))
303 lpwhr->dwContentLength = ~0u;
304 lpwhr->read_chunked = TRUE;
307 if(lpwhr->decoding) {
310 static const WCHAR gzipW[] = {'g','z','i','p',0};
312 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
313 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
314 init_gzip_stream(lpwhr);
317 return lpwhr->dwContentLength;
320 /***********************************************************************
321 * HTTP_Tokenize (internal)
323 * Tokenize a string, allocating memory for the tokens.
325 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
327 LPWSTR * token_array;
334 /* empty string has no tokens */
338 for (i = 0; string[i]; i++)
340 if (!strncmpW(string+i, token_string, strlenW(token_string)))
344 /* we want to skip over separators, but not the null terminator */
345 for (j = 0; j < strlenW(token_string) - 1; j++)
353 /* add 1 for terminating NULL */
354 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
355 token_array[tokens] = NULL;
358 for (i = 0; i < tokens; i++)
361 next_token = strstrW(string, token_string);
362 if (!next_token) next_token = string+strlenW(string);
363 len = next_token - string;
364 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
365 memcpy(token_array[i], string, len*sizeof(WCHAR));
366 token_array[i][len] = '\0';
367 string = next_token+strlenW(token_string);
372 /***********************************************************************
373 * HTTP_FreeTokens (internal)
375 * Frees memory returned from HTTP_Tokenize.
377 static void HTTP_FreeTokens(LPWSTR * token_array)
380 for (i = 0; token_array[i]; i++)
381 HeapFree(GetProcessHeap(), 0, token_array[i]);
382 HeapFree(GetProcessHeap(), 0, token_array);
385 static void HTTP_FixURL(http_request_t *lpwhr)
387 static const WCHAR szSlash[] = { '/',0 };
388 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
390 /* If we don't have a path we set it to root */
391 if (NULL == lpwhr->lpszPath)
392 lpwhr->lpszPath = heap_strdupW(szSlash);
393 else /* remove \r and \n*/
395 int nLen = strlenW(lpwhr->lpszPath);
396 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
399 lpwhr->lpszPath[nLen]='\0';
401 /* Replace '\' with '/' */
404 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
408 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
409 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
410 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
412 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
413 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
415 strcpyW(fixurl + 1, lpwhr->lpszPath);
416 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
417 lpwhr->lpszPath = fixurl;
421 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
423 LPWSTR requestString;
429 static const WCHAR szSpace[] = { ' ',0 };
430 static const WCHAR szColon[] = { ':',' ',0 };
431 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
433 /* allocate space for an array of all the string pointers to be added */
434 len = (lpwhr->nCustHeaders)*4 + 10;
435 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
437 /* add the verb, path and HTTP version string */
445 /* Append custom request headers */
446 for (i = 0; i < lpwhr->nCustHeaders; i++)
448 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
451 req[n++] = lpwhr->pCustHeaders[i].lpszField;
453 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
455 TRACE("Adding custom header %s (%s)\n",
456 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
457 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
462 ERR("oops. buffer overrun\n");
465 requestString = HTTP_build_req( req, 4 );
466 HeapFree( GetProcessHeap(), 0, req );
469 * Set (header) termination string for request
470 * Make sure there's exactly two new lines at the end of the request
472 p = &requestString[strlenW(requestString)-1];
473 while ( (*p == '\n') || (*p == '\r') )
475 strcpyW( p+1, sztwocrlf );
477 return requestString;
480 static void HTTP_ProcessCookies( http_request_t *lpwhr )
484 LPHTTPHEADERW setCookieHeader;
486 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
488 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
490 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
493 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
497 Host = HTTP_GetHeader(lpwhr, hostW);
498 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
499 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
500 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
501 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
503 HeapFree(GetProcessHeap(), 0, buf_url);
509 static void strip_spaces(LPWSTR start)
514 while (*str == ' ' && *str != '\0')
518 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
520 end = start + strlenW(start) - 1;
521 while (end >= start && *end == ' ')
528 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
530 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
531 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
533 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
534 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
535 if (is_basic && pszRealm)
538 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
542 token = strchrW(ptr,'=');
546 while (*realm == ' ' && *realm != '\0')
548 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
549 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
552 while (*token == ' ' && *token != '\0')
556 *pszRealm = heap_strdupW(token);
557 strip_spaces(*pszRealm);
564 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
566 if (!authinfo) return;
568 if (SecIsValidHandle(&authinfo->ctx))
569 DeleteSecurityContext(&authinfo->ctx);
570 if (SecIsValidHandle(&authinfo->cred))
571 FreeCredentialsHandle(&authinfo->cred);
573 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
574 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
575 HeapFree(GetProcessHeap(), 0, authinfo);
578 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
580 basicAuthorizationData *ad;
583 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
585 EnterCriticalSection(&authcache_cs);
586 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
588 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
590 TRACE("Authorization found in cache\n");
591 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
592 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
593 rc = ad->AuthorizationLen;
597 LeaveCriticalSection(&authcache_cs);
601 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
604 basicAuthorizationData* ad = NULL;
606 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
608 EnterCriticalSection(&authcache_cs);
609 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
611 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
612 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
621 TRACE("Found match in cache, replacing\n");
622 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
629 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
630 ad->lpszwHost = heap_strdupW(host);
631 ad->lpszwRealm = heap_strdupW(realm);
632 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
633 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
634 ad->AuthorizationLen = auth_data_len;
635 list_add_head(&basicAuthorizationCache,&ad->entry);
636 TRACE("authorization cached\n");
638 LeaveCriticalSection(&authcache_cs);
641 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
642 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
644 authorizationData *ad;
646 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
648 EnterCriticalSection(&authcache_cs);
649 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
650 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
651 TRACE("Authorization found in cache\n");
653 nt_auth_identity->User = heap_strdupW(ad->user);
654 nt_auth_identity->Password = heap_strdupW(ad->password);
655 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
656 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
657 (!nt_auth_identity->Domain && ad->domain_len)) {
658 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
659 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
660 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
664 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
665 nt_auth_identity->UserLength = ad->user_len;
666 nt_auth_identity->PasswordLength = ad->password_len;
667 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
668 nt_auth_identity->DomainLength = ad->domain_len;
669 LeaveCriticalSection(&authcache_cs);
673 LeaveCriticalSection(&authcache_cs);
678 static void cache_authorization(LPWSTR host, LPWSTR scheme,
679 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
681 authorizationData *ad;
684 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
686 EnterCriticalSection(&authcache_cs);
687 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
688 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
694 HeapFree(GetProcessHeap(), 0, ad->user);
695 HeapFree(GetProcessHeap(), 0, ad->password);
696 HeapFree(GetProcessHeap(), 0, ad->domain);
698 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
700 LeaveCriticalSection(&authcache_cs);
704 ad->host = heap_strdupW(host);
705 ad->scheme = heap_strdupW(scheme);
706 list_add_head(&authorizationCache, &ad->entry);
709 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
710 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
711 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
712 ad->user_len = nt_auth_identity->UserLength;
713 ad->password_len = nt_auth_identity->PasswordLength;
714 ad->domain_len = nt_auth_identity->DomainLength;
716 if(!ad->host || !ad->scheme || !ad->user || !ad->password
717 || (nt_auth_identity->Domain && !ad->domain)) {
718 HeapFree(GetProcessHeap(), 0, ad->host);
719 HeapFree(GetProcessHeap(), 0, ad->scheme);
720 HeapFree(GetProcessHeap(), 0, ad->user);
721 HeapFree(GetProcessHeap(), 0, ad->password);
722 HeapFree(GetProcessHeap(), 0, ad->domain);
723 list_remove(&ad->entry);
724 HeapFree(GetProcessHeap(), 0, ad);
727 LeaveCriticalSection(&authcache_cs);
730 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
731 struct HttpAuthInfo **ppAuthInfo,
732 LPWSTR domain_and_username, LPWSTR password,
735 SECURITY_STATUS sec_status;
736 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
738 LPWSTR szRealm = NULL;
740 TRACE("%s\n", debugstr_w(pszAuthValue));
747 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
751 SecInvalidateHandle(&pAuthInfo->cred);
752 SecInvalidateHandle(&pAuthInfo->ctx);
753 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
755 pAuthInfo->auth_data = NULL;
756 pAuthInfo->auth_data_len = 0;
757 pAuthInfo->finished = FALSE;
759 if (is_basic_auth_value(pszAuthValue,NULL))
761 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
762 pAuthInfo->scheme = heap_strdupW(szBasic);
763 if (!pAuthInfo->scheme)
765 HeapFree(GetProcessHeap(), 0, pAuthInfo);
772 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
774 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
775 if (!pAuthInfo->scheme)
777 HeapFree(GetProcessHeap(), 0, pAuthInfo);
781 if (domain_and_username)
783 WCHAR *user = strchrW(domain_and_username, '\\');
784 WCHAR *domain = domain_and_username;
786 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
788 pAuthData = &nt_auth_identity;
793 user = domain_and_username;
797 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
798 nt_auth_identity.User = user;
799 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
800 nt_auth_identity.Domain = domain;
801 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
802 nt_auth_identity.Password = password;
803 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
805 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
807 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
808 pAuthData = &nt_auth_identity;
810 /* use default credentials */
813 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
814 SECPKG_CRED_OUTBOUND, NULL,
816 NULL, &pAuthInfo->cred,
819 if(pAuthData && !domain_and_username) {
820 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
821 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
822 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
825 if (sec_status == SEC_E_OK)
827 PSecPkgInfoW sec_pkg_info;
828 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
829 if (sec_status == SEC_E_OK)
831 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
832 FreeContextBuffer(sec_pkg_info);
835 if (sec_status != SEC_E_OK)
837 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
838 debugstr_w(pAuthInfo->scheme), sec_status);
839 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
840 HeapFree(GetProcessHeap(), 0, pAuthInfo);
844 *ppAuthInfo = pAuthInfo;
846 else if (pAuthInfo->finished)
849 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
850 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
852 ERR("authentication scheme changed from %s to %s\n",
853 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
857 if (is_basic_auth_value(pszAuthValue,&szRealm))
861 char *auth_data = NULL;
862 UINT auth_data_len = 0;
864 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
866 if (!domain_and_username)
869 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
870 if (auth_data_len == 0)
872 HeapFree(GetProcessHeap(),0,szRealm);
878 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
879 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
881 /* length includes a nul terminator, which will be re-used for the ':' */
882 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
885 HeapFree(GetProcessHeap(),0,szRealm);
889 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
890 auth_data[userlen] = ':';
891 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
892 auth_data_len = userlen + 1 + passlen;
894 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
897 pAuthInfo->auth_data = auth_data;
898 pAuthInfo->auth_data_len = auth_data_len;
899 pAuthInfo->finished = TRUE;
900 HeapFree(GetProcessHeap(),0,szRealm);
907 SecBufferDesc out_desc, in_desc;
909 unsigned char *buffer;
910 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
911 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
913 in.BufferType = SECBUFFER_TOKEN;
917 in_desc.ulVersion = 0;
918 in_desc.cBuffers = 1;
919 in_desc.pBuffers = ∈
921 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
922 if (*pszAuthData == ' ')
925 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
926 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
927 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
930 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
932 out.BufferType = SECBUFFER_TOKEN;
933 out.cbBuffer = pAuthInfo->max_token;
934 out.pvBuffer = buffer;
936 out_desc.ulVersion = 0;
937 out_desc.cBuffers = 1;
938 out_desc.pBuffers = &out;
940 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
941 first ? NULL : &pAuthInfo->ctx,
942 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
943 context_req, 0, SECURITY_NETWORK_DREP,
944 in.pvBuffer ? &in_desc : NULL,
945 0, &pAuthInfo->ctx, &out_desc,
946 &pAuthInfo->attr, &pAuthInfo->exp);
947 if (sec_status == SEC_E_OK)
949 pAuthInfo->finished = TRUE;
950 pAuthInfo->auth_data = out.pvBuffer;
951 pAuthInfo->auth_data_len = out.cbBuffer;
952 TRACE("sending last auth packet\n");
954 else if (sec_status == SEC_I_CONTINUE_NEEDED)
956 pAuthInfo->auth_data = out.pvBuffer;
957 pAuthInfo->auth_data_len = out.cbBuffer;
958 TRACE("sending next auth packet\n");
962 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
963 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
964 destroy_authinfo(pAuthInfo);
973 /***********************************************************************
974 * HTTP_HttpAddRequestHeadersW (internal)
976 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
977 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
982 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
984 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
986 if( dwHeaderLength == ~0U )
987 len = strlenW(lpszHeader);
989 len = dwHeaderLength;
990 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
991 lstrcpynW( buffer, lpszHeader, len + 1);
997 LPWSTR * pFieldAndValue;
1001 while (*lpszEnd != '\0')
1003 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1008 if (*lpszStart == '\0')
1011 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1014 lpszEnd++; /* Jump over newline */
1016 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1017 if (*lpszStart == '\0')
1019 /* Skip 0-length headers */
1020 lpszStart = lpszEnd;
1021 res = ERROR_SUCCESS;
1024 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1027 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
1028 if (res == ERROR_SUCCESS)
1029 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
1030 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1031 HTTP_FreeTokens(pFieldAndValue);
1034 lpszStart = lpszEnd;
1035 } while (res == ERROR_SUCCESS);
1037 HeapFree(GetProcessHeap(), 0, buffer);
1042 /***********************************************************************
1043 * HttpAddRequestHeadersW (WININET.@)
1045 * Adds one or more HTTP header to the request handler
1048 * On Windows if dwHeaderLength includes the trailing '\0', then
1049 * HttpAddRequestHeadersW() adds it too. However this results in an
1050 * invalid Http header which is rejected by some servers so we probably
1051 * don't need to match Windows on that point.
1058 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1059 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1061 http_request_t *lpwhr;
1062 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1064 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1069 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
1070 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
1071 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
1073 WININET_Release( &lpwhr->hdr );
1075 if(res != ERROR_SUCCESS)
1077 return res == ERROR_SUCCESS;
1080 /***********************************************************************
1081 * HttpAddRequestHeadersA (WININET.@)
1083 * Adds one or more HTTP header to the request handler
1090 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1091 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1097 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1099 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1100 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1101 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1102 if( dwHeaderLength != ~0U )
1103 dwHeaderLength = len;
1105 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1107 HeapFree( GetProcessHeap(), 0, hdr );
1112 /***********************************************************************
1113 * HttpOpenRequestA (WININET.@)
1115 * Open a HTTP request handle
1118 * HINTERNET a HTTP request handle on success
1122 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1123 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1124 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1125 DWORD dwFlags, DWORD_PTR dwContext)
1127 LPWSTR szVerb = NULL, szObjectName = NULL;
1128 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1129 INT acceptTypesCount;
1130 HINTERNET rc = FALSE;
1133 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1134 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1135 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1136 dwFlags, dwContext);
1140 szVerb = heap_strdupAtoW(lpszVerb);
1147 szObjectName = heap_strdupAtoW(lpszObjectName);
1148 if ( !szObjectName )
1154 szVersion = heap_strdupAtoW(lpszVersion);
1161 szReferrer = heap_strdupAtoW(lpszReferrer);
1166 if (lpszAcceptTypes)
1168 acceptTypesCount = 0;
1169 types = lpszAcceptTypes;
1174 /* find out how many there are */
1175 if (*types && **types)
1177 TRACE("accept type: %s\n", debugstr_a(*types));
1183 WARN("invalid accept type pointer\n");
1188 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1189 if (!szAcceptTypes) goto end;
1191 acceptTypesCount = 0;
1192 types = lpszAcceptTypes;
1197 if (*types && **types)
1198 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1202 /* ignore invalid pointer */
1207 szAcceptTypes[acceptTypesCount] = NULL;
1210 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1211 szVersion, szReferrer,
1212 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1217 acceptTypesCount = 0;
1218 while (szAcceptTypes[acceptTypesCount])
1220 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1223 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1225 HeapFree(GetProcessHeap(), 0, szReferrer);
1226 HeapFree(GetProcessHeap(), 0, szVersion);
1227 HeapFree(GetProcessHeap(), 0, szObjectName);
1228 HeapFree(GetProcessHeap(), 0, szVerb);
1233 /***********************************************************************
1236 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1239 static const CHAR HTTP_Base64Enc[] =
1240 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1244 /* first 6 bits, all from bin[0] */
1245 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1246 x = (bin[0] & 3) << 4;
1248 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1251 base64[n++] = HTTP_Base64Enc[x];
1256 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1257 x = ( bin[1] & 0x0f ) << 2;
1259 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1262 base64[n++] = HTTP_Base64Enc[x];
1266 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1268 /* last 6 bits, all from bin [2] */
1269 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1277 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1278 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1279 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1280 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1281 static const signed char HTTP_Base64Dec[256] =
1283 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1284 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1285 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1286 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1287 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1288 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1289 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1290 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1291 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1292 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1293 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1294 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1295 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1296 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1297 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1298 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1299 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1300 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1301 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1302 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1303 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1304 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1305 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1306 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1307 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1308 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1312 /***********************************************************************
1315 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1323 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1324 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1325 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1326 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1328 WARN("invalid base64: %s\n", debugstr_w(base64));
1332 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1335 if ((base64[2] == '=') && (base64[3] == '='))
1337 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1338 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1340 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1344 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1347 if (base64[3] == '=')
1349 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1350 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1352 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1356 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1365 /***********************************************************************
1366 * HTTP_InsertAuthorization
1368 * Insert or delete the authorization field in the request header.
1370 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1374 static const WCHAR wszSpace[] = {' ',0};
1375 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1377 WCHAR *authorization = NULL;
1379 if (pAuthInfo->auth_data_len)
1381 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1382 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1383 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1387 strcpyW(authorization, pAuthInfo->scheme);
1388 strcatW(authorization, wszSpace);
1389 HTTP_EncodeBase64(pAuthInfo->auth_data,
1390 pAuthInfo->auth_data_len,
1391 authorization+strlenW(authorization));
1393 /* clear the data as it isn't valid now that it has been sent to the
1394 * server, unless it's Basic authentication which doesn't do
1395 * connection tracking */
1396 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1398 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1399 pAuthInfo->auth_data = NULL;
1400 pAuthInfo->auth_data_len = 0;
1404 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1406 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1408 HeapFree(GetProcessHeap(), 0, authorization);
1413 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1415 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1418 size = sizeof(new_location);
1419 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1421 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1422 strcpyW( url, new_location );
1426 static const WCHAR slash[] = { '/',0 };
1427 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1428 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1429 http_session_t *session = req->lpHttpSession;
1431 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1432 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1434 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1436 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1437 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1439 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1440 if (req->lpszPath[0] != '/') strcatW( url, slash );
1441 strcatW( url, req->lpszPath );
1443 TRACE("url=%s\n", debugstr_w(url));
1447 /***********************************************************************
1448 * HTTP_DealWithProxy
1450 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1452 WCHAR buf[MAXHOSTNAME];
1453 WCHAR protoProxy[MAXHOSTNAME + 15];
1454 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1455 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1456 static WCHAR szNul[] = { 0 };
1457 URL_COMPONENTSW UrlComponents;
1458 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1459 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1460 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1462 memset( &UrlComponents, 0, sizeof UrlComponents );
1463 UrlComponents.dwStructSize = sizeof UrlComponents;
1464 UrlComponents.lpszHostName = buf;
1465 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1467 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1469 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1470 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1471 sprintfW(proxy, szFormat, protoProxy);
1473 strcpyW(proxy, protoProxy);
1474 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1476 if( UrlComponents.dwHostNameLength == 0 )
1479 if( !lpwhr->lpszPath )
1480 lpwhr->lpszPath = szNul;
1482 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1483 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1485 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1486 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1487 lpwhs->nServerPort = UrlComponents.nPort;
1489 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1493 #ifndef INET6_ADDRSTRLEN
1494 #define INET6_ADDRSTRLEN 46
1497 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1499 char szaddr[INET6_ADDRSTRLEN];
1500 http_session_t *lpwhs = lpwhr->lpHttpSession;
1503 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1504 INTERNET_STATUS_RESOLVING_NAME,
1505 lpwhs->lpszServerName,
1506 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1508 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1509 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1510 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1511 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1513 switch (lpwhs->socketAddress.ss_family)
1516 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1519 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1522 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1523 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1525 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1526 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1527 INTERNET_STATUS_NAME_RESOLVED,
1528 szaddr, strlen(szaddr)+1);
1530 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1531 return ERROR_SUCCESS;
1534 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1536 LPHTTPHEADERW host_header;
1538 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1540 host_header = HTTP_GetHeader(req, hostW);
1544 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1549 /***********************************************************************
1550 * HTTPREQ_Destroy (internal)
1552 * Deallocate request handle
1555 static void HTTPREQ_Destroy(object_header_t *hdr)
1557 http_request_t *lpwhr = (http_request_t*) hdr;
1562 if(lpwhr->hCacheFile) {
1563 WCHAR url[INTERNET_MAX_URL_LENGTH];
1566 CloseHandle(lpwhr->hCacheFile);
1568 memset(&ft, 0, sizeof(FILETIME));
1569 if(HTTP_GetRequestURL(lpwhr, url)) {
1570 CommitUrlCacheEntryW(url, lpwhr->lpszCacheFile, ft, ft,
1571 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1575 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1577 DeleteCriticalSection( &lpwhr->read_section );
1578 WININET_Release(&lpwhr->lpHttpSession->hdr);
1580 destroy_authinfo(lpwhr->pAuthInfo);
1581 destroy_authinfo(lpwhr->pProxyAuthInfo);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1584 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1585 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1586 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1587 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1589 for (i = 0; i < lpwhr->nCustHeaders; i++)
1591 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1592 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1596 if(lpwhr->gzip_stream) {
1597 if(!lpwhr->gzip_stream->end_of_data)
1598 inflateEnd(&lpwhr->gzip_stream->zstream);
1599 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1603 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1604 HeapFree(GetProcessHeap(), 0, lpwhr);
1607 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1609 http_request_t *lpwhr = (http_request_t*) hdr;
1611 TRACE("%p\n",lpwhr);
1613 if (!NETCON_connected(&lpwhr->netConnection))
1616 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1617 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1619 NETCON_close(&lpwhr->netConnection);
1621 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1622 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1625 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1627 WCHAR szVersion[10];
1628 WCHAR szConnectionResponse[20];
1629 DWORD dwBufferSize = sizeof(szVersion);
1630 BOOL keepalive = FALSE;
1632 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1633 * the connection is keep-alive by default */
1634 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1635 && !strcmpiW(szVersion, g_szHttp1_1))
1640 dwBufferSize = sizeof(szConnectionResponse);
1641 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1642 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1644 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1650 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1652 http_request_t *req = (http_request_t*)hdr;
1655 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1657 http_session_t *lpwhs = req->lpHttpSession;
1658 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1660 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1662 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1663 return ERROR_INSUFFICIENT_BUFFER;
1664 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1665 /* FIXME: can't get a SOCKET from our connection since we don't use
1669 /* FIXME: get source port from req->netConnection */
1670 info->SourcePort = 0;
1671 info->DestPort = lpwhs->nHostPort;
1673 if (HTTP_KeepAlive(req))
1674 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1675 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1676 info->Flags |= IDSI_FLAG_PROXY;
1677 if (req->netConnection.useSSL)
1678 info->Flags |= IDSI_FLAG_SECURE;
1680 return ERROR_SUCCESS;
1683 case INTERNET_OPTION_SECURITY_FLAGS:
1688 if (*size < sizeof(ULONG))
1689 return ERROR_INSUFFICIENT_BUFFER;
1691 *size = sizeof(DWORD);
1693 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1694 flags |= SECURITY_FLAG_SECURE;
1695 flags |= req->netConnection.security_flags;
1696 bits = NETCON_GetCipherStrength(&req->netConnection);
1698 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1699 else if (bits >= 56)
1700 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1702 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1703 *(DWORD *)buffer = flags;
1704 return ERROR_SUCCESS;
1707 case INTERNET_OPTION_HANDLE_TYPE:
1708 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1710 if (*size < sizeof(ULONG))
1711 return ERROR_INSUFFICIENT_BUFFER;
1713 *size = sizeof(DWORD);
1714 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1715 return ERROR_SUCCESS;
1717 case INTERNET_OPTION_URL: {
1718 WCHAR url[INTERNET_MAX_URL_LENGTH];
1723 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1725 TRACE("INTERNET_OPTION_URL\n");
1727 host = HTTP_GetHeader(req, hostW);
1728 strcpyW(url, httpW);
1729 strcatW(url, host->lpszValue);
1730 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1732 strcatW(url, req->lpszPath);
1734 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1737 len = (strlenW(url)+1) * sizeof(WCHAR);
1739 return ERROR_INSUFFICIENT_BUFFER;
1742 strcpyW(buffer, url);
1743 return ERROR_SUCCESS;
1745 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1747 return ERROR_INSUFFICIENT_BUFFER;
1750 return ERROR_SUCCESS;
1754 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1755 INTERNET_CACHE_ENTRY_INFOW *info;
1756 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1757 WCHAR url[INTERNET_MAX_URL_LENGTH];
1758 DWORD nbytes, error;
1761 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1763 if (*size < sizeof(*ts))
1765 *size = sizeof(*ts);
1766 return ERROR_INSUFFICIENT_BUFFER;
1769 HTTP_GetRequestURL(req, url);
1770 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1771 error = GetLastError();
1772 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1774 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1775 return ERROR_OUTOFMEMORY;
1777 GetUrlCacheEntryInfoW(url, info, &nbytes);
1779 ts->ftExpires = info->ExpireTime;
1780 ts->ftLastModified = info->LastModifiedTime;
1782 HeapFree(GetProcessHeap(), 0, info);
1783 *size = sizeof(*ts);
1784 return ERROR_SUCCESS;
1789 case INTERNET_OPTION_DATAFILE_NAME: {
1792 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1794 if(!req->lpszCacheFile) {
1796 return ERROR_INTERNET_ITEM_NOT_FOUND;
1800 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1801 if(*size < req_size)
1802 return ERROR_INSUFFICIENT_BUFFER;
1805 memcpy(buffer, req->lpszCacheFile, *size);
1806 return ERROR_SUCCESS;
1808 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1809 if (req_size > *size)
1810 return ERROR_INSUFFICIENT_BUFFER;
1812 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1813 -1, buffer, *size, NULL, NULL);
1814 return ERROR_SUCCESS;
1818 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1819 PCCERT_CONTEXT context;
1821 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1822 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1823 return ERROR_INSUFFICIENT_BUFFER;
1826 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1828 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1831 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1832 info->ftExpiry = context->pCertInfo->NotAfter;
1833 info->ftStart = context->pCertInfo->NotBefore;
1834 len = CertNameToStrA(context->dwCertEncodingType,
1835 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1836 info->lpszSubjectInfo = LocalAlloc(0, len);
1837 if(info->lpszSubjectInfo)
1838 CertNameToStrA(context->dwCertEncodingType,
1839 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1840 info->lpszSubjectInfo, len);
1841 len = CertNameToStrA(context->dwCertEncodingType,
1842 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1843 info->lpszIssuerInfo = LocalAlloc(0, len);
1844 if(info->lpszIssuerInfo)
1845 CertNameToStrA(context->dwCertEncodingType,
1846 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1847 info->lpszIssuerInfo, len);
1848 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1849 CertFreeCertificateContext(context);
1850 return ERROR_SUCCESS;
1855 return INET_QueryOption(hdr, option, buffer, size, unicode);
1858 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1860 http_request_t *req = (http_request_t*)hdr;
1863 case INTERNET_OPTION_SECURITY_FLAGS:
1867 if (!buffer || size != sizeof(DWORD))
1868 return ERROR_INVALID_PARAMETER;
1869 flags = *(DWORD *)buffer;
1870 TRACE("%08x\n", flags);
1871 req->netConnection.security_flags = flags;
1872 return ERROR_SUCCESS;
1874 case INTERNET_OPTION_SEND_TIMEOUT:
1875 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1876 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1878 if (size != sizeof(DWORD))
1879 return ERROR_INVALID_PARAMETER;
1881 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1884 case INTERNET_OPTION_USERNAME:
1885 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1886 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1887 return ERROR_SUCCESS;
1889 case INTERNET_OPTION_PASSWORD:
1890 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1891 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1892 return ERROR_SUCCESS;
1893 case INTERNET_OPTION_HTTP_DECODING:
1894 if(size != sizeof(BOOL))
1895 return ERROR_INVALID_PARAMETER;
1896 req->decoding = *(BOOL*)buffer;
1897 return ERROR_SUCCESS;
1900 return ERROR_INTERNET_INVALID_OPTION;
1903 /* read some more data into the read buffer (the read section must be held) */
1904 static DWORD read_more_data( http_request_t *req, int maxlen )
1911 /* move existing data to the start of the buffer */
1913 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1917 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1919 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1920 maxlen - req->read_size, 0, &len );
1921 if(res == ERROR_SUCCESS)
1922 req->read_size += len;
1927 /* remove some amount of data from the read buffer (the read section must be held) */
1928 static void remove_data( http_request_t *req, int count )
1930 if (!(req->read_size -= count)) req->read_pos = 0;
1931 else req->read_pos += count;
1934 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1936 int count, bytes_read, pos = 0;
1939 EnterCriticalSection( &req->read_section );
1942 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1946 count = eol - (req->read_buf + req->read_pos);
1947 bytes_read = count + 1;
1949 else count = bytes_read = req->read_size;
1951 count = min( count, *len - pos );
1952 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1954 remove_data( req, bytes_read );
1957 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1960 TRACE( "returning empty string\n" );
1961 LeaveCriticalSection( &req->read_section );
1962 INTERNET_SetLastError(res);
1966 LeaveCriticalSection( &req->read_section );
1970 if (pos && buffer[pos - 1] == '\r') pos--;
1973 buffer[*len - 1] = 0;
1974 TRACE( "returning %s\n", debugstr_a(buffer));
1978 /* discard data contents until we reach end of line (the read section must be held) */
1979 static DWORD discard_eol( http_request_t *req )
1985 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1988 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1991 req->read_pos = req->read_size = 0; /* discard everything */
1992 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1993 } while (req->read_size);
1994 return ERROR_SUCCESS;
1997 /* read the size of the next chunk (the read section must be held) */
1998 static DWORD start_next_chunk( http_request_t *req )
2000 DWORD chunk_size = 0, res;
2002 if (!req->dwContentLength) return ERROR_SUCCESS;
2003 if (req->dwContentLength == req->dwContentRead)
2005 /* read terminator for the previous chunk */
2006 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2007 req->dwContentLength = ~0u;
2008 req->dwContentRead = 0;
2012 while (req->read_size)
2014 char ch = req->read_buf[req->read_pos];
2015 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2016 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2017 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2018 else if (ch == ';' || ch == '\r' || ch == '\n')
2020 TRACE( "reading %u byte chunk\n", chunk_size );
2021 req->dwContentLength = chunk_size;
2022 req->dwContentRead = 0;
2023 return discard_eol( req );
2025 remove_data( req, 1 );
2027 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2028 if (!req->read_size)
2030 req->dwContentLength = req->dwContentRead = 0;
2031 return ERROR_SUCCESS;
2036 /* check if we have reached the end of the data to read (the read section must be held) */
2037 static BOOL end_of_read_data( http_request_t *req )
2039 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2040 if (req->read_chunked) return (req->dwContentLength == 0);
2041 if (req->dwContentLength == ~0u) return FALSE;
2042 return (req->dwContentLength == req->dwContentRead);
2045 /* fetch some more data into the read buffer (the read section must be held) */
2046 static DWORD refill_buffer( http_request_t *req )
2048 int len = sizeof(req->read_buf);
2051 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2053 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2056 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2057 if (len <= req->read_size) return ERROR_SUCCESS;
2059 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2060 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2061 return ERROR_SUCCESS;
2064 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2066 DWORD ret = ERROR_SUCCESS;
2070 z_stream *zstream = &req->gzip_stream->zstream;
2074 while(read < size && !req->gzip_stream->end_of_data) {
2075 if(!req->read_size) {
2076 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2080 if(req->dwContentRead == req->dwContentLength)
2083 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2085 zstream->next_in = req->read_buf+req->read_pos;
2086 zstream->avail_in = buf_avail;
2087 zstream->next_out = buf+read;
2088 zstream->avail_out = size-read;
2089 zres = inflate(zstream, Z_FULL_FLUSH);
2090 read = size - zstream->avail_out;
2091 req->dwContentRead += buf_avail-zstream->avail_in;
2092 remove_data(req, buf_avail-zstream->avail_in);
2093 if(zres == Z_STREAM_END) {
2094 TRACE("end of data\n");
2095 req->gzip_stream->end_of_data = TRUE;
2096 inflateEnd(&req->gzip_stream->zstream);
2097 }else if(zres != Z_OK) {
2098 WARN("inflate failed %d\n", zres);
2100 ret = ERROR_INTERNET_DECODING_FAILED;
2110 static void refill_gzip_buffer(http_request_t *req)
2115 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2118 if(req->gzip_stream->buf_pos) {
2119 if(req->gzip_stream->buf_size)
2120 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2121 req->gzip_stream->buf_pos = 0;
2124 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2125 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2126 if(res == ERROR_SUCCESS)
2127 req->gzip_stream->buf_size += len;
2130 /* return the size of data available to be read immediately (the read section must be held) */
2131 static DWORD get_avail_data( http_request_t *req )
2133 if (req->gzip_stream) {
2134 refill_gzip_buffer(req);
2135 return req->gzip_stream->buf_size;
2137 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2139 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2142 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2144 INTERNET_ASYNC_RESULT iar;
2149 EnterCriticalSection( &req->read_section );
2150 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2151 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2152 iar.dwError = first_notif ? 0 : get_avail_data(req);
2157 LeaveCriticalSection( &req->read_section );
2159 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2160 sizeof(INTERNET_ASYNC_RESULT));
2163 /* read data from the http connection (the read section must be held) */
2164 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2166 BOOL finished_reading = FALSE;
2167 int len, bytes_read = 0;
2168 DWORD ret = ERROR_SUCCESS;
2170 EnterCriticalSection( &req->read_section );
2172 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2174 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2177 if(req->gzip_stream) {
2178 if(req->gzip_stream->buf_size) {
2179 bytes_read = min(req->gzip_stream->buf_size, size);
2180 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2181 req->gzip_stream->buf_pos += bytes_read;
2182 req->gzip_stream->buf_size -= bytes_read;
2183 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2187 if(size > bytes_read) {
2188 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2189 if(ret == ERROR_SUCCESS)
2193 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2195 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2197 if (req->read_size) {
2198 bytes_read = min( req->read_size, size );
2199 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2200 remove_data( req, bytes_read );
2203 if (size > bytes_read && (!bytes_read || sync)) {
2204 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2205 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2207 /* always return success, even if the network layer returns an error */
2210 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2211 req->dwContentRead += bytes_read;
2216 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2217 LeaveCriticalSection( &req->read_section );
2219 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2221 DWORD dwBytesWritten;
2223 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2225 WARN("WriteFile failed: %u\n", GetLastError());
2228 if(finished_reading)
2229 HTTP_FinishedReading(req);
2235 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2237 http_request_t *req = (http_request_t*)hdr;
2240 EnterCriticalSection( &req->read_section );
2241 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2242 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2244 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2245 if(res == ERROR_SUCCESS)
2247 LeaveCriticalSection( &req->read_section );
2252 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2254 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2255 http_request_t *req = (http_request_t*)workRequest->hdr;
2256 INTERNET_ASYNC_RESULT iar;
2259 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2261 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2262 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2264 iar.dwResult = res == ERROR_SUCCESS;
2267 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2268 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2269 sizeof(INTERNET_ASYNC_RESULT));
2272 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2273 DWORD flags, DWORD_PTR context)
2275 http_request_t *req = (http_request_t*)hdr;
2276 DWORD res, size, read, error = ERROR_SUCCESS;
2278 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2279 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2281 if (buffers->dwStructSize != sizeof(*buffers))
2282 return ERROR_INVALID_PARAMETER;
2284 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2286 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2288 WORKREQUEST workRequest;
2290 if (TryEnterCriticalSection( &req->read_section ))
2292 if (get_avail_data(req))
2294 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2295 &buffers->dwBufferLength, FALSE);
2296 size = buffers->dwBufferLength;
2297 LeaveCriticalSection( &req->read_section );
2300 LeaveCriticalSection( &req->read_section );
2303 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2304 workRequest.hdr = WININET_AddRef(&req->hdr);
2305 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2307 INTERNET_AsyncCall(&workRequest);
2309 return ERROR_IO_PENDING;
2313 size = buffers->dwBufferLength;
2315 EnterCriticalSection( &req->read_section );
2316 if(hdr->dwError == ERROR_SUCCESS)
2317 hdr->dwError = INTERNET_HANDLE_IN_USE;
2318 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2319 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2322 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2323 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2324 if(res == ERROR_SUCCESS)
2325 read += buffers->dwBufferLength;
2329 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2330 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2332 LeaveCriticalSection( &req->read_section );
2334 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2335 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2336 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2337 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2339 EnterCriticalSection( &req->read_section );
2342 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2343 hdr->dwError = ERROR_SUCCESS;
2345 error = hdr->dwError;
2347 LeaveCriticalSection( &req->read_section );
2348 size = buffers->dwBufferLength;
2349 buffers->dwBufferLength = read;
2352 if (res == ERROR_SUCCESS) {
2353 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2354 &size, sizeof(size));
2357 return res==ERROR_SUCCESS ? error : res;
2360 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2362 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2363 http_request_t *req = (http_request_t*)workRequest->hdr;
2364 INTERNET_ASYNC_RESULT iar;
2367 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2369 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2370 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2372 iar.dwResult = res == ERROR_SUCCESS;
2375 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2376 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2377 sizeof(INTERNET_ASYNC_RESULT));
2380 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2381 DWORD flags, DWORD_PTR context)
2384 http_request_t *req = (http_request_t*)hdr;
2385 DWORD res, size, read, error = ERROR_SUCCESS;
2387 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2388 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2390 if (buffers->dwStructSize != sizeof(*buffers))
2391 return ERROR_INVALID_PARAMETER;
2393 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2395 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2397 WORKREQUEST workRequest;
2399 if (TryEnterCriticalSection( &req->read_section ))
2401 if (get_avail_data(req))
2403 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2404 &buffers->dwBufferLength, FALSE);
2405 size = buffers->dwBufferLength;
2406 LeaveCriticalSection( &req->read_section );
2409 LeaveCriticalSection( &req->read_section );
2412 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2413 workRequest.hdr = WININET_AddRef(&req->hdr);
2414 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2416 INTERNET_AsyncCall(&workRequest);
2418 return ERROR_IO_PENDING;
2422 size = buffers->dwBufferLength;
2424 EnterCriticalSection( &req->read_section );
2425 if(hdr->dwError == ERROR_SUCCESS)
2426 hdr->dwError = INTERNET_HANDLE_IN_USE;
2427 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2428 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2431 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2432 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2433 if(res == ERROR_SUCCESS)
2434 read += buffers->dwBufferLength;
2438 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2439 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2441 LeaveCriticalSection( &req->read_section );
2443 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2444 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2445 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2446 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2448 EnterCriticalSection( &req->read_section );
2451 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2452 hdr->dwError = ERROR_SUCCESS;
2454 error = hdr->dwError;
2456 LeaveCriticalSection( &req->read_section );
2457 size = buffers->dwBufferLength;
2458 buffers->dwBufferLength = read;
2461 if (res == ERROR_SUCCESS) {
2462 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2463 &size, sizeof(size));
2466 return res==ERROR_SUCCESS ? error : res;
2469 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2472 http_request_t *lpwhr = (http_request_t*)hdr;
2474 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2477 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2478 if (res == ERROR_SUCCESS)
2479 lpwhr->dwBytesWritten += *written;
2481 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2485 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2487 http_request_t *req = (http_request_t*)workRequest->hdr;
2489 HTTP_ReceiveRequestData(req, FALSE);
2492 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2494 http_request_t *req = (http_request_t*)hdr;
2496 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2498 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2500 WORKREQUEST workRequest;
2502 /* never wait, if we can't enter the section we queue an async request right away */
2503 if (TryEnterCriticalSection( &req->read_section ))
2505 if ((*available = get_avail_data( req ))) goto done;
2506 if (end_of_read_data( req )) goto done;
2507 LeaveCriticalSection( &req->read_section );
2510 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2511 workRequest.hdr = WININET_AddRef( &req->hdr );
2513 INTERNET_AsyncCall(&workRequest);
2515 return ERROR_IO_PENDING;
2518 EnterCriticalSection( &req->read_section );
2520 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2522 refill_buffer( req );
2523 *available = get_avail_data( req );
2527 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2530 if (NETCON_query_data_available(&req->netConnection, &extra))
2531 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2533 LeaveCriticalSection( &req->read_section );
2535 TRACE( "returning %u\n", *available );
2536 return ERROR_SUCCESS;
2539 static const object_vtbl_t HTTPREQVtbl = {
2541 HTTPREQ_CloseConnection,
2542 HTTPREQ_QueryOption,
2545 HTTPREQ_ReadFileExA,
2546 HTTPREQ_ReadFileExW,
2548 HTTPREQ_QueryDataAvailable,
2552 /***********************************************************************
2553 * HTTP_HttpOpenRequestW (internal)
2555 * Open a HTTP request handle
2558 * HINTERNET a HTTP request handle on success
2562 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2563 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2564 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2565 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2567 appinfo_t *hIC = NULL;
2568 http_request_t *lpwhr;
2569 LPWSTR lpszHostName = NULL;
2570 HINTERNET handle = NULL;
2571 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2576 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2577 hIC = lpwhs->lpAppInfo;
2579 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2582 res = ERROR_OUTOFMEMORY;
2585 lpwhr->hdr.htype = WH_HHTTPREQ;
2586 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2587 lpwhr->hdr.dwFlags = dwFlags;
2588 lpwhr->hdr.dwContext = dwContext;
2589 lpwhr->hdr.refs = 1;
2590 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2591 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2592 lpwhr->dwContentLength = ~0u;
2593 InitializeCriticalSection( &lpwhr->read_section );
2595 WININET_AddRef( &lpwhs->hdr );
2596 lpwhr->lpHttpSession = lpwhs;
2597 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2599 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2600 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2601 if (NULL == lpszHostName)
2603 res = ERROR_OUTOFMEMORY;
2607 res = alloc_handle(&lpwhr->hdr, &handle);
2608 if (res != ERROR_SUCCESS)
2611 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2613 InternetCloseHandle( handle );
2618 if (lpszObjectName && *lpszObjectName) {
2622 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2623 if (rc != E_POINTER)
2624 len = strlenW(lpszObjectName)+1;
2625 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2626 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2627 URL_ESCAPE_SPACES_ONLY);
2630 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2631 strcpyW(lpwhr->lpszPath,lpszObjectName);
2634 static const WCHAR slashW[] = {'/',0};
2636 lpwhr->lpszPath = heap_strdupW(slashW);
2639 if (lpszReferrer && *lpszReferrer)
2640 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2642 if (lpszAcceptTypes)
2645 for (i = 0; lpszAcceptTypes[i]; i++)
2647 if (!*lpszAcceptTypes[i]) continue;
2648 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2649 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2650 HTTP_ADDHDR_FLAG_REQ |
2651 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2655 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2656 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2658 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2659 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2660 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2662 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2663 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2664 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2667 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2668 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2670 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2671 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2672 INTERNET_DEFAULT_HTTPS_PORT :
2673 INTERNET_DEFAULT_HTTP_PORT);
2675 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2676 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2677 INTERNET_DEFAULT_HTTPS_PORT :
2678 INTERNET_DEFAULT_HTTP_PORT);
2680 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2681 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2683 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2684 INTERNET_STATUS_HANDLE_CREATED, &handle,
2688 HeapFree(GetProcessHeap(), 0, lpszHostName);
2690 WININET_Release( &lpwhr->hdr );
2692 TRACE("<-- %p (%p)\n", handle, lpwhr);
2697 /***********************************************************************
2698 * HttpOpenRequestW (WININET.@)
2700 * Open a HTTP request handle
2703 * HINTERNET a HTTP request handle on success
2707 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2708 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2709 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2710 DWORD dwFlags, DWORD_PTR dwContext)
2712 http_session_t *lpwhs;
2713 HINTERNET handle = NULL;
2716 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2717 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2718 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2719 dwFlags, dwContext);
2720 if(lpszAcceptTypes!=NULL)
2723 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2724 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2727 lpwhs = (http_session_t*) get_handle_object( hHttpSession );
2728 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2730 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2735 * My tests seem to show that the windows version does not
2736 * become asynchronous until after this point. And anyhow
2737 * if this call was asynchronous then how would you get the
2738 * necessary HINTERNET pointer returned by this function.
2741 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2742 lpszVersion, lpszReferrer, lpszAcceptTypes,
2743 dwFlags, dwContext, &handle);
2746 WININET_Release( &lpwhs->hdr );
2747 TRACE("returning %p\n", handle);
2748 if(res != ERROR_SUCCESS)
2753 /* read any content returned by the server so that the connection can be
2755 static void HTTP_DrainContent(http_request_t *req)
2759 if (!NETCON_connected(&req->netConnection)) return;
2761 if (req->dwContentLength == -1)
2763 NETCON_close(&req->netConnection);
2766 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2771 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2773 } while (bytes_read);
2776 static const LPCWSTR header_lookup[] = {
2777 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2778 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2779 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2780 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2781 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2782 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2783 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2784 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2785 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2786 szDate, /* HTTP_QUERY_DATE = 9 */
2787 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2788 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2789 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2790 szURI, /* HTTP_QUERY_URI = 13 */
2791 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2792 NULL, /* HTTP_QUERY_COST = 15 */
2793 NULL, /* HTTP_QUERY_LINK = 16 */
2794 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2795 NULL, /* HTTP_QUERY_VERSION = 18 */
2796 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2797 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2798 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2799 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2800 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2801 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2802 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2803 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2804 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2805 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2806 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2807 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2808 NULL, /* HTTP_QUERY_FROM = 31 */
2809 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2810 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2811 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2812 szReferer, /* HTTP_QUERY_REFERER = 35 */
2813 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2814 szServer, /* HTTP_QUERY_SERVER = 37 */
2815 NULL, /* HTTP_TITLE = 38 */
2816 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2817 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2818 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2819 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2820 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2821 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2822 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2823 NULL, /* HTTP_QUERY_REFRESH = 46 */
2824 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2825 szAge, /* HTTP_QUERY_AGE = 48 */
2826 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2827 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2828 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2829 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2830 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2831 szETag, /* HTTP_QUERY_ETAG = 54 */
2832 hostW, /* HTTP_QUERY_HOST = 55 */
2833 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2834 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2835 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2836 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2837 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2838 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2839 szRange, /* HTTP_QUERY_RANGE = 62 */
2840 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2841 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2842 szVary, /* HTTP_QUERY_VARY = 65 */
2843 szVia, /* HTTP_QUERY_VIA = 66 */
2844 szWarning, /* HTTP_QUERY_WARNING = 67 */
2845 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2846 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2847 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2850 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2852 /***********************************************************************
2853 * HTTP_HttpQueryInfoW (internal)
2855 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2856 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2858 LPHTTPHEADERW lphttpHdr = NULL;
2859 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2860 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2861 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2864 /* Find requested header structure */
2867 case HTTP_QUERY_CUSTOM:
2868 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2869 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2871 case HTTP_QUERY_RAW_HEADERS_CRLF:
2875 DWORD res = ERROR_INVALID_PARAMETER;
2878 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2880 headers = lpwhr->lpszRawHeaders;
2883 len = strlenW(headers) * sizeof(WCHAR);
2885 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2887 len += sizeof(WCHAR);
2888 res = ERROR_INSUFFICIENT_BUFFER;
2893 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2896 len = strlenW(szCrLf) * sizeof(WCHAR);
2897 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2899 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2900 res = ERROR_SUCCESS;
2902 *lpdwBufferLength = len;
2905 HeapFree(GetProcessHeap(), 0, headers);
2908 case HTTP_QUERY_RAW_HEADERS:
2910 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2912 LPWSTR pszString = lpBuffer;
2914 for (i = 0; ppszRawHeaderLines[i]; i++)
2915 size += strlenW(ppszRawHeaderLines[i]) + 1;
2917 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2919 HTTP_FreeTokens(ppszRawHeaderLines);
2920 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2921 return ERROR_INSUFFICIENT_BUFFER;
2925 for (i = 0; ppszRawHeaderLines[i]; i++)
2927 DWORD len = strlenW(ppszRawHeaderLines[i]);
2928 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2932 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2934 *lpdwBufferLength = size * sizeof(WCHAR);
2935 HTTP_FreeTokens(ppszRawHeaderLines);
2937 return ERROR_SUCCESS;
2939 case HTTP_QUERY_STATUS_TEXT:
2940 if (lpwhr->lpszStatusText)
2942 DWORD len = strlenW(lpwhr->lpszStatusText);
2943 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2945 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2946 return ERROR_INSUFFICIENT_BUFFER;
2950 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2951 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2953 *lpdwBufferLength = len * sizeof(WCHAR);
2954 return ERROR_SUCCESS;
2957 case HTTP_QUERY_VERSION:
2958 if (lpwhr->lpszVersion)
2960 DWORD len = strlenW(lpwhr->lpszVersion);
2961 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2963 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2964 return ERROR_INSUFFICIENT_BUFFER;
2968 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2969 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2971 *lpdwBufferLength = len * sizeof(WCHAR);
2972 return ERROR_SUCCESS;
2975 case HTTP_QUERY_CONTENT_ENCODING:
2976 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2977 requested_index,request_only);
2980 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2982 if (level < LAST_TABLE_HEADER && header_lookup[level])
2983 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2984 requested_index,request_only);
2988 lphttpHdr = &lpwhr->pCustHeaders[index];
2990 /* Ensure header satisfies requested attributes */
2992 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2993 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2995 return ERROR_HTTP_HEADER_NOT_FOUND;
2998 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3000 /* coalesce value to requested type */
3001 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3003 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3004 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3006 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3012 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3014 tmpTM = *gmtime(&tmpTime);
3015 STHook = (SYSTEMTIME *)lpBuffer;
3016 STHook->wDay = tmpTM.tm_mday;
3017 STHook->wHour = tmpTM.tm_hour;
3018 STHook->wMilliseconds = 0;
3019 STHook->wMinute = tmpTM.tm_min;
3020 STHook->wDayOfWeek = tmpTM.tm_wday;
3021 STHook->wMonth = tmpTM.tm_mon + 1;
3022 STHook->wSecond = tmpTM.tm_sec;
3023 STHook->wYear = tmpTM.tm_year;
3025 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3026 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3027 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3029 else if (lphttpHdr->lpszValue)
3031 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3033 if (len > *lpdwBufferLength)
3035 *lpdwBufferLength = len;
3036 return ERROR_INSUFFICIENT_BUFFER;
3040 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3041 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3043 *lpdwBufferLength = len - sizeof(WCHAR);
3045 return ERROR_SUCCESS;
3048 /***********************************************************************
3049 * HttpQueryInfoW (WININET.@)
3051 * Queries for information about an HTTP request
3058 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3059 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3061 http_request_t *lpwhr;
3064 if (TRACE_ON(wininet)) {
3065 #define FE(x) { x, #x }
3066 static const wininet_flag_info query_flags[] = {
3067 FE(HTTP_QUERY_MIME_VERSION),
3068 FE(HTTP_QUERY_CONTENT_TYPE),
3069 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3070 FE(HTTP_QUERY_CONTENT_ID),
3071 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3072 FE(HTTP_QUERY_CONTENT_LENGTH),
3073 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3074 FE(HTTP_QUERY_ALLOW),
3075 FE(HTTP_QUERY_PUBLIC),
3076 FE(HTTP_QUERY_DATE),
3077 FE(HTTP_QUERY_EXPIRES),
3078 FE(HTTP_QUERY_LAST_MODIFIED),
3079 FE(HTTP_QUERY_MESSAGE_ID),
3081 FE(HTTP_QUERY_DERIVED_FROM),
3082 FE(HTTP_QUERY_COST),
3083 FE(HTTP_QUERY_LINK),
3084 FE(HTTP_QUERY_PRAGMA),
3085 FE(HTTP_QUERY_VERSION),
3086 FE(HTTP_QUERY_STATUS_CODE),
3087 FE(HTTP_QUERY_STATUS_TEXT),
3088 FE(HTTP_QUERY_RAW_HEADERS),
3089 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3090 FE(HTTP_QUERY_CONNECTION),
3091 FE(HTTP_QUERY_ACCEPT),
3092 FE(HTTP_QUERY_ACCEPT_CHARSET),
3093 FE(HTTP_QUERY_ACCEPT_ENCODING),
3094 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3095 FE(HTTP_QUERY_AUTHORIZATION),
3096 FE(HTTP_QUERY_CONTENT_ENCODING),
3097 FE(HTTP_QUERY_FORWARDED),
3098 FE(HTTP_QUERY_FROM),
3099 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3100 FE(HTTP_QUERY_LOCATION),
3101 FE(HTTP_QUERY_ORIG_URI),
3102 FE(HTTP_QUERY_REFERER),
3103 FE(HTTP_QUERY_RETRY_AFTER),
3104 FE(HTTP_QUERY_SERVER),
3105 FE(HTTP_QUERY_TITLE),
3106 FE(HTTP_QUERY_USER_AGENT),
3107 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3108 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3109 FE(HTTP_QUERY_ACCEPT_RANGES),
3110 FE(HTTP_QUERY_SET_COOKIE),
3111 FE(HTTP_QUERY_COOKIE),
3112 FE(HTTP_QUERY_REQUEST_METHOD),
3113 FE(HTTP_QUERY_REFRESH),
3114 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3116 FE(HTTP_QUERY_CACHE_CONTROL),
3117 FE(HTTP_QUERY_CONTENT_BASE),
3118 FE(HTTP_QUERY_CONTENT_LOCATION),
3119 FE(HTTP_QUERY_CONTENT_MD5),
3120 FE(HTTP_QUERY_CONTENT_RANGE),
3121 FE(HTTP_QUERY_ETAG),
3122 FE(HTTP_QUERY_HOST),
3123 FE(HTTP_QUERY_IF_MATCH),
3124 FE(HTTP_QUERY_IF_NONE_MATCH),
3125 FE(HTTP_QUERY_IF_RANGE),
3126 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3127 FE(HTTP_QUERY_MAX_FORWARDS),
3128 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3129 FE(HTTP_QUERY_RANGE),
3130 FE(HTTP_QUERY_TRANSFER_ENCODING),
3131 FE(HTTP_QUERY_UPGRADE),
3132 FE(HTTP_QUERY_VARY),
3134 FE(HTTP_QUERY_WARNING),
3135 FE(HTTP_QUERY_CUSTOM)
3137 static const wininet_flag_info modifier_flags[] = {
3138 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3139 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3140 FE(HTTP_QUERY_FLAG_NUMBER),
3141 FE(HTTP_QUERY_FLAG_COALESCE)
3144 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3145 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3148 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3149 TRACE(" Attribute:");
3150 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3151 if (query_flags[i].val == info) {
3152 TRACE(" %s", query_flags[i].name);
3156 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3157 TRACE(" Unknown (%08x)", info);
3160 TRACE(" Modifier:");
3161 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3162 if (modifier_flags[i].val & info_mod) {
3163 TRACE(" %s", modifier_flags[i].name);
3164 info_mod &= ~ modifier_flags[i].val;
3169 TRACE(" Unknown (%08x)", info_mod);
3174 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
3175 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3177 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3181 if (lpBuffer == NULL)
3182 *lpdwBufferLength = 0;
3183 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3184 lpBuffer, lpdwBufferLength, lpdwIndex);
3188 WININET_Release( &lpwhr->hdr );
3190 TRACE("%u <--\n", res);
3191 if(res != ERROR_SUCCESS)
3193 return res == ERROR_SUCCESS;
3196 /***********************************************************************
3197 * HttpQueryInfoA (WININET.@)
3199 * Queries for information about an HTTP request
3206 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3207 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3213 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3214 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3216 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3217 lpdwBufferLength, lpdwIndex );
3223 len = (*lpdwBufferLength)*sizeof(WCHAR);
3224 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3226 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3232 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3233 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3234 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3235 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3242 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3246 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3247 lpBuffer, *lpdwBufferLength, NULL, NULL );
3248 *lpdwBufferLength = len - 1;
3250 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3253 /* since the strings being returned from HttpQueryInfoW should be
3254 * only ASCII characters, it is reasonable to assume that all of
3255 * the Unicode characters can be reduced to a single byte */
3256 *lpdwBufferLength = len / sizeof(WCHAR);
3258 HeapFree(GetProcessHeap(), 0, bufferW );
3263 /***********************************************************************
3264 * HTTP_GetRedirectURL (internal)
3266 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3268 static WCHAR szHttp[] = {'h','t','t','p',0};
3269 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3270 http_session_t *lpwhs = lpwhr->lpHttpSession;
3271 URL_COMPONENTSW urlComponents;
3272 DWORD url_length = 0;
3274 LPWSTR combined_url;
3276 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3277 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3278 urlComponents.dwSchemeLength = 0;
3279 urlComponents.lpszHostName = lpwhs->lpszHostName;
3280 urlComponents.dwHostNameLength = 0;
3281 urlComponents.nPort = lpwhs->nHostPort;
3282 urlComponents.lpszUserName = lpwhs->lpszUserName;
3283 urlComponents.dwUserNameLength = 0;
3284 urlComponents.lpszPassword = NULL;
3285 urlComponents.dwPasswordLength = 0;
3286 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3287 urlComponents.dwUrlPathLength = 0;
3288 urlComponents.lpszExtraInfo = NULL;
3289 urlComponents.dwExtraInfoLength = 0;
3291 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3292 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3295 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3297 /* convert from bytes to characters */
3298 url_length = url_length / sizeof(WCHAR) - 1;
3299 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3301 HeapFree(GetProcessHeap(), 0, orig_url);
3306 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3307 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3309 HeapFree(GetProcessHeap(), 0, orig_url);
3312 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3314 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3316 HeapFree(GetProcessHeap(), 0, orig_url);
3317 HeapFree(GetProcessHeap(), 0, combined_url);
3320 HeapFree(GetProcessHeap(), 0, orig_url);
3321 return combined_url;
3325 /***********************************************************************
3326 * HTTP_HandleRedirect (internal)
3328 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3330 http_session_t *lpwhs = lpwhr->lpHttpSession;
3331 appinfo_t *hIC = lpwhs->lpAppInfo;
3332 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3333 WCHAR path[INTERNET_MAX_URL_LENGTH];
3338 /* if it's an absolute path, keep the same session info */
3339 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3343 URL_COMPONENTSW urlComponents;
3344 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3345 static WCHAR szHttp[] = {'h','t','t','p',0};
3346 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3352 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3353 urlComponents.lpszScheme = protocol;
3354 urlComponents.dwSchemeLength = 32;
3355 urlComponents.lpszHostName = hostName;
3356 urlComponents.dwHostNameLength = MAXHOSTNAME;
3357 urlComponents.lpszUserName = userName;
3358 urlComponents.dwUserNameLength = 1024;
3359 urlComponents.lpszPassword = NULL;
3360 urlComponents.dwPasswordLength = 0;
3361 urlComponents.lpszUrlPath = path;
3362 urlComponents.dwUrlPathLength = 2048;
3363 urlComponents.lpszExtraInfo = NULL;
3364 urlComponents.dwExtraInfoLength = 0;
3365 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3366 return INTERNET_GetLastError();
3368 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3369 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3371 TRACE("redirect from secure page to non-secure page\n");
3372 /* FIXME: warn about from secure redirect to non-secure page */
3373 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3375 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3376 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3378 TRACE("redirect from non-secure page to secure page\n");
3379 /* FIXME: notify about redirect to secure page */
3380 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3383 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3385 if (lstrlenW(protocol)>4) /*https*/
3386 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3388 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3393 * This upsets redirects to binary files on sourceforge.net
3394 * and gives an html page instead of the target file
3395 * Examination of the HTTP request sent by native wininet.dll
3396 * reveals that it doesn't send a referrer in that case.
3397 * Maybe there's a flag that enables this, or maybe a referrer
3398 * shouldn't be added in case of a redirect.
3401 /* consider the current host as the referrer */
3402 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3403 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3404 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3405 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3408 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3409 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3410 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3413 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3414 len = lstrlenW(hostName);
3415 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3416 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3417 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3420 lpwhs->lpszHostName = heap_strdupW(hostName);
3422 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3424 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3425 lpwhs->lpszUserName = NULL;
3427 lpwhs->lpszUserName = heap_strdupW(userName);
3431 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3435 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3436 lpwhs->lpszServerName = heap_strdupW(hostName);
3437 lpwhs->nServerPort = urlComponents.nPort;
3439 NETCON_close(&lpwhr->netConnection);
3440 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3443 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3444 if (res != ERROR_SUCCESS)
3447 lpwhr->read_pos = lpwhr->read_size = 0;
3448 lpwhr->read_chunked = FALSE;
3452 TRACE("Redirect through proxy\n");
3455 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3456 lpwhr->lpszPath=NULL;
3462 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3463 if (rc != E_POINTER)
3464 needed = strlenW(path)+1;
3465 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3466 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3467 URL_ESCAPE_SPACES_ONLY);
3470 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3471 strcpyW(lpwhr->lpszPath,path);
3475 /* Remove custom content-type/length headers on redirects. */
3476 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3478 HTTP_DeleteCustomHeader(lpwhr, index);
3479 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3481 HTTP_DeleteCustomHeader(lpwhr, index);
3483 return ERROR_SUCCESS;
3486 /***********************************************************************
3487 * HTTP_build_req (internal)
3489 * concatenate all the strings in the request together
3491 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3496 for( t = list; *t ; t++ )
3497 len += strlenW( *t );
3500 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3503 for( t = list; *t ; t++ )
3509 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3512 LPWSTR requestString;
3518 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3519 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3520 http_session_t *lpwhs = lpwhr->lpHttpSession;
3524 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3525 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3526 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3527 HeapFree( GetProcessHeap(), 0, lpszPath );
3529 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3530 NULL, 0, NULL, NULL );
3531 len--; /* the nul terminator isn't needed */
3532 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3533 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3534 ascii_req, len, NULL, NULL );
3535 HeapFree( GetProcessHeap(), 0, requestString );
3537 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3539 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3540 HeapFree( GetProcessHeap(), 0, ascii_req );
3541 if (res != ERROR_SUCCESS)
3544 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3546 return ERROR_HTTP_INVALID_HEADER;
3548 return ERROR_SUCCESS;
3551 static void HTTP_InsertCookies(http_request_t *lpwhr)
3553 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3554 LPWSTR lpszCookies, lpszUrl = NULL;
3555 DWORD nCookieSize, size;
3556 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3558 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3559 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3560 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3562 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3565 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3567 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3568 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3570 cnt += sprintfW(lpszCookies, szCookie);
3571 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3572 strcatW(lpszCookies, szCrLf);
3574 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3575 HeapFree(GetProcessHeap(), 0, lpszCookies);
3578 HeapFree(GetProcessHeap(), 0, lpszUrl);
3581 /***********************************************************************
3582 * HTTP_HttpSendRequestW (internal)
3584 * Sends the specified request to the HTTP server
3591 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3592 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3593 DWORD dwContentLength, BOOL bEndRequest)
3596 BOOL redirected = FALSE;
3597 LPWSTR requestString = NULL;
3600 INTERNET_ASYNC_RESULT iar;
3601 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3602 static const WCHAR szContentLength[] =
3603 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3604 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3607 TRACE("--> %p\n", lpwhr);
3609 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3611 /* if the verb is NULL default to GET */
3612 if (!lpwhr->lpszVerb)
3613 lpwhr->lpszVerb = heap_strdupW(szGET);
3615 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3617 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3618 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3619 lpwhr->dwBytesToWrite = dwContentLength;
3621 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3623 WCHAR *agent_header;
3624 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3627 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3628 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3629 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3631 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3632 HeapFree(GetProcessHeap(), 0, agent_header);
3634 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3636 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3637 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3639 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3641 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3642 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3643 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3649 BOOL reusing_connection;
3654 /* like native, just in case the caller forgot to call InternetReadFile
3655 * for all the data */
3656 HTTP_DrainContent(lpwhr);
3657 lpwhr->dwContentRead = 0;
3659 lpwhr->dwContentLength = ~0u;
3660 lpwhr->dwBytesToWrite = 0;
3663 if (TRACE_ON(wininet))
3665 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3666 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3670 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3672 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3674 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3675 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3677 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3678 HTTP_InsertCookies(lpwhr);
3680 /* add the headers the caller supplied */
3681 if( lpszHeaders && dwHeaderLength )
3683 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3684 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3687 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3689 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3690 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3691 HeapFree(GetProcessHeap(), 0, url);
3694 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3697 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3699 /* Send the request and store the results */
3700 if(NETCON_connected(&lpwhr->netConnection))
3701 reusing_connection = TRUE;
3703 reusing_connection = FALSE;
3705 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3708 /* send the request as ASCII, tack on the optional data */
3709 if (!lpOptional || redirected)
3710 dwOptionalLength = 0;
3711 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3712 NULL, 0, NULL, NULL );
3713 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3714 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3715 ascii_req, len, NULL, NULL );
3717 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3718 len = (len + dwOptionalLength - 1);
3720 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3722 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3723 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3725 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3726 HeapFree( GetProcessHeap(), 0, ascii_req );
3728 lpwhr->dwBytesWritten = dwOptionalLength;
3730 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3731 INTERNET_STATUS_REQUEST_SENT,
3732 &len, sizeof(DWORD));
3739 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3740 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3742 if (res != ERROR_SUCCESS)
3745 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3746 /* FIXME: We should know that connection is closed before sending
3747 * headers. Otherwise wrong callbacks are executed */
3748 if(!responseLen && reusing_connection) {
3749 TRACE("Connection closed by server, reconnecting\n");
3754 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3755 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3758 HTTP_ProcessCookies(lpwhr);
3760 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3762 dwBufferSize = sizeof(dwStatusCode);
3763 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3764 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3767 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3769 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3770 dwBufferSize=sizeof(szNewLocation);
3771 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3772 dwStatusCode == HTTP_STATUS_MOVED ||
3773 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3774 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3776 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3778 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3779 lpwhr->lpszVerb = heap_strdupW(szGET);
3781 HTTP_DrainContent(lpwhr);
3782 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3784 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3785 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3786 res = HTTP_HandleRedirect(lpwhr, new_url);
3787 if (res == ERROR_SUCCESS)
3789 HeapFree(GetProcessHeap(), 0, requestString);
3792 HeapFree( GetProcessHeap(), 0, new_url );
3797 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3799 WCHAR szAuthValue[2048];
3801 if (dwStatusCode == HTTP_STATUS_DENIED)
3803 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3805 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3807 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3809 lpwhr->lpHttpSession->lpszUserName,
3810 lpwhr->lpHttpSession->lpszPassword,
3813 HeapFree(GetProcessHeap(), 0, requestString);
3820 TRACE("Cleaning wrong authorization data\n");
3821 destroy_authinfo(lpwhr->pAuthInfo);
3822 lpwhr->pAuthInfo = NULL;
3825 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3828 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3830 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3831 &lpwhr->pProxyAuthInfo,
3832 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3833 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3842 TRACE("Cleaning wrong proxy authorization data\n");
3843 destroy_authinfo(lpwhr->pProxyAuthInfo);
3844 lpwhr->pProxyAuthInfo = NULL;
3850 res = ERROR_SUCCESS;
3854 if(res == ERROR_SUCCESS) {
3855 WCHAR url[INTERNET_MAX_URL_LENGTH];
3856 WCHAR cacheFileName[MAX_PATH+1];
3859 b = HTTP_GetRequestURL(lpwhr, url);
3861 WARN("Could not get URL\n");
3865 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3867 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3868 CloseHandle(lpwhr->hCacheFile);
3870 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3871 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3872 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3873 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3874 WARN("Could not create file: %u\n", GetLastError());
3875 lpwhr->hCacheFile = NULL;
3878 WARN("Could not create cache entry: %08x\n", GetLastError());
3884 HeapFree(GetProcessHeap(), 0, requestString);
3886 /* TODO: send notification for P3P header */
3888 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3890 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3891 HTTP_ReceiveRequestData(lpwhr, TRUE);
3894 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3897 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3898 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3899 sizeof(INTERNET_ASYNC_RESULT));
3907 /***********************************************************************
3909 * Helper functions for the HttpSendRequest(Ex) functions
3912 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3914 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3915 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3917 TRACE("%p\n", lpwhr);
3919 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3920 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3921 req->dwContentLength, req->bEndRequest);
3923 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3927 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3931 INTERNET_ASYNC_RESULT iar;
3932 DWORD res = ERROR_SUCCESS;
3934 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3935 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3937 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3939 res = ERROR_HTTP_HEADER_NOT_FOUND;
3941 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3942 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3944 /* process cookies here. Is this right? */
3945 HTTP_ProcessCookies(lpwhr);
3947 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3949 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3951 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3952 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3953 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3955 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3956 dwBufferSize=sizeof(szNewLocation);
3957 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3959 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3961 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3962 lpwhr->lpszVerb = heap_strdupW(szGET);
3964 HTTP_DrainContent(lpwhr);
3965 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3967 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3968 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3969 res = HTTP_HandleRedirect(lpwhr, new_url);
3970 if (res == ERROR_SUCCESS)
3971 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3972 HeapFree( GetProcessHeap(), 0, new_url );
3978 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3981 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3982 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3983 sizeof(INTERNET_ASYNC_RESULT));
3987 /***********************************************************************
3988 * HttpEndRequestA (WININET.@)
3990 * Ends an HTTP request that was started by HttpSendRequestEx
3993 * TRUE if successful
3997 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3998 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4000 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4004 SetLastError(ERROR_INVALID_PARAMETER);
4008 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4011 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4013 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4014 http_request_t *lpwhr = (http_request_t*)work->hdr;
4016 TRACE("%p\n", lpwhr);
4018 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4021 /***********************************************************************
4022 * HttpEndRequestW (WININET.@)
4024 * Ends an HTTP request that was started by HttpSendRequestEx
4027 * TRUE if successful
4031 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4032 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4034 http_request_t *lpwhr;
4041 SetLastError(ERROR_INVALID_PARAMETER);
4045 lpwhr = (http_request_t*) get_handle_object( hRequest );
4047 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4049 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4051 WININET_Release( &lpwhr->hdr );
4054 lpwhr->hdr.dwFlags |= dwFlags;
4056 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4059 struct WORKREQ_HTTPENDREQUESTW *request;
4061 work.asyncproc = AsyncHttpEndRequestProc;
4062 work.hdr = WININET_AddRef( &lpwhr->hdr );
4064 request = &work.u.HttpEndRequestW;
4065 request->dwFlags = dwFlags;
4066 request->dwContext = dwContext;
4068 INTERNET_AsyncCall(&work);
4069 res = ERROR_IO_PENDING;
4072 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4074 WININET_Release( &lpwhr->hdr );
4075 TRACE("%u <--\n", res);
4076 if(res != ERROR_SUCCESS)
4078 return res == ERROR_SUCCESS;
4081 /***********************************************************************
4082 * HttpSendRequestExA (WININET.@)
4084 * Sends the specified request to the HTTP server and allows chunked
4089 * Failure: FALSE, call GetLastError() for more information.
4091 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4092 LPINTERNET_BUFFERSA lpBuffersIn,
4093 LPINTERNET_BUFFERSA lpBuffersOut,
4094 DWORD dwFlags, DWORD_PTR dwContext)
4096 INTERNET_BUFFERSW BuffersInW;
4099 LPWSTR header = NULL;
4101 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4102 lpBuffersOut, dwFlags, dwContext);
4106 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4107 if (lpBuffersIn->lpcszHeader)
4109 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4110 lpBuffersIn->dwHeadersLength,0,0);
4111 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4112 if (!(BuffersInW.lpcszHeader = header))
4114 SetLastError(ERROR_OUTOFMEMORY);
4117 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4118 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4122 BuffersInW.lpcszHeader = NULL;
4123 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4124 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4125 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4126 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4127 BuffersInW.Next = NULL;
4130 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4132 HeapFree(GetProcessHeap(),0,header);
4137 /***********************************************************************
4138 * HttpSendRequestExW (WININET.@)
4140 * Sends the specified request to the HTTP server and allows chunked
4145 * Failure: FALSE, call GetLastError() for more information.
4147 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4148 LPINTERNET_BUFFERSW lpBuffersIn,
4149 LPINTERNET_BUFFERSW lpBuffersOut,
4150 DWORD dwFlags, DWORD_PTR dwContext)
4152 http_request_t *lpwhr;
4153 http_session_t *lpwhs;
4157 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4158 lpBuffersOut, dwFlags, dwContext);
4160 lpwhr = (http_request_t*) get_handle_object( hRequest );
4162 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4164 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4168 lpwhs = lpwhr->lpHttpSession;
4169 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4170 hIC = lpwhs->lpAppInfo;
4171 assert(hIC->hdr.htype == WH_HINIT);
4173 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4175 WORKREQUEST workRequest;
4176 struct WORKREQ_HTTPSENDREQUESTW *req;
4178 workRequest.asyncproc = AsyncHttpSendRequestProc;
4179 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4180 req = &workRequest.u.HttpSendRequestW;
4185 if (lpBuffersIn->lpcszHeader)
4187 if (lpBuffersIn->dwHeadersLength == ~0u)
4188 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4190 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4192 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4193 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4195 else req->lpszHeader = NULL;
4197 req->dwHeaderLength = size / sizeof(WCHAR);
4198 req->lpOptional = lpBuffersIn->lpvBuffer;
4199 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4200 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4204 req->lpszHeader = NULL;
4205 req->dwHeaderLength = 0;
4206 req->lpOptional = NULL;
4207 req->dwOptionalLength = 0;
4208 req->dwContentLength = 0;
4211 req->bEndRequest = FALSE;
4213 INTERNET_AsyncCall(&workRequest);
4215 * This is from windows.
4217 res = ERROR_IO_PENDING;
4222 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4223 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4224 lpBuffersIn->dwBufferTotal, FALSE);
4226 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4231 WININET_Release( &lpwhr->hdr );
4235 return res == ERROR_SUCCESS;
4238 /***********************************************************************
4239 * HttpSendRequestW (WININET.@)
4241 * Sends the specified request to the HTTP server
4248 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4249 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4251 http_request_t *lpwhr;
4252 http_session_t *lpwhs = NULL;
4253 appinfo_t *hIC = NULL;
4254 DWORD res = ERROR_SUCCESS;
4256 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4257 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4259 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
4260 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4262 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4266 lpwhs = lpwhr->lpHttpSession;
4267 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4269 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4273 hIC = lpwhs->lpAppInfo;
4274 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4276 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4280 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4282 WORKREQUEST workRequest;
4283 struct WORKREQ_HTTPSENDREQUESTW *req;
4285 workRequest.asyncproc = AsyncHttpSendRequestProc;
4286 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4287 req = &workRequest.u.HttpSendRequestW;
4292 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4293 else size = dwHeaderLength * sizeof(WCHAR);
4295 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4296 memcpy(req->lpszHeader, lpszHeaders, size);
4299 req->lpszHeader = 0;
4300 req->dwHeaderLength = dwHeaderLength;
4301 req->lpOptional = lpOptional;
4302 req->dwOptionalLength = dwOptionalLength;
4303 req->dwContentLength = dwOptionalLength;
4304 req->bEndRequest = TRUE;
4306 INTERNET_AsyncCall(&workRequest);
4308 * This is from windows.
4310 res = ERROR_IO_PENDING;
4314 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4315 dwHeaderLength, lpOptional, dwOptionalLength,
4316 dwOptionalLength, TRUE);
4320 WININET_Release( &lpwhr->hdr );
4323 return res == ERROR_SUCCESS;
4326 /***********************************************************************
4327 * HttpSendRequestA (WININET.@)
4329 * Sends the specified request to the HTTP server
4336 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4337 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4340 LPWSTR szHeaders=NULL;
4341 DWORD nLen=dwHeaderLength;
4342 if(lpszHeaders!=NULL)
4344 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4345 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4346 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4348 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4349 HeapFree(GetProcessHeap(),0,szHeaders);
4353 /***********************************************************************
4354 * HTTPSESSION_Destroy (internal)
4356 * Deallocate session handle
4359 static void HTTPSESSION_Destroy(object_header_t *hdr)
4361 http_session_t *lpwhs = (http_session_t*) hdr;
4363 TRACE("%p\n", lpwhs);
4365 WININET_Release(&lpwhs->lpAppInfo->hdr);
4367 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4368 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4369 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4370 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4371 HeapFree(GetProcessHeap(), 0, lpwhs);
4374 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4377 case INTERNET_OPTION_HANDLE_TYPE:
4378 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4380 if (*size < sizeof(ULONG))
4381 return ERROR_INSUFFICIENT_BUFFER;
4383 *size = sizeof(DWORD);
4384 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4385 return ERROR_SUCCESS;
4388 return INET_QueryOption(hdr, option, buffer, size, unicode);
4391 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4393 http_session_t *ses = (http_session_t*)hdr;
4396 case INTERNET_OPTION_USERNAME:
4398 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4399 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4400 return ERROR_SUCCESS;
4402 case INTERNET_OPTION_PASSWORD:
4404 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4405 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4406 return ERROR_SUCCESS;
4411 return ERROR_INTERNET_INVALID_OPTION;
4414 static const object_vtbl_t HTTPSESSIONVtbl = {
4415 HTTPSESSION_Destroy,
4417 HTTPSESSION_QueryOption,
4418 HTTPSESSION_SetOption,
4427 /***********************************************************************
4428 * HTTP_Connect (internal)
4430 * Create http session handle
4433 * HINTERNET a session handle on success
4437 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4438 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4439 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4440 DWORD dwInternalFlags, HINTERNET *ret)
4442 http_session_t *lpwhs = NULL;
4443 HINTERNET handle = NULL;
4448 if (!lpszServerName || !lpszServerName[0])
4449 return ERROR_INVALID_PARAMETER;
4451 assert( hIC->hdr.htype == WH_HINIT );
4453 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4455 return ERROR_OUTOFMEMORY;
4458 * According to my tests. The name is not resolved until a request is sent
4461 lpwhs->hdr.htype = WH_HHTTPSESSION;
4462 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4463 lpwhs->hdr.dwFlags = dwFlags;
4464 lpwhs->hdr.dwContext = dwContext;
4465 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4466 lpwhs->hdr.refs = 1;
4467 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4469 WININET_AddRef( &hIC->hdr );
4470 lpwhs->lpAppInfo = hIC;
4471 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4473 res = alloc_handle(&lpwhs->hdr, &handle);
4474 if (res != ERROR_SUCCESS)
4476 ERR("Failed to alloc handle\n");
4480 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4481 if(hIC->lpszProxyBypass)
4482 FIXME("Proxy bypass is ignored.\n");
4484 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4485 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4486 if (lpszUserName && lpszUserName[0])
4487 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4488 if (lpszPassword && lpszPassword[0])
4489 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4490 lpwhs->nServerPort = nServerPort;
4491 lpwhs->nHostPort = nServerPort;
4493 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4494 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4496 INTERNET_SendCallback(&hIC->hdr, dwContext,
4497 INTERNET_STATUS_HANDLE_CREATED, &handle,
4503 WININET_Release( &lpwhs->hdr );
4506 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4510 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4512 if(res == ERROR_SUCCESS)
4518 /***********************************************************************
4519 * HTTP_OpenConnection (internal)
4521 * Connect to a web server
4528 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4530 http_session_t *lpwhs;
4531 appinfo_t *hIC = NULL;
4532 char szaddr[INET6_ADDRSTRLEN];
4534 DWORD res = ERROR_SUCCESS;
4539 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4541 res = ERROR_INVALID_PARAMETER;
4545 if (NETCON_connected(&lpwhr->netConnection))
4547 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4549 lpwhs = lpwhr->lpHttpSession;
4551 hIC = lpwhs->lpAppInfo;
4552 switch (lpwhs->socketAddress.ss_family)
4555 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4558 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4561 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4562 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4564 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4565 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4566 INTERNET_STATUS_CONNECTING_TO_SERVER,
4570 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4571 if (res != ERROR_SUCCESS)
4573 WARN("Socket creation failed: %u\n", res);
4577 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4579 if(res != ERROR_SUCCESS)
4582 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4583 INTERNET_STATUS_CONNECTED_TO_SERVER,
4584 szaddr, strlen(szaddr)+1);
4586 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4588 /* Note: we differ from Microsoft's WinINet here. they seem to have
4589 * a bug that causes no status callbacks to be sent when starting
4590 * a tunnel to a proxy server using the CONNECT verb. i believe our
4591 * behaviour to be more correct and to not cause any incompatibilities
4592 * because using a secure connection through a proxy server is a rare
4593 * case that would be hard for anyone to depend on */
4594 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4595 HTTPREQ_CloseConnection(&lpwhr->hdr);
4599 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4600 if(res != ERROR_SUCCESS)
4602 WARN("Couldn't connect securely to host\n");
4604 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4605 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4606 || res == ERROR_INTERNET_INVALID_CA
4607 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4608 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4609 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4610 || res == ERROR_INTERNET_SEC_INVALID_CERT
4611 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4612 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4614 HTTPREQ_CloseConnection(&lpwhr->hdr);
4621 lpwhr->read_pos = lpwhr->read_size = 0;
4622 lpwhr->read_chunked = FALSE;
4624 TRACE("%d <--\n", res);
4629 /***********************************************************************
4630 * HTTP_clear_response_headers (internal)
4632 * clear out any old response headers
4634 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4638 for( i=0; i<lpwhr->nCustHeaders; i++)
4640 if( !lpwhr->pCustHeaders[i].lpszField )
4642 if( !lpwhr->pCustHeaders[i].lpszValue )
4644 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4646 HTTP_DeleteCustomHeader( lpwhr, i );
4651 /***********************************************************************
4652 * HTTP_GetResponseHeaders (internal)
4654 * Read server response
4661 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4664 WCHAR buffer[MAX_REPLY_LEN];
4665 DWORD buflen = MAX_REPLY_LEN;
4666 BOOL bSuccess = FALSE;
4668 char bufferA[MAX_REPLY_LEN];
4669 LPWSTR status_code = NULL, status_text = NULL;
4670 DWORD cchMaxRawHeaders = 1024;
4671 LPWSTR lpszRawHeaders = NULL;
4673 DWORD cchRawHeaders = 0;
4674 BOOL codeHundred = FALSE;
4678 if (!NETCON_connected(&lpwhr->netConnection))
4682 static const WCHAR szHundred[] = {'1','0','0',0};
4684 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4686 buflen = MAX_REPLY_LEN;
4687 if (!read_line(lpwhr, bufferA, &buflen))
4690 /* clear old response headers (eg. from a redirect response) */
4692 HTTP_clear_response_headers( lpwhr );
4697 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4698 /* check is this a status code line? */
4699 if (!strncmpW(buffer, g_szHttp1_0, 4))
4701 /* split the version from the status code */
4702 status_code = strchrW( buffer, ' ' );
4707 /* split the status code from the status text */
4708 status_text = strchrW( status_code, ' ' );
4713 TRACE("version [%s] status code [%s] status text [%s]\n",
4714 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4716 codeHundred = (!strcmpW(status_code, szHundred));
4718 else if (!codeHundred)
4720 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4722 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4723 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4725 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4726 lpwhr->lpszStatusText = heap_strdupW(szOK);
4728 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4729 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4734 } while (codeHundred);
4736 /* Add status code */
4737 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4738 HTTP_ADDHDR_FLAG_REPLACE);
4740 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4741 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4743 lpwhr->lpszVersion = heap_strdupW(buffer);
4744 lpwhr->lpszStatusText = heap_strdupW(status_text);
4746 /* Restore the spaces */
4747 *(status_code-1) = ' ';
4748 *(status_text-1) = ' ';
4750 /* regenerate raw headers */
4751 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4752 if (!lpszRawHeaders) goto lend;
4754 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4755 cchMaxRawHeaders *= 2;
4756 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4757 if (temp == NULL) goto lend;
4758 lpszRawHeaders = temp;
4759 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4760 cchRawHeaders += (buflen-1);
4761 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4762 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4763 lpszRawHeaders[cchRawHeaders] = '\0';
4765 /* Parse each response line */
4768 buflen = MAX_REPLY_LEN;
4769 if (read_line(lpwhr, bufferA, &buflen))
4771 LPWSTR * pFieldAndValue;
4773 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4775 if (!bufferA[0]) break;
4776 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4778 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4781 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4782 cchMaxRawHeaders *= 2;
4783 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4784 if (temp == NULL) goto lend;
4785 lpszRawHeaders = temp;
4786 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4787 cchRawHeaders += (buflen-1);
4788 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4789 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4790 lpszRawHeaders[cchRawHeaders] = '\0';
4792 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4793 HTTP_ADDREQ_FLAG_ADD );
4795 HTTP_FreeTokens(pFieldAndValue);
4806 /* make sure the response header is terminated with an empty line. Some apps really
4807 truly care about that empty line being there for some reason. Just add it to the
4809 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4811 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4812 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4813 if (temp == NULL) goto lend;
4814 lpszRawHeaders = temp;
4817 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4819 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4820 lpwhr->lpszRawHeaders = lpszRawHeaders;
4821 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4831 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4836 /***********************************************************************
4837 * HTTP_InterpretHttpHeader (internal)
4839 * Parse server response
4843 * Pointer to array of field, value, NULL on success.
4846 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4848 LPWSTR * pTokenPair;
4852 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4854 pszColon = strchrW(buffer, ':');
4855 /* must have two tokens */
4858 HTTP_FreeTokens(pTokenPair);
4860 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4864 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4867 HTTP_FreeTokens(pTokenPair);
4870 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4871 pTokenPair[0][pszColon - buffer] = '\0';
4875 len = strlenW(pszColon);
4876 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4879 HTTP_FreeTokens(pTokenPair);
4882 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4884 strip_spaces(pTokenPair[0]);
4885 strip_spaces(pTokenPair[1]);
4887 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4891 /***********************************************************************
4892 * HTTP_ProcessHeader (internal)
4894 * Stuff header into header tables according to <dwModifier>
4898 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4900 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4902 LPHTTPHEADERW lphttpHdr = NULL;
4904 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4905 DWORD res = ERROR_HTTP_INVALID_HEADER;
4907 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4909 /* REPLACE wins out over ADD */
4910 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4911 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4913 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4916 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4920 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4921 return ERROR_HTTP_INVALID_HEADER;
4922 lphttpHdr = &lpwhr->pCustHeaders[index];
4928 hdr.lpszField = (LPWSTR)field;
4929 hdr.lpszValue = (LPWSTR)value;
4930 hdr.wFlags = hdr.wCount = 0;
4932 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4933 hdr.wFlags |= HDR_ISREQUEST;
4935 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4937 /* no value to delete */
4938 else return ERROR_SUCCESS;
4940 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4941 lphttpHdr->wFlags |= HDR_ISREQUEST;
4943 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4945 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4947 HTTP_DeleteCustomHeader( lpwhr, index );
4953 hdr.lpszField = (LPWSTR)field;
4954 hdr.lpszValue = (LPWSTR)value;
4955 hdr.wFlags = hdr.wCount = 0;
4957 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4958 hdr.wFlags |= HDR_ISREQUEST;
4960 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4963 return ERROR_SUCCESS;
4965 else if (dwModifier & COALESCEFLAGS)
4970 INT origlen = strlenW(lphttpHdr->lpszValue);
4971 INT valuelen = strlenW(value);
4973 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4976 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4978 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4981 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4984 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4986 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4989 lphttpHdr->lpszValue = lpsztmp;
4990 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4993 lphttpHdr->lpszValue[origlen] = ch;
4995 lphttpHdr->lpszValue[origlen] = ' ';
4999 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5000 lphttpHdr->lpszValue[len] = '\0';
5001 res = ERROR_SUCCESS;
5005 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5006 res = ERROR_OUTOFMEMORY;
5009 TRACE("<-- %d\n", res);
5014 /***********************************************************************
5015 * HTTP_FinishedReading (internal)
5017 * Called when all content from server has been read by client.
5020 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5022 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5029 HTTPREQ_CloseConnection(&lpwhr->hdr);
5032 /* FIXME: store data in the URL cache here */
5038 /***********************************************************************
5039 * HTTP_GetCustomHeaderIndex (internal)
5041 * Return index of custom header from header array
5044 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5045 int requested_index, BOOL request_only)
5049 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5051 for (index = 0; index < lpwhr->nCustHeaders; index++)
5053 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5056 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5059 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5062 if (requested_index == 0)
5067 if (index >= lpwhr->nCustHeaders)
5070 TRACE("Return: %d\n", index);
5075 /***********************************************************************
5076 * HTTP_InsertCustomHeader (internal)
5078 * Insert header into array
5081 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5084 LPHTTPHEADERW lph = NULL;
5086 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5087 count = lpwhr->nCustHeaders + 1;
5089 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5091 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5094 return ERROR_OUTOFMEMORY;
5096 lpwhr->pCustHeaders = lph;
5097 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5098 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5099 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5100 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5101 lpwhr->nCustHeaders++;
5103 return ERROR_SUCCESS;
5107 /***********************************************************************
5108 * HTTP_DeleteCustomHeader (internal)
5110 * Delete header from array
5111 * If this function is called, the indexs may change.
5113 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5115 if( lpwhr->nCustHeaders <= 0 )
5117 if( index >= lpwhr->nCustHeaders )
5119 lpwhr->nCustHeaders--;
5121 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5122 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5124 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5125 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5126 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5132 /***********************************************************************
5133 * HTTP_VerifyValidHeader (internal)
5135 * Verify the given header is not invalid for the given http request
5138 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5140 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5141 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5142 return ERROR_HTTP_INVALID_HEADER;
5144 return ERROR_SUCCESS;
5147 /***********************************************************************
5148 * IsHostInProxyBypassList (@)
5153 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5155 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5159 /***********************************************************************
5160 * InternetShowSecurityInfoByURLA (@)
5162 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5164 FIXME("stub: %s %p\n", url, window);
5168 /***********************************************************************
5169 * InternetShowSecurityInfoByURLW (@)
5171 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5173 FIXME("stub: %s %p\n", debugstr_w(url), window);