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];
1576 CloseHandle(request->hCacheFile);
1578 if(HTTP_GetRequestURL(request, url)) {
1581 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1582 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1583 request->last_modified, NORMAL_CACHE_ENTRY,
1584 request->rawHeaders, headersLen, NULL, 0);
1588 HeapFree(GetProcessHeap(), 0, request->cacheFile);
1590 DeleteCriticalSection( &request->read_section );
1591 WININET_Release(&request->session->hdr);
1593 destroy_authinfo(request->authInfo);
1594 destroy_authinfo(request->proxyAuthInfo);
1596 HeapFree(GetProcessHeap(), 0, request->path);
1597 HeapFree(GetProcessHeap(), 0, request->verb);
1598 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
1599 HeapFree(GetProcessHeap(), 0, request->version);
1600 HeapFree(GetProcessHeap(), 0, request->statusText);
1602 for (i = 0; i < request->nCustHeaders; i++)
1604 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
1605 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
1609 if(request->gzip_stream) {
1610 if(!request->gzip_stream->end_of_data)
1611 inflateEnd(&request->gzip_stream->zstream);
1612 HeapFree(GetProcessHeap(), 0, request->gzip_stream);
1616 HeapFree(GetProcessHeap(), 0, request->custHeaders);
1619 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1621 http_request_t *request = (http_request_t*) hdr;
1623 TRACE("%p\n",request);
1625 if (!NETCON_connected(&request->netConnection))
1628 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1629 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1631 NETCON_close(&request->netConnection);
1633 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1634 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1637 static BOOL HTTP_KeepAlive(http_request_t *request)
1639 WCHAR szVersion[10];
1640 WCHAR szConnectionResponse[20];
1641 DWORD dwBufferSize = sizeof(szVersion);
1642 BOOL keepalive = FALSE;
1644 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1645 * the connection is keep-alive by default */
1646 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1647 && !strcmpiW(szVersion, g_szHttp1_1))
1652 dwBufferSize = sizeof(szConnectionResponse);
1653 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1654 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1656 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1662 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1664 http_request_t *req = (http_request_t*)hdr;
1667 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1669 http_session_t *session = req->session;
1670 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1672 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1674 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1675 return ERROR_INSUFFICIENT_BUFFER;
1676 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1677 /* FIXME: can't get a SOCKET from our connection since we don't use
1681 /* FIXME: get source port from req->netConnection */
1682 info->SourcePort = 0;
1683 info->DestPort = session->hostPort;
1685 if (HTTP_KeepAlive(req))
1686 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1687 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1688 info->Flags |= IDSI_FLAG_PROXY;
1689 if (req->netConnection.useSSL)
1690 info->Flags |= IDSI_FLAG_SECURE;
1692 return ERROR_SUCCESS;
1695 case INTERNET_OPTION_SECURITY_FLAGS:
1700 if (*size < sizeof(ULONG))
1701 return ERROR_INSUFFICIENT_BUFFER;
1703 *size = sizeof(DWORD);
1705 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1706 flags |= SECURITY_FLAG_SECURE;
1707 flags |= req->netConnection.security_flags;
1708 bits = NETCON_GetCipherStrength(&req->netConnection);
1710 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1711 else if (bits >= 56)
1712 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1714 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1715 *(DWORD *)buffer = flags;
1716 return ERROR_SUCCESS;
1719 case INTERNET_OPTION_HANDLE_TYPE:
1720 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1722 if (*size < sizeof(ULONG))
1723 return ERROR_INSUFFICIENT_BUFFER;
1725 *size = sizeof(DWORD);
1726 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1727 return ERROR_SUCCESS;
1729 case INTERNET_OPTION_URL: {
1730 WCHAR url[INTERNET_MAX_URL_LENGTH];
1735 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1737 TRACE("INTERNET_OPTION_URL\n");
1739 host = HTTP_GetHeader(req, hostW);
1740 strcpyW(url, httpW);
1741 strcatW(url, host->lpszValue);
1742 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1744 strcatW(url, req->path);
1746 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1749 len = (strlenW(url)+1) * sizeof(WCHAR);
1751 return ERROR_INSUFFICIENT_BUFFER;
1754 strcpyW(buffer, url);
1755 return ERROR_SUCCESS;
1757 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1759 return ERROR_INSUFFICIENT_BUFFER;
1762 return ERROR_SUCCESS;
1766 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1767 INTERNET_CACHE_ENTRY_INFOW *info;
1768 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1769 WCHAR url[INTERNET_MAX_URL_LENGTH];
1770 DWORD nbytes, error;
1773 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1775 if (*size < sizeof(*ts))
1777 *size = sizeof(*ts);
1778 return ERROR_INSUFFICIENT_BUFFER;
1781 HTTP_GetRequestURL(req, url);
1782 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1783 error = GetLastError();
1784 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1786 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1787 return ERROR_OUTOFMEMORY;
1789 GetUrlCacheEntryInfoW(url, info, &nbytes);
1791 ts->ftExpires = info->ExpireTime;
1792 ts->ftLastModified = info->LastModifiedTime;
1794 HeapFree(GetProcessHeap(), 0, info);
1795 *size = sizeof(*ts);
1796 return ERROR_SUCCESS;
1801 case INTERNET_OPTION_DATAFILE_NAME: {
1804 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1806 if(!req->cacheFile) {
1808 return ERROR_INTERNET_ITEM_NOT_FOUND;
1812 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
1813 if(*size < req_size)
1814 return ERROR_INSUFFICIENT_BUFFER;
1817 memcpy(buffer, req->cacheFile, *size);
1818 return ERROR_SUCCESS;
1820 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
1821 if (req_size > *size)
1822 return ERROR_INSUFFICIENT_BUFFER;
1824 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
1825 -1, buffer, *size, NULL, NULL);
1826 return ERROR_SUCCESS;
1830 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1831 PCCERT_CONTEXT context;
1833 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1834 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1835 return ERROR_INSUFFICIENT_BUFFER;
1838 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1840 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1843 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1844 info->ftExpiry = context->pCertInfo->NotAfter;
1845 info->ftStart = context->pCertInfo->NotBefore;
1846 len = CertNameToStrA(context->dwCertEncodingType,
1847 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1848 info->lpszSubjectInfo = LocalAlloc(0, len);
1849 if(info->lpszSubjectInfo)
1850 CertNameToStrA(context->dwCertEncodingType,
1851 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1852 info->lpszSubjectInfo, len);
1853 len = CertNameToStrA(context->dwCertEncodingType,
1854 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1855 info->lpszIssuerInfo = LocalAlloc(0, len);
1856 if(info->lpszIssuerInfo)
1857 CertNameToStrA(context->dwCertEncodingType,
1858 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1859 info->lpszIssuerInfo, len);
1860 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1861 CertFreeCertificateContext(context);
1862 return ERROR_SUCCESS;
1867 return INET_QueryOption(hdr, option, buffer, size, unicode);
1870 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1872 http_request_t *req = (http_request_t*)hdr;
1875 case INTERNET_OPTION_SECURITY_FLAGS:
1879 if (!buffer || size != sizeof(DWORD))
1880 return ERROR_INVALID_PARAMETER;
1881 flags = *(DWORD *)buffer;
1882 TRACE("%08x\n", flags);
1883 req->netConnection.security_flags = flags;
1884 return ERROR_SUCCESS;
1886 case INTERNET_OPTION_SEND_TIMEOUT:
1887 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1888 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1890 if (size != sizeof(DWORD))
1891 return ERROR_INVALID_PARAMETER;
1893 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1896 case INTERNET_OPTION_USERNAME:
1897 HeapFree(GetProcessHeap(), 0, req->session->userName);
1898 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1899 return ERROR_SUCCESS;
1901 case INTERNET_OPTION_PASSWORD:
1902 HeapFree(GetProcessHeap(), 0, req->session->password);
1903 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1904 return ERROR_SUCCESS;
1905 case INTERNET_OPTION_HTTP_DECODING:
1906 if(size != sizeof(BOOL))
1907 return ERROR_INVALID_PARAMETER;
1908 req->decoding = *(BOOL*)buffer;
1909 return ERROR_SUCCESS;
1912 return ERROR_INTERNET_INVALID_OPTION;
1915 /* read some more data into the read buffer (the read section must be held) */
1916 static DWORD read_more_data( http_request_t *req, int maxlen )
1923 /* move existing data to the start of the buffer */
1925 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1929 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1931 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1932 maxlen - req->read_size, 0, &len );
1933 if(res == ERROR_SUCCESS)
1934 req->read_size += len;
1939 /* remove some amount of data from the read buffer (the read section must be held) */
1940 static void remove_data( http_request_t *req, int count )
1942 if (!(req->read_size -= count)) req->read_pos = 0;
1943 else req->read_pos += count;
1946 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1948 int count, bytes_read, pos = 0;
1951 EnterCriticalSection( &req->read_section );
1954 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1958 count = eol - (req->read_buf + req->read_pos);
1959 bytes_read = count + 1;
1961 else count = bytes_read = req->read_size;
1963 count = min( count, *len - pos );
1964 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1966 remove_data( req, bytes_read );
1969 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1972 TRACE( "returning empty string\n" );
1973 LeaveCriticalSection( &req->read_section );
1974 INTERNET_SetLastError(res);
1978 LeaveCriticalSection( &req->read_section );
1982 if (pos && buffer[pos - 1] == '\r') pos--;
1985 buffer[*len - 1] = 0;
1986 TRACE( "returning %s\n", debugstr_a(buffer));
1990 /* discard data contents until we reach end of line (the read section must be held) */
1991 static DWORD discard_eol( http_request_t *req )
1997 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2000 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2003 req->read_pos = req->read_size = 0; /* discard everything */
2004 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2005 } while (req->read_size);
2006 return ERROR_SUCCESS;
2009 /* read the size of the next chunk (the read section must be held) */
2010 static DWORD start_next_chunk( http_request_t *req )
2012 DWORD chunk_size = 0, res;
2014 if (!req->contentLength) return ERROR_SUCCESS;
2015 if (req->contentLength == req->contentRead)
2017 /* read terminator for the previous chunk */
2018 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2019 req->contentLength = ~0u;
2020 req->contentRead = 0;
2024 while (req->read_size)
2026 char ch = req->read_buf[req->read_pos];
2027 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2028 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2029 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2030 else if (ch == ';' || ch == '\r' || ch == '\n')
2032 TRACE( "reading %u byte chunk\n", chunk_size );
2033 req->contentLength = chunk_size;
2034 req->contentRead = 0;
2035 return discard_eol( req );
2037 remove_data( req, 1 );
2039 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2040 if (!req->read_size)
2042 req->contentLength = req->contentRead = 0;
2043 return ERROR_SUCCESS;
2048 /* check if we have reached the end of the data to read (the read section must be held) */
2049 static BOOL end_of_read_data( http_request_t *req )
2051 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2052 if (req->read_chunked) return (req->contentLength == 0);
2053 if (req->contentLength == ~0u) return FALSE;
2054 return (req->contentLength == req->contentRead);
2057 /* fetch some more data into the read buffer (the read section must be held) */
2058 static DWORD refill_buffer( http_request_t *req )
2060 int len = sizeof(req->read_buf);
2063 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2065 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2068 if (req->contentLength != ~0u) len = min( len, req->contentLength - req->contentRead );
2069 if (len <= req->read_size) return ERROR_SUCCESS;
2071 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2072 if (!req->read_size) req->contentLength = req->contentRead = 0;
2073 return ERROR_SUCCESS;
2076 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2078 DWORD ret = ERROR_SUCCESS;
2082 z_stream *zstream = &req->gzip_stream->zstream;
2086 while(read < size && !req->gzip_stream->end_of_data) {
2087 if(!req->read_size) {
2088 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2092 if(req->contentRead == req->contentLength)
2095 buf_avail = req->contentLength == ~0 ? req->read_size : min(req->read_size, req->contentLength-req->contentRead);
2097 zstream->next_in = req->read_buf+req->read_pos;
2098 zstream->avail_in = buf_avail;
2099 zstream->next_out = buf+read;
2100 zstream->avail_out = size-read;
2101 zres = inflate(zstream, Z_FULL_FLUSH);
2102 read = size - zstream->avail_out;
2103 req->contentRead += buf_avail-zstream->avail_in;
2104 remove_data(req, buf_avail-zstream->avail_in);
2105 if(zres == Z_STREAM_END) {
2106 TRACE("end of data\n");
2107 req->gzip_stream->end_of_data = TRUE;
2108 inflateEnd(&req->gzip_stream->zstream);
2109 }else if(zres != Z_OK) {
2110 WARN("inflate failed %d\n", zres);
2112 ret = ERROR_INTERNET_DECODING_FAILED;
2122 static void refill_gzip_buffer(http_request_t *req)
2127 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2130 if(req->gzip_stream->buf_pos) {
2131 if(req->gzip_stream->buf_size)
2132 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2133 req->gzip_stream->buf_pos = 0;
2136 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2137 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2138 if(res == ERROR_SUCCESS)
2139 req->gzip_stream->buf_size += len;
2142 /* return the size of data available to be read immediately (the read section must be held) */
2143 static DWORD get_avail_data( http_request_t *req )
2145 if (req->gzip_stream) {
2146 refill_gzip_buffer(req);
2147 return req->gzip_stream->buf_size;
2149 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2151 return min( req->read_size, req->contentLength - req->contentRead );
2154 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2156 INTERNET_ASYNC_RESULT iar;
2161 EnterCriticalSection( &req->read_section );
2162 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2163 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2164 iar.dwError = first_notif ? 0 : get_avail_data(req);
2169 LeaveCriticalSection( &req->read_section );
2171 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2172 sizeof(INTERNET_ASYNC_RESULT));
2175 /* read data from the http connection (the read section must be held) */
2176 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2178 BOOL finished_reading = FALSE;
2179 int len, bytes_read = 0;
2180 DWORD ret = ERROR_SUCCESS;
2182 EnterCriticalSection( &req->read_section );
2184 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2186 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2189 if(req->gzip_stream) {
2190 if(req->gzip_stream->buf_size) {
2191 bytes_read = min(req->gzip_stream->buf_size, size);
2192 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2193 req->gzip_stream->buf_pos += bytes_read;
2194 req->gzip_stream->buf_size -= bytes_read;
2195 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2199 if(size > bytes_read) {
2200 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2201 if(ret == ERROR_SUCCESS)
2205 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2207 if (req->contentLength != ~0u) size = min( size, req->contentLength - req->contentRead );
2209 if (req->read_size) {
2210 bytes_read = min( req->read_size, size );
2211 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2212 remove_data( req, bytes_read );
2215 if (size > bytes_read && (!bytes_read || sync)) {
2216 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2217 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2219 /* always return success, even if the network layer returns an error */
2222 finished_reading = !bytes_read && req->contentRead == req->contentLength;
2223 req->contentRead += bytes_read;
2228 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->contentRead, req->contentLength );
2229 LeaveCriticalSection( &req->read_section );
2231 if(ret == ERROR_SUCCESS && req->cacheFile) {
2233 DWORD dwBytesWritten;
2235 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2237 WARN("WriteFile failed: %u\n", GetLastError());
2240 if(finished_reading)
2241 HTTP_FinishedReading(req);
2247 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2249 http_request_t *req = (http_request_t*)hdr;
2252 EnterCriticalSection( &req->read_section );
2253 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2254 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2256 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2257 if(res == ERROR_SUCCESS)
2259 LeaveCriticalSection( &req->read_section );
2264 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2266 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2267 http_request_t *req = (http_request_t*)workRequest->hdr;
2268 INTERNET_ASYNC_RESULT iar;
2271 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2273 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2274 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2276 iar.dwResult = res == ERROR_SUCCESS;
2279 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2280 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2281 sizeof(INTERNET_ASYNC_RESULT));
2284 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2285 DWORD flags, DWORD_PTR context)
2287 http_request_t *req = (http_request_t*)hdr;
2288 DWORD res, size, read, error = ERROR_SUCCESS;
2290 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2291 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2293 if (buffers->dwStructSize != sizeof(*buffers))
2294 return ERROR_INVALID_PARAMETER;
2296 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2298 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2300 WORKREQUEST workRequest;
2302 if (TryEnterCriticalSection( &req->read_section ))
2304 if (get_avail_data(req))
2306 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2307 &buffers->dwBufferLength, FALSE);
2308 size = buffers->dwBufferLength;
2309 LeaveCriticalSection( &req->read_section );
2312 LeaveCriticalSection( &req->read_section );
2315 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2316 workRequest.hdr = WININET_AddRef(&req->hdr);
2317 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2319 INTERNET_AsyncCall(&workRequest);
2321 return ERROR_IO_PENDING;
2325 size = buffers->dwBufferLength;
2327 EnterCriticalSection( &req->read_section );
2328 if(hdr->dwError == ERROR_SUCCESS)
2329 hdr->dwError = INTERNET_HANDLE_IN_USE;
2330 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2331 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2334 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2335 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2336 if(res == ERROR_SUCCESS)
2337 read += buffers->dwBufferLength;
2341 if(!req->read_chunked || read==size || req->contentLength!=req->contentRead
2342 || !req->contentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2344 LeaveCriticalSection( &req->read_section );
2346 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2347 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2348 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2349 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2351 EnterCriticalSection( &req->read_section );
2354 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2355 hdr->dwError = ERROR_SUCCESS;
2357 error = hdr->dwError;
2359 LeaveCriticalSection( &req->read_section );
2360 size = buffers->dwBufferLength;
2361 buffers->dwBufferLength = read;
2364 if (res == ERROR_SUCCESS) {
2365 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2366 &size, sizeof(size));
2369 return res==ERROR_SUCCESS ? error : res;
2372 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2374 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2375 http_request_t *req = (http_request_t*)workRequest->hdr;
2376 INTERNET_ASYNC_RESULT iar;
2379 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2381 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2382 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2384 iar.dwResult = res == ERROR_SUCCESS;
2387 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2388 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2389 sizeof(INTERNET_ASYNC_RESULT));
2392 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2393 DWORD flags, DWORD_PTR context)
2396 http_request_t *req = (http_request_t*)hdr;
2397 DWORD res, size, read, error = ERROR_SUCCESS;
2399 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2400 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2402 if (buffers->dwStructSize != sizeof(*buffers))
2403 return ERROR_INVALID_PARAMETER;
2405 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2407 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2409 WORKREQUEST workRequest;
2411 if (TryEnterCriticalSection( &req->read_section ))
2413 if (get_avail_data(req))
2415 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2416 &buffers->dwBufferLength, FALSE);
2417 size = buffers->dwBufferLength;
2418 LeaveCriticalSection( &req->read_section );
2421 LeaveCriticalSection( &req->read_section );
2424 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2425 workRequest.hdr = WININET_AddRef(&req->hdr);
2426 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2428 INTERNET_AsyncCall(&workRequest);
2430 return ERROR_IO_PENDING;
2434 size = buffers->dwBufferLength;
2436 EnterCriticalSection( &req->read_section );
2437 if(hdr->dwError == ERROR_SUCCESS)
2438 hdr->dwError = INTERNET_HANDLE_IN_USE;
2439 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2440 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2443 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2444 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2445 if(res == ERROR_SUCCESS)
2446 read += buffers->dwBufferLength;
2450 if(!req->read_chunked || read==size || req->contentLength!=req->contentRead
2451 || !req->contentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2453 LeaveCriticalSection( &req->read_section );
2455 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2456 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2457 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2458 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2460 EnterCriticalSection( &req->read_section );
2463 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2464 hdr->dwError = ERROR_SUCCESS;
2466 error = hdr->dwError;
2468 LeaveCriticalSection( &req->read_section );
2469 size = buffers->dwBufferLength;
2470 buffers->dwBufferLength = read;
2473 if (res == ERROR_SUCCESS) {
2474 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2475 &size, sizeof(size));
2478 return res==ERROR_SUCCESS ? error : res;
2481 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2484 http_request_t *request = (http_request_t*)hdr;
2486 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2489 res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2490 if (res == ERROR_SUCCESS)
2491 request->bytesWritten += *written;
2493 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2497 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2499 http_request_t *req = (http_request_t*)workRequest->hdr;
2501 HTTP_ReceiveRequestData(req, FALSE);
2504 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2506 http_request_t *req = (http_request_t*)hdr;
2508 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2510 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2512 WORKREQUEST workRequest;
2514 /* never wait, if we can't enter the section we queue an async request right away */
2515 if (TryEnterCriticalSection( &req->read_section ))
2517 if ((*available = get_avail_data( req ))) goto done;
2518 if (end_of_read_data( req )) goto done;
2519 LeaveCriticalSection( &req->read_section );
2522 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2523 workRequest.hdr = WININET_AddRef( &req->hdr );
2525 INTERNET_AsyncCall(&workRequest);
2527 return ERROR_IO_PENDING;
2530 EnterCriticalSection( &req->read_section );
2532 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2534 refill_buffer( req );
2535 *available = get_avail_data( req );
2539 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2542 if (NETCON_query_data_available(&req->netConnection, &extra))
2543 *available = min( *available + extra, req->contentLength - req->contentRead );
2545 LeaveCriticalSection( &req->read_section );
2547 TRACE( "returning %u\n", *available );
2548 return ERROR_SUCCESS;
2551 static const object_vtbl_t HTTPREQVtbl = {
2553 HTTPREQ_CloseConnection,
2554 HTTPREQ_QueryOption,
2557 HTTPREQ_ReadFileExA,
2558 HTTPREQ_ReadFileExW,
2560 HTTPREQ_QueryDataAvailable,
2564 /***********************************************************************
2565 * HTTP_HttpOpenRequestW (internal)
2567 * Open a HTTP request handle
2570 * HINTERNET a HTTP request handle on success
2574 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2575 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2576 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2577 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2579 appinfo_t *hIC = NULL;
2580 http_request_t *request;
2581 LPWSTR lpszHostName = NULL;
2582 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2587 assert( session->hdr.htype == WH_HHTTPSESSION );
2588 hIC = session->appInfo;
2590 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2592 return ERROR_OUTOFMEMORY;
2594 request->hdr.htype = WH_HHTTPREQ;
2595 request->hdr.dwFlags = dwFlags;
2596 request->hdr.dwContext = dwContext;
2597 request->contentLength = ~0u;
2599 InitializeCriticalSection( &request->read_section );
2601 WININET_AddRef( &session->hdr );
2602 request->session = session;
2603 list_add_head( &session->hdr.children, &request->hdr.entry );
2605 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2606 (strlenW(session->hostName) + 7 /* length of ":65535" + 1 */));
2607 if (NULL == lpszHostName)
2609 res = ERROR_OUTOFMEMORY;
2613 if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2616 if (lpszObjectName && *lpszObjectName) {
2620 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2621 if (rc != E_POINTER)
2622 len = strlenW(lpszObjectName)+1;
2623 request->path = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2624 rc = UrlEscapeW(lpszObjectName, request->path, &len,
2625 URL_ESCAPE_SPACES_ONLY);
2628 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2629 strcpyW(request->path,lpszObjectName);
2632 static const WCHAR slashW[] = {'/',0};
2634 request->path = heap_strdupW(slashW);
2637 if (lpszReferrer && *lpszReferrer)
2638 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2640 if (lpszAcceptTypes)
2643 for (i = 0; lpszAcceptTypes[i]; i++)
2645 if (!*lpszAcceptTypes[i]) continue;
2646 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2647 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2648 HTTP_ADDHDR_FLAG_REQ |
2649 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2653 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2654 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2656 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2657 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2658 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2660 sprintfW(lpszHostName, szHostForm, session->hostName, session->hostPort);
2661 HTTP_ProcessHeader(request, hostW, lpszHostName,
2662 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2665 HTTP_ProcessHeader(request, hostW, session->hostName,
2666 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2668 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2669 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2670 INTERNET_DEFAULT_HTTPS_PORT :
2671 INTERNET_DEFAULT_HTTP_PORT);
2673 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2674 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2675 INTERNET_DEFAULT_HTTPS_PORT :
2676 INTERNET_DEFAULT_HTTP_PORT);
2678 if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2679 HTTP_DealWithProxy( hIC, session, request );
2681 INTERNET_SendCallback(&session->hdr, dwContext,
2682 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2686 TRACE("<-- %u (%p)\n", res, request);
2688 HeapFree(GetProcessHeap(), 0, lpszHostName);
2689 if(res != ERROR_SUCCESS) {
2690 WININET_Release( &request->hdr );
2695 *ret = request->hdr.hInternet;
2696 return ERROR_SUCCESS;
2699 /***********************************************************************
2700 * HttpOpenRequestW (WININET.@)
2702 * Open a HTTP request handle
2705 * HINTERNET a HTTP request handle on success
2709 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2710 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2711 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2712 DWORD dwFlags, DWORD_PTR dwContext)
2714 http_session_t *session;
2715 HINTERNET handle = NULL;
2718 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2719 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2720 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2721 dwFlags, dwContext);
2722 if(lpszAcceptTypes!=NULL)
2725 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2726 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2729 session = (http_session_t*) get_handle_object( hHttpSession );
2730 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
2732 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2737 * My tests seem to show that the windows version does not
2738 * become asynchronous until after this point. And anyhow
2739 * if this call was asynchronous then how would you get the
2740 * necessary HINTERNET pointer returned by this function.
2743 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2744 lpszVersion, lpszReferrer, lpszAcceptTypes,
2745 dwFlags, dwContext, &handle);
2748 WININET_Release( &session->hdr );
2749 TRACE("returning %p\n", handle);
2750 if(res != ERROR_SUCCESS)
2755 /* read any content returned by the server so that the connection can be
2757 static void HTTP_DrainContent(http_request_t *req)
2761 if (!NETCON_connected(&req->netConnection)) return;
2763 if (req->contentLength == -1)
2765 NETCON_close(&req->netConnection);
2768 if (!strcmpW(req->verb, szHEAD)) return;
2773 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2775 } while (bytes_read);
2778 static const LPCWSTR header_lookup[] = {
2779 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2780 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2781 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2782 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2783 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2784 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2785 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2786 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2787 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2788 szDate, /* HTTP_QUERY_DATE = 9 */
2789 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2790 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2791 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2792 szURI, /* HTTP_QUERY_URI = 13 */
2793 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2794 NULL, /* HTTP_QUERY_COST = 15 */
2795 NULL, /* HTTP_QUERY_LINK = 16 */
2796 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2797 NULL, /* HTTP_QUERY_VERSION = 18 */
2798 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2799 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2800 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2801 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2802 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2803 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2804 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2805 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2806 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2807 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2808 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2809 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2810 NULL, /* HTTP_QUERY_FROM = 31 */
2811 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2812 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2813 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2814 szReferer, /* HTTP_QUERY_REFERER = 35 */
2815 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2816 szServer, /* HTTP_QUERY_SERVER = 37 */
2817 NULL, /* HTTP_TITLE = 38 */
2818 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2819 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2820 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2821 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2822 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2823 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2824 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2825 NULL, /* HTTP_QUERY_REFRESH = 46 */
2826 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2827 szAge, /* HTTP_QUERY_AGE = 48 */
2828 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2829 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2830 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2831 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2832 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2833 szETag, /* HTTP_QUERY_ETAG = 54 */
2834 hostW, /* HTTP_QUERY_HOST = 55 */
2835 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2836 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2837 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2838 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2839 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2840 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2841 szRange, /* HTTP_QUERY_RANGE = 62 */
2842 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2843 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2844 szVary, /* HTTP_QUERY_VARY = 65 */
2845 szVia, /* HTTP_QUERY_VIA = 66 */
2846 szWarning, /* HTTP_QUERY_WARNING = 67 */
2847 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2848 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2849 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2852 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2854 /***********************************************************************
2855 * HTTP_HttpQueryInfoW (internal)
2857 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
2858 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2860 LPHTTPHEADERW lphttpHdr = NULL;
2861 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2862 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2863 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2866 /* Find requested header structure */
2869 case HTTP_QUERY_CUSTOM:
2870 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2871 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
2873 case HTTP_QUERY_RAW_HEADERS_CRLF:
2877 DWORD res = ERROR_INVALID_PARAMETER;
2880 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
2882 headers = request->rawHeaders;
2885 len = strlenW(headers) * sizeof(WCHAR);
2887 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2889 len += sizeof(WCHAR);
2890 res = ERROR_INSUFFICIENT_BUFFER;
2895 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2898 len = strlenW(szCrLf) * sizeof(WCHAR);
2899 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2901 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2902 res = ERROR_SUCCESS;
2904 *lpdwBufferLength = len;
2907 HeapFree(GetProcessHeap(), 0, headers);
2910 case HTTP_QUERY_RAW_HEADERS:
2912 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
2914 LPWSTR pszString = lpBuffer;
2916 for (i = 0; ppszRawHeaderLines[i]; i++)
2917 size += strlenW(ppszRawHeaderLines[i]) + 1;
2919 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2921 HTTP_FreeTokens(ppszRawHeaderLines);
2922 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2923 return ERROR_INSUFFICIENT_BUFFER;
2927 for (i = 0; ppszRawHeaderLines[i]; i++)
2929 DWORD len = strlenW(ppszRawHeaderLines[i]);
2930 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2934 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2936 *lpdwBufferLength = size * sizeof(WCHAR);
2937 HTTP_FreeTokens(ppszRawHeaderLines);
2939 return ERROR_SUCCESS;
2941 case HTTP_QUERY_STATUS_TEXT:
2942 if (request->statusText)
2944 DWORD len = strlenW(request->statusText);
2945 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2947 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2948 return ERROR_INSUFFICIENT_BUFFER;
2952 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
2953 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2955 *lpdwBufferLength = len * sizeof(WCHAR);
2956 return ERROR_SUCCESS;
2959 case HTTP_QUERY_VERSION:
2960 if (request->version)
2962 DWORD len = strlenW(request->version);
2963 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2965 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2966 return ERROR_INSUFFICIENT_BUFFER;
2970 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
2971 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2973 *lpdwBufferLength = len * sizeof(WCHAR);
2974 return ERROR_SUCCESS;
2977 case HTTP_QUERY_CONTENT_ENCODING:
2978 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2979 requested_index,request_only);
2982 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2984 if (level < LAST_TABLE_HEADER && header_lookup[level])
2985 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
2986 requested_index,request_only);
2990 lphttpHdr = &request->custHeaders[index];
2992 /* Ensure header satisfies requested attributes */
2994 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2995 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2997 return ERROR_HTTP_HEADER_NOT_FOUND;
3000 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3002 /* coalesce value to requested type */
3003 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3005 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3006 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3008 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3014 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3016 tmpTM = *gmtime(&tmpTime);
3017 STHook = (SYSTEMTIME *)lpBuffer;
3018 STHook->wDay = tmpTM.tm_mday;
3019 STHook->wHour = tmpTM.tm_hour;
3020 STHook->wMilliseconds = 0;
3021 STHook->wMinute = tmpTM.tm_min;
3022 STHook->wDayOfWeek = tmpTM.tm_wday;
3023 STHook->wMonth = tmpTM.tm_mon + 1;
3024 STHook->wSecond = tmpTM.tm_sec;
3025 STHook->wYear = tmpTM.tm_year;
3027 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3028 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3029 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3031 else if (lphttpHdr->lpszValue)
3033 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3035 if (len > *lpdwBufferLength)
3037 *lpdwBufferLength = len;
3038 return ERROR_INSUFFICIENT_BUFFER;
3042 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3043 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3045 *lpdwBufferLength = len - sizeof(WCHAR);
3047 return ERROR_SUCCESS;
3050 /***********************************************************************
3051 * HttpQueryInfoW (WININET.@)
3053 * Queries for information about an HTTP request
3060 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3061 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3063 http_request_t *request;
3066 if (TRACE_ON(wininet)) {
3067 #define FE(x) { x, #x }
3068 static const wininet_flag_info query_flags[] = {
3069 FE(HTTP_QUERY_MIME_VERSION),
3070 FE(HTTP_QUERY_CONTENT_TYPE),
3071 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3072 FE(HTTP_QUERY_CONTENT_ID),
3073 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3074 FE(HTTP_QUERY_CONTENT_LENGTH),
3075 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3076 FE(HTTP_QUERY_ALLOW),
3077 FE(HTTP_QUERY_PUBLIC),
3078 FE(HTTP_QUERY_DATE),
3079 FE(HTTP_QUERY_EXPIRES),
3080 FE(HTTP_QUERY_LAST_MODIFIED),
3081 FE(HTTP_QUERY_MESSAGE_ID),
3083 FE(HTTP_QUERY_DERIVED_FROM),
3084 FE(HTTP_QUERY_COST),
3085 FE(HTTP_QUERY_LINK),
3086 FE(HTTP_QUERY_PRAGMA),
3087 FE(HTTP_QUERY_VERSION),
3088 FE(HTTP_QUERY_STATUS_CODE),
3089 FE(HTTP_QUERY_STATUS_TEXT),
3090 FE(HTTP_QUERY_RAW_HEADERS),
3091 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3092 FE(HTTP_QUERY_CONNECTION),
3093 FE(HTTP_QUERY_ACCEPT),
3094 FE(HTTP_QUERY_ACCEPT_CHARSET),
3095 FE(HTTP_QUERY_ACCEPT_ENCODING),
3096 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3097 FE(HTTP_QUERY_AUTHORIZATION),
3098 FE(HTTP_QUERY_CONTENT_ENCODING),
3099 FE(HTTP_QUERY_FORWARDED),
3100 FE(HTTP_QUERY_FROM),
3101 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3102 FE(HTTP_QUERY_LOCATION),
3103 FE(HTTP_QUERY_ORIG_URI),
3104 FE(HTTP_QUERY_REFERER),
3105 FE(HTTP_QUERY_RETRY_AFTER),
3106 FE(HTTP_QUERY_SERVER),
3107 FE(HTTP_QUERY_TITLE),
3108 FE(HTTP_QUERY_USER_AGENT),
3109 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3110 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3111 FE(HTTP_QUERY_ACCEPT_RANGES),
3112 FE(HTTP_QUERY_SET_COOKIE),
3113 FE(HTTP_QUERY_COOKIE),
3114 FE(HTTP_QUERY_REQUEST_METHOD),
3115 FE(HTTP_QUERY_REFRESH),
3116 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3118 FE(HTTP_QUERY_CACHE_CONTROL),
3119 FE(HTTP_QUERY_CONTENT_BASE),
3120 FE(HTTP_QUERY_CONTENT_LOCATION),
3121 FE(HTTP_QUERY_CONTENT_MD5),
3122 FE(HTTP_QUERY_CONTENT_RANGE),
3123 FE(HTTP_QUERY_ETAG),
3124 FE(HTTP_QUERY_HOST),
3125 FE(HTTP_QUERY_IF_MATCH),
3126 FE(HTTP_QUERY_IF_NONE_MATCH),
3127 FE(HTTP_QUERY_IF_RANGE),
3128 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3129 FE(HTTP_QUERY_MAX_FORWARDS),
3130 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3131 FE(HTTP_QUERY_RANGE),
3132 FE(HTTP_QUERY_TRANSFER_ENCODING),
3133 FE(HTTP_QUERY_UPGRADE),
3134 FE(HTTP_QUERY_VARY),
3136 FE(HTTP_QUERY_WARNING),
3137 FE(HTTP_QUERY_CUSTOM)
3139 static const wininet_flag_info modifier_flags[] = {
3140 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3141 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3142 FE(HTTP_QUERY_FLAG_NUMBER),
3143 FE(HTTP_QUERY_FLAG_COALESCE)
3146 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3147 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3150 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3151 TRACE(" Attribute:");
3152 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3153 if (query_flags[i].val == info) {
3154 TRACE(" %s", query_flags[i].name);
3158 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3159 TRACE(" Unknown (%08x)", info);
3162 TRACE(" Modifier:");
3163 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3164 if (modifier_flags[i].val & info_mod) {
3165 TRACE(" %s", modifier_flags[i].name);
3166 info_mod &= ~ modifier_flags[i].val;
3171 TRACE(" Unknown (%08x)", info_mod);
3176 request = (http_request_t*) get_handle_object( hHttpRequest );
3177 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3179 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3183 if (lpBuffer == NULL)
3184 *lpdwBufferLength = 0;
3185 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3186 lpBuffer, lpdwBufferLength, lpdwIndex);
3190 WININET_Release( &request->hdr );
3192 TRACE("%u <--\n", res);
3193 if(res != ERROR_SUCCESS)
3195 return res == ERROR_SUCCESS;
3198 /***********************************************************************
3199 * HttpQueryInfoA (WININET.@)
3201 * Queries for information about an HTTP request
3208 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3209 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3215 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3216 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3218 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3219 lpdwBufferLength, lpdwIndex );
3225 len = (*lpdwBufferLength)*sizeof(WCHAR);
3226 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3228 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3234 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3235 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3236 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3237 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3244 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3248 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3249 lpBuffer, *lpdwBufferLength, NULL, NULL );
3250 *lpdwBufferLength = len - 1;
3252 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3255 /* since the strings being returned from HttpQueryInfoW should be
3256 * only ASCII characters, it is reasonable to assume that all of
3257 * the Unicode characters can be reduced to a single byte */
3258 *lpdwBufferLength = len / sizeof(WCHAR);
3260 HeapFree(GetProcessHeap(), 0, bufferW );
3265 /***********************************************************************
3266 * HTTP_GetRedirectURL (internal)
3268 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3270 static WCHAR szHttp[] = {'h','t','t','p',0};
3271 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3272 http_session_t *session = request->session;
3273 URL_COMPONENTSW urlComponents;
3274 DWORD url_length = 0;
3276 LPWSTR combined_url;
3278 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3279 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3280 urlComponents.dwSchemeLength = 0;
3281 urlComponents.lpszHostName = session->hostName;
3282 urlComponents.dwHostNameLength = 0;
3283 urlComponents.nPort = session->hostPort;
3284 urlComponents.lpszUserName = session->userName;
3285 urlComponents.dwUserNameLength = 0;
3286 urlComponents.lpszPassword = NULL;
3287 urlComponents.dwPasswordLength = 0;
3288 urlComponents.lpszUrlPath = request->path;
3289 urlComponents.dwUrlPathLength = 0;
3290 urlComponents.lpszExtraInfo = NULL;
3291 urlComponents.dwExtraInfoLength = 0;
3293 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3294 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3297 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3299 /* convert from bytes to characters */
3300 url_length = url_length / sizeof(WCHAR) - 1;
3301 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3303 HeapFree(GetProcessHeap(), 0, orig_url);
3308 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3309 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3311 HeapFree(GetProcessHeap(), 0, orig_url);
3314 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3316 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3318 HeapFree(GetProcessHeap(), 0, orig_url);
3319 HeapFree(GetProcessHeap(), 0, combined_url);
3322 HeapFree(GetProcessHeap(), 0, orig_url);
3323 return combined_url;
3327 /***********************************************************************
3328 * HTTP_HandleRedirect (internal)
3330 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3332 http_session_t *session = request->session;
3333 appinfo_t *hIC = session->appInfo;
3334 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3335 WCHAR path[INTERNET_MAX_URL_LENGTH];
3340 /* if it's an absolute path, keep the same session info */
3341 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3345 URL_COMPONENTSW urlComponents;
3346 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3347 static WCHAR szHttp[] = {'h','t','t','p',0};
3348 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3354 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3355 urlComponents.lpszScheme = protocol;
3356 urlComponents.dwSchemeLength = 32;
3357 urlComponents.lpszHostName = hostName;
3358 urlComponents.dwHostNameLength = MAXHOSTNAME;
3359 urlComponents.lpszUserName = userName;
3360 urlComponents.dwUserNameLength = 1024;
3361 urlComponents.lpszPassword = NULL;
3362 urlComponents.dwPasswordLength = 0;
3363 urlComponents.lpszUrlPath = path;
3364 urlComponents.dwUrlPathLength = 2048;
3365 urlComponents.lpszExtraInfo = NULL;
3366 urlComponents.dwExtraInfoLength = 0;
3367 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3368 return INTERNET_GetLastError();
3370 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3371 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3373 TRACE("redirect from secure page to non-secure page\n");
3374 /* FIXME: warn about from secure redirect to non-secure page */
3375 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3377 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3378 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3380 TRACE("redirect from non-secure page to secure page\n");
3381 /* FIXME: notify about redirect to secure page */
3382 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3385 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3387 if (lstrlenW(protocol)>4) /*https*/
3388 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3390 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3395 * This upsets redirects to binary files on sourceforge.net
3396 * and gives an html page instead of the target file
3397 * Examination of the HTTP request sent by native wininet.dll
3398 * reveals that it doesn't send a referrer in that case.
3399 * Maybe there's a flag that enables this, or maybe a referrer
3400 * shouldn't be added in case of a redirect.
3403 /* consider the current host as the referrer */
3404 if (session->lpszServerName && *session->lpszServerName)
3405 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3406 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3407 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3410 HeapFree(GetProcessHeap(), 0, session->hostName);
3411 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3412 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3415 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3416 len = lstrlenW(hostName);
3417 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3418 session->hostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3419 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3422 session->hostName = heap_strdupW(hostName);
3424 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3426 HeapFree(GetProcessHeap(), 0, session->userName);
3427 session->userName = NULL;
3429 session->userName = heap_strdupW(userName);
3433 if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3437 HeapFree(GetProcessHeap(), 0, session->serverName);
3438 session->serverName = heap_strdupW(hostName);
3439 session->serverPort = urlComponents.nPort;
3441 NETCON_close(&request->netConnection);
3442 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3445 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3446 if (res != ERROR_SUCCESS)
3449 request->read_pos = request->read_size = 0;
3450 request->read_chunked = FALSE;
3454 TRACE("Redirect through proxy\n");
3457 HeapFree(GetProcessHeap(), 0, request->path);
3464 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3465 if (rc != E_POINTER)
3466 needed = strlenW(path)+1;
3467 request->path = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3468 rc = UrlEscapeW(path, request->path, &needed,
3469 URL_ESCAPE_SPACES_ONLY);
3472 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3473 strcpyW(request->path,path);
3477 /* Remove custom content-type/length headers on redirects. */
3478 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3480 HTTP_DeleteCustomHeader(request, index);
3481 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3483 HTTP_DeleteCustomHeader(request, index);
3485 return ERROR_SUCCESS;
3488 /***********************************************************************
3489 * HTTP_build_req (internal)
3491 * concatenate all the strings in the request together
3493 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3498 for( t = list; *t ; t++ )
3499 len += strlenW( *t );
3502 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3505 for( t = list; *t ; t++ )
3511 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3514 LPWSTR requestString;
3520 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3521 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3522 http_session_t *session = request->session;
3526 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( session->hostName ) + 13)*sizeof(WCHAR) );
3527 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3528 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3529 HeapFree( GetProcessHeap(), 0, lpszPath );
3531 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3532 NULL, 0, NULL, NULL );
3533 len--; /* the nul terminator isn't needed */
3534 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3535 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3536 ascii_req, len, NULL, NULL );
3537 HeapFree( GetProcessHeap(), 0, requestString );
3539 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3541 res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3542 HeapFree( GetProcessHeap(), 0, ascii_req );
3543 if (res != ERROR_SUCCESS)
3546 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3548 return ERROR_HTTP_INVALID_HEADER;
3550 return ERROR_SUCCESS;
3553 static void HTTP_InsertCookies(http_request_t *request)
3555 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3556 LPWSTR lpszCookies, lpszUrl = NULL;
3557 DWORD nCookieSize, size;
3558 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3560 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3561 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3562 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3564 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3567 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3569 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3570 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3572 cnt += sprintfW(lpszCookies, szCookie);
3573 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3574 strcatW(lpszCookies, szCrLf);
3576 HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3577 HeapFree(GetProcessHeap(), 0, lpszCookies);
3580 HeapFree(GetProcessHeap(), 0, lpszUrl);
3583 static WORD HTTP_ParseDay(LPCWSTR day)
3585 static const WCHAR sun[] = { 's','u','n',0 };
3586 static const WCHAR mon[] = { 'm','o','n',0 };
3587 static const WCHAR tue[] = { 't','u','e',0 };
3588 static const WCHAR wed[] = { 'w','e','d',0 };
3589 static const WCHAR thu[] = { 't','h','u',0 };
3590 static const WCHAR fri[] = { 'f','r','i',0 };
3591 static const WCHAR sat[] = { 's','a','t',0 };
3593 if (!strcmpiW(day, sun)) return 0;
3594 if (!strcmpiW(day, mon)) return 1;
3595 if (!strcmpiW(day, tue)) return 2;
3596 if (!strcmpiW(day, wed)) return 3;
3597 if (!strcmpiW(day, thu)) return 4;
3598 if (!strcmpiW(day, fri)) return 5;
3599 if (!strcmpiW(day, sat)) return 6;
3604 static WORD HTTP_ParseMonth(LPCWSTR month)
3606 static const WCHAR jan[] = { 'j','a','n',0 };
3607 static const WCHAR feb[] = { 'f','e','b',0 };
3608 static const WCHAR mar[] = { 'm','a','r',0 };
3609 static const WCHAR apr[] = { 'a','p','r',0 };
3610 static const WCHAR may[] = { 'm','a','y',0 };
3611 static const WCHAR jun[] = { 'j','u','n',0 };
3612 static const WCHAR jul[] = { 'j','u','l',0 };
3613 static const WCHAR aug[] = { 'a','u','g',0 };
3614 static const WCHAR sep[] = { 's','e','p',0 };
3615 static const WCHAR oct[] = { 'o','c','t',0 };
3616 static const WCHAR nov[] = { 'n','o','v',0 };
3617 static const WCHAR dec[] = { 'd','e','c',0 };
3619 if (!strcmpiW(month, jan)) return 1;
3620 if (!strcmpiW(month, feb)) return 2;
3621 if (!strcmpiW(month, mar)) return 3;
3622 if (!strcmpiW(month, apr)) return 4;
3623 if (!strcmpiW(month, may)) return 5;
3624 if (!strcmpiW(month, jun)) return 6;
3625 if (!strcmpiW(month, jul)) return 7;
3626 if (!strcmpiW(month, aug)) return 8;
3627 if (!strcmpiW(month, sep)) return 9;
3628 if (!strcmpiW(month, oct)) return 10;
3629 if (!strcmpiW(month, nov)) return 11;
3630 if (!strcmpiW(month, dec)) return 12;
3635 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3636 * optionally preceded by whitespace.
3637 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3638 * st, and sets *str to the first character after the time format.
3640 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
3646 while (isspaceW(*ptr))
3649 num = strtoulW(ptr, &nextPtr, 10);
3650 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3652 ERR("unexpected time format %s\n", debugstr_w(ptr));
3657 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3661 st->wHour = (WORD)num;
3662 num = strtoulW(ptr, &nextPtr, 10);
3663 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3665 ERR("unexpected time format %s\n", debugstr_w(ptr));
3670 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3674 st->wMinute = (WORD)num;
3675 num = strtoulW(ptr, &nextPtr, 10);
3676 if (!nextPtr || nextPtr <= ptr)
3678 ERR("unexpected time format %s\n", debugstr_w(ptr));
3683 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3688 st->wSecond = (WORD)num;
3692 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
3694 static const WCHAR gmt[]= { 'G','M','T',0 };
3695 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
3697 SYSTEMTIME st = { 0 };
3700 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
3701 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
3704 st.wDayOfWeek = HTTP_ParseDay(day);
3705 if (st.wDayOfWeek >= 7)
3707 ERR("unexpected weekday %s\n", debugstr_w(day));
3711 while (isspaceW(*ptr))
3714 for (monthPtr = month; !isspace(*ptr) &&
3715 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3719 st.wMonth = HTTP_ParseMonth(month);
3720 if (!st.wMonth || st.wMonth > 12)
3722 ERR("unexpected month %s\n", debugstr_w(month));
3726 while (isspaceW(*ptr))
3729 num = strtoulW(ptr, &nextPtr, 10);
3730 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3732 ERR("unexpected day %s\n", debugstr_w(ptr));
3736 st.wDay = (WORD)num;
3738 while (isspaceW(*ptr))
3741 if (!HTTP_ParseTime(&st, &ptr))
3744 while (isspaceW(*ptr))
3747 num = strtoulW(ptr, &nextPtr, 10);
3748 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3750 ERR("unexpected year %s\n", debugstr_w(ptr));
3754 st.wYear = (WORD)num;
3756 while (isspaceW(*ptr))
3759 /* asctime() doesn't report a timezone, but some web servers do, so accept
3760 * with or without GMT.
3762 if (*ptr && strcmpW(ptr, gmt))
3764 ERR("unexpected timezone %s\n", debugstr_w(ptr));
3767 return SystemTimeToFileTime(&st, ft);
3770 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
3772 static const WCHAR gmt[]= { 'G','M','T',0 };
3773 WCHAR *nextPtr, day[4], month[4], *monthPtr;
3776 SYSTEMTIME st = { 0 };
3778 ptr = strchrW(value, ',');
3781 if (ptr - value != 3)
3783 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
3786 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
3788 st.wDayOfWeek = HTTP_ParseDay(day);
3789 if (st.wDayOfWeek > 6)
3791 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
3796 while (isspaceW(*ptr))
3799 num = strtoulW(ptr, &nextPtr, 10);
3800 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3802 ERR("unexpected day %s\n", debugstr_w(value));
3806 st.wDay = (WORD)num;
3808 while (isspaceW(*ptr))
3811 for (monthPtr = month; !isspace(*ptr) &&
3812 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3816 st.wMonth = HTTP_ParseMonth(month);
3817 if (!st.wMonth || st.wMonth > 12)
3819 ERR("unexpected month %s\n", debugstr_w(month));
3823 while (isspaceW(*ptr))
3826 num = strtoulW(ptr, &nextPtr, 10);
3827 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3829 ERR("unexpected year %s\n", debugstr_w(value));
3833 st.wYear = (WORD)num;
3835 if (!HTTP_ParseTime(&st, &ptr))
3838 while (isspaceW(*ptr))
3841 if (strcmpW(ptr, gmt))
3843 ERR("unexpected time zone %s\n", debugstr_w(ptr));
3846 return SystemTimeToFileTime(&st, ft);
3849 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
3850 * which may not be the only formats actually seen in the wild.
3851 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
3852 * should be accepted as well.
3854 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
3858 if (strchrW(value, ','))
3859 ret = HTTP_ParseRfc1123Date(value, ft);
3862 ret = HTTP_ParseDateAsAsctime(value, ft);
3864 ERR("unexpected date format %s\n", debugstr_w(value));
3869 static void HTTP_ProcessExpires(http_request_t *request)
3871 BOOL expirationFound = FALSE;
3874 /* Look for a Cache-Control header with a max-age directive, as it takes
3875 * precedence over the Expires header.
3877 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
3878 if (headerIndex != -1)
3880 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
3883 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
3885 LPWSTR comma = strchrW(ptr, ','), end, equal;
3890 end = ptr + strlenW(ptr);
3891 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
3895 static const WCHAR max_age[] = {
3896 'm','a','x','-','a','g','e',0 };
3898 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
3903 age = strtoulW(equal + 1, &nextPtr, 10);
3904 if (nextPtr > equal + 1)
3908 NtQuerySystemTime( &ft );
3909 /* Age is in seconds, FILETIME resolution is in
3910 * 100 nanosecond intervals.
3912 ft.QuadPart += age * (ULONGLONG)1000000;
3913 request->expires.dwLowDateTime = ft.u.LowPart;
3914 request->expires.dwHighDateTime = ft.u.HighPart;
3915 expirationFound = TRUE;
3922 while (isspaceW(*ptr))
3929 if (!expirationFound)
3931 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
3932 if (headerIndex != -1)
3934 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
3937 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
3939 expirationFound = TRUE;
3940 request->expires = ft;
3944 if (!expirationFound)
3948 /* With no known age, default to 10 minutes until expiration. */
3949 NtQuerySystemTime( &t );
3950 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
3951 request->expires.dwLowDateTime = t.u.LowPart;
3952 request->expires.dwHighDateTime = t.u.HighPart;
3956 static void HTTP_ProcessLastModified(http_request_t *request)
3960 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
3961 if (headerIndex != -1)
3963 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
3966 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
3967 request->last_modified = ft;
3971 static void HTTP_CacheRequest(http_request_t *request)
3973 WCHAR url[INTERNET_MAX_URL_LENGTH];
3974 WCHAR cacheFileName[MAX_PATH+1];
3977 b = HTTP_GetRequestURL(request, url);
3979 WARN("Could not get URL\n");
3983 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
3985 HeapFree(GetProcessHeap(), 0, request->cacheFile);
3986 CloseHandle(request->hCacheFile);
3988 request->cacheFile = heap_strdupW(cacheFileName);
3989 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3990 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3991 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
3992 WARN("Could not create file: %u\n", GetLastError());
3993 request->hCacheFile = NULL;
3996 WARN("Could not create cache entry: %08x\n", GetLastError());
4000 /***********************************************************************
4001 * HTTP_HttpSendRequestW (internal)
4003 * Sends the specified request to the HTTP server
4006 * ERROR_SUCCESS on success
4007 * win32 error code on failure
4010 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4011 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4012 DWORD dwContentLength, BOOL bEndRequest)
4015 BOOL redirected = FALSE;
4016 LPWSTR requestString = NULL;
4019 INTERNET_ASYNC_RESULT iar;
4020 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4021 static const WCHAR szContentLength[] =
4022 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4023 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4026 TRACE("--> %p\n", request);
4028 assert(request->hdr.htype == WH_HHTTPREQ);
4030 /* if the verb is NULL default to GET */
4032 request->verb = heap_strdupW(szGET);
4034 if (dwContentLength || strcmpW(request->verb, szGET))
4036 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4037 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4038 request->bytesToWrite = dwContentLength;
4040 if (request->session->appInfo->agent)
4042 WCHAR *agent_header;
4043 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4046 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4047 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4048 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4050 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4051 HeapFree(GetProcessHeap(), 0, agent_header);
4053 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4055 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4056 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4058 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4060 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4061 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4062 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4068 BOOL reusing_connection;
4073 /* like native, just in case the caller forgot to call InternetReadFile
4074 * for all the data */
4075 HTTP_DrainContent(request);
4076 request->contentRead = 0;
4078 request->contentLength = ~0u;
4079 request->bytesToWrite = 0;
4082 if (TRACE_ON(wininet))
4084 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4085 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4088 HTTP_FixURL(request);
4089 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4091 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4093 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4094 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4096 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4097 HTTP_InsertCookies(request);
4099 /* add the headers the caller supplied */
4100 if( lpszHeaders && dwHeaderLength )
4102 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4103 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4106 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4108 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4109 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4110 HeapFree(GetProcessHeap(), 0, url);
4113 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4116 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4118 /* Send the request and store the results */
4119 if(NETCON_connected(&request->netConnection))
4120 reusing_connection = TRUE;
4122 reusing_connection = FALSE;
4124 if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4127 /* send the request as ASCII, tack on the optional data */
4128 if (!lpOptional || redirected)
4129 dwOptionalLength = 0;
4130 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4131 NULL, 0, NULL, NULL );
4132 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
4133 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4134 ascii_req, len, NULL, NULL );
4136 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4137 len = (len + dwOptionalLength - 1);
4139 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4141 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4142 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4144 res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4145 HeapFree( GetProcessHeap(), 0, ascii_req );
4147 request->bytesWritten = dwOptionalLength;
4149 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4150 INTERNET_STATUS_REQUEST_SENT,
4151 &len, sizeof(DWORD));
4158 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4159 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4161 if (res != ERROR_SUCCESS)
4164 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4165 /* FIXME: We should know that connection is closed before sending
4166 * headers. Otherwise wrong callbacks are executed */
4167 if(!responseLen && reusing_connection) {
4168 TRACE("Connection closed by server, reconnecting\n");
4173 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4174 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4177 HTTP_ProcessCookies(request);
4178 HTTP_ProcessExpires(request);
4179 HTTP_ProcessLastModified(request);
4181 if (!set_content_length( request )) HTTP_FinishedReading(request);
4183 dwBufferSize = sizeof(dwStatusCode);
4184 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4185 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4188 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4190 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4191 dwBufferSize=sizeof(szNewLocation);
4192 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4193 dwStatusCode == HTTP_STATUS_MOVED ||
4194 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4195 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4197 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4199 HeapFree(GetProcessHeap(), 0, request->verb);
4200 request->verb = heap_strdupW(szGET);
4202 HTTP_DrainContent(request);
4203 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4205 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4206 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4207 res = HTTP_HandleRedirect(request, new_url);
4208 if (res == ERROR_SUCCESS)
4210 HeapFree(GetProcessHeap(), 0, requestString);
4213 HeapFree( GetProcessHeap(), 0, new_url );
4218 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4220 WCHAR szAuthValue[2048];
4222 if (dwStatusCode == HTTP_STATUS_DENIED)
4224 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4226 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4228 if (HTTP_DoAuthorization(request, szAuthValue,
4230 request->session->userName,
4231 request->session->password,
4234 HeapFree(GetProcessHeap(), 0, requestString);
4241 TRACE("Cleaning wrong authorization data\n");
4242 destroy_authinfo(request->authInfo);
4243 request->authInfo = NULL;
4246 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4249 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4251 if (HTTP_DoAuthorization(request, szAuthValue,
4252 &request->proxyAuthInfo,
4253 request->session->appInfo->proxyUsername,
4254 request->session->appInfo->proxyPassword,
4263 TRACE("Cleaning wrong proxy authorization data\n");
4264 destroy_authinfo(request->proxyAuthInfo);
4265 request->proxyAuthInfo = NULL;
4271 res = ERROR_SUCCESS;
4275 if(res == ERROR_SUCCESS)
4276 HTTP_CacheRequest(request);
4280 HeapFree(GetProcessHeap(), 0, requestString);
4282 /* TODO: send notification for P3P header */
4284 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4286 if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4287 HTTP_ReceiveRequestData(request, TRUE);
4290 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4293 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4294 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4295 sizeof(INTERNET_ASYNC_RESULT));
4303 /***********************************************************************
4305 * Helper functions for the HttpSendRequest(Ex) functions
4308 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4310 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4311 http_request_t *request = (http_request_t*) workRequest->hdr;
4313 TRACE("%p\n", request);
4315 HTTP_HttpSendRequestW(request, req->lpszHeader,
4316 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4317 req->dwContentLength, req->bEndRequest);
4319 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4323 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4327 INTERNET_ASYNC_RESULT iar;
4328 DWORD res = ERROR_SUCCESS;
4330 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4331 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4333 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4335 res = ERROR_HTTP_HEADER_NOT_FOUND;
4337 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4338 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4340 /* process cookies here. Is this right? */
4341 HTTP_ProcessCookies(request);
4342 HTTP_ProcessExpires(request);
4343 HTTP_ProcessLastModified(request);
4345 if (!set_content_length( request )) HTTP_FinishedReading(request);
4347 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4349 DWORD dwCode,dwCodeLength = sizeof(DWORD);
4350 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4351 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
4353 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4354 dwBufferSize=sizeof(szNewLocation);
4355 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4357 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4359 HeapFree(GetProcessHeap(), 0, request->verb);
4360 request->verb = heap_strdupW(szGET);
4362 HTTP_DrainContent(request);
4363 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4365 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4366 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4367 res = HTTP_HandleRedirect(request, new_url);
4368 if (res == ERROR_SUCCESS)
4369 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4370 HeapFree( GetProcessHeap(), 0, new_url );
4376 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4379 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4380 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4381 sizeof(INTERNET_ASYNC_RESULT));
4385 /***********************************************************************
4386 * HttpEndRequestA (WININET.@)
4388 * Ends an HTTP request that was started by HttpSendRequestEx
4391 * TRUE if successful
4395 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4396 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4398 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4402 SetLastError(ERROR_INVALID_PARAMETER);
4406 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4409 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4411 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4412 http_request_t *request = (http_request_t*)work->hdr;
4414 TRACE("%p\n", request);
4416 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4419 /***********************************************************************
4420 * HttpEndRequestW (WININET.@)
4422 * Ends an HTTP request that was started by HttpSendRequestEx
4425 * TRUE if successful
4429 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4430 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4432 http_request_t *request;
4439 SetLastError(ERROR_INVALID_PARAMETER);
4443 request = (http_request_t*) get_handle_object( hRequest );
4445 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4447 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4449 WININET_Release( &request->hdr );
4452 request->hdr.dwFlags |= dwFlags;
4454 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4457 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4459 work.asyncproc = AsyncHttpEndRequestProc;
4460 work.hdr = WININET_AddRef( &request->hdr );
4462 work_endrequest = &work.u.HttpEndRequestW;
4463 work_endrequest->dwFlags = dwFlags;
4464 work_endrequest->dwContext = dwContext;
4466 INTERNET_AsyncCall(&work);
4467 res = ERROR_IO_PENDING;
4470 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4472 WININET_Release( &request->hdr );
4473 TRACE("%u <--\n", res);
4474 if(res != ERROR_SUCCESS)
4476 return res == ERROR_SUCCESS;
4479 /***********************************************************************
4480 * HttpSendRequestExA (WININET.@)
4482 * Sends the specified request to the HTTP server and allows chunked
4487 * Failure: FALSE, call GetLastError() for more information.
4489 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4490 LPINTERNET_BUFFERSA lpBuffersIn,
4491 LPINTERNET_BUFFERSA lpBuffersOut,
4492 DWORD dwFlags, DWORD_PTR dwContext)
4494 INTERNET_BUFFERSW BuffersInW;
4497 LPWSTR header = NULL;
4499 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4500 lpBuffersOut, dwFlags, dwContext);
4504 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4505 if (lpBuffersIn->lpcszHeader)
4507 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4508 lpBuffersIn->dwHeadersLength,0,0);
4509 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4510 if (!(BuffersInW.lpcszHeader = header))
4512 SetLastError(ERROR_OUTOFMEMORY);
4515 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4516 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4520 BuffersInW.lpcszHeader = NULL;
4521 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4522 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4523 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4524 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4525 BuffersInW.Next = NULL;
4528 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4530 HeapFree(GetProcessHeap(),0,header);
4535 /***********************************************************************
4536 * HttpSendRequestExW (WININET.@)
4538 * Sends the specified request to the HTTP server and allows chunked
4543 * Failure: FALSE, call GetLastError() for more information.
4545 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4546 LPINTERNET_BUFFERSW lpBuffersIn,
4547 LPINTERNET_BUFFERSW lpBuffersOut,
4548 DWORD dwFlags, DWORD_PTR dwContext)
4550 http_request_t *request;
4551 http_session_t *session;
4555 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4556 lpBuffersOut, dwFlags, dwContext);
4558 request = (http_request_t*) get_handle_object( hRequest );
4560 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4562 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4566 session = request->session;
4567 assert(session->hdr.htype == WH_HHTTPSESSION);
4568 hIC = session->appInfo;
4569 assert(hIC->hdr.htype == WH_HINIT);
4571 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4573 WORKREQUEST workRequest;
4574 struct WORKREQ_HTTPSENDREQUESTW *req;
4576 workRequest.asyncproc = AsyncHttpSendRequestProc;
4577 workRequest.hdr = WININET_AddRef( &request->hdr );
4578 req = &workRequest.u.HttpSendRequestW;
4583 if (lpBuffersIn->lpcszHeader)
4585 if (lpBuffersIn->dwHeadersLength == ~0u)
4586 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4588 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4590 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4591 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4593 else req->lpszHeader = NULL;
4595 req->dwHeaderLength = size / sizeof(WCHAR);
4596 req->lpOptional = lpBuffersIn->lpvBuffer;
4597 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4598 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4602 req->lpszHeader = NULL;
4603 req->dwHeaderLength = 0;
4604 req->lpOptional = NULL;
4605 req->dwOptionalLength = 0;
4606 req->dwContentLength = 0;
4609 req->bEndRequest = FALSE;
4611 INTERNET_AsyncCall(&workRequest);
4613 * This is from windows.
4615 res = ERROR_IO_PENDING;
4620 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4621 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4622 lpBuffersIn->dwBufferTotal, FALSE);
4624 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4629 WININET_Release( &request->hdr );
4633 return res == ERROR_SUCCESS;
4636 /***********************************************************************
4637 * HttpSendRequestW (WININET.@)
4639 * Sends the specified request to the HTTP server
4646 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4647 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4649 http_request_t *request;
4650 http_session_t *session = NULL;
4651 appinfo_t *hIC = NULL;
4652 DWORD res = ERROR_SUCCESS;
4654 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4655 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4657 request = (http_request_t*) get_handle_object( hHttpRequest );
4658 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4660 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4664 session = request->session;
4665 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
4667 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4671 hIC = session->appInfo;
4672 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4674 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4678 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4680 WORKREQUEST workRequest;
4681 struct WORKREQ_HTTPSENDREQUESTW *req;
4683 workRequest.asyncproc = AsyncHttpSendRequestProc;
4684 workRequest.hdr = WININET_AddRef( &request->hdr );
4685 req = &workRequest.u.HttpSendRequestW;
4690 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4691 else size = dwHeaderLength * sizeof(WCHAR);
4693 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4694 memcpy(req->lpszHeader, lpszHeaders, size);
4697 req->lpszHeader = 0;
4698 req->dwHeaderLength = dwHeaderLength;
4699 req->lpOptional = lpOptional;
4700 req->dwOptionalLength = dwOptionalLength;
4701 req->dwContentLength = dwOptionalLength;
4702 req->bEndRequest = TRUE;
4704 INTERNET_AsyncCall(&workRequest);
4706 * This is from windows.
4708 res = ERROR_IO_PENDING;
4712 res = HTTP_HttpSendRequestW(request, lpszHeaders,
4713 dwHeaderLength, lpOptional, dwOptionalLength,
4714 dwOptionalLength, TRUE);
4718 WININET_Release( &request->hdr );
4721 return res == ERROR_SUCCESS;
4724 /***********************************************************************
4725 * HttpSendRequestA (WININET.@)
4727 * Sends the specified request to the HTTP server
4734 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4735 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4738 LPWSTR szHeaders=NULL;
4739 DWORD nLen=dwHeaderLength;
4740 if(lpszHeaders!=NULL)
4742 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4743 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4744 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4746 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4747 HeapFree(GetProcessHeap(),0,szHeaders);
4751 /***********************************************************************
4752 * HTTPSESSION_Destroy (internal)
4754 * Deallocate session handle
4757 static void HTTPSESSION_Destroy(object_header_t *hdr)
4759 http_session_t *session = (http_session_t*) hdr;
4761 TRACE("%p\n", session);
4763 WININET_Release(&session->appInfo->hdr);
4765 HeapFree(GetProcessHeap(), 0, session->hostName);
4766 HeapFree(GetProcessHeap(), 0, session->serverName);
4767 HeapFree(GetProcessHeap(), 0, session->password);
4768 HeapFree(GetProcessHeap(), 0, session->userName);
4771 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4774 case INTERNET_OPTION_HANDLE_TYPE:
4775 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4777 if (*size < sizeof(ULONG))
4778 return ERROR_INSUFFICIENT_BUFFER;
4780 *size = sizeof(DWORD);
4781 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4782 return ERROR_SUCCESS;
4785 return INET_QueryOption(hdr, option, buffer, size, unicode);
4788 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4790 http_session_t *ses = (http_session_t*)hdr;
4793 case INTERNET_OPTION_USERNAME:
4795 HeapFree(GetProcessHeap(), 0, ses->userName);
4796 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4797 return ERROR_SUCCESS;
4799 case INTERNET_OPTION_PASSWORD:
4801 HeapFree(GetProcessHeap(), 0, ses->password);
4802 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4803 return ERROR_SUCCESS;
4808 return ERROR_INTERNET_INVALID_OPTION;
4811 static const object_vtbl_t HTTPSESSIONVtbl = {
4812 HTTPSESSION_Destroy,
4814 HTTPSESSION_QueryOption,
4815 HTTPSESSION_SetOption,
4824 /***********************************************************************
4825 * HTTP_Connect (internal)
4827 * Create http session handle
4830 * HINTERNET a session handle on success
4834 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4835 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
4836 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4837 DWORD dwInternalFlags, HINTERNET *ret)
4839 http_session_t *session = NULL;
4843 if (!lpszServerName || !lpszServerName[0])
4844 return ERROR_INVALID_PARAMETER;
4846 assert( hIC->hdr.htype == WH_HINIT );
4848 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
4850 return ERROR_OUTOFMEMORY;
4853 * According to my tests. The name is not resolved until a request is sent
4856 session->hdr.htype = WH_HHTTPSESSION;
4857 session->hdr.dwFlags = dwFlags;
4858 session->hdr.dwContext = dwContext;
4859 session->hdr.dwInternalFlags |= dwInternalFlags;
4861 WININET_AddRef( &hIC->hdr );
4862 session->appInfo = hIC;
4863 list_add_head( &hIC->hdr.children, &session->hdr.entry );
4865 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
4866 if(hIC->proxyBypass)
4867 FIXME("Proxy bypass is ignored.\n");
4869 session->serverName = heap_strdupW(lpszServerName);
4870 session->hostName = heap_strdupW(lpszServerName);
4871 if (lpszUserName && lpszUserName[0])
4872 session->userName = heap_strdupW(lpszUserName);
4873 if (lpszPassword && lpszPassword[0])
4874 session->password = heap_strdupW(lpszPassword);
4875 session->serverPort = serverPort;
4876 session->hostPort = serverPort;
4878 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4879 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
4881 INTERNET_SendCallback(&hIC->hdr, dwContext,
4882 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
4887 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4891 TRACE("%p --> %p\n", hIC, session);
4893 *ret = session->hdr.hInternet;
4894 return ERROR_SUCCESS;
4898 /***********************************************************************
4899 * HTTP_OpenConnection (internal)
4901 * Connect to a web server
4908 static DWORD HTTP_OpenConnection(http_request_t *request)
4910 http_session_t *session;
4911 appinfo_t *hIC = NULL;
4912 char szaddr[INET6_ADDRSTRLEN];
4914 DWORD res = ERROR_SUCCESS;
4919 if (request->hdr.htype != WH_HHTTPREQ)
4921 res = ERROR_INVALID_PARAMETER;
4925 if (NETCON_connected(&request->netConnection))
4927 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
4929 session = request->session;
4931 hIC = session->appInfo;
4932 switch (session->socketAddress.ss_family)
4935 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
4938 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
4941 WARN("unsupported family %d\n", session->socketAddress.ss_family);
4942 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4944 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4945 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4946 INTERNET_STATUS_CONNECTING_TO_SERVER,
4950 res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
4951 if (res != ERROR_SUCCESS)
4953 WARN("Socket creation failed: %u\n", res);
4957 res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
4959 if(res != ERROR_SUCCESS)
4962 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4963 INTERNET_STATUS_CONNECTED_TO_SERVER,
4964 szaddr, strlen(szaddr)+1);
4966 if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
4968 /* Note: we differ from Microsoft's WinINet here. they seem to have
4969 * a bug that causes no status callbacks to be sent when starting
4970 * a tunnel to a proxy server using the CONNECT verb. i believe our
4971 * behaviour to be more correct and to not cause any incompatibilities
4972 * because using a secure connection through a proxy server is a rare
4973 * case that would be hard for anyone to depend on */
4974 if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
4975 HTTPREQ_CloseConnection(&request->hdr);
4979 res = NETCON_secure_connect(&request->netConnection, session->hostName);
4980 if(res != ERROR_SUCCESS)
4982 WARN("Couldn't connect securely to host\n");
4984 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4985 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4986 || res == ERROR_INTERNET_INVALID_CA
4987 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4988 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4989 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4990 || res == ERROR_INTERNET_SEC_INVALID_CERT
4991 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4992 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4994 HTTPREQ_CloseConnection(&request->hdr);
5001 request->read_pos = request->read_size = 0;
5002 request->read_chunked = FALSE;
5004 TRACE("%d <--\n", res);
5009 /***********************************************************************
5010 * HTTP_clear_response_headers (internal)
5012 * clear out any old response headers
5014 static void HTTP_clear_response_headers( http_request_t *request )
5018 for( i=0; i<request->nCustHeaders; i++)
5020 if( !request->custHeaders[i].lpszField )
5022 if( !request->custHeaders[i].lpszValue )
5024 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5026 HTTP_DeleteCustomHeader( request, i );
5031 /***********************************************************************
5032 * HTTP_GetResponseHeaders (internal)
5034 * Read server response
5041 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5044 WCHAR buffer[MAX_REPLY_LEN];
5045 DWORD buflen = MAX_REPLY_LEN;
5046 BOOL bSuccess = FALSE;
5048 char bufferA[MAX_REPLY_LEN];
5049 LPWSTR status_code = NULL, status_text = NULL;
5050 DWORD cchMaxRawHeaders = 1024;
5051 LPWSTR lpszRawHeaders = NULL;
5053 DWORD cchRawHeaders = 0;
5054 BOOL codeHundred = FALSE;
5058 if (!NETCON_connected(&request->netConnection))
5062 static const WCHAR szHundred[] = {'1','0','0',0};
5064 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5066 buflen = MAX_REPLY_LEN;
5067 if (!read_line(request, bufferA, &buflen))
5070 /* clear old response headers (eg. from a redirect response) */
5072 HTTP_clear_response_headers( request );
5077 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5078 /* check is this a status code line? */
5079 if (!strncmpW(buffer, g_szHttp1_0, 4))
5081 /* split the version from the status code */
5082 status_code = strchrW( buffer, ' ' );
5087 /* split the status code from the status text */
5088 status_text = strchrW( status_code, ' ' );
5093 TRACE("version [%s] status code [%s] status text [%s]\n",
5094 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5096 codeHundred = (!strcmpW(status_code, szHundred));
5098 else if (!codeHundred)
5100 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5102 HeapFree(GetProcessHeap(), 0, request->version);
5103 HeapFree(GetProcessHeap(), 0, request->statusText);
5105 request->version = heap_strdupW(g_szHttp1_0);
5106 request->statusText = heap_strdupW(szOK);
5108 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5109 request->rawHeaders = heap_strdupW(szDefaultHeader);
5114 } while (codeHundred);
5116 /* Add status code */
5117 HTTP_ProcessHeader(request, szStatus, status_code,
5118 HTTP_ADDHDR_FLAG_REPLACE);
5120 HeapFree(GetProcessHeap(),0,request->version);
5121 HeapFree(GetProcessHeap(),0,request->statusText);
5123 request->version = heap_strdupW(buffer);
5124 request->statusText = heap_strdupW(status_text);
5126 /* Restore the spaces */
5127 *(status_code-1) = ' ';
5128 *(status_text-1) = ' ';
5130 /* regenerate raw headers */
5131 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5132 if (!lpszRawHeaders) goto lend;
5134 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5135 cchMaxRawHeaders *= 2;
5136 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5137 if (temp == NULL) goto lend;
5138 lpszRawHeaders = temp;
5139 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5140 cchRawHeaders += (buflen-1);
5141 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5142 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5143 lpszRawHeaders[cchRawHeaders] = '\0';
5145 /* Parse each response line */
5148 buflen = MAX_REPLY_LEN;
5149 if (read_line(request, bufferA, &buflen))
5151 LPWSTR * pFieldAndValue;
5153 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5155 if (!bufferA[0]) break;
5156 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5158 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5161 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5162 cchMaxRawHeaders *= 2;
5163 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5164 if (temp == NULL) goto lend;
5165 lpszRawHeaders = temp;
5166 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5167 cchRawHeaders += (buflen-1);
5168 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5169 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5170 lpszRawHeaders[cchRawHeaders] = '\0';
5172 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5173 HTTP_ADDREQ_FLAG_ADD );
5175 HTTP_FreeTokens(pFieldAndValue);
5186 /* make sure the response header is terminated with an empty line. Some apps really
5187 truly care about that empty line being there for some reason. Just add it to the
5189 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5191 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5192 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5193 if (temp == NULL) goto lend;
5194 lpszRawHeaders = temp;
5197 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5199 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5200 request->rawHeaders = lpszRawHeaders;
5201 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5211 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5216 /***********************************************************************
5217 * HTTP_InterpretHttpHeader (internal)
5219 * Parse server response
5223 * Pointer to array of field, value, NULL on success.
5226 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5228 LPWSTR * pTokenPair;
5232 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
5234 pszColon = strchrW(buffer, ':');
5235 /* must have two tokens */
5238 HTTP_FreeTokens(pTokenPair);
5240 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5244 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
5247 HTTP_FreeTokens(pTokenPair);
5250 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5251 pTokenPair[0][pszColon - buffer] = '\0';
5255 len = strlenW(pszColon);
5256 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5259 HTTP_FreeTokens(pTokenPair);
5262 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5264 strip_spaces(pTokenPair[0]);
5265 strip_spaces(pTokenPair[1]);
5267 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5271 /***********************************************************************
5272 * HTTP_ProcessHeader (internal)
5274 * Stuff header into header tables according to <dwModifier>
5278 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5280 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5282 LPHTTPHEADERW lphttpHdr = NULL;
5284 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5285 DWORD res = ERROR_HTTP_INVALID_HEADER;
5287 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5289 /* REPLACE wins out over ADD */
5290 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5291 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5293 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5296 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5300 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5301 return ERROR_HTTP_INVALID_HEADER;
5302 lphttpHdr = &request->custHeaders[index];
5308 hdr.lpszField = (LPWSTR)field;
5309 hdr.lpszValue = (LPWSTR)value;
5310 hdr.wFlags = hdr.wCount = 0;
5312 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5313 hdr.wFlags |= HDR_ISREQUEST;
5315 return HTTP_InsertCustomHeader(request, &hdr);
5317 /* no value to delete */
5318 else return ERROR_SUCCESS;
5320 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5321 lphttpHdr->wFlags |= HDR_ISREQUEST;
5323 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5325 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5327 HTTP_DeleteCustomHeader( request, index );
5333 hdr.lpszField = (LPWSTR)field;
5334 hdr.lpszValue = (LPWSTR)value;
5335 hdr.wFlags = hdr.wCount = 0;
5337 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5338 hdr.wFlags |= HDR_ISREQUEST;
5340 return HTTP_InsertCustomHeader(request, &hdr);
5343 return ERROR_SUCCESS;
5345 else if (dwModifier & COALESCEFLAGS)
5350 INT origlen = strlenW(lphttpHdr->lpszValue);
5351 INT valuelen = strlenW(value);
5353 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5356 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5358 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5361 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5364 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5366 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5369 lphttpHdr->lpszValue = lpsztmp;
5370 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5373 lphttpHdr->lpszValue[origlen] = ch;
5375 lphttpHdr->lpszValue[origlen] = ' ';
5379 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5380 lphttpHdr->lpszValue[len] = '\0';
5381 res = ERROR_SUCCESS;
5385 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5386 res = ERROR_OUTOFMEMORY;
5389 TRACE("<-- %d\n", res);
5394 /***********************************************************************
5395 * HTTP_FinishedReading (internal)
5397 * Called when all content from server has been read by client.
5400 static BOOL HTTP_FinishedReading(http_request_t *request)
5402 BOOL keepalive = HTTP_KeepAlive(request);
5409 HTTPREQ_CloseConnection(&request->hdr);
5412 /* FIXME: store data in the URL cache here */
5418 /***********************************************************************
5419 * HTTP_GetCustomHeaderIndex (internal)
5421 * Return index of custom header from header array
5424 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5425 int requested_index, BOOL request_only)
5429 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5431 for (index = 0; index < request->nCustHeaders; index++)
5433 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5436 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5439 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5442 if (requested_index == 0)
5447 if (index >= request->nCustHeaders)
5450 TRACE("Return: %d\n", index);
5455 /***********************************************************************
5456 * HTTP_InsertCustomHeader (internal)
5458 * Insert header into array
5461 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5464 LPHTTPHEADERW lph = NULL;
5466 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5467 count = request->nCustHeaders + 1;
5469 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, request->custHeaders, sizeof(HTTPHEADERW) * count);
5471 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5474 return ERROR_OUTOFMEMORY;
5476 request->custHeaders = lph;
5477 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5478 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5479 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5480 request->custHeaders[count-1].wCount= lpHdr->wCount;
5481 request->nCustHeaders++;
5483 return ERROR_SUCCESS;
5487 /***********************************************************************
5488 * HTTP_DeleteCustomHeader (internal)
5490 * Delete header from array
5491 * If this function is called, the indexs may change.
5493 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5495 if( request->nCustHeaders <= 0 )
5497 if( index >= request->nCustHeaders )
5499 request->nCustHeaders--;
5501 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5502 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5504 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5505 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5506 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5512 /***********************************************************************
5513 * HTTP_VerifyValidHeader (internal)
5515 * Verify the given header is not invalid for the given http request
5518 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5520 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5521 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5522 return ERROR_HTTP_INVALID_HEADER;
5524 return ERROR_SUCCESS;
5527 /***********************************************************************
5528 * IsHostInProxyBypassList (@)
5533 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5535 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5539 /***********************************************************************
5540 * InternetShowSecurityInfoByURLA (@)
5542 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5544 FIXME("stub: %s %p\n", url, window);
5548 /***********************************************************************
5549 * InternetShowSecurityInfoByURLW (@)
5551 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5553 FIXME("stub: %s %p\n", debugstr_w(url), window);