2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
180 typedef struct _authorizationData
186 LPSTR lpszAuthorization;
187 UINT AuthorizationLen;
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
196 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
201 static DWORD HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
210 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
211 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
212 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
213 static void HTTP_DrainContent(http_request_t *req);
214 static BOOL HTTP_FinishedReading(http_request_t *req);
216 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
219 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
220 if (HeaderIndex == -1)
223 return &req->pCustHeaders[HeaderIndex];
228 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
230 return HeapAlloc(GetProcessHeap(), 0, items*size);
233 static void wininet_zfree(voidpf opaque, voidpf address)
235 HeapFree(GetProcessHeap(), 0, address);
238 static void init_gzip_stream(http_request_t *req)
240 gzip_stream_t *gzip_stream;
243 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
244 gzip_stream->zstream.zalloc = wininet_zalloc;
245 gzip_stream->zstream.zfree = wininet_zfree;
246 gzip_stream->zstream.opaque = NULL;
247 gzip_stream->zstream.next_in = NULL;
248 gzip_stream->zstream.avail_in = 0;
249 gzip_stream->zstream.next_out = NULL;
250 gzip_stream->zstream.avail_out = 0;
251 gzip_stream->buf_pos = 0;
252 gzip_stream->buf_size = 0;
253 gzip_stream->end_of_data = FALSE;
255 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
257 ERR("inflateInit failed: %d\n", zres);
258 HeapFree(GetProcessHeap(), 0, gzip_stream);
262 req->gzip_stream = gzip_stream;
264 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
266 HTTP_DeleteCustomHeader(req, index);
271 static void init_gzip_stream(http_request_t *req)
273 ERR("gzip stream not supported, missing zlib.\n");
278 /* set the request content length based on the headers */
279 static DWORD set_content_length( http_request_t *lpwhr )
281 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
285 size = sizeof(lpwhr->dwContentLength);
286 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
287 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
288 lpwhr->dwContentLength = ~0u;
290 size = sizeof(encoding);
291 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
292 !strcmpiW(encoding, szChunked))
294 lpwhr->dwContentLength = ~0u;
295 lpwhr->read_chunked = TRUE;
298 if(lpwhr->decoding) {
301 static const WCHAR gzipW[] = {'g','z','i','p',0};
303 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
304 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
305 init_gzip_stream(lpwhr);
308 return lpwhr->dwContentLength;
311 /***********************************************************************
312 * HTTP_Tokenize (internal)
314 * Tokenize a string, allocating memory for the tokens.
316 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
318 LPWSTR * token_array;
325 /* empty string has no tokens */
329 for (i = 0; string[i]; i++)
331 if (!strncmpW(string+i, token_string, strlenW(token_string)))
335 /* we want to skip over separators, but not the null terminator */
336 for (j = 0; j < strlenW(token_string) - 1; j++)
344 /* add 1 for terminating NULL */
345 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
346 token_array[tokens] = NULL;
349 for (i = 0; i < tokens; i++)
352 next_token = strstrW(string, token_string);
353 if (!next_token) next_token = string+strlenW(string);
354 len = next_token - string;
355 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
356 memcpy(token_array[i], string, len*sizeof(WCHAR));
357 token_array[i][len] = '\0';
358 string = next_token+strlenW(token_string);
363 /***********************************************************************
364 * HTTP_FreeTokens (internal)
366 * Frees memory returned from HTTP_Tokenize.
368 static void HTTP_FreeTokens(LPWSTR * token_array)
371 for (i = 0; token_array[i]; i++)
372 HeapFree(GetProcessHeap(), 0, token_array[i]);
373 HeapFree(GetProcessHeap(), 0, token_array);
376 static void HTTP_FixURL(http_request_t *lpwhr)
378 static const WCHAR szSlash[] = { '/',0 };
379 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
381 /* If we don't have a path we set it to root */
382 if (NULL == lpwhr->lpszPath)
383 lpwhr->lpszPath = heap_strdupW(szSlash);
384 else /* remove \r and \n*/
386 int nLen = strlenW(lpwhr->lpszPath);
387 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
390 lpwhr->lpszPath[nLen]='\0';
392 /* Replace '\' with '/' */
395 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
399 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
400 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
401 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
403 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
404 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
406 strcpyW(fixurl + 1, lpwhr->lpszPath);
407 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
408 lpwhr->lpszPath = fixurl;
412 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
414 LPWSTR requestString;
420 static const WCHAR szSpace[] = { ' ',0 };
421 static const WCHAR szColon[] = { ':',' ',0 };
422 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
424 /* allocate space for an array of all the string pointers to be added */
425 len = (lpwhr->nCustHeaders)*4 + 10;
426 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
428 /* add the verb, path and HTTP version string */
436 /* Append custom request headers */
437 for (i = 0; i < lpwhr->nCustHeaders; i++)
439 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
442 req[n++] = lpwhr->pCustHeaders[i].lpszField;
444 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
446 TRACE("Adding custom header %s (%s)\n",
447 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
448 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
453 ERR("oops. buffer overrun\n");
456 requestString = HTTP_build_req( req, 4 );
457 HeapFree( GetProcessHeap(), 0, req );
460 * Set (header) termination string for request
461 * Make sure there's exactly two new lines at the end of the request
463 p = &requestString[strlenW(requestString)-1];
464 while ( (*p == '\n') || (*p == '\r') )
466 strcpyW( p+1, sztwocrlf );
468 return requestString;
471 static void HTTP_ProcessCookies( http_request_t *lpwhr )
475 LPHTTPHEADERW setCookieHeader;
477 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
479 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
481 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
484 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
488 Host = HTTP_GetHeader(lpwhr, hostW);
489 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
490 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
491 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
492 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
494 HeapFree(GetProcessHeap(), 0, buf_url);
500 static void strip_spaces(LPWSTR start)
505 while (*str == ' ' && *str != '\0')
509 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
511 end = start + strlenW(start) - 1;
512 while (end >= start && *end == ' ')
519 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
521 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
522 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
524 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
525 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
526 if (is_basic && pszRealm)
529 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
533 token = strchrW(ptr,'=');
537 while (*realm == ' ' && *realm != '\0')
539 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
540 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
543 while (*token == ' ' && *token != '\0')
547 *pszRealm = heap_strdupW(token);
548 strip_spaces(*pszRealm);
555 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
557 if (!authinfo) return;
559 if (SecIsValidHandle(&authinfo->ctx))
560 DeleteSecurityContext(&authinfo->ctx);
561 if (SecIsValidHandle(&authinfo->cred))
562 FreeCredentialsHandle(&authinfo->cred);
564 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
565 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
566 HeapFree(GetProcessHeap(), 0, authinfo);
569 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
571 authorizationData *ad;
574 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
576 EnterCriticalSection(&authcache_cs);
577 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
579 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
581 TRACE("Authorization found in cache\n");
582 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
583 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
584 rc = ad->AuthorizationLen;
588 LeaveCriticalSection(&authcache_cs);
592 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
595 authorizationData* ad = NULL;
597 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
599 EnterCriticalSection(&authcache_cs);
600 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
602 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
603 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
612 TRACE("Found match in cache, replacing\n");
613 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
614 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
615 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
616 ad->AuthorizationLen = auth_data_len;
620 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
621 ad->lpszwHost = heap_strdupW(host);
622 ad->lpszwRealm = heap_strdupW(realm);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
626 list_add_head(&basicAuthorizationCache,&ad->entry);
627 TRACE("authorization cached\n");
629 LeaveCriticalSection(&authcache_cs);
632 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
633 struct HttpAuthInfo **ppAuthInfo,
634 LPWSTR domain_and_username, LPWSTR password,
637 SECURITY_STATUS sec_status;
638 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
640 LPWSTR szRealm = NULL;
642 TRACE("%s\n", debugstr_w(pszAuthValue));
649 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
653 SecInvalidateHandle(&pAuthInfo->cred);
654 SecInvalidateHandle(&pAuthInfo->ctx);
655 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
657 pAuthInfo->auth_data = NULL;
658 pAuthInfo->auth_data_len = 0;
659 pAuthInfo->finished = FALSE;
661 if (is_basic_auth_value(pszAuthValue,NULL))
663 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
664 pAuthInfo->scheme = heap_strdupW(szBasic);
665 if (!pAuthInfo->scheme)
667 HeapFree(GetProcessHeap(), 0, pAuthInfo);
674 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
676 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
677 if (!pAuthInfo->scheme)
679 HeapFree(GetProcessHeap(), 0, pAuthInfo);
683 if (domain_and_username)
685 WCHAR *user = strchrW(domain_and_username, '\\');
686 WCHAR *domain = domain_and_username;
688 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
690 pAuthData = &nt_auth_identity;
695 user = domain_and_username;
699 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
700 nt_auth_identity.User = user;
701 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
702 nt_auth_identity.Domain = domain;
703 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
704 nt_auth_identity.Password = password;
705 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
708 /* use default credentials */
711 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
712 SECPKG_CRED_OUTBOUND, NULL,
714 NULL, &pAuthInfo->cred,
716 if (sec_status == SEC_E_OK)
718 PSecPkgInfoW sec_pkg_info;
719 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
720 if (sec_status == SEC_E_OK)
722 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
723 FreeContextBuffer(sec_pkg_info);
726 if (sec_status != SEC_E_OK)
728 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
729 debugstr_w(pAuthInfo->scheme), sec_status);
730 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
731 HeapFree(GetProcessHeap(), 0, pAuthInfo);
735 *ppAuthInfo = pAuthInfo;
737 else if (pAuthInfo->finished)
740 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
741 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
743 ERR("authentication scheme changed from %s to %s\n",
744 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
748 if (is_basic_auth_value(pszAuthValue,&szRealm))
752 char *auth_data = NULL;
753 UINT auth_data_len = 0;
755 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
757 if (!domain_and_username)
760 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
761 if (auth_data_len == 0)
763 HeapFree(GetProcessHeap(),0,szRealm);
769 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
770 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
772 /* length includes a nul terminator, which will be re-used for the ':' */
773 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
776 HeapFree(GetProcessHeap(),0,szRealm);
780 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
781 auth_data[userlen] = ':';
782 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
783 auth_data_len = userlen + 1 + passlen;
785 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
788 pAuthInfo->auth_data = auth_data;
789 pAuthInfo->auth_data_len = auth_data_len;
790 pAuthInfo->finished = TRUE;
791 HeapFree(GetProcessHeap(),0,szRealm);
798 SecBufferDesc out_desc, in_desc;
800 unsigned char *buffer;
801 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
802 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
804 in.BufferType = SECBUFFER_TOKEN;
808 in_desc.ulVersion = 0;
809 in_desc.cBuffers = 1;
810 in_desc.pBuffers = ∈
812 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
813 if (*pszAuthData == ' ')
816 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
817 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
818 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
821 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
823 out.BufferType = SECBUFFER_TOKEN;
824 out.cbBuffer = pAuthInfo->max_token;
825 out.pvBuffer = buffer;
827 out_desc.ulVersion = 0;
828 out_desc.cBuffers = 1;
829 out_desc.pBuffers = &out;
831 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
832 first ? NULL : &pAuthInfo->ctx,
833 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
834 context_req, 0, SECURITY_NETWORK_DREP,
835 in.pvBuffer ? &in_desc : NULL,
836 0, &pAuthInfo->ctx, &out_desc,
837 &pAuthInfo->attr, &pAuthInfo->exp);
838 if (sec_status == SEC_E_OK)
840 pAuthInfo->finished = TRUE;
841 pAuthInfo->auth_data = out.pvBuffer;
842 pAuthInfo->auth_data_len = out.cbBuffer;
843 TRACE("sending last auth packet\n");
845 else if (sec_status == SEC_I_CONTINUE_NEEDED)
847 pAuthInfo->auth_data = out.pvBuffer;
848 pAuthInfo->auth_data_len = out.cbBuffer;
849 TRACE("sending next auth packet\n");
853 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
854 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
855 destroy_authinfo(pAuthInfo);
864 /***********************************************************************
865 * HTTP_HttpAddRequestHeadersW (internal)
867 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
868 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
873 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
875 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
877 if( dwHeaderLength == ~0U )
878 len = strlenW(lpszHeader);
880 len = dwHeaderLength;
881 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
882 lstrcpynW( buffer, lpszHeader, len + 1);
888 LPWSTR * pFieldAndValue;
892 while (*lpszEnd != '\0')
894 if (*lpszEnd == '\r' || *lpszEnd == '\n')
899 if (*lpszStart == '\0')
902 if (*lpszEnd == '\r' || *lpszEnd == '\n')
905 lpszEnd++; /* Jump over newline */
907 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
908 if (*lpszStart == '\0')
910 /* Skip 0-length headers */
915 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
918 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
919 if (res == ERROR_SUCCESS)
920 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
921 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
922 HTTP_FreeTokens(pFieldAndValue);
926 } while (res == ERROR_SUCCESS);
928 HeapFree(GetProcessHeap(), 0, buffer);
933 /***********************************************************************
934 * HttpAddRequestHeadersW (WININET.@)
936 * Adds one or more HTTP header to the request handler
939 * On Windows if dwHeaderLength includes the trailing '\0', then
940 * HttpAddRequestHeadersW() adds it too. However this results in an
941 * invalid Http header which is rejected by some servers so we probably
942 * don't need to match Windows on that point.
949 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
950 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
952 http_request_t *lpwhr;
953 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
955 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
960 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
961 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
962 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
964 WININET_Release( &lpwhr->hdr );
966 if(res != ERROR_SUCCESS)
968 return res == ERROR_SUCCESS;
971 /***********************************************************************
972 * HttpAddRequestHeadersA (WININET.@)
974 * Adds one or more HTTP header to the request handler
981 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
982 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
988 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
990 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
991 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
992 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
993 if( dwHeaderLength != ~0U )
994 dwHeaderLength = len;
996 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
998 HeapFree( GetProcessHeap(), 0, hdr );
1003 /***********************************************************************
1004 * HttpOpenRequestA (WININET.@)
1006 * Open a HTTP request handle
1009 * HINTERNET a HTTP request handle on success
1013 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1014 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1015 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1016 DWORD dwFlags, DWORD_PTR dwContext)
1018 LPWSTR szVerb = NULL, szObjectName = NULL;
1019 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1020 INT acceptTypesCount;
1021 HINTERNET rc = FALSE;
1024 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1025 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1026 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1027 dwFlags, dwContext);
1031 szVerb = heap_strdupAtoW(lpszVerb);
1038 szObjectName = heap_strdupAtoW(lpszObjectName);
1039 if ( !szObjectName )
1045 szVersion = heap_strdupAtoW(lpszVersion);
1052 szReferrer = heap_strdupAtoW(lpszReferrer);
1057 if (lpszAcceptTypes)
1059 acceptTypesCount = 0;
1060 types = lpszAcceptTypes;
1065 /* find out how many there are */
1066 if (*types && **types)
1068 TRACE("accept type: %s\n", debugstr_a(*types));
1074 WARN("invalid accept type pointer\n");
1079 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1080 if (!szAcceptTypes) goto end;
1082 acceptTypesCount = 0;
1083 types = lpszAcceptTypes;
1088 if (*types && **types)
1089 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1093 /* ignore invalid pointer */
1098 szAcceptTypes[acceptTypesCount] = NULL;
1101 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1102 szVersion, szReferrer,
1103 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1108 acceptTypesCount = 0;
1109 while (szAcceptTypes[acceptTypesCount])
1111 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1114 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1116 HeapFree(GetProcessHeap(), 0, szReferrer);
1117 HeapFree(GetProcessHeap(), 0, szVersion);
1118 HeapFree(GetProcessHeap(), 0, szObjectName);
1119 HeapFree(GetProcessHeap(), 0, szVerb);
1124 /***********************************************************************
1127 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1130 static const CHAR HTTP_Base64Enc[] =
1131 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1135 /* first 6 bits, all from bin[0] */
1136 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1137 x = (bin[0] & 3) << 4;
1139 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1142 base64[n++] = HTTP_Base64Enc[x];
1147 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1148 x = ( bin[1] & 0x0f ) << 2;
1150 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1153 base64[n++] = HTTP_Base64Enc[x];
1157 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1159 /* last 6 bits, all from bin [2] */
1160 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1168 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1169 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1170 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1171 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1172 static const signed char HTTP_Base64Dec[256] =
1174 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1175 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1176 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1177 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1178 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1179 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1180 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1181 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1182 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1183 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1184 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1185 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1186 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1187 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1188 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1189 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1190 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1191 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1192 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1193 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1194 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1195 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1196 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1197 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1198 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1199 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1203 /***********************************************************************
1206 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1214 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1215 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1216 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1217 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1219 WARN("invalid base64: %s\n", debugstr_w(base64));
1223 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1226 if ((base64[2] == '=') && (base64[3] == '='))
1228 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1229 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1231 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1235 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1238 if (base64[3] == '=')
1240 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1241 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1243 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1247 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1256 /***********************************************************************
1257 * HTTP_InsertAuthorization
1259 * Insert or delete the authorization field in the request header.
1261 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1265 static const WCHAR wszSpace[] = {' ',0};
1266 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1268 WCHAR *authorization = NULL;
1270 if (pAuthInfo->auth_data_len)
1272 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1273 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1274 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1278 strcpyW(authorization, pAuthInfo->scheme);
1279 strcatW(authorization, wszSpace);
1280 HTTP_EncodeBase64(pAuthInfo->auth_data,
1281 pAuthInfo->auth_data_len,
1282 authorization+strlenW(authorization));
1284 /* clear the data as it isn't valid now that it has been sent to the
1285 * server, unless it's Basic authentication which doesn't do
1286 * connection tracking */
1287 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1289 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1290 pAuthInfo->auth_data = NULL;
1291 pAuthInfo->auth_data_len = 0;
1295 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1297 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1299 HeapFree(GetProcessHeap(), 0, authorization);
1304 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1306 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1309 size = sizeof(new_location);
1310 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1312 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1313 strcpyW( url, new_location );
1317 static const WCHAR slash[] = { '/',0 };
1318 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1319 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1320 http_session_t *session = req->lpHttpSession;
1322 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1323 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1325 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1327 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1328 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1330 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1331 if (req->lpszPath[0] != '/') strcatW( url, slash );
1332 strcatW( url, req->lpszPath );
1334 TRACE("url=%s\n", debugstr_w(url));
1338 /***********************************************************************
1339 * HTTP_DealWithProxy
1341 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1343 WCHAR buf[MAXHOSTNAME];
1344 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1345 static WCHAR szNul[] = { 0 };
1346 URL_COMPONENTSW UrlComponents;
1347 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1348 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1350 memset( &UrlComponents, 0, sizeof UrlComponents );
1351 UrlComponents.dwStructSize = sizeof UrlComponents;
1352 UrlComponents.lpszHostName = buf;
1353 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1355 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1356 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1357 sprintfW(proxy, szFormat, hIC->lpszProxy);
1359 strcpyW(proxy, hIC->lpszProxy);
1360 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1362 if( UrlComponents.dwHostNameLength == 0 )
1365 if( !lpwhr->lpszPath )
1366 lpwhr->lpszPath = szNul;
1368 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1369 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1371 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1372 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1373 lpwhs->nServerPort = UrlComponents.nPort;
1375 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1379 #ifndef INET6_ADDRSTRLEN
1380 #define INET6_ADDRSTRLEN 46
1383 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1385 char szaddr[INET6_ADDRSTRLEN];
1386 http_session_t *lpwhs = lpwhr->lpHttpSession;
1389 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1390 INTERNET_STATUS_RESOLVING_NAME,
1391 lpwhs->lpszServerName,
1392 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1394 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1395 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1396 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1397 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1399 switch (lpwhs->socketAddress.ss_family)
1402 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1405 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1408 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1409 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1411 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1412 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1413 INTERNET_STATUS_NAME_RESOLVED,
1414 szaddr, strlen(szaddr)+1);
1416 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1417 return ERROR_SUCCESS;
1421 /***********************************************************************
1422 * HTTPREQ_Destroy (internal)
1424 * Deallocate request handle
1427 static void HTTPREQ_Destroy(object_header_t *hdr)
1429 http_request_t *lpwhr = (http_request_t*) hdr;
1434 if(lpwhr->hCacheFile)
1435 CloseHandle(lpwhr->hCacheFile);
1437 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1439 DeleteCriticalSection( &lpwhr->read_section );
1440 WININET_Release(&lpwhr->lpHttpSession->hdr);
1442 destroy_authinfo(lpwhr->pAuthInfo);
1443 destroy_authinfo(lpwhr->pProxyAuthInfo);
1445 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1447 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1448 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1451 for (i = 0; i < lpwhr->nCustHeaders; i++)
1453 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1454 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1458 if(lpwhr->gzip_stream) {
1459 if(!lpwhr->gzip_stream->end_of_data)
1460 inflateEnd(&lpwhr->gzip_stream->zstream);
1461 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1465 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1466 HeapFree(GetProcessHeap(), 0, lpwhr);
1469 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1471 http_request_t *lpwhr = (http_request_t*) hdr;
1473 TRACE("%p\n",lpwhr);
1475 if (!NETCON_connected(&lpwhr->netConnection))
1478 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1479 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1481 NETCON_close(&lpwhr->netConnection);
1483 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1484 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1487 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1489 LPHTTPHEADERW host_header;
1491 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1493 host_header = HTTP_GetHeader(req, hostW);
1497 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1501 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1503 WCHAR szVersion[10];
1504 WCHAR szConnectionResponse[20];
1505 DWORD dwBufferSize = sizeof(szVersion);
1506 BOOL keepalive = FALSE;
1508 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1509 * the connection is keep-alive by default */
1510 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1511 && !strcmpiW(szVersion, g_szHttp1_1))
1516 dwBufferSize = sizeof(szConnectionResponse);
1517 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1518 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1520 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1526 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1528 http_request_t *req = (http_request_t*)hdr;
1531 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1533 http_session_t *lpwhs = req->lpHttpSession;
1534 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1536 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1538 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1539 return ERROR_INSUFFICIENT_BUFFER;
1540 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1541 /* FIXME: can't get a SOCKET from our connection since we don't use
1545 /* FIXME: get source port from req->netConnection */
1546 info->SourcePort = 0;
1547 info->DestPort = lpwhs->nHostPort;
1549 if (HTTP_KeepAlive(req))
1550 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1551 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1552 info->Flags |= IDSI_FLAG_PROXY;
1553 if (req->netConnection.useSSL)
1554 info->Flags |= IDSI_FLAG_SECURE;
1556 return ERROR_SUCCESS;
1559 case INTERNET_OPTION_SECURITY_FLAGS:
1561 http_session_t *lpwhs;
1562 lpwhs = req->lpHttpSession;
1564 if (*size < sizeof(ULONG))
1565 return ERROR_INSUFFICIENT_BUFFER;
1567 *size = sizeof(DWORD);
1568 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1569 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1571 *(DWORD*)buffer = 0;
1572 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1573 return ERROR_SUCCESS;
1576 case INTERNET_OPTION_HANDLE_TYPE:
1577 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1579 if (*size < sizeof(ULONG))
1580 return ERROR_INSUFFICIENT_BUFFER;
1582 *size = sizeof(DWORD);
1583 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1584 return ERROR_SUCCESS;
1586 case INTERNET_OPTION_URL: {
1587 WCHAR url[INTERNET_MAX_URL_LENGTH];
1592 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1594 TRACE("INTERNET_OPTION_URL\n");
1596 host = HTTP_GetHeader(req, hostW);
1597 strcpyW(url, httpW);
1598 strcatW(url, host->lpszValue);
1599 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1601 strcatW(url, req->lpszPath);
1603 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1606 len = (strlenW(url)+1) * sizeof(WCHAR);
1608 return ERROR_INSUFFICIENT_BUFFER;
1611 strcpyW(buffer, url);
1612 return ERROR_SUCCESS;
1614 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1616 return ERROR_INSUFFICIENT_BUFFER;
1619 return ERROR_SUCCESS;
1623 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1624 INTERNET_CACHE_ENTRY_INFOW *info;
1625 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1626 WCHAR url[INTERNET_MAX_URL_LENGTH];
1627 DWORD nbytes, error;
1630 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1632 if (*size < sizeof(*ts))
1634 *size = sizeof(*ts);
1635 return ERROR_INSUFFICIENT_BUFFER;
1638 HTTP_GetRequestURL(req, url);
1639 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1640 error = GetLastError();
1641 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1643 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1644 return ERROR_OUTOFMEMORY;
1646 GetUrlCacheEntryInfoW(url, info, &nbytes);
1648 ts->ftExpires = info->ExpireTime;
1649 ts->ftLastModified = info->LastModifiedTime;
1651 HeapFree(GetProcessHeap(), 0, info);
1652 *size = sizeof(*ts);
1653 return ERROR_SUCCESS;
1658 case INTERNET_OPTION_DATAFILE_NAME: {
1661 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1663 if(!req->lpszCacheFile) {
1665 return ERROR_INTERNET_ITEM_NOT_FOUND;
1669 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1670 if(*size < req_size)
1671 return ERROR_INSUFFICIENT_BUFFER;
1674 memcpy(buffer, req->lpszCacheFile, *size);
1675 return ERROR_SUCCESS;
1677 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1678 if (req_size > *size)
1679 return ERROR_INSUFFICIENT_BUFFER;
1681 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1682 -1, buffer, *size, NULL, NULL);
1683 return ERROR_SUCCESS;
1687 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1688 PCCERT_CONTEXT context;
1690 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1691 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1692 return ERROR_INSUFFICIENT_BUFFER;
1695 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1697 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1700 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1701 info->ftExpiry = context->pCertInfo->NotAfter;
1702 info->ftStart = context->pCertInfo->NotBefore;
1704 len = CertNameToStrW(context->dwCertEncodingType,
1705 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1706 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1707 if(info->lpszSubjectInfo)
1708 CertNameToStrW(context->dwCertEncodingType,
1709 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1710 info->lpszSubjectInfo, len);
1711 len = CertNameToStrW(context->dwCertEncodingType,
1712 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1713 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1714 if (info->lpszIssuerInfo)
1715 CertNameToStrW(context->dwCertEncodingType,
1716 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1717 info->lpszIssuerInfo, len);
1719 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1721 len = CertNameToStrA(context->dwCertEncodingType,
1722 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1723 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1724 if(infoA->lpszSubjectInfo)
1725 CertNameToStrA(context->dwCertEncodingType,
1726 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1727 infoA->lpszSubjectInfo, len);
1728 len = CertNameToStrA(context->dwCertEncodingType,
1729 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1730 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1731 if(infoA->lpszIssuerInfo)
1732 CertNameToStrA(context->dwCertEncodingType,
1733 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1734 infoA->lpszIssuerInfo, len);
1738 * Contrary to MSDN, these do not appear to be set.
1740 * lpszSignatureAlgName
1741 * lpszEncryptionAlgName
1744 CertFreeCertificateContext(context);
1745 return ERROR_SUCCESS;
1750 return INET_QueryOption(option, buffer, size, unicode);
1753 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1755 http_request_t *req = (http_request_t*)hdr;
1758 case INTERNET_OPTION_SEND_TIMEOUT:
1759 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1760 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1762 if (size != sizeof(DWORD))
1763 return ERROR_INVALID_PARAMETER;
1765 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1768 case INTERNET_OPTION_USERNAME:
1769 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1770 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1771 return ERROR_SUCCESS;
1773 case INTERNET_OPTION_PASSWORD:
1774 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1775 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1776 return ERROR_SUCCESS;
1777 case INTERNET_OPTION_HTTP_DECODING:
1778 if(size != sizeof(BOOL))
1779 return ERROR_INVALID_PARAMETER;
1780 req->decoding = *(BOOL*)buffer;
1781 return ERROR_SUCCESS;
1784 return ERROR_INTERNET_INVALID_OPTION;
1787 /* read some more data into the read buffer (the read section must be held) */
1788 static DWORD read_more_data( http_request_t *req, int maxlen )
1795 /* move existing data to the start of the buffer */
1797 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1801 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1803 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1804 maxlen - req->read_size, 0, &len );
1805 if(res == ERROR_SUCCESS)
1806 req->read_size += len;
1811 /* remove some amount of data from the read buffer (the read section must be held) */
1812 static void remove_data( http_request_t *req, int count )
1814 if (!(req->read_size -= count)) req->read_pos = 0;
1815 else req->read_pos += count;
1818 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1820 int count, bytes_read, pos = 0;
1823 EnterCriticalSection( &req->read_section );
1826 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1830 count = eol - (req->read_buf + req->read_pos);
1831 bytes_read = count + 1;
1833 else count = bytes_read = req->read_size;
1835 count = min( count, *len - pos );
1836 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1838 remove_data( req, bytes_read );
1841 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1844 TRACE( "returning empty string\n" );
1845 LeaveCriticalSection( &req->read_section );
1846 INTERNET_SetLastError(res);
1850 LeaveCriticalSection( &req->read_section );
1854 if (pos && buffer[pos - 1] == '\r') pos--;
1857 buffer[*len - 1] = 0;
1858 TRACE( "returning %s\n", debugstr_a(buffer));
1862 /* discard data contents until we reach end of line (the read section must be held) */
1863 static DWORD discard_eol( http_request_t *req )
1869 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1872 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1875 req->read_pos = req->read_size = 0; /* discard everything */
1876 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1877 } while (req->read_size);
1878 return ERROR_SUCCESS;
1881 /* read the size of the next chunk (the read section must be held) */
1882 static DWORD start_next_chunk( http_request_t *req )
1884 DWORD chunk_size = 0, res;
1886 if (!req->dwContentLength) return ERROR_SUCCESS;
1887 if (req->dwContentLength == req->dwContentRead)
1889 /* read terminator for the previous chunk */
1890 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1891 req->dwContentLength = ~0u;
1892 req->dwContentRead = 0;
1896 while (req->read_size)
1898 char ch = req->read_buf[req->read_pos];
1899 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1900 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1901 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1902 else if (ch == ';' || ch == '\r' || ch == '\n')
1904 TRACE( "reading %u byte chunk\n", chunk_size );
1905 req->dwContentLength = chunk_size;
1906 req->dwContentRead = 0;
1907 return discard_eol( req );
1909 remove_data( req, 1 );
1911 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1912 if (!req->read_size)
1914 req->dwContentLength = req->dwContentRead = 0;
1915 return ERROR_SUCCESS;
1920 /* check if we have reached the end of the data to read (the read section must be held) */
1921 static BOOL end_of_read_data( http_request_t *req )
1923 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1924 if (req->read_chunked) return (req->dwContentLength == 0);
1925 if (req->dwContentLength == ~0u) return FALSE;
1926 return (req->dwContentLength == req->dwContentRead);
1929 /* fetch some more data into the read buffer (the read section must be held) */
1930 static DWORD refill_buffer( http_request_t *req )
1932 int len = sizeof(req->read_buf);
1935 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1937 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1940 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1941 if (len <= req->read_size) return ERROR_SUCCESS;
1943 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1944 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1945 return ERROR_SUCCESS;
1948 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1950 DWORD ret = ERROR_SUCCESS;
1954 z_stream *zstream = &req->gzip_stream->zstream;
1957 while(read < size && !req->gzip_stream->end_of_data) {
1958 if(!req->read_size) {
1959 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1963 zstream->next_in = req->read_buf+req->read_pos;
1964 zstream->avail_in = req->read_size;
1965 zstream->next_out = buf+read;
1966 zstream->avail_out = size-read;
1967 zres = inflate(zstream, Z_FULL_FLUSH);
1968 read = size - zstream->avail_out;
1969 req->dwContentRead += req->read_size-zstream->avail_in;
1970 remove_data(req, req->read_size-zstream->avail_in);
1971 if(zres == Z_STREAM_END) {
1972 TRACE("end of data\n");
1973 req->gzip_stream->end_of_data = TRUE;
1974 inflateEnd(&req->gzip_stream->zstream);
1975 }else if(zres != Z_OK) {
1976 WARN("inflate failed %d\n", zres);
1978 ret = ERROR_INTERNET_DECODING_FAILED;
1988 static void refill_gzip_buffer(http_request_t *req)
1993 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
1996 if(req->gzip_stream->buf_pos) {
1997 if(req->gzip_stream->buf_size)
1998 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
1999 req->gzip_stream->buf_pos = 0;
2002 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2003 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2004 if(res == ERROR_SUCCESS)
2005 req->gzip_stream->buf_size += len;
2008 /* return the size of data available to be read immediately (the read section must be held) */
2009 static DWORD get_avail_data( http_request_t *req )
2011 if (req->gzip_stream) {
2012 refill_gzip_buffer(req);
2013 return req->gzip_stream->buf_size;
2015 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2017 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2020 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2022 INTERNET_ASYNC_RESULT iar;
2027 EnterCriticalSection( &req->read_section );
2028 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2029 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2030 iar.dwError = first_notif ? 0 : get_avail_data(req);
2035 LeaveCriticalSection( &req->read_section );
2037 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2038 sizeof(INTERNET_ASYNC_RESULT));
2041 /* read data from the http connection (the read section must be held) */
2042 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2044 BOOL finished_reading = FALSE;
2045 int len, bytes_read = 0;
2046 DWORD ret = ERROR_SUCCESS;
2048 EnterCriticalSection( &req->read_section );
2050 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2052 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2055 if(req->gzip_stream) {
2056 if(req->gzip_stream->buf_size) {
2057 bytes_read = min(req->gzip_stream->buf_size, size);
2058 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2059 req->gzip_stream->buf_pos += bytes_read;
2060 req->gzip_stream->buf_size -= bytes_read;
2061 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2065 if(size > bytes_read) {
2066 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2067 if(ret == ERROR_SUCCESS)
2071 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2073 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2075 if (req->read_size) {
2076 bytes_read = min( req->read_size, size );
2077 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2078 remove_data( req, bytes_read );
2081 if (size > bytes_read && (!bytes_read || sync)) {
2082 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2083 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2085 /* always return success, even if the network layer returns an error */
2088 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2089 req->dwContentRead += bytes_read;
2094 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2095 LeaveCriticalSection( &req->read_section );
2097 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2099 DWORD dwBytesWritten;
2101 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2103 WARN("WriteFile failed: %u\n", GetLastError());
2106 if(finished_reading)
2107 HTTP_FinishedReading(req);
2113 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2115 http_request_t *req = (http_request_t*)hdr;
2116 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2119 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2121 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2122 http_request_t *req = (http_request_t*)workRequest->hdr;
2123 INTERNET_ASYNC_RESULT iar;
2126 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2128 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2129 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2131 iar.dwResult = res == ERROR_SUCCESS;
2134 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2135 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2136 sizeof(INTERNET_ASYNC_RESULT));
2139 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2140 DWORD flags, DWORD_PTR context)
2142 http_request_t *req = (http_request_t*)hdr;
2145 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2146 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2148 if (buffers->dwStructSize != sizeof(*buffers))
2149 return ERROR_INVALID_PARAMETER;
2151 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2153 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2155 WORKREQUEST workRequest;
2157 if (TryEnterCriticalSection( &req->read_section ))
2159 if (get_avail_data(req))
2161 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2162 &buffers->dwBufferLength, FALSE);
2163 LeaveCriticalSection( &req->read_section );
2166 LeaveCriticalSection( &req->read_section );
2169 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2170 workRequest.hdr = WININET_AddRef(&req->hdr);
2171 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2173 INTERNET_AsyncCall(&workRequest);
2175 return ERROR_IO_PENDING;
2178 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2179 !(flags & IRF_NO_WAIT));
2182 if (res == ERROR_SUCCESS) {
2183 DWORD size = buffers->dwBufferLength;
2184 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2185 &size, sizeof(size));
2191 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2193 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2194 http_request_t *req = (http_request_t*)workRequest->hdr;
2195 INTERNET_ASYNC_RESULT iar;
2198 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2200 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2201 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2203 iar.dwResult = res == ERROR_SUCCESS;
2206 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2207 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2208 sizeof(INTERNET_ASYNC_RESULT));
2211 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2212 DWORD flags, DWORD_PTR context)
2215 http_request_t *req = (http_request_t*)hdr;
2218 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2219 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2221 if (buffers->dwStructSize != sizeof(*buffers))
2222 return ERROR_INVALID_PARAMETER;
2224 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2226 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2228 WORKREQUEST workRequest;
2230 if (TryEnterCriticalSection( &req->read_section ))
2232 if (get_avail_data(req))
2234 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2235 &buffers->dwBufferLength, FALSE);
2236 LeaveCriticalSection( &req->read_section );
2239 LeaveCriticalSection( &req->read_section );
2242 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2243 workRequest.hdr = WININET_AddRef(&req->hdr);
2244 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2246 INTERNET_AsyncCall(&workRequest);
2248 return ERROR_IO_PENDING;
2251 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2252 !(flags & IRF_NO_WAIT));
2255 if (res == ERROR_SUCCESS) {
2256 DWORD size = buffers->dwBufferLength;
2257 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2258 &size, sizeof(size));
2264 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2267 http_request_t *lpwhr = (http_request_t*)hdr;
2269 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2272 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2273 if (res == ERROR_SUCCESS)
2274 lpwhr->dwBytesWritten += *written;
2276 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2280 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2282 http_request_t *req = (http_request_t*)workRequest->hdr;
2284 HTTP_ReceiveRequestData(req, FALSE);
2287 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2289 http_request_t *req = (http_request_t*)hdr;
2291 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2293 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2295 WORKREQUEST workRequest;
2297 /* never wait, if we can't enter the section we queue an async request right away */
2298 if (TryEnterCriticalSection( &req->read_section ))
2300 if ((*available = get_avail_data( req ))) goto done;
2301 if (end_of_read_data( req )) goto done;
2302 LeaveCriticalSection( &req->read_section );
2305 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2306 workRequest.hdr = WININET_AddRef( &req->hdr );
2308 INTERNET_AsyncCall(&workRequest);
2310 return ERROR_IO_PENDING;
2313 EnterCriticalSection( &req->read_section );
2315 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2317 refill_buffer( req );
2318 *available = get_avail_data( req );
2322 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2325 if (NETCON_query_data_available(&req->netConnection, &extra))
2326 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2328 LeaveCriticalSection( &req->read_section );
2330 TRACE( "returning %u\n", *available );
2331 return ERROR_SUCCESS;
2334 static const object_vtbl_t HTTPREQVtbl = {
2336 HTTPREQ_CloseConnection,
2337 HTTPREQ_QueryOption,
2340 HTTPREQ_ReadFileExA,
2341 HTTPREQ_ReadFileExW,
2343 HTTPREQ_QueryDataAvailable,
2347 /***********************************************************************
2348 * HTTP_HttpOpenRequestW (internal)
2350 * Open a HTTP request handle
2353 * HINTERNET a HTTP request handle on success
2357 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2358 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2359 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2360 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2362 appinfo_t *hIC = NULL;
2363 http_request_t *lpwhr;
2364 LPWSTR lpszHostName = NULL;
2365 HINTERNET handle = NULL;
2366 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2371 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2372 hIC = lpwhs->lpAppInfo;
2374 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2377 res = ERROR_OUTOFMEMORY;
2380 lpwhr->hdr.htype = WH_HHTTPREQ;
2381 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2382 lpwhr->hdr.dwFlags = dwFlags;
2383 lpwhr->hdr.dwContext = dwContext;
2384 lpwhr->hdr.refs = 1;
2385 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2386 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2387 lpwhr->dwContentLength = ~0u;
2388 InitializeCriticalSection( &lpwhr->read_section );
2390 WININET_AddRef( &lpwhs->hdr );
2391 lpwhr->lpHttpSession = lpwhs;
2392 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2394 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2395 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2396 if (NULL == lpszHostName)
2398 res = ERROR_OUTOFMEMORY;
2402 handle = WININET_AllocHandle( &lpwhr->hdr );
2405 res = ERROR_OUTOFMEMORY;
2409 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2411 InternetCloseHandle( handle );
2416 if (lpszObjectName && *lpszObjectName) {
2420 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2421 if (rc != E_POINTER)
2422 len = strlenW(lpszObjectName)+1;
2423 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2424 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2425 URL_ESCAPE_SPACES_ONLY);
2428 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2429 strcpyW(lpwhr->lpszPath,lpszObjectName);
2432 static const WCHAR slashW[] = {'/',0};
2434 lpwhr->lpszPath = heap_strdupW(slashW);
2437 if (lpszReferrer && *lpszReferrer)
2438 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2440 if (lpszAcceptTypes)
2443 for (i = 0; lpszAcceptTypes[i]; i++)
2445 if (!*lpszAcceptTypes[i]) continue;
2446 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2447 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2448 HTTP_ADDHDR_FLAG_REQ |
2449 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2453 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2454 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2456 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2457 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2458 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2460 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2461 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2462 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2465 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2466 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2468 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2469 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2470 INTERNET_DEFAULT_HTTPS_PORT :
2471 INTERNET_DEFAULT_HTTP_PORT);
2473 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2474 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2475 INTERNET_DEFAULT_HTTPS_PORT :
2476 INTERNET_DEFAULT_HTTP_PORT);
2478 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2479 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2481 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2482 INTERNET_STATUS_HANDLE_CREATED, &handle,
2486 HeapFree(GetProcessHeap(), 0, lpszHostName);
2488 WININET_Release( &lpwhr->hdr );
2490 TRACE("<-- %p (%p)\n", handle, lpwhr);
2495 /***********************************************************************
2496 * HttpOpenRequestW (WININET.@)
2498 * Open a HTTP request handle
2501 * HINTERNET a HTTP request handle on success
2505 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2506 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2507 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2508 DWORD dwFlags, DWORD_PTR dwContext)
2510 http_session_t *lpwhs;
2511 HINTERNET handle = NULL;
2514 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2515 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2516 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2517 dwFlags, dwContext);
2518 if(lpszAcceptTypes!=NULL)
2521 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2522 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2525 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2526 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2528 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2533 * My tests seem to show that the windows version does not
2534 * become asynchronous until after this point. And anyhow
2535 * if this call was asynchronous then how would you get the
2536 * necessary HINTERNET pointer returned by this function.
2539 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2540 lpszVersion, lpszReferrer, lpszAcceptTypes,
2541 dwFlags, dwContext, &handle);
2544 WININET_Release( &lpwhs->hdr );
2545 TRACE("returning %p\n", handle);
2546 if(res != ERROR_SUCCESS)
2551 /* read any content returned by the server so that the connection can be
2553 static void HTTP_DrainContent(http_request_t *req)
2557 if (!NETCON_connected(&req->netConnection)) return;
2559 if (req->dwContentLength == -1)
2561 NETCON_close(&req->netConnection);
2564 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2569 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2571 } while (bytes_read);
2574 static const LPCWSTR header_lookup[] = {
2575 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2576 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2577 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2578 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2579 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2580 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2581 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2582 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2583 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2584 szDate, /* HTTP_QUERY_DATE = 9 */
2585 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2586 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2587 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2588 szURI, /* HTTP_QUERY_URI = 13 */
2589 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2590 NULL, /* HTTP_QUERY_COST = 15 */
2591 NULL, /* HTTP_QUERY_LINK = 16 */
2592 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2593 NULL, /* HTTP_QUERY_VERSION = 18 */
2594 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2595 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2596 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2597 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2598 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2599 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2600 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2601 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2602 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2603 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2604 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2605 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2606 NULL, /* HTTP_QUERY_FROM = 31 */
2607 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2608 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2609 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2610 szReferer, /* HTTP_QUERY_REFERER = 35 */
2611 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2612 szServer, /* HTTP_QUERY_SERVER = 37 */
2613 NULL, /* HTTP_TITLE = 38 */
2614 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2615 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2616 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2617 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2618 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2619 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2620 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2621 NULL, /* HTTP_QUERY_REFRESH = 46 */
2622 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2623 szAge, /* HTTP_QUERY_AGE = 48 */
2624 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2625 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2626 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2627 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2628 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2629 szETag, /* HTTP_QUERY_ETAG = 54 */
2630 hostW, /* HTTP_QUERY_HOST = 55 */
2631 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2632 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2633 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2634 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2635 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2636 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2637 szRange, /* HTTP_QUERY_RANGE = 62 */
2638 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2639 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2640 szVary, /* HTTP_QUERY_VARY = 65 */
2641 szVia, /* HTTP_QUERY_VIA = 66 */
2642 szWarning, /* HTTP_QUERY_WARNING = 67 */
2643 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2644 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2645 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2648 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2650 /***********************************************************************
2651 * HTTP_HttpQueryInfoW (internal)
2653 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2654 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2656 LPHTTPHEADERW lphttpHdr = NULL;
2657 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2658 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2659 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2662 /* Find requested header structure */
2665 case HTTP_QUERY_CUSTOM:
2666 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2667 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2669 case HTTP_QUERY_RAW_HEADERS_CRLF:
2673 DWORD res = ERROR_INVALID_PARAMETER;
2676 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2678 headers = lpwhr->lpszRawHeaders;
2681 len = strlenW(headers) * sizeof(WCHAR);
2683 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2685 len += sizeof(WCHAR);
2686 res = ERROR_INSUFFICIENT_BUFFER;
2691 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2694 len = strlenW(szCrLf) * sizeof(WCHAR);
2695 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2697 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2698 res = ERROR_SUCCESS;
2700 *lpdwBufferLength = len;
2703 HeapFree(GetProcessHeap(), 0, headers);
2706 case HTTP_QUERY_RAW_HEADERS:
2708 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2710 LPWSTR pszString = lpBuffer;
2712 for (i = 0; ppszRawHeaderLines[i]; i++)
2713 size += strlenW(ppszRawHeaderLines[i]) + 1;
2715 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2717 HTTP_FreeTokens(ppszRawHeaderLines);
2718 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2719 return ERROR_INSUFFICIENT_BUFFER;
2723 for (i = 0; ppszRawHeaderLines[i]; i++)
2725 DWORD len = strlenW(ppszRawHeaderLines[i]);
2726 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2730 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2732 *lpdwBufferLength = size * sizeof(WCHAR);
2733 HTTP_FreeTokens(ppszRawHeaderLines);
2735 return ERROR_SUCCESS;
2737 case HTTP_QUERY_STATUS_TEXT:
2738 if (lpwhr->lpszStatusText)
2740 DWORD len = strlenW(lpwhr->lpszStatusText);
2741 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2743 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2744 return ERROR_INSUFFICIENT_BUFFER;
2748 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2749 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2751 *lpdwBufferLength = len * sizeof(WCHAR);
2752 return ERROR_SUCCESS;
2755 case HTTP_QUERY_VERSION:
2756 if (lpwhr->lpszVersion)
2758 DWORD len = strlenW(lpwhr->lpszVersion);
2759 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2761 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2762 return ERROR_INSUFFICIENT_BUFFER;
2766 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2767 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2769 *lpdwBufferLength = len * sizeof(WCHAR);
2770 return ERROR_SUCCESS;
2773 case HTTP_QUERY_CONTENT_ENCODING:
2774 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2775 requested_index,request_only);
2778 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2780 if (level < LAST_TABLE_HEADER && header_lookup[level])
2781 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2782 requested_index,request_only);
2786 lphttpHdr = &lpwhr->pCustHeaders[index];
2788 /* Ensure header satisfies requested attributes */
2790 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2791 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2793 return ERROR_HTTP_HEADER_NOT_FOUND;
2796 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2798 /* coalesce value to requested type */
2799 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2801 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2802 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2804 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2810 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2812 tmpTM = *gmtime(&tmpTime);
2813 STHook = (SYSTEMTIME *)lpBuffer;
2814 STHook->wDay = tmpTM.tm_mday;
2815 STHook->wHour = tmpTM.tm_hour;
2816 STHook->wMilliseconds = 0;
2817 STHook->wMinute = tmpTM.tm_min;
2818 STHook->wDayOfWeek = tmpTM.tm_wday;
2819 STHook->wMonth = tmpTM.tm_mon + 1;
2820 STHook->wSecond = tmpTM.tm_sec;
2821 STHook->wYear = tmpTM.tm_year;
2823 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2824 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2825 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2827 else if (lphttpHdr->lpszValue)
2829 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2831 if (len > *lpdwBufferLength)
2833 *lpdwBufferLength = len;
2834 return ERROR_INSUFFICIENT_BUFFER;
2838 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2839 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2841 *lpdwBufferLength = len - sizeof(WCHAR);
2843 return ERROR_SUCCESS;
2846 /***********************************************************************
2847 * HttpQueryInfoW (WININET.@)
2849 * Queries for information about an HTTP request
2856 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2857 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2859 http_request_t *lpwhr;
2862 if (TRACE_ON(wininet)) {
2863 #define FE(x) { x, #x }
2864 static const wininet_flag_info query_flags[] = {
2865 FE(HTTP_QUERY_MIME_VERSION),
2866 FE(HTTP_QUERY_CONTENT_TYPE),
2867 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2868 FE(HTTP_QUERY_CONTENT_ID),
2869 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2870 FE(HTTP_QUERY_CONTENT_LENGTH),
2871 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2872 FE(HTTP_QUERY_ALLOW),
2873 FE(HTTP_QUERY_PUBLIC),
2874 FE(HTTP_QUERY_DATE),
2875 FE(HTTP_QUERY_EXPIRES),
2876 FE(HTTP_QUERY_LAST_MODIFIED),
2877 FE(HTTP_QUERY_MESSAGE_ID),
2879 FE(HTTP_QUERY_DERIVED_FROM),
2880 FE(HTTP_QUERY_COST),
2881 FE(HTTP_QUERY_LINK),
2882 FE(HTTP_QUERY_PRAGMA),
2883 FE(HTTP_QUERY_VERSION),
2884 FE(HTTP_QUERY_STATUS_CODE),
2885 FE(HTTP_QUERY_STATUS_TEXT),
2886 FE(HTTP_QUERY_RAW_HEADERS),
2887 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2888 FE(HTTP_QUERY_CONNECTION),
2889 FE(HTTP_QUERY_ACCEPT),
2890 FE(HTTP_QUERY_ACCEPT_CHARSET),
2891 FE(HTTP_QUERY_ACCEPT_ENCODING),
2892 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2893 FE(HTTP_QUERY_AUTHORIZATION),
2894 FE(HTTP_QUERY_CONTENT_ENCODING),
2895 FE(HTTP_QUERY_FORWARDED),
2896 FE(HTTP_QUERY_FROM),
2897 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2898 FE(HTTP_QUERY_LOCATION),
2899 FE(HTTP_QUERY_ORIG_URI),
2900 FE(HTTP_QUERY_REFERER),
2901 FE(HTTP_QUERY_RETRY_AFTER),
2902 FE(HTTP_QUERY_SERVER),
2903 FE(HTTP_QUERY_TITLE),
2904 FE(HTTP_QUERY_USER_AGENT),
2905 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2906 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2907 FE(HTTP_QUERY_ACCEPT_RANGES),
2908 FE(HTTP_QUERY_SET_COOKIE),
2909 FE(HTTP_QUERY_COOKIE),
2910 FE(HTTP_QUERY_REQUEST_METHOD),
2911 FE(HTTP_QUERY_REFRESH),
2912 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2914 FE(HTTP_QUERY_CACHE_CONTROL),
2915 FE(HTTP_QUERY_CONTENT_BASE),
2916 FE(HTTP_QUERY_CONTENT_LOCATION),
2917 FE(HTTP_QUERY_CONTENT_MD5),
2918 FE(HTTP_QUERY_CONTENT_RANGE),
2919 FE(HTTP_QUERY_ETAG),
2920 FE(HTTP_QUERY_HOST),
2921 FE(HTTP_QUERY_IF_MATCH),
2922 FE(HTTP_QUERY_IF_NONE_MATCH),
2923 FE(HTTP_QUERY_IF_RANGE),
2924 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2925 FE(HTTP_QUERY_MAX_FORWARDS),
2926 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2927 FE(HTTP_QUERY_RANGE),
2928 FE(HTTP_QUERY_TRANSFER_ENCODING),
2929 FE(HTTP_QUERY_UPGRADE),
2930 FE(HTTP_QUERY_VARY),
2932 FE(HTTP_QUERY_WARNING),
2933 FE(HTTP_QUERY_CUSTOM)
2935 static const wininet_flag_info modifier_flags[] = {
2936 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2937 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2938 FE(HTTP_QUERY_FLAG_NUMBER),
2939 FE(HTTP_QUERY_FLAG_COALESCE)
2942 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2943 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2946 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2947 TRACE(" Attribute:");
2948 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2949 if (query_flags[i].val == info) {
2950 TRACE(" %s", query_flags[i].name);
2954 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2955 TRACE(" Unknown (%08x)", info);
2958 TRACE(" Modifier:");
2959 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2960 if (modifier_flags[i].val & info_mod) {
2961 TRACE(" %s", modifier_flags[i].name);
2962 info_mod &= ~ modifier_flags[i].val;
2967 TRACE(" Unknown (%08x)", info_mod);
2972 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2973 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2975 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2979 if (lpBuffer == NULL)
2980 *lpdwBufferLength = 0;
2981 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2982 lpBuffer, lpdwBufferLength, lpdwIndex);
2986 WININET_Release( &lpwhr->hdr );
2988 TRACE("%u <--\n", res);
2989 if(res != ERROR_SUCCESS)
2991 return res == ERROR_SUCCESS;
2994 /***********************************************************************
2995 * HttpQueryInfoA (WININET.@)
2997 * Queries for information about an HTTP request
3004 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3005 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3011 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3012 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3014 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3015 lpdwBufferLength, lpdwIndex );
3021 len = (*lpdwBufferLength)*sizeof(WCHAR);
3022 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3024 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3030 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3031 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3032 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3033 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3040 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3044 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3045 lpBuffer, *lpdwBufferLength, NULL, NULL );
3046 *lpdwBufferLength = len - 1;
3048 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3051 /* since the strings being returned from HttpQueryInfoW should be
3052 * only ASCII characters, it is reasonable to assume that all of
3053 * the Unicode characters can be reduced to a single byte */
3054 *lpdwBufferLength = len / sizeof(WCHAR);
3056 HeapFree(GetProcessHeap(), 0, bufferW );
3061 /***********************************************************************
3062 * HTTP_GetRedirectURL (internal)
3064 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3066 static WCHAR szHttp[] = {'h','t','t','p',0};
3067 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3068 http_session_t *lpwhs = lpwhr->lpHttpSession;
3069 URL_COMPONENTSW urlComponents;
3070 DWORD url_length = 0;
3072 LPWSTR combined_url;
3074 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3075 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3076 urlComponents.dwSchemeLength = 0;
3077 urlComponents.lpszHostName = lpwhs->lpszHostName;
3078 urlComponents.dwHostNameLength = 0;
3079 urlComponents.nPort = lpwhs->nHostPort;
3080 urlComponents.lpszUserName = lpwhs->lpszUserName;
3081 urlComponents.dwUserNameLength = 0;
3082 urlComponents.lpszPassword = NULL;
3083 urlComponents.dwPasswordLength = 0;
3084 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3085 urlComponents.dwUrlPathLength = 0;
3086 urlComponents.lpszExtraInfo = NULL;
3087 urlComponents.dwExtraInfoLength = 0;
3089 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3090 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3093 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3095 /* convert from bytes to characters */
3096 url_length = url_length / sizeof(WCHAR) - 1;
3097 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3099 HeapFree(GetProcessHeap(), 0, orig_url);
3104 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3105 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3107 HeapFree(GetProcessHeap(), 0, orig_url);
3110 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3112 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3114 HeapFree(GetProcessHeap(), 0, orig_url);
3115 HeapFree(GetProcessHeap(), 0, combined_url);
3118 HeapFree(GetProcessHeap(), 0, orig_url);
3119 return combined_url;
3123 /***********************************************************************
3124 * HTTP_HandleRedirect (internal)
3126 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3128 http_session_t *lpwhs = lpwhr->lpHttpSession;
3129 appinfo_t *hIC = lpwhs->lpAppInfo;
3130 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3131 WCHAR path[INTERNET_MAX_URL_LENGTH];
3136 /* if it's an absolute path, keep the same session info */
3137 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3141 URL_COMPONENTSW urlComponents;
3142 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3143 static WCHAR szHttp[] = {'h','t','t','p',0};
3144 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3150 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3151 urlComponents.lpszScheme = protocol;
3152 urlComponents.dwSchemeLength = 32;
3153 urlComponents.lpszHostName = hostName;
3154 urlComponents.dwHostNameLength = MAXHOSTNAME;
3155 urlComponents.lpszUserName = userName;
3156 urlComponents.dwUserNameLength = 1024;
3157 urlComponents.lpszPassword = NULL;
3158 urlComponents.dwPasswordLength = 0;
3159 urlComponents.lpszUrlPath = path;
3160 urlComponents.dwUrlPathLength = 2048;
3161 urlComponents.lpszExtraInfo = NULL;
3162 urlComponents.dwExtraInfoLength = 0;
3163 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3164 return INTERNET_GetLastError();
3166 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3167 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3169 TRACE("redirect from secure page to non-secure page\n");
3170 /* FIXME: warn about from secure redirect to non-secure page */
3171 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3173 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3174 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3176 TRACE("redirect from non-secure page to secure page\n");
3177 /* FIXME: notify about redirect to secure page */
3178 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3181 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3183 if (lstrlenW(protocol)>4) /*https*/
3184 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3186 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3191 * This upsets redirects to binary files on sourceforge.net
3192 * and gives an html page instead of the target file
3193 * Examination of the HTTP request sent by native wininet.dll
3194 * reveals that it doesn't send a referrer in that case.
3195 * Maybe there's a flag that enables this, or maybe a referrer
3196 * shouldn't be added in case of a redirect.
3199 /* consider the current host as the referrer */
3200 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3201 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3202 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3203 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3206 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3207 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3208 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3211 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3212 len = lstrlenW(hostName);
3213 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3214 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3215 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3218 lpwhs->lpszHostName = heap_strdupW(hostName);
3220 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3222 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3223 lpwhs->lpszUserName = NULL;
3225 lpwhs->lpszUserName = heap_strdupW(userName);
3229 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3233 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3234 lpwhs->lpszServerName = heap_strdupW(hostName);
3235 lpwhs->nServerPort = urlComponents.nPort;
3237 NETCON_close(&lpwhr->netConnection);
3238 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3241 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3242 if (res != ERROR_SUCCESS)
3245 lpwhr->read_pos = lpwhr->read_size = 0;
3246 lpwhr->read_chunked = FALSE;
3250 TRACE("Redirect through proxy\n");
3253 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3254 lpwhr->lpszPath=NULL;
3260 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3261 if (rc != E_POINTER)
3262 needed = strlenW(path)+1;
3263 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3264 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3265 URL_ESCAPE_SPACES_ONLY);
3268 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3269 strcpyW(lpwhr->lpszPath,path);
3273 /* Remove custom content-type/length headers on redirects. */
3274 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3276 HTTP_DeleteCustomHeader(lpwhr, index);
3277 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3279 HTTP_DeleteCustomHeader(lpwhr, index);
3281 return ERROR_SUCCESS;
3284 /***********************************************************************
3285 * HTTP_build_req (internal)
3287 * concatenate all the strings in the request together
3289 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3294 for( t = list; *t ; t++ )
3295 len += strlenW( *t );
3298 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3301 for( t = list; *t ; t++ )
3307 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3310 LPWSTR requestString;
3316 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3317 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3318 http_session_t *lpwhs = lpwhr->lpHttpSession;
3322 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3323 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3324 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3325 HeapFree( GetProcessHeap(), 0, lpszPath );
3327 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3328 NULL, 0, NULL, NULL );
3329 len--; /* the nul terminator isn't needed */
3330 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3331 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3332 ascii_req, len, NULL, NULL );
3333 HeapFree( GetProcessHeap(), 0, requestString );
3335 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3337 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3338 HeapFree( GetProcessHeap(), 0, ascii_req );
3339 if (res != ERROR_SUCCESS)
3342 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3344 return ERROR_HTTP_INVALID_HEADER;
3346 return ERROR_SUCCESS;
3349 static void HTTP_InsertCookies(http_request_t *lpwhr)
3351 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3352 LPWSTR lpszCookies, lpszUrl = NULL;
3353 DWORD nCookieSize, size;
3354 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3356 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3357 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3358 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3360 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3363 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3365 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3366 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3368 cnt += sprintfW(lpszCookies, szCookie);
3369 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3370 strcatW(lpszCookies, szCrLf);
3372 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3373 HeapFree(GetProcessHeap(), 0, lpszCookies);
3376 HeapFree(GetProcessHeap(), 0, lpszUrl);
3379 /***********************************************************************
3380 * HTTP_HttpSendRequestW (internal)
3382 * Sends the specified request to the HTTP server
3389 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3390 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3391 DWORD dwContentLength, BOOL bEndRequest)
3394 BOOL redirected = FALSE;
3395 LPWSTR requestString = NULL;
3398 INTERNET_ASYNC_RESULT iar;
3399 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3400 static const WCHAR szContentLength[] =
3401 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3402 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3405 TRACE("--> %p\n", lpwhr);
3407 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3409 /* if the verb is NULL default to GET */
3410 if (!lpwhr->lpszVerb)
3411 lpwhr->lpszVerb = heap_strdupW(szGET);
3413 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3415 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3416 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3417 lpwhr->dwBytesToWrite = dwContentLength;
3419 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3421 WCHAR *agent_header;
3422 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3425 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3426 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3427 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3429 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3430 HeapFree(GetProcessHeap(), 0, agent_header);
3432 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3434 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3435 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3437 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3439 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3440 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3441 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3451 /* like native, just in case the caller forgot to call InternetReadFile
3452 * for all the data */
3453 HTTP_DrainContent(lpwhr);
3454 lpwhr->dwContentRead = 0;
3456 if (TRACE_ON(wininet))
3458 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3459 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3463 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3465 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3467 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3468 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3470 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3471 HTTP_InsertCookies(lpwhr);
3473 /* add the headers the caller supplied */
3474 if( lpszHeaders && dwHeaderLength )
3476 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3477 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3480 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3482 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3483 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3484 HeapFree(GetProcessHeap(), 0, url);
3487 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3490 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3492 /* Send the request and store the results */
3493 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3496 /* send the request as ASCII, tack on the optional data */
3497 if (!lpOptional || redirected)
3498 dwOptionalLength = 0;
3499 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3500 NULL, 0, NULL, NULL );
3501 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3502 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3503 ascii_req, len, NULL, NULL );
3505 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3506 len = (len + dwOptionalLength - 1);
3508 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3510 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3511 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3513 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3514 HeapFree( GetProcessHeap(), 0, ascii_req );
3516 lpwhr->dwBytesWritten = dwOptionalLength;
3518 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3519 INTERNET_STATUS_REQUEST_SENT,
3520 &len, sizeof(DWORD));
3527 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3528 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3530 if (res != ERROR_SUCCESS)
3533 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3535 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3536 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3539 HTTP_ProcessCookies(lpwhr);
3541 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3543 dwBufferSize = sizeof(dwStatusCode);
3544 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3545 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3548 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3550 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3551 dwBufferSize=sizeof(szNewLocation);
3552 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3553 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3555 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3557 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3558 lpwhr->lpszVerb = heap_strdupW(szGET);
3560 HTTP_DrainContent(lpwhr);
3561 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3563 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3564 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3565 res = HTTP_HandleRedirect(lpwhr, new_url);
3566 if (res == ERROR_SUCCESS)
3568 HeapFree(GetProcessHeap(), 0, requestString);
3571 HeapFree( GetProcessHeap(), 0, new_url );
3576 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3578 WCHAR szAuthValue[2048];
3580 if (dwStatusCode == HTTP_STATUS_DENIED)
3582 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3584 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3586 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3588 lpwhr->lpHttpSession->lpszUserName,
3589 lpwhr->lpHttpSession->lpszPassword,
3597 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3600 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3602 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3603 &lpwhr->pProxyAuthInfo,
3604 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3605 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3616 res = ERROR_SUCCESS;
3620 if(res == ERROR_SUCCESS) {
3621 WCHAR url[INTERNET_MAX_URL_LENGTH];
3622 WCHAR cacheFileName[MAX_PATH+1];
3625 b = HTTP_GetRequestURL(lpwhr, url);
3627 WARN("Could not get URL\n");
3631 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3633 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3634 CloseHandle(lpwhr->hCacheFile);
3636 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3637 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3638 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3639 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3640 WARN("Could not create file: %u\n", GetLastError());
3641 lpwhr->hCacheFile = NULL;
3644 WARN("Could not create cache entry: %08x\n", GetLastError());
3650 HeapFree(GetProcessHeap(), 0, requestString);
3652 /* TODO: send notification for P3P header */
3654 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3656 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3657 HTTP_ReceiveRequestData(lpwhr, TRUE);
3660 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3663 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3664 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3665 sizeof(INTERNET_ASYNC_RESULT));
3673 /***********************************************************************
3675 * Helper functions for the HttpSendRequest(Ex) functions
3678 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3680 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3681 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3683 TRACE("%p\n", lpwhr);
3685 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3686 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3687 req->dwContentLength, req->bEndRequest);
3689 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3693 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3697 INTERNET_ASYNC_RESULT iar;
3698 DWORD res = ERROR_SUCCESS;
3700 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3701 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3703 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3705 res = ERROR_HTTP_HEADER_NOT_FOUND;
3707 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3708 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3710 /* process cookies here. Is this right? */
3711 HTTP_ProcessCookies(lpwhr);
3713 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3715 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3717 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3718 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3719 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3721 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3722 dwBufferSize=sizeof(szNewLocation);
3723 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3725 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3727 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3728 lpwhr->lpszVerb = heap_strdupW(szGET);
3730 HTTP_DrainContent(lpwhr);
3731 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3733 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3734 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3735 res = HTTP_HandleRedirect(lpwhr, new_url);
3736 if (res == ERROR_SUCCESS)
3737 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3738 HeapFree( GetProcessHeap(), 0, new_url );
3744 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3747 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3748 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3749 sizeof(INTERNET_ASYNC_RESULT));
3753 /***********************************************************************
3754 * HttpEndRequestA (WININET.@)
3756 * Ends an HTTP request that was started by HttpSendRequestEx
3759 * TRUE if successful
3763 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3764 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3766 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3770 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3774 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3777 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3779 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3780 http_request_t *lpwhr = (http_request_t*)work->hdr;
3782 TRACE("%p\n", lpwhr);
3784 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3787 /***********************************************************************
3788 * HttpEndRequestW (WININET.@)
3790 * Ends an HTTP request that was started by HttpSendRequestEx
3793 * TRUE if successful
3797 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3798 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3800 http_request_t *lpwhr;
3807 SetLastError(ERROR_INVALID_PARAMETER);
3811 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3813 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3815 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3817 WININET_Release( &lpwhr->hdr );
3820 lpwhr->hdr.dwFlags |= dwFlags;
3822 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3825 struct WORKREQ_HTTPENDREQUESTW *request;
3827 work.asyncproc = AsyncHttpEndRequestProc;
3828 work.hdr = WININET_AddRef( &lpwhr->hdr );
3830 request = &work.u.HttpEndRequestW;
3831 request->dwFlags = dwFlags;
3832 request->dwContext = dwContext;
3834 INTERNET_AsyncCall(&work);
3835 res = ERROR_IO_PENDING;
3838 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3840 WININET_Release( &lpwhr->hdr );
3841 TRACE("%u <--\n", res);
3842 if(res != ERROR_SUCCESS)
3844 return res == ERROR_SUCCESS;
3847 /***********************************************************************
3848 * HttpSendRequestExA (WININET.@)
3850 * Sends the specified request to the HTTP server and allows chunked
3855 * Failure: FALSE, call GetLastError() for more information.
3857 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3858 LPINTERNET_BUFFERSA lpBuffersIn,
3859 LPINTERNET_BUFFERSA lpBuffersOut,
3860 DWORD dwFlags, DWORD_PTR dwContext)
3862 INTERNET_BUFFERSW BuffersInW;
3865 LPWSTR header = NULL;
3867 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3868 lpBuffersOut, dwFlags, dwContext);
3872 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3873 if (lpBuffersIn->lpcszHeader)
3875 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3876 lpBuffersIn->dwHeadersLength,0,0);
3877 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3878 if (!(BuffersInW.lpcszHeader = header))
3880 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3883 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3884 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3888 BuffersInW.lpcszHeader = NULL;
3889 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3890 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3891 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3892 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3893 BuffersInW.Next = NULL;
3896 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3898 HeapFree(GetProcessHeap(),0,header);
3903 /***********************************************************************
3904 * HttpSendRequestExW (WININET.@)
3906 * Sends the specified request to the HTTP server and allows chunked
3911 * Failure: FALSE, call GetLastError() for more information.
3913 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3914 LPINTERNET_BUFFERSW lpBuffersIn,
3915 LPINTERNET_BUFFERSW lpBuffersOut,
3916 DWORD dwFlags, DWORD_PTR dwContext)
3918 http_request_t *lpwhr;
3919 http_session_t *lpwhs;
3923 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3924 lpBuffersOut, dwFlags, dwContext);
3926 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3928 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3930 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3934 lpwhs = lpwhr->lpHttpSession;
3935 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3936 hIC = lpwhs->lpAppInfo;
3937 assert(hIC->hdr.htype == WH_HINIT);
3939 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3941 WORKREQUEST workRequest;
3942 struct WORKREQ_HTTPSENDREQUESTW *req;
3944 workRequest.asyncproc = AsyncHttpSendRequestProc;
3945 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3946 req = &workRequest.u.HttpSendRequestW;
3951 if (lpBuffersIn->lpcszHeader)
3953 if (lpBuffersIn->dwHeadersLength == ~0u)
3954 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3956 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3958 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3959 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3961 else req->lpszHeader = NULL;
3963 req->dwHeaderLength = size / sizeof(WCHAR);
3964 req->lpOptional = lpBuffersIn->lpvBuffer;
3965 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3966 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3970 req->lpszHeader = NULL;
3971 req->dwHeaderLength = 0;
3972 req->lpOptional = NULL;
3973 req->dwOptionalLength = 0;
3974 req->dwContentLength = 0;
3977 req->bEndRequest = FALSE;
3979 INTERNET_AsyncCall(&workRequest);
3981 * This is from windows.
3983 res = ERROR_IO_PENDING;
3988 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3989 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3990 lpBuffersIn->dwBufferTotal, FALSE);
3992 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3997 WININET_Release( &lpwhr->hdr );
4000 if(res != ERROR_SUCCESS)
4002 return res == ERROR_SUCCESS;
4005 /***********************************************************************
4006 * HttpSendRequestW (WININET.@)
4008 * Sends the specified request to the HTTP server
4015 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4016 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4018 http_request_t *lpwhr;
4019 http_session_t *lpwhs = NULL;
4020 appinfo_t *hIC = NULL;
4021 DWORD res = ERROR_SUCCESS;
4023 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4024 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4026 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4027 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4029 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4033 lpwhs = lpwhr->lpHttpSession;
4034 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4036 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4040 hIC = lpwhs->lpAppInfo;
4041 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4043 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4047 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4049 WORKREQUEST workRequest;
4050 struct WORKREQ_HTTPSENDREQUESTW *req;
4052 workRequest.asyncproc = AsyncHttpSendRequestProc;
4053 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4054 req = &workRequest.u.HttpSendRequestW;
4059 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4060 else size = dwHeaderLength * sizeof(WCHAR);
4062 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4063 memcpy(req->lpszHeader, lpszHeaders, size);
4066 req->lpszHeader = 0;
4067 req->dwHeaderLength = dwHeaderLength;
4068 req->lpOptional = lpOptional;
4069 req->dwOptionalLength = dwOptionalLength;
4070 req->dwContentLength = dwOptionalLength;
4071 req->bEndRequest = TRUE;
4073 INTERNET_AsyncCall(&workRequest);
4075 * This is from windows.
4077 res = ERROR_IO_PENDING;
4081 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4082 dwHeaderLength, lpOptional, dwOptionalLength,
4083 dwOptionalLength, TRUE);
4087 WININET_Release( &lpwhr->hdr );
4089 if(res != ERROR_SUCCESS)
4091 return res == ERROR_SUCCESS;
4094 /***********************************************************************
4095 * HttpSendRequestA (WININET.@)
4097 * Sends the specified request to the HTTP server
4104 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4105 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4108 LPWSTR szHeaders=NULL;
4109 DWORD nLen=dwHeaderLength;
4110 if(lpszHeaders!=NULL)
4112 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4113 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4114 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4116 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4117 HeapFree(GetProcessHeap(),0,szHeaders);
4121 /***********************************************************************
4122 * HTTPSESSION_Destroy (internal)
4124 * Deallocate session handle
4127 static void HTTPSESSION_Destroy(object_header_t *hdr)
4129 http_session_t *lpwhs = (http_session_t*) hdr;
4131 TRACE("%p\n", lpwhs);
4133 WININET_Release(&lpwhs->lpAppInfo->hdr);
4135 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4136 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4137 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4138 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4139 HeapFree(GetProcessHeap(), 0, lpwhs);
4142 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4145 case INTERNET_OPTION_HANDLE_TYPE:
4146 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4148 if (*size < sizeof(ULONG))
4149 return ERROR_INSUFFICIENT_BUFFER;
4151 *size = sizeof(DWORD);
4152 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4153 return ERROR_SUCCESS;
4156 return INET_QueryOption(option, buffer, size, unicode);
4159 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4161 http_session_t *ses = (http_session_t*)hdr;
4164 case INTERNET_OPTION_USERNAME:
4166 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4167 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4168 return ERROR_SUCCESS;
4170 case INTERNET_OPTION_PASSWORD:
4172 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4173 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4174 return ERROR_SUCCESS;
4179 return ERROR_INTERNET_INVALID_OPTION;
4182 static const object_vtbl_t HTTPSESSIONVtbl = {
4183 HTTPSESSION_Destroy,
4185 HTTPSESSION_QueryOption,
4186 HTTPSESSION_SetOption,
4195 /***********************************************************************
4196 * HTTP_Connect (internal)
4198 * Create http session handle
4201 * HINTERNET a session handle on success
4205 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4206 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4207 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4208 DWORD dwInternalFlags)
4210 http_session_t *lpwhs = NULL;
4211 HINTERNET handle = NULL;
4215 if (!lpszServerName || !lpszServerName[0])
4217 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4221 assert( hIC->hdr.htype == WH_HINIT );
4223 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4226 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4231 * According to my tests. The name is not resolved until a request is sent
4234 lpwhs->hdr.htype = WH_HHTTPSESSION;
4235 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4236 lpwhs->hdr.dwFlags = dwFlags;
4237 lpwhs->hdr.dwContext = dwContext;
4238 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4239 lpwhs->hdr.refs = 1;
4240 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4242 WININET_AddRef( &hIC->hdr );
4243 lpwhs->lpAppInfo = hIC;
4244 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4246 handle = WININET_AllocHandle( &lpwhs->hdr );
4249 ERR("Failed to alloc handle\n");
4250 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4254 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4255 if(strchrW(hIC->lpszProxy, ' '))
4256 FIXME("Several proxies not implemented.\n");
4257 if(hIC->lpszProxyBypass)
4258 FIXME("Proxy bypass is ignored.\n");
4260 if (lpszServerName && lpszServerName[0])
4262 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4263 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4265 if (lpszUserName && lpszUserName[0])
4266 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4267 if (lpszPassword && lpszPassword[0])
4268 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4269 lpwhs->nServerPort = nServerPort;
4270 lpwhs->nHostPort = nServerPort;
4272 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4273 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4275 INTERNET_SendCallback(&hIC->hdr, dwContext,
4276 INTERNET_STATUS_HANDLE_CREATED, &handle,
4282 WININET_Release( &lpwhs->hdr );
4285 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4289 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4294 /***********************************************************************
4295 * HTTP_OpenConnection (internal)
4297 * Connect to a web server
4304 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4306 http_session_t *lpwhs;
4307 appinfo_t *hIC = NULL;
4308 char szaddr[INET6_ADDRSTRLEN];
4310 DWORD res = ERROR_SUCCESS;
4315 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4317 res = ERROR_INVALID_PARAMETER;
4321 if (NETCON_connected(&lpwhr->netConnection))
4323 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4325 lpwhs = lpwhr->lpHttpSession;
4327 hIC = lpwhs->lpAppInfo;
4328 switch (lpwhs->socketAddress.ss_family)
4331 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4334 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4337 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4338 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4340 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4341 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4342 INTERNET_STATUS_CONNECTING_TO_SERVER,
4346 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4347 if (res != ERROR_SUCCESS)
4349 WARN("Socket creation failed: %u\n", res);
4353 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4355 if(res != ERROR_SUCCESS)
4358 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4360 /* Note: we differ from Microsoft's WinINet here. they seem to have
4361 * a bug that causes no status callbacks to be sent when starting
4362 * a tunnel to a proxy server using the CONNECT verb. i believe our
4363 * behaviour to be more correct and to not cause any incompatibilities
4364 * because using a secure connection through a proxy server is a rare
4365 * case that would be hard for anyone to depend on */
4366 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4369 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4370 if(res != ERROR_SUCCESS)
4372 WARN("Couldn't connect securely to host\n");
4377 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4378 INTERNET_STATUS_CONNECTED_TO_SERVER,
4379 szaddr, strlen(szaddr)+1);
4382 lpwhr->read_pos = lpwhr->read_size = 0;
4383 lpwhr->read_chunked = FALSE;
4385 TRACE("%d <--\n", res);
4390 /***********************************************************************
4391 * HTTP_clear_response_headers (internal)
4393 * clear out any old response headers
4395 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4399 for( i=0; i<lpwhr->nCustHeaders; i++)
4401 if( !lpwhr->pCustHeaders[i].lpszField )
4403 if( !lpwhr->pCustHeaders[i].lpszValue )
4405 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4407 HTTP_DeleteCustomHeader( lpwhr, i );
4412 /***********************************************************************
4413 * HTTP_GetResponseHeaders (internal)
4415 * Read server response
4422 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4425 WCHAR buffer[MAX_REPLY_LEN];
4426 DWORD buflen = MAX_REPLY_LEN;
4427 BOOL bSuccess = FALSE;
4429 char bufferA[MAX_REPLY_LEN];
4430 LPWSTR status_code = NULL, status_text = NULL;
4431 DWORD cchMaxRawHeaders = 1024;
4432 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4434 DWORD cchRawHeaders = 0;
4435 BOOL codeHundred = FALSE;
4439 /* clear old response headers (eg. from a redirect response) */
4440 if (clear) HTTP_clear_response_headers( lpwhr );
4442 if (!NETCON_connected(&lpwhr->netConnection))
4446 static const WCHAR szHundred[] = {'1','0','0',0};
4448 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4450 buflen = MAX_REPLY_LEN;
4451 if (!read_line(lpwhr, bufferA, &buflen))
4454 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4455 /* check is this a status code line? */
4456 if (!strncmpW(buffer, g_szHttp1_0, 4))
4458 /* split the version from the status code */
4459 status_code = strchrW( buffer, ' ' );
4464 /* split the status code from the status text */
4465 status_text = strchrW( status_code, ' ' );
4470 TRACE("version [%s] status code [%s] status text [%s]\n",
4471 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4473 codeHundred = (!strcmpW(status_code, szHundred));
4475 else if (!codeHundred)
4477 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4480 } while (codeHundred);
4482 /* Add status code */
4483 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4484 HTTP_ADDHDR_FLAG_REPLACE);
4486 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4487 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4489 lpwhr->lpszVersion = heap_strdupW(buffer);
4490 lpwhr->lpszStatusText = heap_strdupW(status_text);
4492 /* Restore the spaces */
4493 *(status_code-1) = ' ';
4494 *(status_text-1) = ' ';
4496 /* regenerate raw headers */
4497 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4498 cchMaxRawHeaders *= 2;
4499 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4500 if (temp == NULL) goto lend;
4501 lpszRawHeaders = temp;
4502 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4503 cchRawHeaders += (buflen-1);
4504 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4505 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4506 lpszRawHeaders[cchRawHeaders] = '\0';
4508 /* Parse each response line */
4511 buflen = MAX_REPLY_LEN;
4512 if (read_line(lpwhr, bufferA, &buflen))
4514 LPWSTR * pFieldAndValue;
4516 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4518 if (!bufferA[0]) break;
4519 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4521 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4524 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4525 cchMaxRawHeaders *= 2;
4526 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4527 if (temp == NULL) goto lend;
4528 lpszRawHeaders = temp;
4529 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4530 cchRawHeaders += (buflen-1);
4531 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4532 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4533 lpszRawHeaders[cchRawHeaders] = '\0';
4535 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4536 HTTP_ADDREQ_FLAG_ADD );
4538 HTTP_FreeTokens(pFieldAndValue);
4549 /* make sure the response header is terminated with an empty line. Some apps really
4550 truly care about that empty line being there for some reason. Just add it to the
4552 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4554 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4555 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4556 if (temp == NULL) goto lend;
4557 lpszRawHeaders = temp;
4560 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4562 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4563 lpwhr->lpszRawHeaders = lpszRawHeaders;
4564 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4574 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4579 /***********************************************************************
4580 * HTTP_InterpretHttpHeader (internal)
4582 * Parse server response
4586 * Pointer to array of field, value, NULL on success.
4589 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4591 LPWSTR * pTokenPair;
4595 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4597 pszColon = strchrW(buffer, ':');
4598 /* must have two tokens */
4601 HTTP_FreeTokens(pTokenPair);
4603 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4607 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4610 HTTP_FreeTokens(pTokenPair);
4613 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4614 pTokenPair[0][pszColon - buffer] = '\0';
4618 len = strlenW(pszColon);
4619 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4622 HTTP_FreeTokens(pTokenPair);
4625 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4627 strip_spaces(pTokenPair[0]);
4628 strip_spaces(pTokenPair[1]);
4630 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4634 /***********************************************************************
4635 * HTTP_ProcessHeader (internal)
4637 * Stuff header into header tables according to <dwModifier>
4641 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4643 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4645 LPHTTPHEADERW lphttpHdr = NULL;
4647 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4648 DWORD res = ERROR_HTTP_INVALID_HEADER;
4650 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4652 /* REPLACE wins out over ADD */
4653 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4654 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4656 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4659 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4663 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4664 return ERROR_HTTP_INVALID_HEADER;
4665 lphttpHdr = &lpwhr->pCustHeaders[index];
4671 hdr.lpszField = (LPWSTR)field;
4672 hdr.lpszValue = (LPWSTR)value;
4673 hdr.wFlags = hdr.wCount = 0;
4675 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4676 hdr.wFlags |= HDR_ISREQUEST;
4678 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4680 /* no value to delete */
4681 else return ERROR_SUCCESS;
4683 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4684 lphttpHdr->wFlags |= HDR_ISREQUEST;
4686 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4688 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4690 HTTP_DeleteCustomHeader( lpwhr, index );
4696 hdr.lpszField = (LPWSTR)field;
4697 hdr.lpszValue = (LPWSTR)value;
4698 hdr.wFlags = hdr.wCount = 0;
4700 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4701 hdr.wFlags |= HDR_ISREQUEST;
4703 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4706 return ERROR_SUCCESS;
4708 else if (dwModifier & COALESCEFLAGS)
4713 INT origlen = strlenW(lphttpHdr->lpszValue);
4714 INT valuelen = strlenW(value);
4716 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4719 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4721 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4724 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4727 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4729 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4732 lphttpHdr->lpszValue = lpsztmp;
4733 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4736 lphttpHdr->lpszValue[origlen] = ch;
4738 lphttpHdr->lpszValue[origlen] = ' ';
4742 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4743 lphttpHdr->lpszValue[len] = '\0';
4744 res = ERROR_SUCCESS;
4748 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4749 res = ERROR_OUTOFMEMORY;
4752 TRACE("<-- %d\n", res);
4757 /***********************************************************************
4758 * HTTP_FinishedReading (internal)
4760 * Called when all content from server has been read by client.
4763 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4765 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4772 HTTPREQ_CloseConnection(&lpwhr->hdr);
4775 /* FIXME: store data in the URL cache here */
4781 /***********************************************************************
4782 * HTTP_GetCustomHeaderIndex (internal)
4784 * Return index of custom header from header array
4787 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4788 int requested_index, BOOL request_only)
4792 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4794 for (index = 0; index < lpwhr->nCustHeaders; index++)
4796 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4799 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4802 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4805 if (requested_index == 0)
4810 if (index >= lpwhr->nCustHeaders)
4813 TRACE("Return: %d\n", index);
4818 /***********************************************************************
4819 * HTTP_InsertCustomHeader (internal)
4821 * Insert header into array
4824 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4827 LPHTTPHEADERW lph = NULL;
4829 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4830 count = lpwhr->nCustHeaders + 1;
4832 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4834 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4837 return ERROR_OUTOFMEMORY;
4839 lpwhr->pCustHeaders = lph;
4840 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4841 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4842 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4843 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4844 lpwhr->nCustHeaders++;
4846 return ERROR_SUCCESS;
4850 /***********************************************************************
4851 * HTTP_DeleteCustomHeader (internal)
4853 * Delete header from array
4854 * If this function is called, the indexs may change.
4856 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4858 if( lpwhr->nCustHeaders <= 0 )
4860 if( index >= lpwhr->nCustHeaders )
4862 lpwhr->nCustHeaders--;
4864 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4865 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4867 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4868 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4869 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4875 /***********************************************************************
4876 * HTTP_VerifyValidHeader (internal)
4878 * Verify the given header is not invalid for the given http request
4881 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4883 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4884 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4885 return ERROR_HTTP_INVALID_HEADER;
4887 return ERROR_SUCCESS;
4890 /***********************************************************************
4891 * IsHostInProxyBypassList (@)
4896 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4898 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);