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>
60 #define NO_SHLWAPI_STREAM
61 #define NO_SHLWAPI_REG
62 #define NO_SHLWAPI_STRFCNS
63 #define NO_SHLWAPI_GDI
69 #include "wine/debug.h"
70 #include "wine/exception.h"
71 #include "wine/unicode.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
75 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
76 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
77 static const WCHAR szOK[] = {'O','K',0};
78 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
79 static const WCHAR hostW[] = { 'H','o','s','t',0 };
80 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
83 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
84 static const WCHAR szGET[] = { 'G','E','T', 0 };
85 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
86 static const WCHAR szCrLf[] = {'\r','\n', 0};
88 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
89 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
90 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
91 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
92 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
93 static const WCHAR szAge[] = { 'A','g','e',0 };
94 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
95 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
96 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
97 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
98 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
99 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
100 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
101 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
102 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
103 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
104 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
105 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 };
106 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
107 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
108 static const WCHAR szDate[] = { 'D','a','t','e',0 };
109 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
110 static const WCHAR szETag[] = { 'E','T','a','g',0 };
111 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
112 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
113 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
114 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
116 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
117 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
119 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
120 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
121 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
122 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
123 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
124 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
125 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
126 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
127 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
128 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
129 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
130 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
131 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
132 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 };
133 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
134 static const WCHAR szURI[] = { 'U','R','I',0 };
135 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
136 static const WCHAR szVary[] = { 'V','a','r','y',0 };
137 static const WCHAR szVia[] = { 'V','i','a',0 };
138 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
139 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
141 #define MAXHOSTNAME 100
142 #define MAX_FIELD_VALUE_LEN 256
143 #define MAX_FIELD_LEN 256
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
168 unsigned int auth_data_len;
169 BOOL finished; /* finished authenticating */
173 struct gzip_stream_t {
183 typedef struct _basicAuthorizationData
190 UINT authorizationLen;
191 } basicAuthorizationData;
193 typedef struct _authorizationData
207 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
208 static struct list authorizationCache = LIST_INIT(authorizationCache);
210 static CRITICAL_SECTION authcache_cs;
211 static CRITICAL_SECTION_DEBUG critsect_debug =
214 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
215 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
217 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
219 static DWORD HTTP_OpenConnection(http_request_t *req);
220 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
221 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
222 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
223 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
224 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
225 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
226 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
227 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
228 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
229 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
230 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
231 static void HTTP_DrainContent(http_request_t *req);
232 static BOOL HTTP_FinishedReading(http_request_t *req);
234 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
237 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
238 if (HeaderIndex == -1)
241 return &req->custHeaders[HeaderIndex];
246 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
248 return HeapAlloc(GetProcessHeap(), 0, items*size);
251 static void wininet_zfree(voidpf opaque, voidpf address)
253 HeapFree(GetProcessHeap(), 0, address);
256 static void init_gzip_stream(http_request_t *req)
258 gzip_stream_t *gzip_stream;
261 gzip_stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(gzip_stream_t));
262 gzip_stream->zstream.zalloc = wininet_zalloc;
263 gzip_stream->zstream.zfree = wininet_zfree;
265 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
267 ERR("inflateInit failed: %d\n", zres);
268 HeapFree(GetProcessHeap(), 0, gzip_stream);
272 req->gzip_stream = gzip_stream;
274 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
276 HTTP_DeleteCustomHeader(req, index);
281 static void init_gzip_stream(http_request_t *req)
283 ERR("gzip stream not supported, missing zlib.\n");
288 /* set the request content length based on the headers */
289 static DWORD set_content_length( http_request_t *request )
291 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
295 size = sizeof(request->contentLength);
296 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
297 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
298 request->contentLength = ~0u;
300 size = sizeof(encoding);
301 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
302 !strcmpiW(encoding, szChunked))
304 request->contentLength = ~0u;
305 request->read_chunked = TRUE;
308 if(request->decoding) {
311 static const WCHAR gzipW[] = {'g','z','i','p',0};
313 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
314 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
315 init_gzip_stream(request);
318 return request->contentLength;
321 /***********************************************************************
322 * HTTP_Tokenize (internal)
324 * Tokenize a string, allocating memory for the tokens.
326 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
328 LPWSTR * token_array;
335 /* empty string has no tokens */
339 for (i = 0; string[i]; i++)
341 if (!strncmpW(string+i, token_string, strlenW(token_string)))
345 /* we want to skip over separators, but not the null terminator */
346 for (j = 0; j < strlenW(token_string) - 1; j++)
354 /* add 1 for terminating NULL */
355 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
356 token_array[tokens] = NULL;
359 for (i = 0; i < tokens; i++)
362 next_token = strstrW(string, token_string);
363 if (!next_token) next_token = string+strlenW(string);
364 len = next_token - string;
365 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
366 memcpy(token_array[i], string, len*sizeof(WCHAR));
367 token_array[i][len] = '\0';
368 string = next_token+strlenW(token_string);
373 /***********************************************************************
374 * HTTP_FreeTokens (internal)
376 * Frees memory returned from HTTP_Tokenize.
378 static void HTTP_FreeTokens(LPWSTR * token_array)
381 for (i = 0; token_array[i]; i++)
382 HeapFree(GetProcessHeap(), 0, token_array[i]);
383 HeapFree(GetProcessHeap(), 0, token_array);
386 static void HTTP_FixURL(http_request_t *request)
388 static const WCHAR szSlash[] = { '/',0 };
389 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
391 /* If we don't have a path we set it to root */
392 if (NULL == request->path)
393 request->path = heap_strdupW(szSlash);
394 else /* remove \r and \n*/
396 int nLen = strlenW(request->path);
397 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
400 request->path[nLen]='\0';
402 /* Replace '\' with '/' */
405 if (request->path[nLen] == '\\') request->path[nLen]='/';
409 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
410 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
411 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
413 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
414 (strlenW(request->path) + 2)*sizeof(WCHAR));
416 strcpyW(fixurl + 1, request->path);
417 HeapFree( GetProcessHeap(), 0, request->path );
418 request->path = fixurl;
422 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
424 LPWSTR requestString;
430 static const WCHAR szSpace[] = { ' ',0 };
431 static const WCHAR szColon[] = { ':',' ',0 };
432 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
434 /* allocate space for an array of all the string pointers to be added */
435 len = (request->nCustHeaders)*4 + 10;
436 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
438 /* add the verb, path and HTTP version string */
446 /* Append custom request headers */
447 for (i = 0; i < request->nCustHeaders; i++)
449 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
452 req[n++] = request->custHeaders[i].lpszField;
454 req[n++] = request->custHeaders[i].lpszValue;
456 TRACE("Adding custom header %s (%s)\n",
457 debugstr_w(request->custHeaders[i].lpszField),
458 debugstr_w(request->custHeaders[i].lpszValue));
463 ERR("oops. buffer overrun\n");
466 requestString = HTTP_build_req( req, 4 );
467 HeapFree( GetProcessHeap(), 0, req );
470 * Set (header) termination string for request
471 * Make sure there's exactly two new lines at the end of the request
473 p = &requestString[strlenW(requestString)-1];
474 while ( (*p == '\n') || (*p == '\r') )
476 strcpyW( p+1, sztwocrlf );
478 return requestString;
481 static void HTTP_ProcessCookies( http_request_t *request )
485 LPHTTPHEADERW setCookieHeader;
487 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies, FALSE)) != -1)
489 setCookieHeader = &request->custHeaders[HeaderIndex];
491 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
494 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
498 Host = HTTP_GetHeader(request, hostW);
499 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(request->path);
500 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
501 sprintfW(buf_url, szFmt, Host->lpszValue, request->path);
502 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
504 HeapFree(GetProcessHeap(), 0, buf_url);
510 static void strip_spaces(LPWSTR start)
515 while (*str == ' ' && *str != '\0')
519 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
521 end = start + strlenW(start) - 1;
522 while (end >= start && *end == ' ')
529 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
531 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
532 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
534 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
535 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
536 if (is_basic && pszRealm)
539 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
543 token = strchrW(ptr,'=');
547 while (*realm == ' ' && *realm != '\0')
549 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
550 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
553 while (*token == ' ' && *token != '\0')
557 *pszRealm = heap_strdupW(token);
558 strip_spaces(*pszRealm);
565 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
567 if (!authinfo) return;
569 if (SecIsValidHandle(&authinfo->ctx))
570 DeleteSecurityContext(&authinfo->ctx);
571 if (SecIsValidHandle(&authinfo->cred))
572 FreeCredentialsHandle(&authinfo->cred);
574 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
575 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
576 HeapFree(GetProcessHeap(), 0, authinfo);
579 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
581 basicAuthorizationData *ad;
584 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
586 EnterCriticalSection(&authcache_cs);
587 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
589 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
591 TRACE("Authorization found in cache\n");
592 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->authorizationLen);
593 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
594 rc = ad->authorizationLen;
598 LeaveCriticalSection(&authcache_cs);
602 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
605 basicAuthorizationData* ad = NULL;
607 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
609 EnterCriticalSection(&authcache_cs);
610 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
612 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
613 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
622 TRACE("Found match in cache, replacing\n");
623 HeapFree(GetProcessHeap(),0,ad->authorization);
624 ad->authorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
625 memcpy(ad->authorization, auth_data, auth_data_len);
626 ad->authorizationLen = auth_data_len;
630 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
631 ad->host = heap_strdupW(host);
632 ad->host = heap_strdupW(realm);
633 ad->authorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
634 memcpy(ad->authorization, auth_data, auth_data_len);
635 ad->authorizationLen = auth_data_len;
636 list_add_head(&basicAuthorizationCache,&ad->entry);
637 TRACE("authorization cached\n");
639 LeaveCriticalSection(&authcache_cs);
642 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
643 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
645 authorizationData *ad;
647 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
649 EnterCriticalSection(&authcache_cs);
650 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
651 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
652 TRACE("Authorization found in cache\n");
654 nt_auth_identity->User = heap_strdupW(ad->user);
655 nt_auth_identity->Password = heap_strdupW(ad->password);
656 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
657 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
658 (!nt_auth_identity->Domain && ad->domain_len)) {
659 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
660 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
661 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
665 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
666 nt_auth_identity->UserLength = ad->user_len;
667 nt_auth_identity->PasswordLength = ad->password_len;
668 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
669 nt_auth_identity->DomainLength = ad->domain_len;
670 LeaveCriticalSection(&authcache_cs);
674 LeaveCriticalSection(&authcache_cs);
679 static void cache_authorization(LPWSTR host, LPWSTR scheme,
680 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
682 authorizationData *ad;
685 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
687 EnterCriticalSection(&authcache_cs);
688 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
689 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
695 HeapFree(GetProcessHeap(), 0, ad->user);
696 HeapFree(GetProcessHeap(), 0, ad->password);
697 HeapFree(GetProcessHeap(), 0, ad->domain);
699 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
701 LeaveCriticalSection(&authcache_cs);
705 ad->host = heap_strdupW(host);
706 ad->scheme = heap_strdupW(scheme);
707 list_add_head(&authorizationCache, &ad->entry);
710 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
711 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
712 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
713 ad->user_len = nt_auth_identity->UserLength;
714 ad->password_len = nt_auth_identity->PasswordLength;
715 ad->domain_len = nt_auth_identity->DomainLength;
717 if(!ad->host || !ad->scheme || !ad->user || !ad->password
718 || (nt_auth_identity->Domain && !ad->domain)) {
719 HeapFree(GetProcessHeap(), 0, ad->host);
720 HeapFree(GetProcessHeap(), 0, ad->scheme);
721 HeapFree(GetProcessHeap(), 0, ad->user);
722 HeapFree(GetProcessHeap(), 0, ad->password);
723 HeapFree(GetProcessHeap(), 0, ad->domain);
724 list_remove(&ad->entry);
725 HeapFree(GetProcessHeap(), 0, ad);
728 LeaveCriticalSection(&authcache_cs);
731 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
732 struct HttpAuthInfo **ppAuthInfo,
733 LPWSTR domain_and_username, LPWSTR password,
736 SECURITY_STATUS sec_status;
737 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
739 LPWSTR szRealm = NULL;
741 TRACE("%s\n", debugstr_w(pszAuthValue));
748 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
752 SecInvalidateHandle(&pAuthInfo->cred);
753 SecInvalidateHandle(&pAuthInfo->ctx);
754 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
756 pAuthInfo->auth_data = NULL;
757 pAuthInfo->auth_data_len = 0;
758 pAuthInfo->finished = FALSE;
760 if (is_basic_auth_value(pszAuthValue,NULL))
762 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
763 pAuthInfo->scheme = heap_strdupW(szBasic);
764 if (!pAuthInfo->scheme)
766 HeapFree(GetProcessHeap(), 0, pAuthInfo);
773 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
775 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
776 if (!pAuthInfo->scheme)
778 HeapFree(GetProcessHeap(), 0, pAuthInfo);
782 if (domain_and_username)
784 WCHAR *user = strchrW(domain_and_username, '\\');
785 WCHAR *domain = domain_and_username;
787 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
789 pAuthData = &nt_auth_identity;
794 user = domain_and_username;
798 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
799 nt_auth_identity.User = user;
800 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
801 nt_auth_identity.Domain = domain;
802 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
803 nt_auth_identity.Password = password;
804 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
806 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
808 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
809 pAuthData = &nt_auth_identity;
811 /* use default credentials */
814 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
815 SECPKG_CRED_OUTBOUND, NULL,
817 NULL, &pAuthInfo->cred,
820 if(pAuthData && !domain_and_username) {
821 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
822 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
823 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
826 if (sec_status == SEC_E_OK)
828 PSecPkgInfoW sec_pkg_info;
829 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
830 if (sec_status == SEC_E_OK)
832 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
833 FreeContextBuffer(sec_pkg_info);
836 if (sec_status != SEC_E_OK)
838 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
839 debugstr_w(pAuthInfo->scheme), sec_status);
840 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
841 HeapFree(GetProcessHeap(), 0, pAuthInfo);
845 *ppAuthInfo = pAuthInfo;
847 else if (pAuthInfo->finished)
850 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
851 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
853 ERR("authentication scheme changed from %s to %s\n",
854 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
858 if (is_basic_auth_value(pszAuthValue,&szRealm))
862 char *auth_data = NULL;
863 UINT auth_data_len = 0;
865 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
867 if (!domain_and_username)
870 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
871 if (auth_data_len == 0)
873 HeapFree(GetProcessHeap(),0,szRealm);
879 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
880 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
882 /* length includes a nul terminator, which will be re-used for the ':' */
883 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
886 HeapFree(GetProcessHeap(),0,szRealm);
890 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
891 auth_data[userlen] = ':';
892 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
893 auth_data_len = userlen + 1 + passlen;
895 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
898 pAuthInfo->auth_data = auth_data;
899 pAuthInfo->auth_data_len = auth_data_len;
900 pAuthInfo->finished = TRUE;
901 HeapFree(GetProcessHeap(),0,szRealm);
908 SecBufferDesc out_desc, in_desc;
910 unsigned char *buffer;
911 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
912 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
914 in.BufferType = SECBUFFER_TOKEN;
918 in_desc.ulVersion = 0;
919 in_desc.cBuffers = 1;
920 in_desc.pBuffers = ∈
922 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
923 if (*pszAuthData == ' ')
926 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
927 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
928 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
931 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
933 out.BufferType = SECBUFFER_TOKEN;
934 out.cbBuffer = pAuthInfo->max_token;
935 out.pvBuffer = buffer;
937 out_desc.ulVersion = 0;
938 out_desc.cBuffers = 1;
939 out_desc.pBuffers = &out;
941 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
942 first ? NULL : &pAuthInfo->ctx,
943 first ? request->session->serverName : NULL,
944 context_req, 0, SECURITY_NETWORK_DREP,
945 in.pvBuffer ? &in_desc : NULL,
946 0, &pAuthInfo->ctx, &out_desc,
947 &pAuthInfo->attr, &pAuthInfo->exp);
948 if (sec_status == SEC_E_OK)
950 pAuthInfo->finished = TRUE;
951 pAuthInfo->auth_data = out.pvBuffer;
952 pAuthInfo->auth_data_len = out.cbBuffer;
953 TRACE("sending last auth packet\n");
955 else if (sec_status == SEC_I_CONTINUE_NEEDED)
957 pAuthInfo->auth_data = out.pvBuffer;
958 pAuthInfo->auth_data_len = out.cbBuffer;
959 TRACE("sending next auth packet\n");
963 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
964 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
965 destroy_authinfo(pAuthInfo);
974 /***********************************************************************
975 * HTTP_HttpAddRequestHeadersW (internal)
977 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
978 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
983 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
985 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
987 if( dwHeaderLength == ~0U )
988 len = strlenW(lpszHeader);
990 len = dwHeaderLength;
991 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
992 lstrcpynW( buffer, lpszHeader, len + 1);
998 LPWSTR * pFieldAndValue;
1000 lpszEnd = lpszStart;
1002 while (*lpszEnd != '\0')
1004 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1009 if (*lpszStart == '\0')
1012 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1015 lpszEnd++; /* Jump over newline */
1017 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1018 if (*lpszStart == '\0')
1020 /* Skip 0-length headers */
1021 lpszStart = lpszEnd;
1022 res = ERROR_SUCCESS;
1025 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1028 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1029 if (res == ERROR_SUCCESS)
1030 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1031 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1032 HTTP_FreeTokens(pFieldAndValue);
1035 lpszStart = lpszEnd;
1036 } while (res == ERROR_SUCCESS);
1038 HeapFree(GetProcessHeap(), 0, buffer);
1043 /***********************************************************************
1044 * HttpAddRequestHeadersW (WININET.@)
1046 * Adds one or more HTTP header to the request handler
1049 * On Windows if dwHeaderLength includes the trailing '\0', then
1050 * HttpAddRequestHeadersW() adds it too. However this results in an
1051 * invalid Http header which is rejected by some servers so we probably
1052 * don't need to match Windows on that point.
1059 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1060 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1062 http_request_t *request;
1063 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1065 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1070 request = (http_request_t*) get_handle_object( hHttpRequest );
1071 if (request && request->hdr.htype == WH_HHTTPREQ)
1072 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1074 WININET_Release( &request->hdr );
1076 if(res != ERROR_SUCCESS)
1078 return res == ERROR_SUCCESS;
1081 /***********************************************************************
1082 * HttpAddRequestHeadersA (WININET.@)
1084 * Adds one or more HTTP header to the request handler
1091 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1092 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1098 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1100 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1101 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1102 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1103 if( dwHeaderLength != ~0U )
1104 dwHeaderLength = len;
1106 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1108 HeapFree( GetProcessHeap(), 0, hdr );
1113 /***********************************************************************
1114 * HttpOpenRequestA (WININET.@)
1116 * Open a HTTP request handle
1119 * HINTERNET a HTTP request handle on success
1123 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1124 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1125 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1126 DWORD dwFlags, DWORD_PTR dwContext)
1128 LPWSTR szVerb = NULL, szObjectName = NULL;
1129 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1130 INT acceptTypesCount;
1131 HINTERNET rc = FALSE;
1134 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1135 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1136 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1137 dwFlags, dwContext);
1141 szVerb = heap_strdupAtoW(lpszVerb);
1148 szObjectName = heap_strdupAtoW(lpszObjectName);
1149 if ( !szObjectName )
1155 szVersion = heap_strdupAtoW(lpszVersion);
1162 szReferrer = heap_strdupAtoW(lpszReferrer);
1167 if (lpszAcceptTypes)
1169 acceptTypesCount = 0;
1170 types = lpszAcceptTypes;
1175 /* find out how many there are */
1176 if (*types && **types)
1178 TRACE("accept type: %s\n", debugstr_a(*types));
1184 WARN("invalid accept type pointer\n");
1189 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1190 if (!szAcceptTypes) goto end;
1192 acceptTypesCount = 0;
1193 types = lpszAcceptTypes;
1198 if (*types && **types)
1199 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1203 /* ignore invalid pointer */
1208 szAcceptTypes[acceptTypesCount] = NULL;
1211 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1212 szVersion, szReferrer,
1213 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1218 acceptTypesCount = 0;
1219 while (szAcceptTypes[acceptTypesCount])
1221 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1224 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1226 HeapFree(GetProcessHeap(), 0, szReferrer);
1227 HeapFree(GetProcessHeap(), 0, szVersion);
1228 HeapFree(GetProcessHeap(), 0, szObjectName);
1229 HeapFree(GetProcessHeap(), 0, szVerb);
1234 /***********************************************************************
1237 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1240 static const CHAR HTTP_Base64Enc[] =
1241 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1245 /* first 6 bits, all from bin[0] */
1246 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1247 x = (bin[0] & 3) << 4;
1249 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1252 base64[n++] = HTTP_Base64Enc[x];
1257 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1258 x = ( bin[1] & 0x0f ) << 2;
1260 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1263 base64[n++] = HTTP_Base64Enc[x];
1267 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1269 /* last 6 bits, all from bin [2] */
1270 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1278 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1279 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1280 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1281 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1282 static const signed char HTTP_Base64Dec[256] =
1284 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1285 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1286 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1287 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1288 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1289 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1290 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1291 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1292 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1293 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1294 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1295 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1296 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1297 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1298 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1299 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1300 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1301 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1302 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1303 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1304 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1305 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1306 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1307 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1308 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1309 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1313 /***********************************************************************
1316 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1324 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1325 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1326 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1327 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1329 WARN("invalid base64: %s\n", debugstr_w(base64));
1333 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1336 if ((base64[2] == '=') && (base64[3] == '='))
1338 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1339 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1341 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1345 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1348 if (base64[3] == '=')
1350 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1351 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1353 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1357 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1366 /***********************************************************************
1367 * HTTP_InsertAuthorization
1369 * Insert or delete the authorization field in the request header.
1371 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1375 static const WCHAR wszSpace[] = {' ',0};
1376 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1378 WCHAR *authorization = NULL;
1380 if (pAuthInfo->auth_data_len)
1382 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1383 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1384 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1388 strcpyW(authorization, pAuthInfo->scheme);
1389 strcatW(authorization, wszSpace);
1390 HTTP_EncodeBase64(pAuthInfo->auth_data,
1391 pAuthInfo->auth_data_len,
1392 authorization+strlenW(authorization));
1394 /* clear the data as it isn't valid now that it has been sent to the
1395 * server, unless it's Basic authentication which doesn't do
1396 * connection tracking */
1397 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1399 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1400 pAuthInfo->auth_data = NULL;
1401 pAuthInfo->auth_data_len = 0;
1405 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1407 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1409 HeapFree(GetProcessHeap(), 0, authorization);
1414 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1416 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1419 size = sizeof(new_location);
1420 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1422 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1423 strcpyW( url, new_location );
1427 static const WCHAR slash[] = { '/',0 };
1428 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1429 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1430 http_session_t *session = req->session;
1432 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1433 size += strlenW( session->hostName ) + strlenW( req->path );
1435 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1437 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1438 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1440 sprintfW( url, format, session->hostName, session->hostPort );
1441 if (req->path[0] != '/') strcatW( url, slash );
1442 strcatW( url, req->path );
1444 TRACE("url=%s\n", debugstr_w(url));
1448 /***********************************************************************
1449 * HTTP_DealWithProxy
1451 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1453 WCHAR buf[MAXHOSTNAME];
1454 WCHAR protoProxy[MAXHOSTNAME + 15];
1455 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1456 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1457 static WCHAR szNul[] = { 0 };
1458 URL_COMPONENTSW UrlComponents;
1459 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1460 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1461 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1463 memset( &UrlComponents, 0, sizeof UrlComponents );
1464 UrlComponents.dwStructSize = sizeof UrlComponents;
1465 UrlComponents.lpszHostName = buf;
1466 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1468 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1470 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1471 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1472 sprintfW(proxy, szFormat, protoProxy);
1474 strcpyW(proxy, protoProxy);
1475 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1477 if( UrlComponents.dwHostNameLength == 0 )
1480 if( !request->path )
1481 request->path = szNul;
1483 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1484 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1486 HeapFree(GetProcessHeap(), 0, session->serverName);
1487 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1488 session->serverPort = UrlComponents.nPort;
1490 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1494 #ifndef INET6_ADDRSTRLEN
1495 #define INET6_ADDRSTRLEN 46
1498 static DWORD HTTP_ResolveName(http_request_t *request)
1500 char szaddr[INET6_ADDRSTRLEN];
1501 http_session_t *session = request->session;
1504 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1505 INTERNET_STATUS_RESOLVING_NAME,
1506 session->serverName,
1507 (strlenW(session->serverName)+1) * sizeof(WCHAR));
1509 session->sa_len = sizeof(session->socketAddress);
1510 if (!GetAddress(session->serverName, session->serverPort,
1511 (struct sockaddr *)&session->socketAddress, &session->sa_len))
1512 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1514 switch (session->socketAddress.ss_family)
1517 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
1520 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
1523 WARN("unsupported family %d\n", session->socketAddress.ss_family);
1524 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1526 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1527 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1528 INTERNET_STATUS_NAME_RESOLVED,
1529 szaddr, strlen(szaddr)+1);
1531 TRACE("resolved %s to %s\n", debugstr_w(session->serverName), szaddr);
1532 return ERROR_SUCCESS;
1535 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1537 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1538 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1539 static const WCHAR slash[] = { '/',0 };
1540 LPHTTPHEADERW host_header;
1543 host_header = HTTP_GetHeader(req, hostW);
1547 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1551 strcpyW(buf, scheme);
1552 strcatW(buf, host_header->lpszValue);
1553 if (req->path[0] != '/')
1554 strcatW(buf, slash);
1555 strcatW(buf, req->path);
1560 /***********************************************************************
1561 * HTTPREQ_Destroy (internal)
1563 * Deallocate request handle
1566 static void HTTPREQ_Destroy(object_header_t *hdr)
1568 http_request_t *request = (http_request_t*) hdr;
1573 if(request->hCacheFile) {
1574 WCHAR url[INTERNET_MAX_URL_LENGTH];
1577 CloseHandle(request->hCacheFile);
1579 memset(&ft, 0, sizeof(FILETIME));
1580 if(HTTP_GetRequestURL(request, url)) {
1581 CommitUrlCacheEntryW(url, request->cacheFile, request->expires, ft,
1582 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1586 HeapFree(GetProcessHeap(), 0, request->cacheFile);
1588 DeleteCriticalSection( &request->read_section );
1589 WININET_Release(&request->session->hdr);
1591 destroy_authinfo(request->authInfo);
1592 destroy_authinfo(request->proxyAuthInfo);
1594 HeapFree(GetProcessHeap(), 0, request->path);
1595 HeapFree(GetProcessHeap(), 0, request->verb);
1596 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
1597 HeapFree(GetProcessHeap(), 0, request->version);
1598 HeapFree(GetProcessHeap(), 0, request->statusText);
1600 for (i = 0; i < request->nCustHeaders; i++)
1602 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
1603 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
1607 if(request->gzip_stream) {
1608 if(!request->gzip_stream->end_of_data)
1609 inflateEnd(&request->gzip_stream->zstream);
1610 HeapFree(GetProcessHeap(), 0, request->gzip_stream);
1614 HeapFree(GetProcessHeap(), 0, request->custHeaders);
1617 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1619 http_request_t *request = (http_request_t*) hdr;
1621 TRACE("%p\n",request);
1623 if (!NETCON_connected(&request->netConnection))
1626 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1627 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1629 NETCON_close(&request->netConnection);
1631 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1632 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1635 static BOOL HTTP_KeepAlive(http_request_t *request)
1637 WCHAR szVersion[10];
1638 WCHAR szConnectionResponse[20];
1639 DWORD dwBufferSize = sizeof(szVersion);
1640 BOOL keepalive = FALSE;
1642 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1643 * the connection is keep-alive by default */
1644 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1645 && !strcmpiW(szVersion, g_szHttp1_1))
1650 dwBufferSize = sizeof(szConnectionResponse);
1651 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1652 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1654 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1660 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1662 http_request_t *req = (http_request_t*)hdr;
1665 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1667 http_session_t *session = req->session;
1668 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1670 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1672 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1673 return ERROR_INSUFFICIENT_BUFFER;
1674 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1675 /* FIXME: can't get a SOCKET from our connection since we don't use
1679 /* FIXME: get source port from req->netConnection */
1680 info->SourcePort = 0;
1681 info->DestPort = session->hostPort;
1683 if (HTTP_KeepAlive(req))
1684 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1685 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1686 info->Flags |= IDSI_FLAG_PROXY;
1687 if (req->netConnection.useSSL)
1688 info->Flags |= IDSI_FLAG_SECURE;
1690 return ERROR_SUCCESS;
1693 case INTERNET_OPTION_SECURITY_FLAGS:
1698 if (*size < sizeof(ULONG))
1699 return ERROR_INSUFFICIENT_BUFFER;
1701 *size = sizeof(DWORD);
1703 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1704 flags |= SECURITY_FLAG_SECURE;
1705 flags |= req->netConnection.security_flags;
1706 bits = NETCON_GetCipherStrength(&req->netConnection);
1708 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1709 else if (bits >= 56)
1710 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1712 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1713 *(DWORD *)buffer = flags;
1714 return ERROR_SUCCESS;
1717 case INTERNET_OPTION_HANDLE_TYPE:
1718 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1720 if (*size < sizeof(ULONG))
1721 return ERROR_INSUFFICIENT_BUFFER;
1723 *size = sizeof(DWORD);
1724 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1725 return ERROR_SUCCESS;
1727 case INTERNET_OPTION_URL: {
1728 WCHAR url[INTERNET_MAX_URL_LENGTH];
1733 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1735 TRACE("INTERNET_OPTION_URL\n");
1737 host = HTTP_GetHeader(req, hostW);
1738 strcpyW(url, httpW);
1739 strcatW(url, host->lpszValue);
1740 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1742 strcatW(url, req->path);
1744 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1747 len = (strlenW(url)+1) * sizeof(WCHAR);
1749 return ERROR_INSUFFICIENT_BUFFER;
1752 strcpyW(buffer, url);
1753 return ERROR_SUCCESS;
1755 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1757 return ERROR_INSUFFICIENT_BUFFER;
1760 return ERROR_SUCCESS;
1764 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1765 INTERNET_CACHE_ENTRY_INFOW *info;
1766 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1767 WCHAR url[INTERNET_MAX_URL_LENGTH];
1768 DWORD nbytes, error;
1771 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1773 if (*size < sizeof(*ts))
1775 *size = sizeof(*ts);
1776 return ERROR_INSUFFICIENT_BUFFER;
1779 HTTP_GetRequestURL(req, url);
1780 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1781 error = GetLastError();
1782 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1784 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1785 return ERROR_OUTOFMEMORY;
1787 GetUrlCacheEntryInfoW(url, info, &nbytes);
1789 ts->ftExpires = info->ExpireTime;
1790 ts->ftLastModified = info->LastModifiedTime;
1792 HeapFree(GetProcessHeap(), 0, info);
1793 *size = sizeof(*ts);
1794 return ERROR_SUCCESS;
1799 case INTERNET_OPTION_DATAFILE_NAME: {
1802 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1804 if(!req->cacheFile) {
1806 return ERROR_INTERNET_ITEM_NOT_FOUND;
1810 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
1811 if(*size < req_size)
1812 return ERROR_INSUFFICIENT_BUFFER;
1815 memcpy(buffer, req->cacheFile, *size);
1816 return ERROR_SUCCESS;
1818 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
1819 if (req_size > *size)
1820 return ERROR_INSUFFICIENT_BUFFER;
1822 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
1823 -1, buffer, *size, NULL, NULL);
1824 return ERROR_SUCCESS;
1828 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1829 PCCERT_CONTEXT context;
1831 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1832 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1833 return ERROR_INSUFFICIENT_BUFFER;
1836 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1838 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1841 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1842 info->ftExpiry = context->pCertInfo->NotAfter;
1843 info->ftStart = context->pCertInfo->NotBefore;
1844 len = CertNameToStrA(context->dwCertEncodingType,
1845 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1846 info->lpszSubjectInfo = LocalAlloc(0, len);
1847 if(info->lpszSubjectInfo)
1848 CertNameToStrA(context->dwCertEncodingType,
1849 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1850 info->lpszSubjectInfo, len);
1851 len = CertNameToStrA(context->dwCertEncodingType,
1852 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1853 info->lpszIssuerInfo = LocalAlloc(0, len);
1854 if(info->lpszIssuerInfo)
1855 CertNameToStrA(context->dwCertEncodingType,
1856 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1857 info->lpszIssuerInfo, len);
1858 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1859 CertFreeCertificateContext(context);
1860 return ERROR_SUCCESS;
1865 return INET_QueryOption(hdr, option, buffer, size, unicode);
1868 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1870 http_request_t *req = (http_request_t*)hdr;
1873 case INTERNET_OPTION_SECURITY_FLAGS:
1877 if (!buffer || size != sizeof(DWORD))
1878 return ERROR_INVALID_PARAMETER;
1879 flags = *(DWORD *)buffer;
1880 TRACE("%08x\n", flags);
1881 req->netConnection.security_flags = flags;
1882 return ERROR_SUCCESS;
1884 case INTERNET_OPTION_SEND_TIMEOUT:
1885 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1886 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1888 if (size != sizeof(DWORD))
1889 return ERROR_INVALID_PARAMETER;
1891 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1894 case INTERNET_OPTION_USERNAME:
1895 HeapFree(GetProcessHeap(), 0, req->session->userName);
1896 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1897 return ERROR_SUCCESS;
1899 case INTERNET_OPTION_PASSWORD:
1900 HeapFree(GetProcessHeap(), 0, req->session->password);
1901 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1902 return ERROR_SUCCESS;
1903 case INTERNET_OPTION_HTTP_DECODING:
1904 if(size != sizeof(BOOL))
1905 return ERROR_INVALID_PARAMETER;
1906 req->decoding = *(BOOL*)buffer;
1907 return ERROR_SUCCESS;
1910 return ERROR_INTERNET_INVALID_OPTION;
1913 /* read some more data into the read buffer (the read section must be held) */
1914 static DWORD read_more_data( http_request_t *req, int maxlen )
1921 /* move existing data to the start of the buffer */
1923 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1927 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1929 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1930 maxlen - req->read_size, 0, &len );
1931 if(res == ERROR_SUCCESS)
1932 req->read_size += len;
1937 /* remove some amount of data from the read buffer (the read section must be held) */
1938 static void remove_data( http_request_t *req, int count )
1940 if (!(req->read_size -= count)) req->read_pos = 0;
1941 else req->read_pos += count;
1944 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1946 int count, bytes_read, pos = 0;
1949 EnterCriticalSection( &req->read_section );
1952 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1956 count = eol - (req->read_buf + req->read_pos);
1957 bytes_read = count + 1;
1959 else count = bytes_read = req->read_size;
1961 count = min( count, *len - pos );
1962 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1964 remove_data( req, bytes_read );
1967 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1970 TRACE( "returning empty string\n" );
1971 LeaveCriticalSection( &req->read_section );
1972 INTERNET_SetLastError(res);
1976 LeaveCriticalSection( &req->read_section );
1980 if (pos && buffer[pos - 1] == '\r') pos--;
1983 buffer[*len - 1] = 0;
1984 TRACE( "returning %s\n", debugstr_a(buffer));
1988 /* discard data contents until we reach end of line (the read section must be held) */
1989 static DWORD discard_eol( http_request_t *req )
1995 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1998 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2001 req->read_pos = req->read_size = 0; /* discard everything */
2002 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2003 } while (req->read_size);
2004 return ERROR_SUCCESS;
2007 /* read the size of the next chunk (the read section must be held) */
2008 static DWORD start_next_chunk( http_request_t *req )
2010 DWORD chunk_size = 0, res;
2012 if (!req->contentLength) return ERROR_SUCCESS;
2013 if (req->contentLength == req->contentRead)
2015 /* read terminator for the previous chunk */
2016 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2017 req->contentLength = ~0u;
2018 req->contentRead = 0;
2022 while (req->read_size)
2024 char ch = req->read_buf[req->read_pos];
2025 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2026 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2027 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2028 else if (ch == ';' || ch == '\r' || ch == '\n')
2030 TRACE( "reading %u byte chunk\n", chunk_size );
2031 req->contentLength = chunk_size;
2032 req->contentRead = 0;
2033 return discard_eol( req );
2035 remove_data( req, 1 );
2037 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2038 if (!req->read_size)
2040 req->contentLength = req->contentRead = 0;
2041 return ERROR_SUCCESS;
2046 /* check if we have reached the end of the data to read (the read section must be held) */
2047 static BOOL end_of_read_data( http_request_t *req )
2049 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2050 if (req->read_chunked) return (req->contentLength == 0);
2051 if (req->contentLength == ~0u) return FALSE;
2052 return (req->contentLength == req->contentRead);
2055 /* fetch some more data into the read buffer (the read section must be held) */
2056 static DWORD refill_buffer( http_request_t *req )
2058 int len = sizeof(req->read_buf);
2061 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2063 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2066 if (req->contentLength != ~0u) len = min( len, req->contentLength - req->contentRead );
2067 if (len <= req->read_size) return ERROR_SUCCESS;
2069 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2070 if (!req->read_size) req->contentLength = req->contentRead = 0;
2071 return ERROR_SUCCESS;
2074 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2076 DWORD ret = ERROR_SUCCESS;
2080 z_stream *zstream = &req->gzip_stream->zstream;
2084 while(read < size && !req->gzip_stream->end_of_data) {
2085 if(!req->read_size) {
2086 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2090 if(req->contentRead == req->contentLength)
2093 buf_avail = req->contentLength == ~0 ? req->read_size : min(req->read_size, req->contentLength-req->contentRead);
2095 zstream->next_in = req->read_buf+req->read_pos;
2096 zstream->avail_in = buf_avail;
2097 zstream->next_out = buf+read;
2098 zstream->avail_out = size-read;
2099 zres = inflate(zstream, Z_FULL_FLUSH);
2100 read = size - zstream->avail_out;
2101 req->contentRead += buf_avail-zstream->avail_in;
2102 remove_data(req, buf_avail-zstream->avail_in);
2103 if(zres == Z_STREAM_END) {
2104 TRACE("end of data\n");
2105 req->gzip_stream->end_of_data = TRUE;
2106 inflateEnd(&req->gzip_stream->zstream);
2107 }else if(zres != Z_OK) {
2108 WARN("inflate failed %d\n", zres);
2110 ret = ERROR_INTERNET_DECODING_FAILED;
2120 static void refill_gzip_buffer(http_request_t *req)
2125 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2128 if(req->gzip_stream->buf_pos) {
2129 if(req->gzip_stream->buf_size)
2130 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2131 req->gzip_stream->buf_pos = 0;
2134 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2135 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2136 if(res == ERROR_SUCCESS)
2137 req->gzip_stream->buf_size += len;
2140 /* return the size of data available to be read immediately (the read section must be held) */
2141 static DWORD get_avail_data( http_request_t *req )
2143 if (req->gzip_stream) {
2144 refill_gzip_buffer(req);
2145 return req->gzip_stream->buf_size;
2147 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2149 return min( req->read_size, req->contentLength - req->contentRead );
2152 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2154 INTERNET_ASYNC_RESULT iar;
2159 EnterCriticalSection( &req->read_section );
2160 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2161 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2162 iar.dwError = first_notif ? 0 : get_avail_data(req);
2167 LeaveCriticalSection( &req->read_section );
2169 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2170 sizeof(INTERNET_ASYNC_RESULT));
2173 /* read data from the http connection (the read section must be held) */
2174 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2176 BOOL finished_reading = FALSE;
2177 int len, bytes_read = 0;
2178 DWORD ret = ERROR_SUCCESS;
2180 EnterCriticalSection( &req->read_section );
2182 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2184 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2187 if(req->gzip_stream) {
2188 if(req->gzip_stream->buf_size) {
2189 bytes_read = min(req->gzip_stream->buf_size, size);
2190 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2191 req->gzip_stream->buf_pos += bytes_read;
2192 req->gzip_stream->buf_size -= bytes_read;
2193 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2197 if(size > bytes_read) {
2198 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2199 if(ret == ERROR_SUCCESS)
2203 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2205 if (req->contentLength != ~0u) size = min( size, req->contentLength - req->contentRead );
2207 if (req->read_size) {
2208 bytes_read = min( req->read_size, size );
2209 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2210 remove_data( req, bytes_read );
2213 if (size > bytes_read && (!bytes_read || sync)) {
2214 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2215 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2217 /* always return success, even if the network layer returns an error */
2220 finished_reading = !bytes_read && req->contentRead == req->contentLength;
2221 req->contentRead += bytes_read;
2226 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->contentRead, req->contentLength );
2227 LeaveCriticalSection( &req->read_section );
2229 if(ret == ERROR_SUCCESS && req->cacheFile) {
2231 DWORD dwBytesWritten;
2233 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2235 WARN("WriteFile failed: %u\n", GetLastError());
2238 if(finished_reading)
2239 HTTP_FinishedReading(req);
2245 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2247 http_request_t *req = (http_request_t*)hdr;
2250 EnterCriticalSection( &req->read_section );
2251 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2252 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2254 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2255 if(res == ERROR_SUCCESS)
2257 LeaveCriticalSection( &req->read_section );
2262 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2264 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2265 http_request_t *req = (http_request_t*)workRequest->hdr;
2266 INTERNET_ASYNC_RESULT iar;
2269 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2271 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2272 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2274 iar.dwResult = res == ERROR_SUCCESS;
2277 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2278 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2279 sizeof(INTERNET_ASYNC_RESULT));
2282 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2283 DWORD flags, DWORD_PTR context)
2285 http_request_t *req = (http_request_t*)hdr;
2286 DWORD res, size, read, error = ERROR_SUCCESS;
2288 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2289 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2291 if (buffers->dwStructSize != sizeof(*buffers))
2292 return ERROR_INVALID_PARAMETER;
2294 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2296 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2298 WORKREQUEST workRequest;
2300 if (TryEnterCriticalSection( &req->read_section ))
2302 if (get_avail_data(req))
2304 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2305 &buffers->dwBufferLength, FALSE);
2306 size = buffers->dwBufferLength;
2307 LeaveCriticalSection( &req->read_section );
2310 LeaveCriticalSection( &req->read_section );
2313 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2314 workRequest.hdr = WININET_AddRef(&req->hdr);
2315 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2317 INTERNET_AsyncCall(&workRequest);
2319 return ERROR_IO_PENDING;
2323 size = buffers->dwBufferLength;
2325 EnterCriticalSection( &req->read_section );
2326 if(hdr->dwError == ERROR_SUCCESS)
2327 hdr->dwError = INTERNET_HANDLE_IN_USE;
2328 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2329 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2332 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2333 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2334 if(res == ERROR_SUCCESS)
2335 read += buffers->dwBufferLength;
2339 if(!req->read_chunked || read==size || req->contentLength!=req->contentRead
2340 || !req->contentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2342 LeaveCriticalSection( &req->read_section );
2344 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2345 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2346 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2347 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2349 EnterCriticalSection( &req->read_section );
2352 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2353 hdr->dwError = ERROR_SUCCESS;
2355 error = hdr->dwError;
2357 LeaveCriticalSection( &req->read_section );
2358 size = buffers->dwBufferLength;
2359 buffers->dwBufferLength = read;
2362 if (res == ERROR_SUCCESS) {
2363 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2364 &size, sizeof(size));
2367 return res==ERROR_SUCCESS ? error : res;
2370 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2372 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2373 http_request_t *req = (http_request_t*)workRequest->hdr;
2374 INTERNET_ASYNC_RESULT iar;
2377 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2379 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2380 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2382 iar.dwResult = res == ERROR_SUCCESS;
2385 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2386 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2387 sizeof(INTERNET_ASYNC_RESULT));
2390 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2391 DWORD flags, DWORD_PTR context)
2394 http_request_t *req = (http_request_t*)hdr;
2395 DWORD res, size, read, error = ERROR_SUCCESS;
2397 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2398 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2400 if (buffers->dwStructSize != sizeof(*buffers))
2401 return ERROR_INVALID_PARAMETER;
2403 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2405 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2407 WORKREQUEST workRequest;
2409 if (TryEnterCriticalSection( &req->read_section ))
2411 if (get_avail_data(req))
2413 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2414 &buffers->dwBufferLength, FALSE);
2415 size = buffers->dwBufferLength;
2416 LeaveCriticalSection( &req->read_section );
2419 LeaveCriticalSection( &req->read_section );
2422 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2423 workRequest.hdr = WININET_AddRef(&req->hdr);
2424 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2426 INTERNET_AsyncCall(&workRequest);
2428 return ERROR_IO_PENDING;
2432 size = buffers->dwBufferLength;
2434 EnterCriticalSection( &req->read_section );
2435 if(hdr->dwError == ERROR_SUCCESS)
2436 hdr->dwError = INTERNET_HANDLE_IN_USE;
2437 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2438 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2441 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2442 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2443 if(res == ERROR_SUCCESS)
2444 read += buffers->dwBufferLength;
2448 if(!req->read_chunked || read==size || req->contentLength!=req->contentRead
2449 || !req->contentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2451 LeaveCriticalSection( &req->read_section );
2453 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2454 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2455 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2456 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2458 EnterCriticalSection( &req->read_section );
2461 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2462 hdr->dwError = ERROR_SUCCESS;
2464 error = hdr->dwError;
2466 LeaveCriticalSection( &req->read_section );
2467 size = buffers->dwBufferLength;
2468 buffers->dwBufferLength = read;
2471 if (res == ERROR_SUCCESS) {
2472 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2473 &size, sizeof(size));
2476 return res==ERROR_SUCCESS ? error : res;
2479 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2482 http_request_t *request = (http_request_t*)hdr;
2484 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2487 res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2488 if (res == ERROR_SUCCESS)
2489 request->bytesWritten += *written;
2491 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2495 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2497 http_request_t *req = (http_request_t*)workRequest->hdr;
2499 HTTP_ReceiveRequestData(req, FALSE);
2502 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2504 http_request_t *req = (http_request_t*)hdr;
2506 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2508 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2510 WORKREQUEST workRequest;
2512 /* never wait, if we can't enter the section we queue an async request right away */
2513 if (TryEnterCriticalSection( &req->read_section ))
2515 if ((*available = get_avail_data( req ))) goto done;
2516 if (end_of_read_data( req )) goto done;
2517 LeaveCriticalSection( &req->read_section );
2520 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2521 workRequest.hdr = WININET_AddRef( &req->hdr );
2523 INTERNET_AsyncCall(&workRequest);
2525 return ERROR_IO_PENDING;
2528 EnterCriticalSection( &req->read_section );
2530 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2532 refill_buffer( req );
2533 *available = get_avail_data( req );
2537 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2540 if (NETCON_query_data_available(&req->netConnection, &extra))
2541 *available = min( *available + extra, req->contentLength - req->contentRead );
2543 LeaveCriticalSection( &req->read_section );
2545 TRACE( "returning %u\n", *available );
2546 return ERROR_SUCCESS;
2549 static const object_vtbl_t HTTPREQVtbl = {
2551 HTTPREQ_CloseConnection,
2552 HTTPREQ_QueryOption,
2555 HTTPREQ_ReadFileExA,
2556 HTTPREQ_ReadFileExW,
2558 HTTPREQ_QueryDataAvailable,
2562 /***********************************************************************
2563 * HTTP_HttpOpenRequestW (internal)
2565 * Open a HTTP request handle
2568 * HINTERNET a HTTP request handle on success
2572 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2573 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2574 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2575 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2577 appinfo_t *hIC = NULL;
2578 http_request_t *request;
2579 LPWSTR lpszHostName = NULL;
2580 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2585 assert( session->hdr.htype == WH_HHTTPSESSION );
2586 hIC = session->appInfo;
2588 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2590 return ERROR_OUTOFMEMORY;
2592 request->hdr.htype = WH_HHTTPREQ;
2593 request->hdr.dwFlags = dwFlags;
2594 request->hdr.dwContext = dwContext;
2595 request->contentLength = ~0u;
2597 InitializeCriticalSection( &request->read_section );
2599 WININET_AddRef( &session->hdr );
2600 request->session = session;
2601 list_add_head( &session->hdr.children, &request->hdr.entry );
2603 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2604 (strlenW(session->hostName) + 7 /* length of ":65535" + 1 */));
2605 if (NULL == lpszHostName)
2607 res = ERROR_OUTOFMEMORY;
2611 if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2614 if (lpszObjectName && *lpszObjectName) {
2618 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2619 if (rc != E_POINTER)
2620 len = strlenW(lpszObjectName)+1;
2621 request->path = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2622 rc = UrlEscapeW(lpszObjectName, request->path, &len,
2623 URL_ESCAPE_SPACES_ONLY);
2626 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2627 strcpyW(request->path,lpszObjectName);
2630 static const WCHAR slashW[] = {'/',0};
2632 request->path = heap_strdupW(slashW);
2635 if (lpszReferrer && *lpszReferrer)
2636 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2638 if (lpszAcceptTypes)
2641 for (i = 0; lpszAcceptTypes[i]; i++)
2643 if (!*lpszAcceptTypes[i]) continue;
2644 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2645 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2646 HTTP_ADDHDR_FLAG_REQ |
2647 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2651 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2652 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2654 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2655 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2656 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2658 sprintfW(lpszHostName, szHostForm, session->hostName, session->hostPort);
2659 HTTP_ProcessHeader(request, hostW, lpszHostName,
2660 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2663 HTTP_ProcessHeader(request, hostW, session->hostName,
2664 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2666 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2667 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2668 INTERNET_DEFAULT_HTTPS_PORT :
2669 INTERNET_DEFAULT_HTTP_PORT);
2671 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2672 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2673 INTERNET_DEFAULT_HTTPS_PORT :
2674 INTERNET_DEFAULT_HTTP_PORT);
2676 if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2677 HTTP_DealWithProxy( hIC, session, request );
2679 INTERNET_SendCallback(&session->hdr, dwContext,
2680 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2684 TRACE("<-- %u (%p)\n", res, request);
2686 HeapFree(GetProcessHeap(), 0, lpszHostName);
2687 if(res != ERROR_SUCCESS) {
2688 WININET_Release( &request->hdr );
2693 *ret = request->hdr.hInternet;
2694 return ERROR_SUCCESS;
2697 /***********************************************************************
2698 * HttpOpenRequestW (WININET.@)
2700 * Open a HTTP request handle
2703 * HINTERNET a HTTP request handle on success
2707 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2708 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2709 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2710 DWORD dwFlags, DWORD_PTR dwContext)
2712 http_session_t *session;
2713 HINTERNET handle = NULL;
2716 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2717 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2718 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2719 dwFlags, dwContext);
2720 if(lpszAcceptTypes!=NULL)
2723 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2724 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2727 session = (http_session_t*) get_handle_object( hHttpSession );
2728 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
2730 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2735 * My tests seem to show that the windows version does not
2736 * become asynchronous until after this point. And anyhow
2737 * if this call was asynchronous then how would you get the
2738 * necessary HINTERNET pointer returned by this function.
2741 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2742 lpszVersion, lpszReferrer, lpszAcceptTypes,
2743 dwFlags, dwContext, &handle);
2746 WININET_Release( &session->hdr );
2747 TRACE("returning %p\n", handle);
2748 if(res != ERROR_SUCCESS)
2753 /* read any content returned by the server so that the connection can be
2755 static void HTTP_DrainContent(http_request_t *req)
2759 if (!NETCON_connected(&req->netConnection)) return;
2761 if (req->contentLength == -1)
2763 NETCON_close(&req->netConnection);
2766 if (!strcmpW(req->verb, szHEAD)) return;
2771 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2773 } while (bytes_read);
2776 static const LPCWSTR header_lookup[] = {
2777 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2778 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2779 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2780 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2781 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2782 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2783 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2784 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2785 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2786 szDate, /* HTTP_QUERY_DATE = 9 */
2787 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2788 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2789 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2790 szURI, /* HTTP_QUERY_URI = 13 */
2791 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2792 NULL, /* HTTP_QUERY_COST = 15 */
2793 NULL, /* HTTP_QUERY_LINK = 16 */
2794 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2795 NULL, /* HTTP_QUERY_VERSION = 18 */
2796 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2797 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2798 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2799 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2800 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2801 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2802 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2803 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2804 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2805 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2806 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2807 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2808 NULL, /* HTTP_QUERY_FROM = 31 */
2809 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2810 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2811 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2812 szReferer, /* HTTP_QUERY_REFERER = 35 */
2813 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2814 szServer, /* HTTP_QUERY_SERVER = 37 */
2815 NULL, /* HTTP_TITLE = 38 */
2816 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2817 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2818 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2819 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2820 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2821 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2822 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2823 NULL, /* HTTP_QUERY_REFRESH = 46 */
2824 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2825 szAge, /* HTTP_QUERY_AGE = 48 */
2826 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2827 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2828 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2829 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2830 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2831 szETag, /* HTTP_QUERY_ETAG = 54 */
2832 hostW, /* HTTP_QUERY_HOST = 55 */
2833 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2834 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2835 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2836 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2837 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2838 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2839 szRange, /* HTTP_QUERY_RANGE = 62 */
2840 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2841 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2842 szVary, /* HTTP_QUERY_VARY = 65 */
2843 szVia, /* HTTP_QUERY_VIA = 66 */
2844 szWarning, /* HTTP_QUERY_WARNING = 67 */
2845 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2846 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2847 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2850 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2852 /***********************************************************************
2853 * HTTP_HttpQueryInfoW (internal)
2855 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
2856 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2858 LPHTTPHEADERW lphttpHdr = NULL;
2859 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2860 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2861 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2864 /* Find requested header structure */
2867 case HTTP_QUERY_CUSTOM:
2868 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2869 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
2871 case HTTP_QUERY_RAW_HEADERS_CRLF:
2875 DWORD res = ERROR_INVALID_PARAMETER;
2878 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
2880 headers = request->rawHeaders;
2883 len = strlenW(headers) * sizeof(WCHAR);
2885 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2887 len += sizeof(WCHAR);
2888 res = ERROR_INSUFFICIENT_BUFFER;
2893 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2896 len = strlenW(szCrLf) * sizeof(WCHAR);
2897 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2899 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2900 res = ERROR_SUCCESS;
2902 *lpdwBufferLength = len;
2905 HeapFree(GetProcessHeap(), 0, headers);
2908 case HTTP_QUERY_RAW_HEADERS:
2910 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
2912 LPWSTR pszString = lpBuffer;
2914 for (i = 0; ppszRawHeaderLines[i]; i++)
2915 size += strlenW(ppszRawHeaderLines[i]) + 1;
2917 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2919 HTTP_FreeTokens(ppszRawHeaderLines);
2920 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2921 return ERROR_INSUFFICIENT_BUFFER;
2925 for (i = 0; ppszRawHeaderLines[i]; i++)
2927 DWORD len = strlenW(ppszRawHeaderLines[i]);
2928 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2932 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2934 *lpdwBufferLength = size * sizeof(WCHAR);
2935 HTTP_FreeTokens(ppszRawHeaderLines);
2937 return ERROR_SUCCESS;
2939 case HTTP_QUERY_STATUS_TEXT:
2940 if (request->statusText)
2942 DWORD len = strlenW(request->statusText);
2943 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2945 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2946 return ERROR_INSUFFICIENT_BUFFER;
2950 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
2951 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2953 *lpdwBufferLength = len * sizeof(WCHAR);
2954 return ERROR_SUCCESS;
2957 case HTTP_QUERY_VERSION:
2958 if (request->version)
2960 DWORD len = strlenW(request->version);
2961 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2963 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2964 return ERROR_INSUFFICIENT_BUFFER;
2968 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
2969 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2971 *lpdwBufferLength = len * sizeof(WCHAR);
2972 return ERROR_SUCCESS;
2975 case HTTP_QUERY_CONTENT_ENCODING:
2976 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2977 requested_index,request_only);
2980 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2982 if (level < LAST_TABLE_HEADER && header_lookup[level])
2983 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
2984 requested_index,request_only);
2988 lphttpHdr = &request->custHeaders[index];
2990 /* Ensure header satisfies requested attributes */
2992 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2993 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2995 return ERROR_HTTP_HEADER_NOT_FOUND;
2998 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3000 /* coalesce value to requested type */
3001 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3003 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3004 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3006 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3012 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3014 tmpTM = *gmtime(&tmpTime);
3015 STHook = (SYSTEMTIME *)lpBuffer;
3016 STHook->wDay = tmpTM.tm_mday;
3017 STHook->wHour = tmpTM.tm_hour;
3018 STHook->wMilliseconds = 0;
3019 STHook->wMinute = tmpTM.tm_min;
3020 STHook->wDayOfWeek = tmpTM.tm_wday;
3021 STHook->wMonth = tmpTM.tm_mon + 1;
3022 STHook->wSecond = tmpTM.tm_sec;
3023 STHook->wYear = tmpTM.tm_year;
3025 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3026 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3027 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3029 else if (lphttpHdr->lpszValue)
3031 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3033 if (len > *lpdwBufferLength)
3035 *lpdwBufferLength = len;
3036 return ERROR_INSUFFICIENT_BUFFER;
3040 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3041 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3043 *lpdwBufferLength = len - sizeof(WCHAR);
3045 return ERROR_SUCCESS;
3048 /***********************************************************************
3049 * HttpQueryInfoW (WININET.@)
3051 * Queries for information about an HTTP request
3058 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3059 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3061 http_request_t *request;
3064 if (TRACE_ON(wininet)) {
3065 #define FE(x) { x, #x }
3066 static const wininet_flag_info query_flags[] = {
3067 FE(HTTP_QUERY_MIME_VERSION),
3068 FE(HTTP_QUERY_CONTENT_TYPE),
3069 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3070 FE(HTTP_QUERY_CONTENT_ID),
3071 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3072 FE(HTTP_QUERY_CONTENT_LENGTH),
3073 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3074 FE(HTTP_QUERY_ALLOW),
3075 FE(HTTP_QUERY_PUBLIC),
3076 FE(HTTP_QUERY_DATE),
3077 FE(HTTP_QUERY_EXPIRES),
3078 FE(HTTP_QUERY_LAST_MODIFIED),
3079 FE(HTTP_QUERY_MESSAGE_ID),
3081 FE(HTTP_QUERY_DERIVED_FROM),
3082 FE(HTTP_QUERY_COST),
3083 FE(HTTP_QUERY_LINK),
3084 FE(HTTP_QUERY_PRAGMA),
3085 FE(HTTP_QUERY_VERSION),
3086 FE(HTTP_QUERY_STATUS_CODE),
3087 FE(HTTP_QUERY_STATUS_TEXT),
3088 FE(HTTP_QUERY_RAW_HEADERS),
3089 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3090 FE(HTTP_QUERY_CONNECTION),
3091 FE(HTTP_QUERY_ACCEPT),
3092 FE(HTTP_QUERY_ACCEPT_CHARSET),
3093 FE(HTTP_QUERY_ACCEPT_ENCODING),
3094 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3095 FE(HTTP_QUERY_AUTHORIZATION),
3096 FE(HTTP_QUERY_CONTENT_ENCODING),
3097 FE(HTTP_QUERY_FORWARDED),
3098 FE(HTTP_QUERY_FROM),
3099 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3100 FE(HTTP_QUERY_LOCATION),
3101 FE(HTTP_QUERY_ORIG_URI),
3102 FE(HTTP_QUERY_REFERER),
3103 FE(HTTP_QUERY_RETRY_AFTER),
3104 FE(HTTP_QUERY_SERVER),
3105 FE(HTTP_QUERY_TITLE),
3106 FE(HTTP_QUERY_USER_AGENT),
3107 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3108 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3109 FE(HTTP_QUERY_ACCEPT_RANGES),
3110 FE(HTTP_QUERY_SET_COOKIE),
3111 FE(HTTP_QUERY_COOKIE),
3112 FE(HTTP_QUERY_REQUEST_METHOD),
3113 FE(HTTP_QUERY_REFRESH),
3114 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3116 FE(HTTP_QUERY_CACHE_CONTROL),
3117 FE(HTTP_QUERY_CONTENT_BASE),
3118 FE(HTTP_QUERY_CONTENT_LOCATION),
3119 FE(HTTP_QUERY_CONTENT_MD5),
3120 FE(HTTP_QUERY_CONTENT_RANGE),
3121 FE(HTTP_QUERY_ETAG),
3122 FE(HTTP_QUERY_HOST),
3123 FE(HTTP_QUERY_IF_MATCH),
3124 FE(HTTP_QUERY_IF_NONE_MATCH),
3125 FE(HTTP_QUERY_IF_RANGE),
3126 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3127 FE(HTTP_QUERY_MAX_FORWARDS),
3128 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3129 FE(HTTP_QUERY_RANGE),
3130 FE(HTTP_QUERY_TRANSFER_ENCODING),
3131 FE(HTTP_QUERY_UPGRADE),
3132 FE(HTTP_QUERY_VARY),
3134 FE(HTTP_QUERY_WARNING),
3135 FE(HTTP_QUERY_CUSTOM)
3137 static const wininet_flag_info modifier_flags[] = {
3138 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3139 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3140 FE(HTTP_QUERY_FLAG_NUMBER),
3141 FE(HTTP_QUERY_FLAG_COALESCE)
3144 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3145 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3148 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3149 TRACE(" Attribute:");
3150 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3151 if (query_flags[i].val == info) {
3152 TRACE(" %s", query_flags[i].name);
3156 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3157 TRACE(" Unknown (%08x)", info);
3160 TRACE(" Modifier:");
3161 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3162 if (modifier_flags[i].val & info_mod) {
3163 TRACE(" %s", modifier_flags[i].name);
3164 info_mod &= ~ modifier_flags[i].val;
3169 TRACE(" Unknown (%08x)", info_mod);
3174 request = (http_request_t*) get_handle_object( hHttpRequest );
3175 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3177 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3181 if (lpBuffer == NULL)
3182 *lpdwBufferLength = 0;
3183 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3184 lpBuffer, lpdwBufferLength, lpdwIndex);
3188 WININET_Release( &request->hdr );
3190 TRACE("%u <--\n", res);
3191 if(res != ERROR_SUCCESS)
3193 return res == ERROR_SUCCESS;
3196 /***********************************************************************
3197 * HttpQueryInfoA (WININET.@)
3199 * Queries for information about an HTTP request
3206 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3207 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3213 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3214 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3216 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3217 lpdwBufferLength, lpdwIndex );
3223 len = (*lpdwBufferLength)*sizeof(WCHAR);
3224 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3226 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3232 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3233 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3234 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3235 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3242 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3246 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3247 lpBuffer, *lpdwBufferLength, NULL, NULL );
3248 *lpdwBufferLength = len - 1;
3250 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3253 /* since the strings being returned from HttpQueryInfoW should be
3254 * only ASCII characters, it is reasonable to assume that all of
3255 * the Unicode characters can be reduced to a single byte */
3256 *lpdwBufferLength = len / sizeof(WCHAR);
3258 HeapFree(GetProcessHeap(), 0, bufferW );
3263 /***********************************************************************
3264 * HTTP_GetRedirectURL (internal)
3266 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3268 static WCHAR szHttp[] = {'h','t','t','p',0};
3269 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3270 http_session_t *session = request->session;
3271 URL_COMPONENTSW urlComponents;
3272 DWORD url_length = 0;
3274 LPWSTR combined_url;
3276 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3277 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3278 urlComponents.dwSchemeLength = 0;
3279 urlComponents.lpszHostName = session->hostName;
3280 urlComponents.dwHostNameLength = 0;
3281 urlComponents.nPort = session->hostPort;
3282 urlComponents.lpszUserName = session->userName;
3283 urlComponents.dwUserNameLength = 0;
3284 urlComponents.lpszPassword = NULL;
3285 urlComponents.dwPasswordLength = 0;
3286 urlComponents.lpszUrlPath = request->path;
3287 urlComponents.dwUrlPathLength = 0;
3288 urlComponents.lpszExtraInfo = NULL;
3289 urlComponents.dwExtraInfoLength = 0;
3291 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3292 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3295 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3297 /* convert from bytes to characters */
3298 url_length = url_length / sizeof(WCHAR) - 1;
3299 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3301 HeapFree(GetProcessHeap(), 0, orig_url);
3306 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3307 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3309 HeapFree(GetProcessHeap(), 0, orig_url);
3312 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3314 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3316 HeapFree(GetProcessHeap(), 0, orig_url);
3317 HeapFree(GetProcessHeap(), 0, combined_url);
3320 HeapFree(GetProcessHeap(), 0, orig_url);
3321 return combined_url;
3325 /***********************************************************************
3326 * HTTP_HandleRedirect (internal)
3328 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3330 http_session_t *session = request->session;
3331 appinfo_t *hIC = session->appInfo;
3332 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3333 WCHAR path[INTERNET_MAX_URL_LENGTH];
3338 /* if it's an absolute path, keep the same session info */
3339 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3343 URL_COMPONENTSW urlComponents;
3344 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3345 static WCHAR szHttp[] = {'h','t','t','p',0};
3346 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3352 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3353 urlComponents.lpszScheme = protocol;
3354 urlComponents.dwSchemeLength = 32;
3355 urlComponents.lpszHostName = hostName;
3356 urlComponents.dwHostNameLength = MAXHOSTNAME;
3357 urlComponents.lpszUserName = userName;
3358 urlComponents.dwUserNameLength = 1024;
3359 urlComponents.lpszPassword = NULL;
3360 urlComponents.dwPasswordLength = 0;
3361 urlComponents.lpszUrlPath = path;
3362 urlComponents.dwUrlPathLength = 2048;
3363 urlComponents.lpszExtraInfo = NULL;
3364 urlComponents.dwExtraInfoLength = 0;
3365 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3366 return INTERNET_GetLastError();
3368 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3369 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3371 TRACE("redirect from secure page to non-secure page\n");
3372 /* FIXME: warn about from secure redirect to non-secure page */
3373 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3375 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3376 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3378 TRACE("redirect from non-secure page to secure page\n");
3379 /* FIXME: notify about redirect to secure page */
3380 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3383 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3385 if (lstrlenW(protocol)>4) /*https*/
3386 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3388 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3393 * This upsets redirects to binary files on sourceforge.net
3394 * and gives an html page instead of the target file
3395 * Examination of the HTTP request sent by native wininet.dll
3396 * reveals that it doesn't send a referrer in that case.
3397 * Maybe there's a flag that enables this, or maybe a referrer
3398 * shouldn't be added in case of a redirect.
3401 /* consider the current host as the referrer */
3402 if (session->lpszServerName && *session->lpszServerName)
3403 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3404 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3405 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3408 HeapFree(GetProcessHeap(), 0, session->hostName);
3409 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3410 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3413 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3414 len = lstrlenW(hostName);
3415 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3416 session->hostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3417 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3420 session->hostName = heap_strdupW(hostName);
3422 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3424 HeapFree(GetProcessHeap(), 0, session->userName);
3425 session->userName = NULL;
3427 session->userName = heap_strdupW(userName);
3431 if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3435 HeapFree(GetProcessHeap(), 0, session->serverName);
3436 session->serverName = heap_strdupW(hostName);
3437 session->serverPort = urlComponents.nPort;
3439 NETCON_close(&request->netConnection);
3440 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3443 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3444 if (res != ERROR_SUCCESS)
3447 request->read_pos = request->read_size = 0;
3448 request->read_chunked = FALSE;
3452 TRACE("Redirect through proxy\n");
3455 HeapFree(GetProcessHeap(), 0, request->path);
3462 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3463 if (rc != E_POINTER)
3464 needed = strlenW(path)+1;
3465 request->path = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3466 rc = UrlEscapeW(path, request->path, &needed,
3467 URL_ESCAPE_SPACES_ONLY);
3470 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3471 strcpyW(request->path,path);
3475 /* Remove custom content-type/length headers on redirects. */
3476 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3478 HTTP_DeleteCustomHeader(request, index);
3479 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3481 HTTP_DeleteCustomHeader(request, index);
3483 return ERROR_SUCCESS;
3486 /***********************************************************************
3487 * HTTP_build_req (internal)
3489 * concatenate all the strings in the request together
3491 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3496 for( t = list; *t ; t++ )
3497 len += strlenW( *t );
3500 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3503 for( t = list; *t ; t++ )
3509 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3512 LPWSTR requestString;
3518 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3519 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3520 http_session_t *session = request->session;
3524 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( session->hostName ) + 13)*sizeof(WCHAR) );
3525 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3526 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3527 HeapFree( GetProcessHeap(), 0, lpszPath );
3529 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3530 NULL, 0, NULL, NULL );
3531 len--; /* the nul terminator isn't needed */
3532 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3533 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3534 ascii_req, len, NULL, NULL );
3535 HeapFree( GetProcessHeap(), 0, requestString );
3537 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3539 res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3540 HeapFree( GetProcessHeap(), 0, ascii_req );
3541 if (res != ERROR_SUCCESS)
3544 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3546 return ERROR_HTTP_INVALID_HEADER;
3548 return ERROR_SUCCESS;
3551 static void HTTP_InsertCookies(http_request_t *request)
3553 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3554 LPWSTR lpszCookies, lpszUrl = NULL;
3555 DWORD nCookieSize, size;
3556 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3558 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3559 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3560 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3562 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3565 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3567 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3568 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3570 cnt += sprintfW(lpszCookies, szCookie);
3571 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3572 strcatW(lpszCookies, szCrLf);
3574 HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3575 HeapFree(GetProcessHeap(), 0, lpszCookies);
3578 HeapFree(GetProcessHeap(), 0, lpszUrl);
3581 static WORD HTTP_ParseDay(LPCWSTR day)
3583 static const WCHAR sun[] = { 's','u','n',0 };
3584 static const WCHAR mon[] = { 'm','o','n',0 };
3585 static const WCHAR tue[] = { 't','u','e',0 };
3586 static const WCHAR wed[] = { 'w','e','d',0 };
3587 static const WCHAR thu[] = { 't','h','u',0 };
3588 static const WCHAR fri[] = { 'f','r','i',0 };
3589 static const WCHAR sat[] = { 's','a','t',0 };
3591 if (!strcmpiW(day, sun)) return 0;
3592 if (!strcmpiW(day, mon)) return 1;
3593 if (!strcmpiW(day, tue)) return 2;
3594 if (!strcmpiW(day, wed)) return 3;
3595 if (!strcmpiW(day, thu)) return 4;
3596 if (!strcmpiW(day, fri)) return 5;
3597 if (!strcmpiW(day, sat)) return 6;
3602 static WORD HTTP_ParseMonth(LPCWSTR month)
3604 static const WCHAR jan[] = { 'j','a','n',0 };
3605 static const WCHAR feb[] = { 'f','e','b',0 };
3606 static const WCHAR mar[] = { 'm','a','r',0 };
3607 static const WCHAR apr[] = { 'a','p','r',0 };
3608 static const WCHAR may[] = { 'm','a','y',0 };
3609 static const WCHAR jun[] = { 'j','u','n',0 };
3610 static const WCHAR jul[] = { 'j','u','l',0 };
3611 static const WCHAR aug[] = { 'a','u','g',0 };
3612 static const WCHAR sep[] = { 's','e','p',0 };
3613 static const WCHAR oct[] = { 'o','c','t',0 };
3614 static const WCHAR nov[] = { 'n','o','v',0 };
3615 static const WCHAR dec[] = { 'd','e','c',0 };
3617 if (!strcmpiW(month, jan)) return 1;
3618 if (!strcmpiW(month, feb)) return 2;
3619 if (!strcmpiW(month, mar)) return 3;
3620 if (!strcmpiW(month, apr)) return 4;
3621 if (!strcmpiW(month, may)) return 5;
3622 if (!strcmpiW(month, jun)) return 6;
3623 if (!strcmpiW(month, jul)) return 7;
3624 if (!strcmpiW(month, aug)) return 8;
3625 if (!strcmpiW(month, sep)) return 9;
3626 if (!strcmpiW(month, oct)) return 10;
3627 if (!strcmpiW(month, nov)) return 11;
3628 if (!strcmpiW(month, dec)) return 12;
3633 /* FIXME: only accepts dates in RFC 1123 format, which is the only correct
3634 * format for HTTP, but which may not be the only format actually seen in the
3635 * wild. http://www.hackcraft.net/web/datetime/ suggests at least RFC 850
3636 * dates and dates as formatted by asctime() should be accepted as well.
3638 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
3640 static const WCHAR gmt[]= { 'G','M','T',0 };
3641 WCHAR *ptr, *nextPtr, day[4], month[4], *monthPtr;
3645 ptr = strchrW(value, ',');
3648 ERR("unexpected date format %s\n", debugstr_w(value));
3651 if (ptr - value != 3)
3653 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
3656 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
3658 st.wDayOfWeek = HTTP_ParseDay(day);
3659 if (st.wDayOfWeek > 6)
3661 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
3666 while (isspaceW(*ptr))
3669 num = strtoulW(ptr, &nextPtr, 10);
3670 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3672 ERR("unexpected day %s\n", debugstr_w(value));
3676 st.wDay = (WORD)num;
3678 while (isspaceW(*ptr))
3681 for (monthPtr = month; !isspace(*ptr) &&
3682 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3686 st.wMonth = HTTP_ParseMonth(month);
3687 if (!st.wMonth || st.wMonth > 12)
3689 ERR("unexpected month %s\n", debugstr_w(month));
3693 while (isspaceW(*ptr))
3696 num = strtoulW(ptr, &nextPtr, 10);
3697 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3699 ERR("unexpected year %s\n", debugstr_w(value));
3703 st.wYear = (WORD)num;
3705 while (isspaceW(*ptr))
3708 num = strtoulW(ptr, &nextPtr, 10);
3709 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3711 ERR("unexpected time format %s\n", debugstr_w(ptr));
3716 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3720 st.wHour = (WORD)num;
3721 num = strtoulW(ptr, &nextPtr, 10);
3722 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3724 ERR("unexpected time format %s\n", debugstr_w(ptr));
3729 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3733 st.wMinute = (WORD)num;
3734 num = strtoulW(ptr, &nextPtr, 10);
3735 if (!nextPtr || nextPtr <= ptr)
3737 ERR("unexpected time format %s\n", debugstr_w(ptr));
3742 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3746 st.wSecond = (WORD)num;
3748 while (isspaceW(*ptr))
3751 if (strcmpW(ptr, gmt))
3753 ERR("unexpected time zone %s\n", debugstr_w(ptr));
3756 return SystemTimeToFileTime(&st, ft);
3759 static void HTTP_ProcessExpires(http_request_t *request)
3761 BOOL expirationFound = FALSE;
3764 /* Look for a Cache-Control header with a max-age directive, as it takes
3765 * precedence over the Expires header.
3767 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
3768 if (headerIndex != -1)
3770 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
3773 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
3775 LPWSTR comma = strchrW(ptr, ','), end, equal;
3780 end = ptr + strlenW(ptr);
3781 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
3785 static const WCHAR max_age[] = {
3786 'm','a','x','-','a','g','e',0 };
3788 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
3793 age = strtoulW(equal + 1, &nextPtr, 10);
3794 if (nextPtr > equal + 1)
3798 NtQuerySystemTime( &ft );
3799 /* Age is in seconds, FILETIME resolution is in
3800 * 100 nanosecond intervals.
3802 ft.QuadPart += age * (ULONGLONG)1000000;
3803 request->expires.dwLowDateTime = ft.u.LowPart;
3804 request->expires.dwHighDateTime = ft.u.HighPart;
3805 expirationFound = TRUE;
3812 while (isspaceW(*ptr))
3819 if (!expirationFound)
3821 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
3822 if (headerIndex != -1)
3824 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
3827 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
3829 expirationFound = TRUE;
3830 request->expires = ft;
3834 if (!expirationFound)
3838 /* With no known age, default to 10 minutes until expiration. */
3839 NtQuerySystemTime( &t );
3840 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
3841 request->expires.dwLowDateTime = t.u.LowPart;
3842 request->expires.dwHighDateTime = t.u.HighPart;
3846 static void HTTP_CacheRequest(http_request_t *request)
3848 WCHAR url[INTERNET_MAX_URL_LENGTH];
3849 WCHAR cacheFileName[MAX_PATH+1];
3852 b = HTTP_GetRequestURL(request, url);
3854 WARN("Could not get URL\n");
3858 b = CreateUrlCacheEntryW(url, request->contentLength > 0 ? request->contentLength : 0, NULL, cacheFileName, 0);
3860 HeapFree(GetProcessHeap(), 0, request->cacheFile);
3861 CloseHandle(request->hCacheFile);
3863 request->cacheFile = heap_strdupW(cacheFileName);
3864 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3865 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3866 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
3867 WARN("Could not create file: %u\n", GetLastError());
3868 request->hCacheFile = NULL;
3871 WARN("Could not create cache entry: %08x\n", GetLastError());
3875 /***********************************************************************
3876 * HTTP_HttpSendRequestW (internal)
3878 * Sends the specified request to the HTTP server
3885 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
3886 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3887 DWORD dwContentLength, BOOL bEndRequest)
3890 BOOL redirected = FALSE;
3891 LPWSTR requestString = NULL;
3894 INTERNET_ASYNC_RESULT iar;
3895 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3896 static const WCHAR szContentLength[] =
3897 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3898 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3901 TRACE("--> %p\n", request);
3903 assert(request->hdr.htype == WH_HHTTPREQ);
3905 /* if the verb is NULL default to GET */
3907 request->verb = heap_strdupW(szGET);
3909 if (dwContentLength || strcmpW(request->verb, szGET))
3911 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3912 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3913 request->bytesToWrite = dwContentLength;
3915 if (request->session->appInfo->agent)
3917 WCHAR *agent_header;
3918 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3921 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
3922 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3923 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
3925 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3926 HeapFree(GetProcessHeap(), 0, agent_header);
3928 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3930 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3931 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3933 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
3935 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3936 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3937 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3943 BOOL reusing_connection;
3948 /* like native, just in case the caller forgot to call InternetReadFile
3949 * for all the data */
3950 HTTP_DrainContent(request);
3951 request->contentRead = 0;
3953 request->contentLength = ~0u;
3954 request->bytesToWrite = 0;
3957 if (TRACE_ON(wininet))
3959 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3960 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
3963 HTTP_FixURL(request);
3964 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3966 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3968 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
3969 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
3971 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3972 HTTP_InsertCookies(request);
3974 /* add the headers the caller supplied */
3975 if( lpszHeaders && dwHeaderLength )
3977 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
3978 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3981 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
3983 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
3984 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
3985 HeapFree(GetProcessHeap(), 0, url);
3988 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3991 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3993 /* Send the request and store the results */
3994 if(NETCON_connected(&request->netConnection))
3995 reusing_connection = TRUE;
3997 reusing_connection = FALSE;
3999 if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4002 /* send the request as ASCII, tack on the optional data */
4003 if (!lpOptional || redirected)
4004 dwOptionalLength = 0;
4005 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4006 NULL, 0, NULL, NULL );
4007 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
4008 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4009 ascii_req, len, NULL, NULL );
4011 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4012 len = (len + dwOptionalLength - 1);
4014 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4016 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4017 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4019 res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4020 HeapFree( GetProcessHeap(), 0, ascii_req );
4022 request->bytesWritten = dwOptionalLength;
4024 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4025 INTERNET_STATUS_REQUEST_SENT,
4026 &len, sizeof(DWORD));
4033 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4034 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4036 if (res != ERROR_SUCCESS)
4039 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4040 /* FIXME: We should know that connection is closed before sending
4041 * headers. Otherwise wrong callbacks are executed */
4042 if(!responseLen && reusing_connection) {
4043 TRACE("Connection closed by server, reconnecting\n");
4048 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4049 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4052 HTTP_ProcessCookies(request);
4053 HTTP_ProcessExpires(request);
4055 if (!set_content_length( request )) HTTP_FinishedReading(request);
4057 dwBufferSize = sizeof(dwStatusCode);
4058 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4059 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4062 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4064 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4065 dwBufferSize=sizeof(szNewLocation);
4066 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4067 dwStatusCode == HTTP_STATUS_MOVED ||
4068 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4069 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4071 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4073 HeapFree(GetProcessHeap(), 0, request->verb);
4074 request->verb = heap_strdupW(szGET);
4076 HTTP_DrainContent(request);
4077 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4079 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4080 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4081 res = HTTP_HandleRedirect(request, new_url);
4082 if (res == ERROR_SUCCESS)
4084 HeapFree(GetProcessHeap(), 0, requestString);
4087 HeapFree( GetProcessHeap(), 0, new_url );
4092 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4094 WCHAR szAuthValue[2048];
4096 if (dwStatusCode == HTTP_STATUS_DENIED)
4098 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4100 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4102 if (HTTP_DoAuthorization(request, szAuthValue,
4104 request->session->userName,
4105 request->session->password,
4108 HeapFree(GetProcessHeap(), 0, requestString);
4115 TRACE("Cleaning wrong authorization data\n");
4116 destroy_authinfo(request->authInfo);
4117 request->authInfo = NULL;
4120 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4123 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4125 if (HTTP_DoAuthorization(request, szAuthValue,
4126 &request->proxyAuthInfo,
4127 request->session->appInfo->proxyUsername,
4128 request->session->appInfo->proxyPassword,
4137 TRACE("Cleaning wrong proxy authorization data\n");
4138 destroy_authinfo(request->proxyAuthInfo);
4139 request->proxyAuthInfo = NULL;
4145 res = ERROR_SUCCESS;
4149 if(res == ERROR_SUCCESS)
4150 HTTP_CacheRequest(request);
4154 HeapFree(GetProcessHeap(), 0, requestString);
4156 /* TODO: send notification for P3P header */
4158 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4160 if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4161 HTTP_ReceiveRequestData(request, TRUE);
4164 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4167 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4168 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4169 sizeof(INTERNET_ASYNC_RESULT));
4177 /***********************************************************************
4179 * Helper functions for the HttpSendRequest(Ex) functions
4182 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4184 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4185 http_request_t *request = (http_request_t*) workRequest->hdr;
4187 TRACE("%p\n", request);
4189 HTTP_HttpSendRequestW(request, req->lpszHeader,
4190 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4191 req->dwContentLength, req->bEndRequest);
4193 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4197 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4201 INTERNET_ASYNC_RESULT iar;
4202 DWORD res = ERROR_SUCCESS;
4204 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4205 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4207 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4209 res = ERROR_HTTP_HEADER_NOT_FOUND;
4211 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4212 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4214 /* process cookies here. Is this right? */
4215 HTTP_ProcessCookies(request);
4216 HTTP_ProcessExpires(request);
4218 if (!set_content_length( request )) HTTP_FinishedReading(request);
4220 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4222 DWORD dwCode,dwCodeLength = sizeof(DWORD);
4223 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4224 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
4226 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4227 dwBufferSize=sizeof(szNewLocation);
4228 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4230 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4232 HeapFree(GetProcessHeap(), 0, request->verb);
4233 request->verb = heap_strdupW(szGET);
4235 HTTP_DrainContent(request);
4236 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4238 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4239 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4240 res = HTTP_HandleRedirect(request, new_url);
4241 if (res == ERROR_SUCCESS)
4242 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4243 HeapFree( GetProcessHeap(), 0, new_url );
4249 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4252 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4253 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4254 sizeof(INTERNET_ASYNC_RESULT));
4258 /***********************************************************************
4259 * HttpEndRequestA (WININET.@)
4261 * Ends an HTTP request that was started by HttpSendRequestEx
4264 * TRUE if successful
4268 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4269 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4271 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4275 SetLastError(ERROR_INVALID_PARAMETER);
4279 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4282 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4284 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4285 http_request_t *request = (http_request_t*)work->hdr;
4287 TRACE("%p\n", request);
4289 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4292 /***********************************************************************
4293 * HttpEndRequestW (WININET.@)
4295 * Ends an HTTP request that was started by HttpSendRequestEx
4298 * TRUE if successful
4302 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4303 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4305 http_request_t *request;
4312 SetLastError(ERROR_INVALID_PARAMETER);
4316 request = (http_request_t*) get_handle_object( hRequest );
4318 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4320 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4322 WININET_Release( &request->hdr );
4325 request->hdr.dwFlags |= dwFlags;
4327 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4330 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4332 work.asyncproc = AsyncHttpEndRequestProc;
4333 work.hdr = WININET_AddRef( &request->hdr );
4335 work_endrequest = &work.u.HttpEndRequestW;
4336 work_endrequest->dwFlags = dwFlags;
4337 work_endrequest->dwContext = dwContext;
4339 INTERNET_AsyncCall(&work);
4340 res = ERROR_IO_PENDING;
4343 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4345 WININET_Release( &request->hdr );
4346 TRACE("%u <--\n", res);
4347 if(res != ERROR_SUCCESS)
4349 return res == ERROR_SUCCESS;
4352 /***********************************************************************
4353 * HttpSendRequestExA (WININET.@)
4355 * Sends the specified request to the HTTP server and allows chunked
4360 * Failure: FALSE, call GetLastError() for more information.
4362 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4363 LPINTERNET_BUFFERSA lpBuffersIn,
4364 LPINTERNET_BUFFERSA lpBuffersOut,
4365 DWORD dwFlags, DWORD_PTR dwContext)
4367 INTERNET_BUFFERSW BuffersInW;
4370 LPWSTR header = NULL;
4372 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4373 lpBuffersOut, dwFlags, dwContext);
4377 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4378 if (lpBuffersIn->lpcszHeader)
4380 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4381 lpBuffersIn->dwHeadersLength,0,0);
4382 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4383 if (!(BuffersInW.lpcszHeader = header))
4385 SetLastError(ERROR_OUTOFMEMORY);
4388 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4389 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4393 BuffersInW.lpcszHeader = NULL;
4394 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4395 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4396 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4397 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4398 BuffersInW.Next = NULL;
4401 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4403 HeapFree(GetProcessHeap(),0,header);
4408 /***********************************************************************
4409 * HttpSendRequestExW (WININET.@)
4411 * Sends the specified request to the HTTP server and allows chunked
4416 * Failure: FALSE, call GetLastError() for more information.
4418 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4419 LPINTERNET_BUFFERSW lpBuffersIn,
4420 LPINTERNET_BUFFERSW lpBuffersOut,
4421 DWORD dwFlags, DWORD_PTR dwContext)
4423 http_request_t *request;
4424 http_session_t *session;
4428 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4429 lpBuffersOut, dwFlags, dwContext);
4431 request = (http_request_t*) get_handle_object( hRequest );
4433 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4435 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4439 session = request->session;
4440 assert(session->hdr.htype == WH_HHTTPSESSION);
4441 hIC = session->appInfo;
4442 assert(hIC->hdr.htype == WH_HINIT);
4444 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4446 WORKREQUEST workRequest;
4447 struct WORKREQ_HTTPSENDREQUESTW *req;
4449 workRequest.asyncproc = AsyncHttpSendRequestProc;
4450 workRequest.hdr = WININET_AddRef( &request->hdr );
4451 req = &workRequest.u.HttpSendRequestW;
4456 if (lpBuffersIn->lpcszHeader)
4458 if (lpBuffersIn->dwHeadersLength == ~0u)
4459 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4461 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4463 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4464 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4466 else req->lpszHeader = NULL;
4468 req->dwHeaderLength = size / sizeof(WCHAR);
4469 req->lpOptional = lpBuffersIn->lpvBuffer;
4470 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4471 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4475 req->lpszHeader = NULL;
4476 req->dwHeaderLength = 0;
4477 req->lpOptional = NULL;
4478 req->dwOptionalLength = 0;
4479 req->dwContentLength = 0;
4482 req->bEndRequest = FALSE;
4484 INTERNET_AsyncCall(&workRequest);
4486 * This is from windows.
4488 res = ERROR_IO_PENDING;
4493 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4494 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4495 lpBuffersIn->dwBufferTotal, FALSE);
4497 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4502 WININET_Release( &request->hdr );
4506 return res == ERROR_SUCCESS;
4509 /***********************************************************************
4510 * HttpSendRequestW (WININET.@)
4512 * Sends the specified request to the HTTP server
4519 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4520 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4522 http_request_t *request;
4523 http_session_t *session = NULL;
4524 appinfo_t *hIC = NULL;
4525 DWORD res = ERROR_SUCCESS;
4527 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4528 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4530 request = (http_request_t*) get_handle_object( hHttpRequest );
4531 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4533 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4537 session = request->session;
4538 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
4540 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4544 hIC = session->appInfo;
4545 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4547 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4551 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4553 WORKREQUEST workRequest;
4554 struct WORKREQ_HTTPSENDREQUESTW *req;
4556 workRequest.asyncproc = AsyncHttpSendRequestProc;
4557 workRequest.hdr = WININET_AddRef( &request->hdr );
4558 req = &workRequest.u.HttpSendRequestW;
4563 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4564 else size = dwHeaderLength * sizeof(WCHAR);
4566 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4567 memcpy(req->lpszHeader, lpszHeaders, size);
4570 req->lpszHeader = 0;
4571 req->dwHeaderLength = dwHeaderLength;
4572 req->lpOptional = lpOptional;
4573 req->dwOptionalLength = dwOptionalLength;
4574 req->dwContentLength = dwOptionalLength;
4575 req->bEndRequest = TRUE;
4577 INTERNET_AsyncCall(&workRequest);
4579 * This is from windows.
4581 res = ERROR_IO_PENDING;
4585 res = HTTP_HttpSendRequestW(request, lpszHeaders,
4586 dwHeaderLength, lpOptional, dwOptionalLength,
4587 dwOptionalLength, TRUE);
4591 WININET_Release( &request->hdr );
4594 return res == ERROR_SUCCESS;
4597 /***********************************************************************
4598 * HttpSendRequestA (WININET.@)
4600 * Sends the specified request to the HTTP server
4607 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4608 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4611 LPWSTR szHeaders=NULL;
4612 DWORD nLen=dwHeaderLength;
4613 if(lpszHeaders!=NULL)
4615 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4616 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4617 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4619 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4620 HeapFree(GetProcessHeap(),0,szHeaders);
4624 /***********************************************************************
4625 * HTTPSESSION_Destroy (internal)
4627 * Deallocate session handle
4630 static void HTTPSESSION_Destroy(object_header_t *hdr)
4632 http_session_t *session = (http_session_t*) hdr;
4634 TRACE("%p\n", session);
4636 WININET_Release(&session->appInfo->hdr);
4638 HeapFree(GetProcessHeap(), 0, session->hostName);
4639 HeapFree(GetProcessHeap(), 0, session->serverName);
4640 HeapFree(GetProcessHeap(), 0, session->password);
4641 HeapFree(GetProcessHeap(), 0, session->userName);
4644 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4647 case INTERNET_OPTION_HANDLE_TYPE:
4648 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4650 if (*size < sizeof(ULONG))
4651 return ERROR_INSUFFICIENT_BUFFER;
4653 *size = sizeof(DWORD);
4654 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4655 return ERROR_SUCCESS;
4658 return INET_QueryOption(hdr, option, buffer, size, unicode);
4661 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4663 http_session_t *ses = (http_session_t*)hdr;
4666 case INTERNET_OPTION_USERNAME:
4668 HeapFree(GetProcessHeap(), 0, ses->userName);
4669 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4670 return ERROR_SUCCESS;
4672 case INTERNET_OPTION_PASSWORD:
4674 HeapFree(GetProcessHeap(), 0, ses->password);
4675 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4676 return ERROR_SUCCESS;
4681 return ERROR_INTERNET_INVALID_OPTION;
4684 static const object_vtbl_t HTTPSESSIONVtbl = {
4685 HTTPSESSION_Destroy,
4687 HTTPSESSION_QueryOption,
4688 HTTPSESSION_SetOption,
4697 /***********************************************************************
4698 * HTTP_Connect (internal)
4700 * Create http session handle
4703 * HINTERNET a session handle on success
4707 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4708 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
4709 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4710 DWORD dwInternalFlags, HINTERNET *ret)
4712 http_session_t *session = NULL;
4716 if (!lpszServerName || !lpszServerName[0])
4717 return ERROR_INVALID_PARAMETER;
4719 assert( hIC->hdr.htype == WH_HINIT );
4721 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
4723 return ERROR_OUTOFMEMORY;
4726 * According to my tests. The name is not resolved until a request is sent
4729 session->hdr.htype = WH_HHTTPSESSION;
4730 session->hdr.dwFlags = dwFlags;
4731 session->hdr.dwContext = dwContext;
4732 session->hdr.dwInternalFlags |= dwInternalFlags;
4734 WININET_AddRef( &hIC->hdr );
4735 session->appInfo = hIC;
4736 list_add_head( &hIC->hdr.children, &session->hdr.entry );
4738 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
4739 if(hIC->proxyBypass)
4740 FIXME("Proxy bypass is ignored.\n");
4742 session->serverName = heap_strdupW(lpszServerName);
4743 session->hostName = heap_strdupW(lpszServerName);
4744 if (lpszUserName && lpszUserName[0])
4745 session->userName = heap_strdupW(lpszUserName);
4746 if (lpszPassword && lpszPassword[0])
4747 session->password = heap_strdupW(lpszPassword);
4748 session->serverPort = serverPort;
4749 session->hostPort = serverPort;
4751 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4752 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
4754 INTERNET_SendCallback(&hIC->hdr, dwContext,
4755 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
4760 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4764 TRACE("%p --> %p\n", hIC, session);
4766 *ret = session->hdr.hInternet;
4767 return ERROR_SUCCESS;
4771 /***********************************************************************
4772 * HTTP_OpenConnection (internal)
4774 * Connect to a web server
4781 static DWORD HTTP_OpenConnection(http_request_t *request)
4783 http_session_t *session;
4784 appinfo_t *hIC = NULL;
4785 char szaddr[INET6_ADDRSTRLEN];
4787 DWORD res = ERROR_SUCCESS;
4792 if (request->hdr.htype != WH_HHTTPREQ)
4794 res = ERROR_INVALID_PARAMETER;
4798 if (NETCON_connected(&request->netConnection))
4800 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
4802 session = request->session;
4804 hIC = session->appInfo;
4805 switch (session->socketAddress.ss_family)
4808 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
4811 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
4814 WARN("unsupported family %d\n", session->socketAddress.ss_family);
4815 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4817 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4818 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4819 INTERNET_STATUS_CONNECTING_TO_SERVER,
4823 res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
4824 if (res != ERROR_SUCCESS)
4826 WARN("Socket creation failed: %u\n", res);
4830 res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
4832 if(res != ERROR_SUCCESS)
4835 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4836 INTERNET_STATUS_CONNECTED_TO_SERVER,
4837 szaddr, strlen(szaddr)+1);
4839 if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
4841 /* Note: we differ from Microsoft's WinINet here. they seem to have
4842 * a bug that causes no status callbacks to be sent when starting
4843 * a tunnel to a proxy server using the CONNECT verb. i believe our
4844 * behaviour to be more correct and to not cause any incompatibilities
4845 * because using a secure connection through a proxy server is a rare
4846 * case that would be hard for anyone to depend on */
4847 if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
4848 HTTPREQ_CloseConnection(&request->hdr);
4852 res = NETCON_secure_connect(&request->netConnection, session->hostName);
4853 if(res != ERROR_SUCCESS)
4855 WARN("Couldn't connect securely to host\n");
4857 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4858 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4859 || res == ERROR_INTERNET_INVALID_CA
4860 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4861 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4862 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4863 || res == ERROR_INTERNET_SEC_INVALID_CERT
4864 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4865 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4867 HTTPREQ_CloseConnection(&request->hdr);
4874 request->read_pos = request->read_size = 0;
4875 request->read_chunked = FALSE;
4877 TRACE("%d <--\n", res);
4882 /***********************************************************************
4883 * HTTP_clear_response_headers (internal)
4885 * clear out any old response headers
4887 static void HTTP_clear_response_headers( http_request_t *request )
4891 for( i=0; i<request->nCustHeaders; i++)
4893 if( !request->custHeaders[i].lpszField )
4895 if( !request->custHeaders[i].lpszValue )
4897 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
4899 HTTP_DeleteCustomHeader( request, i );
4904 /***********************************************************************
4905 * HTTP_GetResponseHeaders (internal)
4907 * Read server response
4914 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
4917 WCHAR buffer[MAX_REPLY_LEN];
4918 DWORD buflen = MAX_REPLY_LEN;
4919 BOOL bSuccess = FALSE;
4921 char bufferA[MAX_REPLY_LEN];
4922 LPWSTR status_code = NULL, status_text = NULL;
4923 DWORD cchMaxRawHeaders = 1024;
4924 LPWSTR lpszRawHeaders = NULL;
4926 DWORD cchRawHeaders = 0;
4927 BOOL codeHundred = FALSE;
4931 if (!NETCON_connected(&request->netConnection))
4935 static const WCHAR szHundred[] = {'1','0','0',0};
4937 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4939 buflen = MAX_REPLY_LEN;
4940 if (!read_line(request, bufferA, &buflen))
4943 /* clear old response headers (eg. from a redirect response) */
4945 HTTP_clear_response_headers( request );
4950 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4951 /* check is this a status code line? */
4952 if (!strncmpW(buffer, g_szHttp1_0, 4))
4954 /* split the version from the status code */
4955 status_code = strchrW( buffer, ' ' );
4960 /* split the status code from the status text */
4961 status_text = strchrW( status_code, ' ' );
4966 TRACE("version [%s] status code [%s] status text [%s]\n",
4967 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4969 codeHundred = (!strcmpW(status_code, szHundred));
4971 else if (!codeHundred)
4973 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4975 HeapFree(GetProcessHeap(), 0, request->version);
4976 HeapFree(GetProcessHeap(), 0, request->statusText);
4978 request->version = heap_strdupW(g_szHttp1_0);
4979 request->statusText = heap_strdupW(szOK);
4981 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
4982 request->rawHeaders = heap_strdupW(szDefaultHeader);
4987 } while (codeHundred);
4989 /* Add status code */
4990 HTTP_ProcessHeader(request, szStatus, status_code,
4991 HTTP_ADDHDR_FLAG_REPLACE);
4993 HeapFree(GetProcessHeap(),0,request->version);
4994 HeapFree(GetProcessHeap(),0,request->statusText);
4996 request->version = heap_strdupW(buffer);
4997 request->statusText = heap_strdupW(status_text);
4999 /* Restore the spaces */
5000 *(status_code-1) = ' ';
5001 *(status_text-1) = ' ';
5003 /* regenerate raw headers */
5004 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5005 if (!lpszRawHeaders) goto lend;
5007 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5008 cchMaxRawHeaders *= 2;
5009 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5010 if (temp == NULL) goto lend;
5011 lpszRawHeaders = temp;
5012 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5013 cchRawHeaders += (buflen-1);
5014 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5015 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5016 lpszRawHeaders[cchRawHeaders] = '\0';
5018 /* Parse each response line */
5021 buflen = MAX_REPLY_LEN;
5022 if (read_line(request, bufferA, &buflen))
5024 LPWSTR * pFieldAndValue;
5026 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5028 if (!bufferA[0]) break;
5029 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5031 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5034 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5035 cchMaxRawHeaders *= 2;
5036 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5037 if (temp == NULL) goto lend;
5038 lpszRawHeaders = temp;
5039 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5040 cchRawHeaders += (buflen-1);
5041 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5042 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5043 lpszRawHeaders[cchRawHeaders] = '\0';
5045 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5046 HTTP_ADDREQ_FLAG_ADD );
5048 HTTP_FreeTokens(pFieldAndValue);
5059 /* make sure the response header is terminated with an empty line. Some apps really
5060 truly care about that empty line being there for some reason. Just add it to the
5062 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5064 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5065 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5066 if (temp == NULL) goto lend;
5067 lpszRawHeaders = temp;
5070 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5072 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5073 request->rawHeaders = lpszRawHeaders;
5074 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5084 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5089 /***********************************************************************
5090 * HTTP_InterpretHttpHeader (internal)
5092 * Parse server response
5096 * Pointer to array of field, value, NULL on success.
5099 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5101 LPWSTR * pTokenPair;
5105 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
5107 pszColon = strchrW(buffer, ':');
5108 /* must have two tokens */
5111 HTTP_FreeTokens(pTokenPair);
5113 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5117 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
5120 HTTP_FreeTokens(pTokenPair);
5123 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5124 pTokenPair[0][pszColon - buffer] = '\0';
5128 len = strlenW(pszColon);
5129 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5132 HTTP_FreeTokens(pTokenPair);
5135 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5137 strip_spaces(pTokenPair[0]);
5138 strip_spaces(pTokenPair[1]);
5140 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5144 /***********************************************************************
5145 * HTTP_ProcessHeader (internal)
5147 * Stuff header into header tables according to <dwModifier>
5151 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5153 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5155 LPHTTPHEADERW lphttpHdr = NULL;
5157 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5158 DWORD res = ERROR_HTTP_INVALID_HEADER;
5160 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5162 /* REPLACE wins out over ADD */
5163 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5164 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5166 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5169 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5173 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5174 return ERROR_HTTP_INVALID_HEADER;
5175 lphttpHdr = &request->custHeaders[index];
5181 hdr.lpszField = (LPWSTR)field;
5182 hdr.lpszValue = (LPWSTR)value;
5183 hdr.wFlags = hdr.wCount = 0;
5185 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5186 hdr.wFlags |= HDR_ISREQUEST;
5188 return HTTP_InsertCustomHeader(request, &hdr);
5190 /* no value to delete */
5191 else return ERROR_SUCCESS;
5193 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5194 lphttpHdr->wFlags |= HDR_ISREQUEST;
5196 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5198 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5200 HTTP_DeleteCustomHeader( request, index );
5206 hdr.lpszField = (LPWSTR)field;
5207 hdr.lpszValue = (LPWSTR)value;
5208 hdr.wFlags = hdr.wCount = 0;
5210 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5211 hdr.wFlags |= HDR_ISREQUEST;
5213 return HTTP_InsertCustomHeader(request, &hdr);
5216 return ERROR_SUCCESS;
5218 else if (dwModifier & COALESCEFLAGS)
5223 INT origlen = strlenW(lphttpHdr->lpszValue);
5224 INT valuelen = strlenW(value);
5226 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5229 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5231 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5234 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5237 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5239 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5242 lphttpHdr->lpszValue = lpsztmp;
5243 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5246 lphttpHdr->lpszValue[origlen] = ch;
5248 lphttpHdr->lpszValue[origlen] = ' ';
5252 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5253 lphttpHdr->lpszValue[len] = '\0';
5254 res = ERROR_SUCCESS;
5258 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5259 res = ERROR_OUTOFMEMORY;
5262 TRACE("<-- %d\n", res);
5267 /***********************************************************************
5268 * HTTP_FinishedReading (internal)
5270 * Called when all content from server has been read by client.
5273 static BOOL HTTP_FinishedReading(http_request_t *request)
5275 BOOL keepalive = HTTP_KeepAlive(request);
5282 HTTPREQ_CloseConnection(&request->hdr);
5285 /* FIXME: store data in the URL cache here */
5291 /***********************************************************************
5292 * HTTP_GetCustomHeaderIndex (internal)
5294 * Return index of custom header from header array
5297 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5298 int requested_index, BOOL request_only)
5302 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5304 for (index = 0; index < request->nCustHeaders; index++)
5306 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5309 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5312 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5315 if (requested_index == 0)
5320 if (index >= request->nCustHeaders)
5323 TRACE("Return: %d\n", index);
5328 /***********************************************************************
5329 * HTTP_InsertCustomHeader (internal)
5331 * Insert header into array
5334 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5337 LPHTTPHEADERW lph = NULL;
5339 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5340 count = request->nCustHeaders + 1;
5342 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, request->custHeaders, sizeof(HTTPHEADERW) * count);
5344 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5347 return ERROR_OUTOFMEMORY;
5349 request->custHeaders = lph;
5350 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5351 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5352 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5353 request->custHeaders[count-1].wCount= lpHdr->wCount;
5354 request->nCustHeaders++;
5356 return ERROR_SUCCESS;
5360 /***********************************************************************
5361 * HTTP_DeleteCustomHeader (internal)
5363 * Delete header from array
5364 * If this function is called, the indexs may change.
5366 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5368 if( request->nCustHeaders <= 0 )
5370 if( index >= request->nCustHeaders )
5372 request->nCustHeaders--;
5374 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5375 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5377 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5378 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5379 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5385 /***********************************************************************
5386 * HTTP_VerifyValidHeader (internal)
5388 * Verify the given header is not invalid for the given http request
5391 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5393 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5394 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5395 return ERROR_HTTP_INVALID_HEADER;
5397 return ERROR_SUCCESS;
5400 /***********************************************************************
5401 * IsHostInProxyBypassList (@)
5406 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5408 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5412 /***********************************************************************
5413 * InternetShowSecurityInfoByURLA (@)
5415 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5417 FIXME("stub: %s %p\n", url, window);
5421 /***********************************************************************
5422 * InternetShowSecurityInfoByURLW (@)
5424 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5426 FIXME("stub: %s %p\n", debugstr_w(url), window);