2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR szOK[] = {'O','K',0};
77 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf[] = {'\r','\n', 0};
87 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge[] = { 'A','g','e',0 };
93 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
105 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
132 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia[] = { 'V','i','a',0 };
137 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 struct gzip_stream_t {
182 typedef struct _authorizationData
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
192 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
194 static CRITICAL_SECTION authcache_cs;
195 static CRITICAL_SECTION_DEBUG critsect_debug =
198 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
199 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
201 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
203 static DWORD HTTP_OpenConnection(http_request_t *req);
204 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
205 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
206 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
207 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
208 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
209 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
210 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
211 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
212 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
213 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
214 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
215 static void HTTP_DrainContent(http_request_t *req);
216 static BOOL HTTP_FinishedReading(http_request_t *req);
218 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
221 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
222 if (HeaderIndex == -1)
225 return &req->pCustHeaders[HeaderIndex];
230 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
232 return HeapAlloc(GetProcessHeap(), 0, items*size);
235 static void wininet_zfree(voidpf opaque, voidpf address)
237 HeapFree(GetProcessHeap(), 0, address);
240 static void init_gzip_stream(http_request_t *req)
242 gzip_stream_t *gzip_stream;
245 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
246 gzip_stream->zstream.zalloc = wininet_zalloc;
247 gzip_stream->zstream.zfree = wininet_zfree;
248 gzip_stream->zstream.opaque = NULL;
249 gzip_stream->zstream.next_in = NULL;
250 gzip_stream->zstream.avail_in = 0;
251 gzip_stream->zstream.next_out = NULL;
252 gzip_stream->zstream.avail_out = 0;
253 gzip_stream->buf_pos = 0;
254 gzip_stream->buf_size = 0;
255 gzip_stream->end_of_data = FALSE;
257 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
259 ERR("inflateInit failed: %d\n", zres);
260 HeapFree(GetProcessHeap(), 0, gzip_stream);
264 req->gzip_stream = gzip_stream;
266 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
268 HTTP_DeleteCustomHeader(req, index);
273 static void init_gzip_stream(http_request_t *req)
275 ERR("gzip stream not supported, missing zlib.\n");
280 /* set the request content length based on the headers */
281 static DWORD set_content_length( http_request_t *lpwhr )
283 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
287 size = sizeof(lpwhr->dwContentLength);
288 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
289 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
290 lpwhr->dwContentLength = ~0u;
292 size = sizeof(encoding);
293 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
294 !strcmpiW(encoding, szChunked))
296 lpwhr->dwContentLength = ~0u;
297 lpwhr->read_chunked = TRUE;
300 if(lpwhr->decoding) {
303 static const WCHAR gzipW[] = {'g','z','i','p',0};
305 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
306 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
307 init_gzip_stream(lpwhr);
310 return lpwhr->dwContentLength;
313 /***********************************************************************
314 * HTTP_Tokenize (internal)
316 * Tokenize a string, allocating memory for the tokens.
318 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
320 LPWSTR * token_array;
327 /* empty string has no tokens */
331 for (i = 0; string[i]; i++)
333 if (!strncmpW(string+i, token_string, strlenW(token_string)))
337 /* we want to skip over separators, but not the null terminator */
338 for (j = 0; j < strlenW(token_string) - 1; j++)
346 /* add 1 for terminating NULL */
347 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
348 token_array[tokens] = NULL;
351 for (i = 0; i < tokens; i++)
354 next_token = strstrW(string, token_string);
355 if (!next_token) next_token = string+strlenW(string);
356 len = next_token - string;
357 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
358 memcpy(token_array[i], string, len*sizeof(WCHAR));
359 token_array[i][len] = '\0';
360 string = next_token+strlenW(token_string);
365 /***********************************************************************
366 * HTTP_FreeTokens (internal)
368 * Frees memory returned from HTTP_Tokenize.
370 static void HTTP_FreeTokens(LPWSTR * token_array)
373 for (i = 0; token_array[i]; i++)
374 HeapFree(GetProcessHeap(), 0, token_array[i]);
375 HeapFree(GetProcessHeap(), 0, token_array);
378 static void HTTP_FixURL(http_request_t *lpwhr)
380 static const WCHAR szSlash[] = { '/',0 };
381 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
383 /* If we don't have a path we set it to root */
384 if (NULL == lpwhr->lpszPath)
385 lpwhr->lpszPath = heap_strdupW(szSlash);
386 else /* remove \r and \n*/
388 int nLen = strlenW(lpwhr->lpszPath);
389 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
392 lpwhr->lpszPath[nLen]='\0';
394 /* Replace '\' with '/' */
397 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
401 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
402 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
403 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
405 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
406 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
408 strcpyW(fixurl + 1, lpwhr->lpszPath);
409 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
410 lpwhr->lpszPath = fixurl;
414 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
416 LPWSTR requestString;
422 static const WCHAR szSpace[] = { ' ',0 };
423 static const WCHAR szColon[] = { ':',' ',0 };
424 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
426 /* allocate space for an array of all the string pointers to be added */
427 len = (lpwhr->nCustHeaders)*4 + 10;
428 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
430 /* add the verb, path and HTTP version string */
438 /* Append custom request headers */
439 for (i = 0; i < lpwhr->nCustHeaders; i++)
441 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
444 req[n++] = lpwhr->pCustHeaders[i].lpszField;
446 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
448 TRACE("Adding custom header %s (%s)\n",
449 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
450 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
455 ERR("oops. buffer overrun\n");
458 requestString = HTTP_build_req( req, 4 );
459 HeapFree( GetProcessHeap(), 0, req );
462 * Set (header) termination string for request
463 * Make sure there's exactly two new lines at the end of the request
465 p = &requestString[strlenW(requestString)-1];
466 while ( (*p == '\n') || (*p == '\r') )
468 strcpyW( p+1, sztwocrlf );
470 return requestString;
473 static void HTTP_ProcessCookies( http_request_t *lpwhr )
477 LPHTTPHEADERW setCookieHeader;
479 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
481 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
483 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
486 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
490 Host = HTTP_GetHeader(lpwhr, hostW);
491 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
492 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
493 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
494 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
496 HeapFree(GetProcessHeap(), 0, buf_url);
502 static void strip_spaces(LPWSTR start)
507 while (*str == ' ' && *str != '\0')
511 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
513 end = start + strlenW(start) - 1;
514 while (end >= start && *end == ' ')
521 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
523 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
524 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
526 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
527 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
528 if (is_basic && pszRealm)
531 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
535 token = strchrW(ptr,'=');
539 while (*realm == ' ' && *realm != '\0')
541 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
542 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
545 while (*token == ' ' && *token != '\0')
549 *pszRealm = heap_strdupW(token);
550 strip_spaces(*pszRealm);
557 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
559 if (!authinfo) return;
561 if (SecIsValidHandle(&authinfo->ctx))
562 DeleteSecurityContext(&authinfo->ctx);
563 if (SecIsValidHandle(&authinfo->cred))
564 FreeCredentialsHandle(&authinfo->cred);
566 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
567 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
568 HeapFree(GetProcessHeap(), 0, authinfo);
571 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
573 authorizationData *ad;
576 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
578 EnterCriticalSection(&authcache_cs);
579 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
581 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
583 TRACE("Authorization found in cache\n");
584 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
585 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
586 rc = ad->AuthorizationLen;
590 LeaveCriticalSection(&authcache_cs);
594 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
597 authorizationData* ad = NULL;
599 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
601 EnterCriticalSection(&authcache_cs);
602 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
604 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
605 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
614 TRACE("Found match in cache, replacing\n");
615 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
616 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
617 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
618 ad->AuthorizationLen = auth_data_len;
622 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
623 ad->lpszwHost = heap_strdupW(host);
624 ad->lpszwRealm = heap_strdupW(realm);
625 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
626 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
627 ad->AuthorizationLen = auth_data_len;
628 list_add_head(&basicAuthorizationCache,&ad->entry);
629 TRACE("authorization cached\n");
631 LeaveCriticalSection(&authcache_cs);
634 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
635 struct HttpAuthInfo **ppAuthInfo,
636 LPWSTR domain_and_username, LPWSTR password,
639 SECURITY_STATUS sec_status;
640 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
642 LPWSTR szRealm = NULL;
644 TRACE("%s\n", debugstr_w(pszAuthValue));
651 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
655 SecInvalidateHandle(&pAuthInfo->cred);
656 SecInvalidateHandle(&pAuthInfo->ctx);
657 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
659 pAuthInfo->auth_data = NULL;
660 pAuthInfo->auth_data_len = 0;
661 pAuthInfo->finished = FALSE;
663 if (is_basic_auth_value(pszAuthValue,NULL))
665 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
666 pAuthInfo->scheme = heap_strdupW(szBasic);
667 if (!pAuthInfo->scheme)
669 HeapFree(GetProcessHeap(), 0, pAuthInfo);
676 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
678 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
679 if (!pAuthInfo->scheme)
681 HeapFree(GetProcessHeap(), 0, pAuthInfo);
685 if (domain_and_username)
687 WCHAR *user = strchrW(domain_and_username, '\\');
688 WCHAR *domain = domain_and_username;
690 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
692 pAuthData = &nt_auth_identity;
697 user = domain_and_username;
701 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
702 nt_auth_identity.User = user;
703 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
704 nt_auth_identity.Domain = domain;
705 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
706 nt_auth_identity.Password = password;
707 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
710 /* use default credentials */
713 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
714 SECPKG_CRED_OUTBOUND, NULL,
716 NULL, &pAuthInfo->cred,
718 if (sec_status == SEC_E_OK)
720 PSecPkgInfoW sec_pkg_info;
721 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
722 if (sec_status == SEC_E_OK)
724 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
725 FreeContextBuffer(sec_pkg_info);
728 if (sec_status != SEC_E_OK)
730 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
731 debugstr_w(pAuthInfo->scheme), sec_status);
732 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
733 HeapFree(GetProcessHeap(), 0, pAuthInfo);
737 *ppAuthInfo = pAuthInfo;
739 else if (pAuthInfo->finished)
742 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
743 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
745 ERR("authentication scheme changed from %s to %s\n",
746 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
750 if (is_basic_auth_value(pszAuthValue,&szRealm))
754 char *auth_data = NULL;
755 UINT auth_data_len = 0;
757 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
759 if (!domain_and_username)
762 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
763 if (auth_data_len == 0)
765 HeapFree(GetProcessHeap(),0,szRealm);
771 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
772 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
774 /* length includes a nul terminator, which will be re-used for the ':' */
775 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
778 HeapFree(GetProcessHeap(),0,szRealm);
782 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
783 auth_data[userlen] = ':';
784 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
785 auth_data_len = userlen + 1 + passlen;
787 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
790 pAuthInfo->auth_data = auth_data;
791 pAuthInfo->auth_data_len = auth_data_len;
792 pAuthInfo->finished = TRUE;
793 HeapFree(GetProcessHeap(),0,szRealm);
800 SecBufferDesc out_desc, in_desc;
802 unsigned char *buffer;
803 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
804 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
806 in.BufferType = SECBUFFER_TOKEN;
810 in_desc.ulVersion = 0;
811 in_desc.cBuffers = 1;
812 in_desc.pBuffers = ∈
814 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
815 if (*pszAuthData == ' ')
818 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
819 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
820 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
823 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
825 out.BufferType = SECBUFFER_TOKEN;
826 out.cbBuffer = pAuthInfo->max_token;
827 out.pvBuffer = buffer;
829 out_desc.ulVersion = 0;
830 out_desc.cBuffers = 1;
831 out_desc.pBuffers = &out;
833 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
834 first ? NULL : &pAuthInfo->ctx,
835 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
836 context_req, 0, SECURITY_NETWORK_DREP,
837 in.pvBuffer ? &in_desc : NULL,
838 0, &pAuthInfo->ctx, &out_desc,
839 &pAuthInfo->attr, &pAuthInfo->exp);
840 if (sec_status == SEC_E_OK)
842 pAuthInfo->finished = TRUE;
843 pAuthInfo->auth_data = out.pvBuffer;
844 pAuthInfo->auth_data_len = out.cbBuffer;
845 TRACE("sending last auth packet\n");
847 else if (sec_status == SEC_I_CONTINUE_NEEDED)
849 pAuthInfo->auth_data = out.pvBuffer;
850 pAuthInfo->auth_data_len = out.cbBuffer;
851 TRACE("sending next auth packet\n");
855 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
856 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
857 destroy_authinfo(pAuthInfo);
866 /***********************************************************************
867 * HTTP_HttpAddRequestHeadersW (internal)
869 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
870 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
875 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
877 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
879 if( dwHeaderLength == ~0U )
880 len = strlenW(lpszHeader);
882 len = dwHeaderLength;
883 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
884 lstrcpynW( buffer, lpszHeader, len + 1);
890 LPWSTR * pFieldAndValue;
894 while (*lpszEnd != '\0')
896 if (*lpszEnd == '\r' || *lpszEnd == '\n')
901 if (*lpszStart == '\0')
904 if (*lpszEnd == '\r' || *lpszEnd == '\n')
907 lpszEnd++; /* Jump over newline */
909 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
910 if (*lpszStart == '\0')
912 /* Skip 0-length headers */
917 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
920 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
921 if (res == ERROR_SUCCESS)
922 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
923 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
924 HTTP_FreeTokens(pFieldAndValue);
928 } while (res == ERROR_SUCCESS);
930 HeapFree(GetProcessHeap(), 0, buffer);
935 /***********************************************************************
936 * HttpAddRequestHeadersW (WININET.@)
938 * Adds one or more HTTP header to the request handler
941 * On Windows if dwHeaderLength includes the trailing '\0', then
942 * HttpAddRequestHeadersW() adds it too. However this results in an
943 * invalid Http header which is rejected by some servers so we probably
944 * don't need to match Windows on that point.
951 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
952 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
954 http_request_t *lpwhr;
955 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
957 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
962 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
963 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
964 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
966 WININET_Release( &lpwhr->hdr );
968 if(res != ERROR_SUCCESS)
970 return res == ERROR_SUCCESS;
973 /***********************************************************************
974 * HttpAddRequestHeadersA (WININET.@)
976 * Adds one or more HTTP header to the request handler
983 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
984 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
990 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
992 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
993 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
994 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
995 if( dwHeaderLength != ~0U )
996 dwHeaderLength = len;
998 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1000 HeapFree( GetProcessHeap(), 0, hdr );
1005 /***********************************************************************
1006 * HttpOpenRequestA (WININET.@)
1008 * Open a HTTP request handle
1011 * HINTERNET a HTTP request handle on success
1015 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1016 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1017 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1018 DWORD dwFlags, DWORD_PTR dwContext)
1020 LPWSTR szVerb = NULL, szObjectName = NULL;
1021 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1022 INT acceptTypesCount;
1023 HINTERNET rc = FALSE;
1026 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1027 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1028 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1029 dwFlags, dwContext);
1033 szVerb = heap_strdupAtoW(lpszVerb);
1040 szObjectName = heap_strdupAtoW(lpszObjectName);
1041 if ( !szObjectName )
1047 szVersion = heap_strdupAtoW(lpszVersion);
1054 szReferrer = heap_strdupAtoW(lpszReferrer);
1059 if (lpszAcceptTypes)
1061 acceptTypesCount = 0;
1062 types = lpszAcceptTypes;
1067 /* find out how many there are */
1068 if (*types && **types)
1070 TRACE("accept type: %s\n", debugstr_a(*types));
1076 WARN("invalid accept type pointer\n");
1081 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1082 if (!szAcceptTypes) goto end;
1084 acceptTypesCount = 0;
1085 types = lpszAcceptTypes;
1090 if (*types && **types)
1091 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1095 /* ignore invalid pointer */
1100 szAcceptTypes[acceptTypesCount] = NULL;
1103 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1104 szVersion, szReferrer,
1105 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1110 acceptTypesCount = 0;
1111 while (szAcceptTypes[acceptTypesCount])
1113 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1116 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1118 HeapFree(GetProcessHeap(), 0, szReferrer);
1119 HeapFree(GetProcessHeap(), 0, szVersion);
1120 HeapFree(GetProcessHeap(), 0, szObjectName);
1121 HeapFree(GetProcessHeap(), 0, szVerb);
1126 /***********************************************************************
1129 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1132 static const CHAR HTTP_Base64Enc[] =
1133 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1137 /* first 6 bits, all from bin[0] */
1138 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1139 x = (bin[0] & 3) << 4;
1141 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1144 base64[n++] = HTTP_Base64Enc[x];
1149 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1150 x = ( bin[1] & 0x0f ) << 2;
1152 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1155 base64[n++] = HTTP_Base64Enc[x];
1159 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1161 /* last 6 bits, all from bin [2] */
1162 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1170 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1171 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1172 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1173 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1174 static const signed char HTTP_Base64Dec[256] =
1176 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1177 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1178 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1179 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1180 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1181 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1182 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1183 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1184 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1185 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1186 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1187 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1188 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1189 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1190 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1191 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1192 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1193 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1194 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1195 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1196 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1197 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1198 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1199 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1200 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1201 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1205 /***********************************************************************
1208 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1216 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1217 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1218 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1219 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1221 WARN("invalid base64: %s\n", debugstr_w(base64));
1225 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1228 if ((base64[2] == '=') && (base64[3] == '='))
1230 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1231 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1233 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1237 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1240 if (base64[3] == '=')
1242 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1243 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1245 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1249 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1258 /***********************************************************************
1259 * HTTP_InsertAuthorization
1261 * Insert or delete the authorization field in the request header.
1263 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1267 static const WCHAR wszSpace[] = {' ',0};
1268 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1270 WCHAR *authorization = NULL;
1272 if (pAuthInfo->auth_data_len)
1274 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1275 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1276 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1280 strcpyW(authorization, pAuthInfo->scheme);
1281 strcatW(authorization, wszSpace);
1282 HTTP_EncodeBase64(pAuthInfo->auth_data,
1283 pAuthInfo->auth_data_len,
1284 authorization+strlenW(authorization));
1286 /* clear the data as it isn't valid now that it has been sent to the
1287 * server, unless it's Basic authentication which doesn't do
1288 * connection tracking */
1289 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1291 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1292 pAuthInfo->auth_data = NULL;
1293 pAuthInfo->auth_data_len = 0;
1297 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1299 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1301 HeapFree(GetProcessHeap(), 0, authorization);
1306 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1308 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1311 size = sizeof(new_location);
1312 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1314 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1315 strcpyW( url, new_location );
1319 static const WCHAR slash[] = { '/',0 };
1320 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1321 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1322 http_session_t *session = req->lpHttpSession;
1324 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1325 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1327 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1329 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1330 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1332 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1333 if (req->lpszPath[0] != '/') strcatW( url, slash );
1334 strcatW( url, req->lpszPath );
1336 TRACE("url=%s\n", debugstr_w(url));
1340 /***********************************************************************
1341 * HTTP_DealWithProxy
1343 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1345 WCHAR buf[MAXHOSTNAME];
1346 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1347 static WCHAR szNul[] = { 0 };
1348 URL_COMPONENTSW UrlComponents;
1349 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1350 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1352 memset( &UrlComponents, 0, sizeof UrlComponents );
1353 UrlComponents.dwStructSize = sizeof UrlComponents;
1354 UrlComponents.lpszHostName = buf;
1355 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1357 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1358 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1359 sprintfW(proxy, szFormat, hIC->lpszProxy);
1361 strcpyW(proxy, hIC->lpszProxy);
1362 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1364 if( UrlComponents.dwHostNameLength == 0 )
1367 if( !lpwhr->lpszPath )
1368 lpwhr->lpszPath = szNul;
1370 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1371 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1373 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1374 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1375 lpwhs->nServerPort = UrlComponents.nPort;
1377 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1381 #ifndef INET6_ADDRSTRLEN
1382 #define INET6_ADDRSTRLEN 46
1385 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1387 char szaddr[INET6_ADDRSTRLEN];
1388 http_session_t *lpwhs = lpwhr->lpHttpSession;
1391 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1392 INTERNET_STATUS_RESOLVING_NAME,
1393 lpwhs->lpszServerName,
1394 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1396 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1397 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1398 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1399 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1401 switch (lpwhs->socketAddress.ss_family)
1404 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1407 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1410 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1411 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1413 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1414 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1415 INTERNET_STATUS_NAME_RESOLVED,
1416 szaddr, strlen(szaddr)+1);
1418 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1419 return ERROR_SUCCESS;
1423 /***********************************************************************
1424 * HTTPREQ_Destroy (internal)
1426 * Deallocate request handle
1429 static void HTTPREQ_Destroy(object_header_t *hdr)
1431 http_request_t *lpwhr = (http_request_t*) hdr;
1436 if(lpwhr->hCacheFile)
1437 CloseHandle(lpwhr->hCacheFile);
1439 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1441 DeleteCriticalSection( &lpwhr->read_section );
1442 WININET_Release(&lpwhr->lpHttpSession->hdr);
1444 destroy_authinfo(lpwhr->pAuthInfo);
1445 destroy_authinfo(lpwhr->pProxyAuthInfo);
1447 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1448 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1450 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1451 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1453 for (i = 0; i < lpwhr->nCustHeaders; i++)
1455 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1456 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1460 if(lpwhr->gzip_stream) {
1461 if(!lpwhr->gzip_stream->end_of_data)
1462 inflateEnd(&lpwhr->gzip_stream->zstream);
1463 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1467 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1468 HeapFree(GetProcessHeap(), 0, lpwhr);
1471 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1473 http_request_t *lpwhr = (http_request_t*) hdr;
1475 TRACE("%p\n",lpwhr);
1477 if (!NETCON_connected(&lpwhr->netConnection))
1480 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1481 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1483 NETCON_close(&lpwhr->netConnection);
1485 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1486 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1489 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1491 LPHTTPHEADERW host_header;
1493 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1495 host_header = HTTP_GetHeader(req, hostW);
1499 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1503 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1505 WCHAR szVersion[10];
1506 WCHAR szConnectionResponse[20];
1507 DWORD dwBufferSize = sizeof(szVersion);
1508 BOOL keepalive = FALSE;
1510 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1511 * the connection is keep-alive by default */
1512 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1513 && !strcmpiW(szVersion, g_szHttp1_1))
1518 dwBufferSize = sizeof(szConnectionResponse);
1519 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1520 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1522 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1528 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1530 http_request_t *req = (http_request_t*)hdr;
1533 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1535 http_session_t *lpwhs = req->lpHttpSession;
1536 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1538 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1540 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1541 return ERROR_INSUFFICIENT_BUFFER;
1542 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1543 /* FIXME: can't get a SOCKET from our connection since we don't use
1547 /* FIXME: get source port from req->netConnection */
1548 info->SourcePort = 0;
1549 info->DestPort = lpwhs->nHostPort;
1551 if (HTTP_KeepAlive(req))
1552 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1553 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1554 info->Flags |= IDSI_FLAG_PROXY;
1555 if (req->netConnection.useSSL)
1556 info->Flags |= IDSI_FLAG_SECURE;
1558 return ERROR_SUCCESS;
1561 case INTERNET_OPTION_SECURITY_FLAGS:
1563 http_session_t *lpwhs;
1564 lpwhs = req->lpHttpSession;
1566 if (*size < sizeof(ULONG))
1567 return ERROR_INSUFFICIENT_BUFFER;
1569 *size = sizeof(DWORD);
1570 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1571 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1573 *(DWORD*)buffer = 0;
1574 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1575 return ERROR_SUCCESS;
1578 case INTERNET_OPTION_HANDLE_TYPE:
1579 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1581 if (*size < sizeof(ULONG))
1582 return ERROR_INSUFFICIENT_BUFFER;
1584 *size = sizeof(DWORD);
1585 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1586 return ERROR_SUCCESS;
1588 case INTERNET_OPTION_URL: {
1589 WCHAR url[INTERNET_MAX_URL_LENGTH];
1594 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1596 TRACE("INTERNET_OPTION_URL\n");
1598 host = HTTP_GetHeader(req, hostW);
1599 strcpyW(url, httpW);
1600 strcatW(url, host->lpszValue);
1601 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1603 strcatW(url, req->lpszPath);
1605 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1608 len = (strlenW(url)+1) * sizeof(WCHAR);
1610 return ERROR_INSUFFICIENT_BUFFER;
1613 strcpyW(buffer, url);
1614 return ERROR_SUCCESS;
1616 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1618 return ERROR_INSUFFICIENT_BUFFER;
1621 return ERROR_SUCCESS;
1625 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1626 INTERNET_CACHE_ENTRY_INFOW *info;
1627 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1628 WCHAR url[INTERNET_MAX_URL_LENGTH];
1629 DWORD nbytes, error;
1632 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1634 if (*size < sizeof(*ts))
1636 *size = sizeof(*ts);
1637 return ERROR_INSUFFICIENT_BUFFER;
1640 HTTP_GetRequestURL(req, url);
1641 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1642 error = GetLastError();
1643 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1645 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1646 return ERROR_OUTOFMEMORY;
1648 GetUrlCacheEntryInfoW(url, info, &nbytes);
1650 ts->ftExpires = info->ExpireTime;
1651 ts->ftLastModified = info->LastModifiedTime;
1653 HeapFree(GetProcessHeap(), 0, info);
1654 *size = sizeof(*ts);
1655 return ERROR_SUCCESS;
1660 case INTERNET_OPTION_DATAFILE_NAME: {
1663 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1665 if(!req->lpszCacheFile) {
1667 return ERROR_INTERNET_ITEM_NOT_FOUND;
1671 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1672 if(*size < req_size)
1673 return ERROR_INSUFFICIENT_BUFFER;
1676 memcpy(buffer, req->lpszCacheFile, *size);
1677 return ERROR_SUCCESS;
1679 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1680 if (req_size > *size)
1681 return ERROR_INSUFFICIENT_BUFFER;
1683 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1684 -1, buffer, *size, NULL, NULL);
1685 return ERROR_SUCCESS;
1689 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1690 PCCERT_CONTEXT context;
1692 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1693 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1694 return ERROR_INSUFFICIENT_BUFFER;
1697 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1699 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1702 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1703 info->ftExpiry = context->pCertInfo->NotAfter;
1704 info->ftStart = context->pCertInfo->NotBefore;
1706 len = CertNameToStrW(context->dwCertEncodingType,
1707 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1708 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1709 if(info->lpszSubjectInfo)
1710 CertNameToStrW(context->dwCertEncodingType,
1711 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1712 info->lpszSubjectInfo, len);
1713 len = CertNameToStrW(context->dwCertEncodingType,
1714 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1715 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1716 if (info->lpszIssuerInfo)
1717 CertNameToStrW(context->dwCertEncodingType,
1718 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1719 info->lpszIssuerInfo, len);
1721 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1723 len = CertNameToStrA(context->dwCertEncodingType,
1724 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1725 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1726 if(infoA->lpszSubjectInfo)
1727 CertNameToStrA(context->dwCertEncodingType,
1728 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1729 infoA->lpszSubjectInfo, len);
1730 len = CertNameToStrA(context->dwCertEncodingType,
1731 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1732 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1733 if(infoA->lpszIssuerInfo)
1734 CertNameToStrA(context->dwCertEncodingType,
1735 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1736 infoA->lpszIssuerInfo, len);
1740 * Contrary to MSDN, these do not appear to be set.
1742 * lpszSignatureAlgName
1743 * lpszEncryptionAlgName
1746 CertFreeCertificateContext(context);
1747 return ERROR_SUCCESS;
1752 return INET_QueryOption(option, buffer, size, unicode);
1755 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1757 http_request_t *req = (http_request_t*)hdr;
1760 case INTERNET_OPTION_SEND_TIMEOUT:
1761 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1762 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1764 if (size != sizeof(DWORD))
1765 return ERROR_INVALID_PARAMETER;
1767 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1770 case INTERNET_OPTION_USERNAME:
1771 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1772 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1773 return ERROR_SUCCESS;
1775 case INTERNET_OPTION_PASSWORD:
1776 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1777 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1778 return ERROR_SUCCESS;
1779 case INTERNET_OPTION_HTTP_DECODING:
1780 if(size != sizeof(BOOL))
1781 return ERROR_INVALID_PARAMETER;
1782 req->decoding = *(BOOL*)buffer;
1783 return ERROR_SUCCESS;
1786 return ERROR_INTERNET_INVALID_OPTION;
1789 /* read some more data into the read buffer (the read section must be held) */
1790 static DWORD read_more_data( http_request_t *req, int maxlen )
1797 /* move existing data to the start of the buffer */
1799 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1803 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1805 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1806 maxlen - req->read_size, 0, &len );
1807 if(res == ERROR_SUCCESS)
1808 req->read_size += len;
1813 /* remove some amount of data from the read buffer (the read section must be held) */
1814 static void remove_data( http_request_t *req, int count )
1816 if (!(req->read_size -= count)) req->read_pos = 0;
1817 else req->read_pos += count;
1820 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1822 int count, bytes_read, pos = 0;
1825 EnterCriticalSection( &req->read_section );
1828 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1832 count = eol - (req->read_buf + req->read_pos);
1833 bytes_read = count + 1;
1835 else count = bytes_read = req->read_size;
1837 count = min( count, *len - pos );
1838 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1840 remove_data( req, bytes_read );
1843 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1846 TRACE( "returning empty string\n" );
1847 LeaveCriticalSection( &req->read_section );
1848 INTERNET_SetLastError(res);
1852 LeaveCriticalSection( &req->read_section );
1856 if (pos && buffer[pos - 1] == '\r') pos--;
1859 buffer[*len - 1] = 0;
1860 TRACE( "returning %s\n", debugstr_a(buffer));
1864 /* discard data contents until we reach end of line (the read section must be held) */
1865 static DWORD discard_eol( http_request_t *req )
1871 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1874 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1877 req->read_pos = req->read_size = 0; /* discard everything */
1878 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1879 } while (req->read_size);
1880 return ERROR_SUCCESS;
1883 /* read the size of the next chunk (the read section must be held) */
1884 static DWORD start_next_chunk( http_request_t *req )
1886 DWORD chunk_size = 0, res;
1888 if (!req->dwContentLength) return ERROR_SUCCESS;
1889 if (req->dwContentLength == req->dwContentRead)
1891 /* read terminator for the previous chunk */
1892 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1893 req->dwContentLength = ~0u;
1894 req->dwContentRead = 0;
1898 while (req->read_size)
1900 char ch = req->read_buf[req->read_pos];
1901 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1902 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1903 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1904 else if (ch == ';' || ch == '\r' || ch == '\n')
1906 TRACE( "reading %u byte chunk\n", chunk_size );
1907 req->dwContentLength = chunk_size;
1908 req->dwContentRead = 0;
1909 return discard_eol( req );
1911 remove_data( req, 1 );
1913 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1914 if (!req->read_size)
1916 req->dwContentLength = req->dwContentRead = 0;
1917 return ERROR_SUCCESS;
1922 /* check if we have reached the end of the data to read (the read section must be held) */
1923 static BOOL end_of_read_data( http_request_t *req )
1925 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1926 if (req->read_chunked) return (req->dwContentLength == 0);
1927 if (req->dwContentLength == ~0u) return FALSE;
1928 return (req->dwContentLength == req->dwContentRead);
1931 /* fetch some more data into the read buffer (the read section must be held) */
1932 static DWORD refill_buffer( http_request_t *req )
1934 int len = sizeof(req->read_buf);
1937 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1939 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1942 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1943 if (len <= req->read_size) return ERROR_SUCCESS;
1945 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1946 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1947 return ERROR_SUCCESS;
1950 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1952 DWORD ret = ERROR_SUCCESS;
1956 z_stream *zstream = &req->gzip_stream->zstream;
1960 while(read < size && !req->gzip_stream->end_of_data) {
1961 if(!req->read_size) {
1962 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1966 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1968 zstream->next_in = req->read_buf+req->read_pos;
1969 zstream->avail_in = buf_avail;
1970 zstream->next_out = buf+read;
1971 zstream->avail_out = size-read;
1972 zres = inflate(zstream, Z_FULL_FLUSH);
1973 read = size - zstream->avail_out;
1974 req->dwContentRead += buf_avail-zstream->avail_in;
1975 remove_data(req, buf_avail-zstream->avail_in);
1976 if(zres == Z_STREAM_END) {
1977 TRACE("end of data\n");
1978 req->gzip_stream->end_of_data = TRUE;
1979 inflateEnd(&req->gzip_stream->zstream);
1980 }else if(zres != Z_OK) {
1981 WARN("inflate failed %d\n", zres);
1983 ret = ERROR_INTERNET_DECODING_FAILED;
1993 static void refill_gzip_buffer(http_request_t *req)
1998 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2001 if(req->gzip_stream->buf_pos) {
2002 if(req->gzip_stream->buf_size)
2003 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2004 req->gzip_stream->buf_pos = 0;
2007 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2008 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2009 if(res == ERROR_SUCCESS)
2010 req->gzip_stream->buf_size += len;
2013 /* return the size of data available to be read immediately (the read section must be held) */
2014 static DWORD get_avail_data( http_request_t *req )
2016 if (req->gzip_stream) {
2017 refill_gzip_buffer(req);
2018 return req->gzip_stream->buf_size;
2020 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2022 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2025 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2027 INTERNET_ASYNC_RESULT iar;
2032 EnterCriticalSection( &req->read_section );
2033 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2034 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2035 iar.dwError = first_notif ? 0 : get_avail_data(req);
2040 LeaveCriticalSection( &req->read_section );
2042 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2043 sizeof(INTERNET_ASYNC_RESULT));
2046 /* read data from the http connection (the read section must be held) */
2047 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2049 BOOL finished_reading = FALSE;
2050 int len, bytes_read = 0;
2051 DWORD ret = ERROR_SUCCESS;
2053 EnterCriticalSection( &req->read_section );
2055 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2057 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2060 if(req->gzip_stream) {
2061 if(req->gzip_stream->buf_size) {
2062 bytes_read = min(req->gzip_stream->buf_size, size);
2063 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2064 req->gzip_stream->buf_pos += bytes_read;
2065 req->gzip_stream->buf_size -= bytes_read;
2066 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2070 if(size > bytes_read) {
2071 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2072 if(ret == ERROR_SUCCESS)
2076 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2078 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2080 if (req->read_size) {
2081 bytes_read = min( req->read_size, size );
2082 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2083 remove_data( req, bytes_read );
2086 if (size > bytes_read && (!bytes_read || sync)) {
2087 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2088 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2090 /* always return success, even if the network layer returns an error */
2093 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2094 req->dwContentRead += bytes_read;
2099 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2100 LeaveCriticalSection( &req->read_section );
2102 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2104 DWORD dwBytesWritten;
2106 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2108 WARN("WriteFile failed: %u\n", GetLastError());
2111 if(finished_reading)
2112 HTTP_FinishedReading(req);
2118 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2120 http_request_t *req = (http_request_t*)hdr;
2121 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2124 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2126 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2127 http_request_t *req = (http_request_t*)workRequest->hdr;
2128 INTERNET_ASYNC_RESULT iar;
2131 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2133 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2134 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2136 iar.dwResult = res == ERROR_SUCCESS;
2139 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2140 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2141 sizeof(INTERNET_ASYNC_RESULT));
2144 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2145 DWORD flags, DWORD_PTR context)
2147 http_request_t *req = (http_request_t*)hdr;
2150 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2151 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2153 if (buffers->dwStructSize != sizeof(*buffers))
2154 return ERROR_INVALID_PARAMETER;
2156 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2158 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2160 WORKREQUEST workRequest;
2162 if (TryEnterCriticalSection( &req->read_section ))
2164 if (get_avail_data(req))
2166 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2167 &buffers->dwBufferLength, FALSE);
2168 LeaveCriticalSection( &req->read_section );
2171 LeaveCriticalSection( &req->read_section );
2174 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2175 workRequest.hdr = WININET_AddRef(&req->hdr);
2176 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2178 INTERNET_AsyncCall(&workRequest);
2180 return ERROR_IO_PENDING;
2183 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2184 !(flags & IRF_NO_WAIT));
2187 if (res == ERROR_SUCCESS) {
2188 DWORD size = buffers->dwBufferLength;
2189 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2190 &size, sizeof(size));
2196 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2198 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2199 http_request_t *req = (http_request_t*)workRequest->hdr;
2200 INTERNET_ASYNC_RESULT iar;
2203 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2205 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2206 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2208 iar.dwResult = res == ERROR_SUCCESS;
2211 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2212 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2213 sizeof(INTERNET_ASYNC_RESULT));
2216 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2217 DWORD flags, DWORD_PTR context)
2220 http_request_t *req = (http_request_t*)hdr;
2223 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2224 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2226 if (buffers->dwStructSize != sizeof(*buffers))
2227 return ERROR_INVALID_PARAMETER;
2229 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2231 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2233 WORKREQUEST workRequest;
2235 if (TryEnterCriticalSection( &req->read_section ))
2237 if (get_avail_data(req))
2239 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2240 &buffers->dwBufferLength, FALSE);
2241 LeaveCriticalSection( &req->read_section );
2244 LeaveCriticalSection( &req->read_section );
2247 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2248 workRequest.hdr = WININET_AddRef(&req->hdr);
2249 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2251 INTERNET_AsyncCall(&workRequest);
2253 return ERROR_IO_PENDING;
2256 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2257 !(flags & IRF_NO_WAIT));
2260 if (res == ERROR_SUCCESS) {
2261 DWORD size = buffers->dwBufferLength;
2262 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2263 &size, sizeof(size));
2269 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2272 http_request_t *lpwhr = (http_request_t*)hdr;
2274 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2277 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2278 if (res == ERROR_SUCCESS)
2279 lpwhr->dwBytesWritten += *written;
2281 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2285 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2287 http_request_t *req = (http_request_t*)workRequest->hdr;
2289 HTTP_ReceiveRequestData(req, FALSE);
2292 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2294 http_request_t *req = (http_request_t*)hdr;
2296 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2298 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2300 WORKREQUEST workRequest;
2302 /* never wait, if we can't enter the section we queue an async request right away */
2303 if (TryEnterCriticalSection( &req->read_section ))
2305 if ((*available = get_avail_data( req ))) goto done;
2306 if (end_of_read_data( req )) goto done;
2307 LeaveCriticalSection( &req->read_section );
2310 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2311 workRequest.hdr = WININET_AddRef( &req->hdr );
2313 INTERNET_AsyncCall(&workRequest);
2315 return ERROR_IO_PENDING;
2318 EnterCriticalSection( &req->read_section );
2320 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2322 refill_buffer( req );
2323 *available = get_avail_data( req );
2327 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2330 if (NETCON_query_data_available(&req->netConnection, &extra))
2331 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2333 LeaveCriticalSection( &req->read_section );
2335 TRACE( "returning %u\n", *available );
2336 return ERROR_SUCCESS;
2339 static const object_vtbl_t HTTPREQVtbl = {
2341 HTTPREQ_CloseConnection,
2342 HTTPREQ_QueryOption,
2345 HTTPREQ_ReadFileExA,
2346 HTTPREQ_ReadFileExW,
2348 HTTPREQ_QueryDataAvailable,
2352 /***********************************************************************
2353 * HTTP_HttpOpenRequestW (internal)
2355 * Open a HTTP request handle
2358 * HINTERNET a HTTP request handle on success
2362 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2363 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2364 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2365 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2367 appinfo_t *hIC = NULL;
2368 http_request_t *lpwhr;
2369 LPWSTR lpszHostName = NULL;
2370 HINTERNET handle = NULL;
2371 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2376 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2377 hIC = lpwhs->lpAppInfo;
2379 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2382 res = ERROR_OUTOFMEMORY;
2385 lpwhr->hdr.htype = WH_HHTTPREQ;
2386 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2387 lpwhr->hdr.dwFlags = dwFlags;
2388 lpwhr->hdr.dwContext = dwContext;
2389 lpwhr->hdr.refs = 1;
2390 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2391 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2392 lpwhr->dwContentLength = ~0u;
2393 InitializeCriticalSection( &lpwhr->read_section );
2395 WININET_AddRef( &lpwhs->hdr );
2396 lpwhr->lpHttpSession = lpwhs;
2397 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2399 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2400 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2401 if (NULL == lpszHostName)
2403 res = ERROR_OUTOFMEMORY;
2407 handle = WININET_AllocHandle( &lpwhr->hdr );
2410 res = ERROR_OUTOFMEMORY;
2414 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2416 InternetCloseHandle( handle );
2421 if (lpszObjectName && *lpszObjectName) {
2425 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2426 if (rc != E_POINTER)
2427 len = strlenW(lpszObjectName)+1;
2428 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2429 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2430 URL_ESCAPE_SPACES_ONLY);
2433 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2434 strcpyW(lpwhr->lpszPath,lpszObjectName);
2437 static const WCHAR slashW[] = {'/',0};
2439 lpwhr->lpszPath = heap_strdupW(slashW);
2442 if (lpszReferrer && *lpszReferrer)
2443 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2445 if (lpszAcceptTypes)
2448 for (i = 0; lpszAcceptTypes[i]; i++)
2450 if (!*lpszAcceptTypes[i]) continue;
2451 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2452 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2453 HTTP_ADDHDR_FLAG_REQ |
2454 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2458 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2459 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2461 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2462 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2463 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2465 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2466 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2467 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2470 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2471 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2473 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2474 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2475 INTERNET_DEFAULT_HTTPS_PORT :
2476 INTERNET_DEFAULT_HTTP_PORT);
2478 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2479 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2480 INTERNET_DEFAULT_HTTPS_PORT :
2481 INTERNET_DEFAULT_HTTP_PORT);
2483 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2484 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2486 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2487 INTERNET_STATUS_HANDLE_CREATED, &handle,
2491 HeapFree(GetProcessHeap(), 0, lpszHostName);
2493 WININET_Release( &lpwhr->hdr );
2495 TRACE("<-- %p (%p)\n", handle, lpwhr);
2500 /***********************************************************************
2501 * HttpOpenRequestW (WININET.@)
2503 * Open a HTTP request handle
2506 * HINTERNET a HTTP request handle on success
2510 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2511 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2512 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2513 DWORD dwFlags, DWORD_PTR dwContext)
2515 http_session_t *lpwhs;
2516 HINTERNET handle = NULL;
2519 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2520 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2521 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2522 dwFlags, dwContext);
2523 if(lpszAcceptTypes!=NULL)
2526 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2527 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2530 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2531 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2533 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2538 * My tests seem to show that the windows version does not
2539 * become asynchronous until after this point. And anyhow
2540 * if this call was asynchronous then how would you get the
2541 * necessary HINTERNET pointer returned by this function.
2544 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2545 lpszVersion, lpszReferrer, lpszAcceptTypes,
2546 dwFlags, dwContext, &handle);
2549 WININET_Release( &lpwhs->hdr );
2550 TRACE("returning %p\n", handle);
2551 if(res != ERROR_SUCCESS)
2556 /* read any content returned by the server so that the connection can be
2558 static void HTTP_DrainContent(http_request_t *req)
2562 if (!NETCON_connected(&req->netConnection)) return;
2564 if (req->dwContentLength == -1)
2566 NETCON_close(&req->netConnection);
2569 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2574 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2576 } while (bytes_read);
2579 static const LPCWSTR header_lookup[] = {
2580 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2581 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2582 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2583 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2584 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2585 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2586 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2587 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2588 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2589 szDate, /* HTTP_QUERY_DATE = 9 */
2590 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2591 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2592 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2593 szURI, /* HTTP_QUERY_URI = 13 */
2594 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2595 NULL, /* HTTP_QUERY_COST = 15 */
2596 NULL, /* HTTP_QUERY_LINK = 16 */
2597 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2598 NULL, /* HTTP_QUERY_VERSION = 18 */
2599 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2600 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2601 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2602 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2603 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2604 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2605 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2606 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2607 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2608 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2609 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2610 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2611 NULL, /* HTTP_QUERY_FROM = 31 */
2612 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2613 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2614 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2615 szReferer, /* HTTP_QUERY_REFERER = 35 */
2616 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2617 szServer, /* HTTP_QUERY_SERVER = 37 */
2618 NULL, /* HTTP_TITLE = 38 */
2619 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2620 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2621 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2622 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2623 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2624 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2625 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2626 NULL, /* HTTP_QUERY_REFRESH = 46 */
2627 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2628 szAge, /* HTTP_QUERY_AGE = 48 */
2629 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2630 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2631 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2632 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2633 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2634 szETag, /* HTTP_QUERY_ETAG = 54 */
2635 hostW, /* HTTP_QUERY_HOST = 55 */
2636 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2637 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2638 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2639 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2640 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2641 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2642 szRange, /* HTTP_QUERY_RANGE = 62 */
2643 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2644 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2645 szVary, /* HTTP_QUERY_VARY = 65 */
2646 szVia, /* HTTP_QUERY_VIA = 66 */
2647 szWarning, /* HTTP_QUERY_WARNING = 67 */
2648 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2649 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2650 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2653 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2655 /***********************************************************************
2656 * HTTP_HttpQueryInfoW (internal)
2658 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2659 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2661 LPHTTPHEADERW lphttpHdr = NULL;
2662 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2663 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2664 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2667 /* Find requested header structure */
2670 case HTTP_QUERY_CUSTOM:
2671 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2672 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2674 case HTTP_QUERY_RAW_HEADERS_CRLF:
2678 DWORD res = ERROR_INVALID_PARAMETER;
2681 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2683 headers = lpwhr->lpszRawHeaders;
2686 len = strlenW(headers) * sizeof(WCHAR);
2688 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2690 len += sizeof(WCHAR);
2691 res = ERROR_INSUFFICIENT_BUFFER;
2696 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2699 len = strlenW(szCrLf) * sizeof(WCHAR);
2700 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2702 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2703 res = ERROR_SUCCESS;
2705 *lpdwBufferLength = len;
2708 HeapFree(GetProcessHeap(), 0, headers);
2711 case HTTP_QUERY_RAW_HEADERS:
2713 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2715 LPWSTR pszString = lpBuffer;
2717 for (i = 0; ppszRawHeaderLines[i]; i++)
2718 size += strlenW(ppszRawHeaderLines[i]) + 1;
2720 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2722 HTTP_FreeTokens(ppszRawHeaderLines);
2723 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2724 return ERROR_INSUFFICIENT_BUFFER;
2728 for (i = 0; ppszRawHeaderLines[i]; i++)
2730 DWORD len = strlenW(ppszRawHeaderLines[i]);
2731 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2735 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2737 *lpdwBufferLength = size * sizeof(WCHAR);
2738 HTTP_FreeTokens(ppszRawHeaderLines);
2740 return ERROR_SUCCESS;
2742 case HTTP_QUERY_STATUS_TEXT:
2743 if (lpwhr->lpszStatusText)
2745 DWORD len = strlenW(lpwhr->lpszStatusText);
2746 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2748 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2749 return ERROR_INSUFFICIENT_BUFFER;
2753 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2754 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2756 *lpdwBufferLength = len * sizeof(WCHAR);
2757 return ERROR_SUCCESS;
2760 case HTTP_QUERY_VERSION:
2761 if (lpwhr->lpszVersion)
2763 DWORD len = strlenW(lpwhr->lpszVersion);
2764 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2766 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2767 return ERROR_INSUFFICIENT_BUFFER;
2771 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2772 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2774 *lpdwBufferLength = len * sizeof(WCHAR);
2775 return ERROR_SUCCESS;
2778 case HTTP_QUERY_CONTENT_ENCODING:
2779 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2780 requested_index,request_only);
2783 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2785 if (level < LAST_TABLE_HEADER && header_lookup[level])
2786 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2787 requested_index,request_only);
2791 lphttpHdr = &lpwhr->pCustHeaders[index];
2793 /* Ensure header satisfies requested attributes */
2795 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2796 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2798 return ERROR_HTTP_HEADER_NOT_FOUND;
2801 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2803 /* coalesce value to requested type */
2804 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2806 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2807 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2809 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2815 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2817 tmpTM = *gmtime(&tmpTime);
2818 STHook = (SYSTEMTIME *)lpBuffer;
2819 STHook->wDay = tmpTM.tm_mday;
2820 STHook->wHour = tmpTM.tm_hour;
2821 STHook->wMilliseconds = 0;
2822 STHook->wMinute = tmpTM.tm_min;
2823 STHook->wDayOfWeek = tmpTM.tm_wday;
2824 STHook->wMonth = tmpTM.tm_mon + 1;
2825 STHook->wSecond = tmpTM.tm_sec;
2826 STHook->wYear = tmpTM.tm_year;
2828 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2829 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2830 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2832 else if (lphttpHdr->lpszValue)
2834 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2836 if (len > *lpdwBufferLength)
2838 *lpdwBufferLength = len;
2839 return ERROR_INSUFFICIENT_BUFFER;
2843 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2844 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2846 *lpdwBufferLength = len - sizeof(WCHAR);
2848 return ERROR_SUCCESS;
2851 /***********************************************************************
2852 * HttpQueryInfoW (WININET.@)
2854 * Queries for information about an HTTP request
2861 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2862 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2864 http_request_t *lpwhr;
2867 if (TRACE_ON(wininet)) {
2868 #define FE(x) { x, #x }
2869 static const wininet_flag_info query_flags[] = {
2870 FE(HTTP_QUERY_MIME_VERSION),
2871 FE(HTTP_QUERY_CONTENT_TYPE),
2872 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2873 FE(HTTP_QUERY_CONTENT_ID),
2874 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2875 FE(HTTP_QUERY_CONTENT_LENGTH),
2876 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2877 FE(HTTP_QUERY_ALLOW),
2878 FE(HTTP_QUERY_PUBLIC),
2879 FE(HTTP_QUERY_DATE),
2880 FE(HTTP_QUERY_EXPIRES),
2881 FE(HTTP_QUERY_LAST_MODIFIED),
2882 FE(HTTP_QUERY_MESSAGE_ID),
2884 FE(HTTP_QUERY_DERIVED_FROM),
2885 FE(HTTP_QUERY_COST),
2886 FE(HTTP_QUERY_LINK),
2887 FE(HTTP_QUERY_PRAGMA),
2888 FE(HTTP_QUERY_VERSION),
2889 FE(HTTP_QUERY_STATUS_CODE),
2890 FE(HTTP_QUERY_STATUS_TEXT),
2891 FE(HTTP_QUERY_RAW_HEADERS),
2892 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2893 FE(HTTP_QUERY_CONNECTION),
2894 FE(HTTP_QUERY_ACCEPT),
2895 FE(HTTP_QUERY_ACCEPT_CHARSET),
2896 FE(HTTP_QUERY_ACCEPT_ENCODING),
2897 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2898 FE(HTTP_QUERY_AUTHORIZATION),
2899 FE(HTTP_QUERY_CONTENT_ENCODING),
2900 FE(HTTP_QUERY_FORWARDED),
2901 FE(HTTP_QUERY_FROM),
2902 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2903 FE(HTTP_QUERY_LOCATION),
2904 FE(HTTP_QUERY_ORIG_URI),
2905 FE(HTTP_QUERY_REFERER),
2906 FE(HTTP_QUERY_RETRY_AFTER),
2907 FE(HTTP_QUERY_SERVER),
2908 FE(HTTP_QUERY_TITLE),
2909 FE(HTTP_QUERY_USER_AGENT),
2910 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2911 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2912 FE(HTTP_QUERY_ACCEPT_RANGES),
2913 FE(HTTP_QUERY_SET_COOKIE),
2914 FE(HTTP_QUERY_COOKIE),
2915 FE(HTTP_QUERY_REQUEST_METHOD),
2916 FE(HTTP_QUERY_REFRESH),
2917 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2919 FE(HTTP_QUERY_CACHE_CONTROL),
2920 FE(HTTP_QUERY_CONTENT_BASE),
2921 FE(HTTP_QUERY_CONTENT_LOCATION),
2922 FE(HTTP_QUERY_CONTENT_MD5),
2923 FE(HTTP_QUERY_CONTENT_RANGE),
2924 FE(HTTP_QUERY_ETAG),
2925 FE(HTTP_QUERY_HOST),
2926 FE(HTTP_QUERY_IF_MATCH),
2927 FE(HTTP_QUERY_IF_NONE_MATCH),
2928 FE(HTTP_QUERY_IF_RANGE),
2929 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2930 FE(HTTP_QUERY_MAX_FORWARDS),
2931 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2932 FE(HTTP_QUERY_RANGE),
2933 FE(HTTP_QUERY_TRANSFER_ENCODING),
2934 FE(HTTP_QUERY_UPGRADE),
2935 FE(HTTP_QUERY_VARY),
2937 FE(HTTP_QUERY_WARNING),
2938 FE(HTTP_QUERY_CUSTOM)
2940 static const wininet_flag_info modifier_flags[] = {
2941 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2942 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2943 FE(HTTP_QUERY_FLAG_NUMBER),
2944 FE(HTTP_QUERY_FLAG_COALESCE)
2947 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2948 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2951 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2952 TRACE(" Attribute:");
2953 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2954 if (query_flags[i].val == info) {
2955 TRACE(" %s", query_flags[i].name);
2959 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2960 TRACE(" Unknown (%08x)", info);
2963 TRACE(" Modifier:");
2964 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2965 if (modifier_flags[i].val & info_mod) {
2966 TRACE(" %s", modifier_flags[i].name);
2967 info_mod &= ~ modifier_flags[i].val;
2972 TRACE(" Unknown (%08x)", info_mod);
2977 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2978 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2980 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2984 if (lpBuffer == NULL)
2985 *lpdwBufferLength = 0;
2986 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2987 lpBuffer, lpdwBufferLength, lpdwIndex);
2991 WININET_Release( &lpwhr->hdr );
2993 TRACE("%u <--\n", res);
2994 if(res != ERROR_SUCCESS)
2996 return res == ERROR_SUCCESS;
2999 /***********************************************************************
3000 * HttpQueryInfoA (WININET.@)
3002 * Queries for information about an HTTP request
3009 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3010 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3016 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3017 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3019 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3020 lpdwBufferLength, lpdwIndex );
3026 len = (*lpdwBufferLength)*sizeof(WCHAR);
3027 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3029 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3035 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3036 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3037 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3038 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3045 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3049 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3050 lpBuffer, *lpdwBufferLength, NULL, NULL );
3051 *lpdwBufferLength = len - 1;
3053 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3056 /* since the strings being returned from HttpQueryInfoW should be
3057 * only ASCII characters, it is reasonable to assume that all of
3058 * the Unicode characters can be reduced to a single byte */
3059 *lpdwBufferLength = len / sizeof(WCHAR);
3061 HeapFree(GetProcessHeap(), 0, bufferW );
3066 /***********************************************************************
3067 * HTTP_GetRedirectURL (internal)
3069 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3071 static WCHAR szHttp[] = {'h','t','t','p',0};
3072 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3073 http_session_t *lpwhs = lpwhr->lpHttpSession;
3074 URL_COMPONENTSW urlComponents;
3075 DWORD url_length = 0;
3077 LPWSTR combined_url;
3079 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3080 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3081 urlComponents.dwSchemeLength = 0;
3082 urlComponents.lpszHostName = lpwhs->lpszHostName;
3083 urlComponents.dwHostNameLength = 0;
3084 urlComponents.nPort = lpwhs->nHostPort;
3085 urlComponents.lpszUserName = lpwhs->lpszUserName;
3086 urlComponents.dwUserNameLength = 0;
3087 urlComponents.lpszPassword = NULL;
3088 urlComponents.dwPasswordLength = 0;
3089 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3090 urlComponents.dwUrlPathLength = 0;
3091 urlComponents.lpszExtraInfo = NULL;
3092 urlComponents.dwExtraInfoLength = 0;
3094 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3095 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3098 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3100 /* convert from bytes to characters */
3101 url_length = url_length / sizeof(WCHAR) - 1;
3102 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3104 HeapFree(GetProcessHeap(), 0, orig_url);
3109 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3110 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3112 HeapFree(GetProcessHeap(), 0, orig_url);
3115 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3117 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3119 HeapFree(GetProcessHeap(), 0, orig_url);
3120 HeapFree(GetProcessHeap(), 0, combined_url);
3123 HeapFree(GetProcessHeap(), 0, orig_url);
3124 return combined_url;
3128 /***********************************************************************
3129 * HTTP_HandleRedirect (internal)
3131 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3133 http_session_t *lpwhs = lpwhr->lpHttpSession;
3134 appinfo_t *hIC = lpwhs->lpAppInfo;
3135 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3136 WCHAR path[INTERNET_MAX_URL_LENGTH];
3141 /* if it's an absolute path, keep the same session info */
3142 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3146 URL_COMPONENTSW urlComponents;
3147 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3148 static WCHAR szHttp[] = {'h','t','t','p',0};
3149 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3155 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3156 urlComponents.lpszScheme = protocol;
3157 urlComponents.dwSchemeLength = 32;
3158 urlComponents.lpszHostName = hostName;
3159 urlComponents.dwHostNameLength = MAXHOSTNAME;
3160 urlComponents.lpszUserName = userName;
3161 urlComponents.dwUserNameLength = 1024;
3162 urlComponents.lpszPassword = NULL;
3163 urlComponents.dwPasswordLength = 0;
3164 urlComponents.lpszUrlPath = path;
3165 urlComponents.dwUrlPathLength = 2048;
3166 urlComponents.lpszExtraInfo = NULL;
3167 urlComponents.dwExtraInfoLength = 0;
3168 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3169 return INTERNET_GetLastError();
3171 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3172 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3174 TRACE("redirect from secure page to non-secure page\n");
3175 /* FIXME: warn about from secure redirect to non-secure page */
3176 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3178 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3179 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3181 TRACE("redirect from non-secure page to secure page\n");
3182 /* FIXME: notify about redirect to secure page */
3183 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3186 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3188 if (lstrlenW(protocol)>4) /*https*/
3189 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3191 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3196 * This upsets redirects to binary files on sourceforge.net
3197 * and gives an html page instead of the target file
3198 * Examination of the HTTP request sent by native wininet.dll
3199 * reveals that it doesn't send a referrer in that case.
3200 * Maybe there's a flag that enables this, or maybe a referrer
3201 * shouldn't be added in case of a redirect.
3204 /* consider the current host as the referrer */
3205 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3206 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3207 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3208 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3211 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3212 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3213 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3216 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3217 len = lstrlenW(hostName);
3218 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3219 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3220 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3223 lpwhs->lpszHostName = heap_strdupW(hostName);
3225 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3227 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3228 lpwhs->lpszUserName = NULL;
3230 lpwhs->lpszUserName = heap_strdupW(userName);
3234 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3238 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3239 lpwhs->lpszServerName = heap_strdupW(hostName);
3240 lpwhs->nServerPort = urlComponents.nPort;
3242 NETCON_close(&lpwhr->netConnection);
3243 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3246 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3247 if (res != ERROR_SUCCESS)
3250 lpwhr->read_pos = lpwhr->read_size = 0;
3251 lpwhr->read_chunked = FALSE;
3255 TRACE("Redirect through proxy\n");
3258 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3259 lpwhr->lpszPath=NULL;
3265 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3266 if (rc != E_POINTER)
3267 needed = strlenW(path)+1;
3268 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3269 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3270 URL_ESCAPE_SPACES_ONLY);
3273 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3274 strcpyW(lpwhr->lpszPath,path);
3278 /* Remove custom content-type/length headers on redirects. */
3279 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3281 HTTP_DeleteCustomHeader(lpwhr, index);
3282 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3284 HTTP_DeleteCustomHeader(lpwhr, index);
3286 return ERROR_SUCCESS;
3289 /***********************************************************************
3290 * HTTP_build_req (internal)
3292 * concatenate all the strings in the request together
3294 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3299 for( t = list; *t ; t++ )
3300 len += strlenW( *t );
3303 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3306 for( t = list; *t ; t++ )
3312 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3315 LPWSTR requestString;
3321 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3322 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3323 http_session_t *lpwhs = lpwhr->lpHttpSession;
3327 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3328 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3329 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3330 HeapFree( GetProcessHeap(), 0, lpszPath );
3332 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3333 NULL, 0, NULL, NULL );
3334 len--; /* the nul terminator isn't needed */
3335 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3336 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3337 ascii_req, len, NULL, NULL );
3338 HeapFree( GetProcessHeap(), 0, requestString );
3340 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3342 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3343 HeapFree( GetProcessHeap(), 0, ascii_req );
3344 if (res != ERROR_SUCCESS)
3347 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3349 return ERROR_HTTP_INVALID_HEADER;
3351 return ERROR_SUCCESS;
3354 static void HTTP_InsertCookies(http_request_t *lpwhr)
3356 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3357 LPWSTR lpszCookies, lpszUrl = NULL;
3358 DWORD nCookieSize, size;
3359 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3361 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3362 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3363 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3365 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3368 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3370 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3371 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3373 cnt += sprintfW(lpszCookies, szCookie);
3374 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3375 strcatW(lpszCookies, szCrLf);
3377 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3378 HeapFree(GetProcessHeap(), 0, lpszCookies);
3381 HeapFree(GetProcessHeap(), 0, lpszUrl);
3384 /***********************************************************************
3385 * HTTP_HttpSendRequestW (internal)
3387 * Sends the specified request to the HTTP server
3394 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3395 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3396 DWORD dwContentLength, BOOL bEndRequest)
3399 BOOL redirected = FALSE;
3400 LPWSTR requestString = NULL;
3403 INTERNET_ASYNC_RESULT iar;
3404 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3405 static const WCHAR szContentLength[] =
3406 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3407 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3410 TRACE("--> %p\n", lpwhr);
3412 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3414 /* if the verb is NULL default to GET */
3415 if (!lpwhr->lpszVerb)
3416 lpwhr->lpszVerb = heap_strdupW(szGET);
3418 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3420 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3421 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3422 lpwhr->dwBytesToWrite = dwContentLength;
3424 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3426 WCHAR *agent_header;
3427 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3430 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3431 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3432 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3434 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3435 HeapFree(GetProcessHeap(), 0, agent_header);
3437 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3439 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3440 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3442 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3444 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3445 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3446 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3456 /* like native, just in case the caller forgot to call InternetReadFile
3457 * for all the data */
3458 HTTP_DrainContent(lpwhr);
3459 lpwhr->dwContentRead = 0;
3461 if (TRACE_ON(wininet))
3463 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3464 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3468 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3470 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3472 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3473 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3475 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3476 HTTP_InsertCookies(lpwhr);
3478 /* add the headers the caller supplied */
3479 if( lpszHeaders && dwHeaderLength )
3481 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3482 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3485 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3487 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3488 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3489 HeapFree(GetProcessHeap(), 0, url);
3492 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3495 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3497 /* Send the request and store the results */
3498 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3501 /* send the request as ASCII, tack on the optional data */
3502 if (!lpOptional || redirected)
3503 dwOptionalLength = 0;
3504 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3505 NULL, 0, NULL, NULL );
3506 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3507 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3508 ascii_req, len, NULL, NULL );
3510 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3511 len = (len + dwOptionalLength - 1);
3513 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3515 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3516 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3518 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3519 HeapFree( GetProcessHeap(), 0, ascii_req );
3521 lpwhr->dwBytesWritten = dwOptionalLength;
3523 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3524 INTERNET_STATUS_REQUEST_SENT,
3525 &len, sizeof(DWORD));
3532 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3533 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3535 if (res != ERROR_SUCCESS)
3538 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3540 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3541 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3544 HTTP_ProcessCookies(lpwhr);
3546 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3548 dwBufferSize = sizeof(dwStatusCode);
3549 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3550 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3553 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3555 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3556 dwBufferSize=sizeof(szNewLocation);
3557 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3558 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3560 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3562 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3563 lpwhr->lpszVerb = heap_strdupW(szGET);
3565 HTTP_DrainContent(lpwhr);
3566 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3568 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3569 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3570 res = HTTP_HandleRedirect(lpwhr, new_url);
3571 if (res == ERROR_SUCCESS)
3573 HeapFree(GetProcessHeap(), 0, requestString);
3576 HeapFree( GetProcessHeap(), 0, new_url );
3581 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3583 WCHAR szAuthValue[2048];
3585 if (dwStatusCode == HTTP_STATUS_DENIED)
3587 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3589 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3591 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3593 lpwhr->lpHttpSession->lpszUserName,
3594 lpwhr->lpHttpSession->lpszPassword,
3597 HeapFree(GetProcessHeap(), 0, requestString);
3603 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3606 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3608 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3609 &lpwhr->pProxyAuthInfo,
3610 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3611 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3622 res = ERROR_SUCCESS;
3626 if(res == ERROR_SUCCESS) {
3627 WCHAR url[INTERNET_MAX_URL_LENGTH];
3628 WCHAR cacheFileName[MAX_PATH+1];
3631 b = HTTP_GetRequestURL(lpwhr, url);
3633 WARN("Could not get URL\n");
3637 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3639 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3640 CloseHandle(lpwhr->hCacheFile);
3642 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3643 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3644 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3645 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3646 WARN("Could not create file: %u\n", GetLastError());
3647 lpwhr->hCacheFile = NULL;
3650 WARN("Could not create cache entry: %08x\n", GetLastError());
3656 HeapFree(GetProcessHeap(), 0, requestString);
3658 /* TODO: send notification for P3P header */
3660 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3662 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3663 HTTP_ReceiveRequestData(lpwhr, TRUE);
3666 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3669 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3670 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3671 sizeof(INTERNET_ASYNC_RESULT));
3679 /***********************************************************************
3681 * Helper functions for the HttpSendRequest(Ex) functions
3684 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3686 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3687 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3689 TRACE("%p\n", lpwhr);
3691 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3692 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3693 req->dwContentLength, req->bEndRequest);
3695 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3699 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3703 INTERNET_ASYNC_RESULT iar;
3704 DWORD res = ERROR_SUCCESS;
3706 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3707 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3709 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3711 res = ERROR_HTTP_HEADER_NOT_FOUND;
3713 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3714 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3716 /* process cookies here. Is this right? */
3717 HTTP_ProcessCookies(lpwhr);
3719 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3721 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3723 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3724 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3725 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3727 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3728 dwBufferSize=sizeof(szNewLocation);
3729 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3731 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3733 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3734 lpwhr->lpszVerb = heap_strdupW(szGET);
3736 HTTP_DrainContent(lpwhr);
3737 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3739 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3740 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3741 res = HTTP_HandleRedirect(lpwhr, new_url);
3742 if (res == ERROR_SUCCESS)
3743 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3744 HeapFree( GetProcessHeap(), 0, new_url );
3750 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3753 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3754 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3755 sizeof(INTERNET_ASYNC_RESULT));
3759 /***********************************************************************
3760 * HttpEndRequestA (WININET.@)
3762 * Ends an HTTP request that was started by HttpSendRequestEx
3765 * TRUE if successful
3769 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3770 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3772 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3776 SetLastError(ERROR_INVALID_PARAMETER);
3780 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3783 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3785 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3786 http_request_t *lpwhr = (http_request_t*)work->hdr;
3788 TRACE("%p\n", lpwhr);
3790 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3793 /***********************************************************************
3794 * HttpEndRequestW (WININET.@)
3796 * Ends an HTTP request that was started by HttpSendRequestEx
3799 * TRUE if successful
3803 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3804 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3806 http_request_t *lpwhr;
3813 SetLastError(ERROR_INVALID_PARAMETER);
3817 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3819 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3821 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3823 WININET_Release( &lpwhr->hdr );
3826 lpwhr->hdr.dwFlags |= dwFlags;
3828 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3831 struct WORKREQ_HTTPENDREQUESTW *request;
3833 work.asyncproc = AsyncHttpEndRequestProc;
3834 work.hdr = WININET_AddRef( &lpwhr->hdr );
3836 request = &work.u.HttpEndRequestW;
3837 request->dwFlags = dwFlags;
3838 request->dwContext = dwContext;
3840 INTERNET_AsyncCall(&work);
3841 res = ERROR_IO_PENDING;
3844 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3846 WININET_Release( &lpwhr->hdr );
3847 TRACE("%u <--\n", res);
3848 if(res != ERROR_SUCCESS)
3850 return res == ERROR_SUCCESS;
3853 /***********************************************************************
3854 * HttpSendRequestExA (WININET.@)
3856 * Sends the specified request to the HTTP server and allows chunked
3861 * Failure: FALSE, call GetLastError() for more information.
3863 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3864 LPINTERNET_BUFFERSA lpBuffersIn,
3865 LPINTERNET_BUFFERSA lpBuffersOut,
3866 DWORD dwFlags, DWORD_PTR dwContext)
3868 INTERNET_BUFFERSW BuffersInW;
3871 LPWSTR header = NULL;
3873 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3874 lpBuffersOut, dwFlags, dwContext);
3878 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3879 if (lpBuffersIn->lpcszHeader)
3881 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3882 lpBuffersIn->dwHeadersLength,0,0);
3883 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3884 if (!(BuffersInW.lpcszHeader = header))
3886 SetLastError(ERROR_OUTOFMEMORY);
3889 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3890 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3894 BuffersInW.lpcszHeader = NULL;
3895 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3896 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3897 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3898 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3899 BuffersInW.Next = NULL;
3902 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3904 HeapFree(GetProcessHeap(),0,header);
3909 /***********************************************************************
3910 * HttpSendRequestExW (WININET.@)
3912 * Sends the specified request to the HTTP server and allows chunked
3917 * Failure: FALSE, call GetLastError() for more information.
3919 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3920 LPINTERNET_BUFFERSW lpBuffersIn,
3921 LPINTERNET_BUFFERSW lpBuffersOut,
3922 DWORD dwFlags, DWORD_PTR dwContext)
3924 http_request_t *lpwhr;
3925 http_session_t *lpwhs;
3929 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3930 lpBuffersOut, dwFlags, dwContext);
3932 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3934 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3936 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3940 lpwhs = lpwhr->lpHttpSession;
3941 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3942 hIC = lpwhs->lpAppInfo;
3943 assert(hIC->hdr.htype == WH_HINIT);
3945 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3947 WORKREQUEST workRequest;
3948 struct WORKREQ_HTTPSENDREQUESTW *req;
3950 workRequest.asyncproc = AsyncHttpSendRequestProc;
3951 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3952 req = &workRequest.u.HttpSendRequestW;
3957 if (lpBuffersIn->lpcszHeader)
3959 if (lpBuffersIn->dwHeadersLength == ~0u)
3960 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3962 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3964 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3965 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3967 else req->lpszHeader = NULL;
3969 req->dwHeaderLength = size / sizeof(WCHAR);
3970 req->lpOptional = lpBuffersIn->lpvBuffer;
3971 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3972 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3976 req->lpszHeader = NULL;
3977 req->dwHeaderLength = 0;
3978 req->lpOptional = NULL;
3979 req->dwOptionalLength = 0;
3980 req->dwContentLength = 0;
3983 req->bEndRequest = FALSE;
3985 INTERNET_AsyncCall(&workRequest);
3987 * This is from windows.
3989 res = ERROR_IO_PENDING;
3994 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3995 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3996 lpBuffersIn->dwBufferTotal, FALSE);
3998 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4003 WININET_Release( &lpwhr->hdr );
4007 return res == ERROR_SUCCESS;
4010 /***********************************************************************
4011 * HttpSendRequestW (WININET.@)
4013 * Sends the specified request to the HTTP server
4020 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4021 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4023 http_request_t *lpwhr;
4024 http_session_t *lpwhs = NULL;
4025 appinfo_t *hIC = NULL;
4026 DWORD res = ERROR_SUCCESS;
4028 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4029 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4031 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4032 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4034 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4038 lpwhs = lpwhr->lpHttpSession;
4039 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4041 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4045 hIC = lpwhs->lpAppInfo;
4046 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4048 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4052 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4054 WORKREQUEST workRequest;
4055 struct WORKREQ_HTTPSENDREQUESTW *req;
4057 workRequest.asyncproc = AsyncHttpSendRequestProc;
4058 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4059 req = &workRequest.u.HttpSendRequestW;
4064 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4065 else size = dwHeaderLength * sizeof(WCHAR);
4067 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4068 memcpy(req->lpszHeader, lpszHeaders, size);
4071 req->lpszHeader = 0;
4072 req->dwHeaderLength = dwHeaderLength;
4073 req->lpOptional = lpOptional;
4074 req->dwOptionalLength = dwOptionalLength;
4075 req->dwContentLength = dwOptionalLength;
4076 req->bEndRequest = TRUE;
4078 INTERNET_AsyncCall(&workRequest);
4080 * This is from windows.
4082 res = ERROR_IO_PENDING;
4086 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4087 dwHeaderLength, lpOptional, dwOptionalLength,
4088 dwOptionalLength, TRUE);
4092 WININET_Release( &lpwhr->hdr );
4095 return res == ERROR_SUCCESS;
4098 /***********************************************************************
4099 * HttpSendRequestA (WININET.@)
4101 * Sends the specified request to the HTTP server
4108 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4109 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4112 LPWSTR szHeaders=NULL;
4113 DWORD nLen=dwHeaderLength;
4114 if(lpszHeaders!=NULL)
4116 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4117 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4118 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4120 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4121 HeapFree(GetProcessHeap(),0,szHeaders);
4125 /***********************************************************************
4126 * HTTPSESSION_Destroy (internal)
4128 * Deallocate session handle
4131 static void HTTPSESSION_Destroy(object_header_t *hdr)
4133 http_session_t *lpwhs = (http_session_t*) hdr;
4135 TRACE("%p\n", lpwhs);
4137 WININET_Release(&lpwhs->lpAppInfo->hdr);
4139 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4140 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4141 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4142 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4143 HeapFree(GetProcessHeap(), 0, lpwhs);
4146 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4149 case INTERNET_OPTION_HANDLE_TYPE:
4150 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4152 if (*size < sizeof(ULONG))
4153 return ERROR_INSUFFICIENT_BUFFER;
4155 *size = sizeof(DWORD);
4156 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4157 return ERROR_SUCCESS;
4160 return INET_QueryOption(option, buffer, size, unicode);
4163 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4165 http_session_t *ses = (http_session_t*)hdr;
4168 case INTERNET_OPTION_USERNAME:
4170 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4171 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4172 return ERROR_SUCCESS;
4174 case INTERNET_OPTION_PASSWORD:
4176 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4177 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4178 return ERROR_SUCCESS;
4183 return ERROR_INTERNET_INVALID_OPTION;
4186 static const object_vtbl_t HTTPSESSIONVtbl = {
4187 HTTPSESSION_Destroy,
4189 HTTPSESSION_QueryOption,
4190 HTTPSESSION_SetOption,
4199 /***********************************************************************
4200 * HTTP_Connect (internal)
4202 * Create http session handle
4205 * HINTERNET a session handle on success
4209 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4210 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4211 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4212 DWORD dwInternalFlags, HINTERNET *ret)
4214 http_session_t *lpwhs = NULL;
4215 HINTERNET handle = NULL;
4216 DWORD res = ERROR_SUCCESS;
4220 if (!lpszServerName || !lpszServerName[0])
4221 return ERROR_INVALID_PARAMETER;
4223 assert( hIC->hdr.htype == WH_HINIT );
4225 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4227 return ERROR_OUTOFMEMORY;
4230 * According to my tests. The name is not resolved until a request is sent
4233 lpwhs->hdr.htype = WH_HHTTPSESSION;
4234 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4235 lpwhs->hdr.dwFlags = dwFlags;
4236 lpwhs->hdr.dwContext = dwContext;
4237 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4238 lpwhs->hdr.refs = 1;
4239 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4241 WININET_AddRef( &hIC->hdr );
4242 lpwhs->lpAppInfo = hIC;
4243 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4245 handle = WININET_AllocHandle( &lpwhs->hdr );
4248 ERR("Failed to alloc handle\n");
4249 res = ERROR_OUTOFMEMORY;
4253 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4254 if(strchrW(hIC->lpszProxy, ' '))
4255 FIXME("Several proxies not implemented.\n");
4256 if(hIC->lpszProxyBypass)
4257 FIXME("Proxy bypass is ignored.\n");
4259 if (lpszServerName && lpszServerName[0])
4261 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4262 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4264 if (lpszUserName && lpszUserName[0])
4265 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4266 if (lpszPassword && lpszPassword[0])
4267 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4268 lpwhs->nServerPort = nServerPort;
4269 lpwhs->nHostPort = nServerPort;
4271 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4272 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4274 INTERNET_SendCallback(&hIC->hdr, dwContext,
4275 INTERNET_STATUS_HANDLE_CREATED, &handle,
4281 WININET_Release( &lpwhs->hdr );
4284 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4288 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4290 if(res == ERROR_SUCCESS)
4296 /***********************************************************************
4297 * HTTP_OpenConnection (internal)
4299 * Connect to a web server
4306 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4308 http_session_t *lpwhs;
4309 appinfo_t *hIC = NULL;
4310 char szaddr[INET6_ADDRSTRLEN];
4312 DWORD res = ERROR_SUCCESS;
4317 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4319 res = ERROR_INVALID_PARAMETER;
4323 if (NETCON_connected(&lpwhr->netConnection))
4325 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4327 lpwhs = lpwhr->lpHttpSession;
4329 hIC = lpwhs->lpAppInfo;
4330 switch (lpwhs->socketAddress.ss_family)
4333 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4336 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4339 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4340 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4342 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4343 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4344 INTERNET_STATUS_CONNECTING_TO_SERVER,
4348 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4349 if (res != ERROR_SUCCESS)
4351 WARN("Socket creation failed: %u\n", res);
4355 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4357 if(res != ERROR_SUCCESS)
4360 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4362 /* Note: we differ from Microsoft's WinINet here. they seem to have
4363 * a bug that causes no status callbacks to be sent when starting
4364 * a tunnel to a proxy server using the CONNECT verb. i believe our
4365 * behaviour to be more correct and to not cause any incompatibilities
4366 * because using a secure connection through a proxy server is a rare
4367 * case that would be hard for anyone to depend on */
4368 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4371 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4372 if(res != ERROR_SUCCESS)
4374 WARN("Couldn't connect securely to host\n");
4379 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4380 INTERNET_STATUS_CONNECTED_TO_SERVER,
4381 szaddr, strlen(szaddr)+1);
4384 lpwhr->read_pos = lpwhr->read_size = 0;
4385 lpwhr->read_chunked = FALSE;
4387 TRACE("%d <--\n", res);
4392 /***********************************************************************
4393 * HTTP_clear_response_headers (internal)
4395 * clear out any old response headers
4397 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4401 for( i=0; i<lpwhr->nCustHeaders; i++)
4403 if( !lpwhr->pCustHeaders[i].lpszField )
4405 if( !lpwhr->pCustHeaders[i].lpszValue )
4407 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4409 HTTP_DeleteCustomHeader( lpwhr, i );
4414 /***********************************************************************
4415 * HTTP_GetResponseHeaders (internal)
4417 * Read server response
4424 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4427 WCHAR buffer[MAX_REPLY_LEN];
4428 DWORD buflen = MAX_REPLY_LEN;
4429 BOOL bSuccess = FALSE;
4431 char bufferA[MAX_REPLY_LEN];
4432 LPWSTR status_code = NULL, status_text = NULL;
4433 DWORD cchMaxRawHeaders = 1024;
4434 LPWSTR lpszRawHeaders = NULL;
4436 DWORD cchRawHeaders = 0;
4437 BOOL codeHundred = FALSE;
4441 /* clear old response headers (eg. from a redirect response) */
4442 if (clear) HTTP_clear_response_headers( lpwhr );
4444 if (!NETCON_connected(&lpwhr->netConnection))
4448 static const WCHAR szHundred[] = {'1','0','0',0};
4450 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4452 buflen = MAX_REPLY_LEN;
4453 if (!read_line(lpwhr, bufferA, &buflen))
4456 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4457 /* check is this a status code line? */
4458 if (!strncmpW(buffer, g_szHttp1_0, 4))
4460 /* split the version from the status code */
4461 status_code = strchrW( buffer, ' ' );
4466 /* split the status code from the status text */
4467 status_text = strchrW( status_code, ' ' );
4472 TRACE("version [%s] status code [%s] status text [%s]\n",
4473 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4475 codeHundred = (!strcmpW(status_code, szHundred));
4477 else if (!codeHundred)
4479 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4481 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4482 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4484 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4485 lpwhr->lpszStatusText = heap_strdupW(szOK);
4487 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4488 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4493 } while (codeHundred);
4495 /* Add status code */
4496 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4497 HTTP_ADDHDR_FLAG_REPLACE);
4499 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4500 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4502 lpwhr->lpszVersion = heap_strdupW(buffer);
4503 lpwhr->lpszStatusText = heap_strdupW(status_text);
4505 /* Restore the spaces */
4506 *(status_code-1) = ' ';
4507 *(status_text-1) = ' ';
4509 /* regenerate raw headers */
4510 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4511 if (!lpszRawHeaders) goto lend;
4513 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4514 cchMaxRawHeaders *= 2;
4515 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4516 if (temp == NULL) goto lend;
4517 lpszRawHeaders = temp;
4518 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4519 cchRawHeaders += (buflen-1);
4520 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4521 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4522 lpszRawHeaders[cchRawHeaders] = '\0';
4524 /* Parse each response line */
4527 buflen = MAX_REPLY_LEN;
4528 if (read_line(lpwhr, bufferA, &buflen))
4530 LPWSTR * pFieldAndValue;
4532 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4534 if (!bufferA[0]) break;
4535 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4537 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4540 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4541 cchMaxRawHeaders *= 2;
4542 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4543 if (temp == NULL) goto lend;
4544 lpszRawHeaders = temp;
4545 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4546 cchRawHeaders += (buflen-1);
4547 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4548 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4549 lpszRawHeaders[cchRawHeaders] = '\0';
4551 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4552 HTTP_ADDREQ_FLAG_ADD );
4554 HTTP_FreeTokens(pFieldAndValue);
4565 /* make sure the response header is terminated with an empty line. Some apps really
4566 truly care about that empty line being there for some reason. Just add it to the
4568 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4570 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4571 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4572 if (temp == NULL) goto lend;
4573 lpszRawHeaders = temp;
4576 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4578 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4579 lpwhr->lpszRawHeaders = lpszRawHeaders;
4580 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4590 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4595 /***********************************************************************
4596 * HTTP_InterpretHttpHeader (internal)
4598 * Parse server response
4602 * Pointer to array of field, value, NULL on success.
4605 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4607 LPWSTR * pTokenPair;
4611 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4613 pszColon = strchrW(buffer, ':');
4614 /* must have two tokens */
4617 HTTP_FreeTokens(pTokenPair);
4619 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4623 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4626 HTTP_FreeTokens(pTokenPair);
4629 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4630 pTokenPair[0][pszColon - buffer] = '\0';
4634 len = strlenW(pszColon);
4635 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4638 HTTP_FreeTokens(pTokenPair);
4641 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4643 strip_spaces(pTokenPair[0]);
4644 strip_spaces(pTokenPair[1]);
4646 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4650 /***********************************************************************
4651 * HTTP_ProcessHeader (internal)
4653 * Stuff header into header tables according to <dwModifier>
4657 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4659 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4661 LPHTTPHEADERW lphttpHdr = NULL;
4663 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4664 DWORD res = ERROR_HTTP_INVALID_HEADER;
4666 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4668 /* REPLACE wins out over ADD */
4669 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4670 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4672 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4675 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4679 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4680 return ERROR_HTTP_INVALID_HEADER;
4681 lphttpHdr = &lpwhr->pCustHeaders[index];
4687 hdr.lpszField = (LPWSTR)field;
4688 hdr.lpszValue = (LPWSTR)value;
4689 hdr.wFlags = hdr.wCount = 0;
4691 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4692 hdr.wFlags |= HDR_ISREQUEST;
4694 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4696 /* no value to delete */
4697 else return ERROR_SUCCESS;
4699 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4700 lphttpHdr->wFlags |= HDR_ISREQUEST;
4702 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4704 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4706 HTTP_DeleteCustomHeader( lpwhr, index );
4712 hdr.lpszField = (LPWSTR)field;
4713 hdr.lpszValue = (LPWSTR)value;
4714 hdr.wFlags = hdr.wCount = 0;
4716 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4717 hdr.wFlags |= HDR_ISREQUEST;
4719 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4722 return ERROR_SUCCESS;
4724 else if (dwModifier & COALESCEFLAGS)
4729 INT origlen = strlenW(lphttpHdr->lpszValue);
4730 INT valuelen = strlenW(value);
4732 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4735 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4737 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4740 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4743 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4745 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4748 lphttpHdr->lpszValue = lpsztmp;
4749 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4752 lphttpHdr->lpszValue[origlen] = ch;
4754 lphttpHdr->lpszValue[origlen] = ' ';
4758 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4759 lphttpHdr->lpszValue[len] = '\0';
4760 res = ERROR_SUCCESS;
4764 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4765 res = ERROR_OUTOFMEMORY;
4768 TRACE("<-- %d\n", res);
4773 /***********************************************************************
4774 * HTTP_FinishedReading (internal)
4776 * Called when all content from server has been read by client.
4779 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4781 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4788 HTTPREQ_CloseConnection(&lpwhr->hdr);
4791 /* FIXME: store data in the URL cache here */
4797 /***********************************************************************
4798 * HTTP_GetCustomHeaderIndex (internal)
4800 * Return index of custom header from header array
4803 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4804 int requested_index, BOOL request_only)
4808 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4810 for (index = 0; index < lpwhr->nCustHeaders; index++)
4812 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4815 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4818 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4821 if (requested_index == 0)
4826 if (index >= lpwhr->nCustHeaders)
4829 TRACE("Return: %d\n", index);
4834 /***********************************************************************
4835 * HTTP_InsertCustomHeader (internal)
4837 * Insert header into array
4840 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4843 LPHTTPHEADERW lph = NULL;
4845 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4846 count = lpwhr->nCustHeaders + 1;
4848 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4850 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4853 return ERROR_OUTOFMEMORY;
4855 lpwhr->pCustHeaders = lph;
4856 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4857 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4858 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4859 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4860 lpwhr->nCustHeaders++;
4862 return ERROR_SUCCESS;
4866 /***********************************************************************
4867 * HTTP_DeleteCustomHeader (internal)
4869 * Delete header from array
4870 * If this function is called, the indexs may change.
4872 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4874 if( lpwhr->nCustHeaders <= 0 )
4876 if( index >= lpwhr->nCustHeaders )
4878 lpwhr->nCustHeaders--;
4880 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4881 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4883 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4884 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4885 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4891 /***********************************************************************
4892 * HTTP_VerifyValidHeader (internal)
4894 * Verify the given header is not invalid for the given http request
4897 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4899 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4900 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4901 return ERROR_HTTP_INVALID_HEADER;
4903 return ERROR_SUCCESS;
4906 /***********************************************************************
4907 * IsHostInProxyBypassList (@)
4912 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4914 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);