2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
180 typedef struct _authorizationData
186 LPSTR lpszAuthorization;
187 UINT AuthorizationLen;
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
196 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
201 static BOOL HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
210 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
211 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
212 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
213 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
214 static void HTTP_DrainContent(http_request_t *req);
215 static BOOL HTTP_FinishedReading(http_request_t *req);
217 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
220 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
221 if (HeaderIndex == -1)
224 return &req->pCustHeaders[HeaderIndex];
229 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
231 return HeapAlloc(GetProcessHeap(), 0, items*size);
234 static void wininet_zfree(voidpf opaque, voidpf address)
236 HeapFree(GetProcessHeap(), 0, address);
239 static void init_gzip_stream(http_request_t *req)
241 gzip_stream_t *gzip_stream;
244 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
245 gzip_stream->zstream.zalloc = wininet_zalloc;
246 gzip_stream->zstream.zfree = wininet_zfree;
247 gzip_stream->zstream.opaque = NULL;
248 gzip_stream->zstream.next_in = NULL;
249 gzip_stream->zstream.avail_in = 0;
250 gzip_stream->zstream.next_out = NULL;
251 gzip_stream->zstream.avail_out = 0;
252 gzip_stream->buf_pos = 0;
253 gzip_stream->buf_size = 0;
254 gzip_stream->end_of_data = FALSE;
256 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
258 ERR("inflateInit failed: %d\n", zres);
259 HeapFree(GetProcessHeap(), 0, gzip_stream);
263 req->gzip_stream = gzip_stream;
265 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
267 HTTP_DeleteCustomHeader(req, index);
272 static void init_gzip_stream(http_request_t *req)
274 ERR("gzip stream not supported, missing zlib.\n");
279 /* set the request content length based on the headers */
280 static DWORD set_content_length( http_request_t *lpwhr )
282 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
286 size = sizeof(lpwhr->dwContentLength);
287 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
288 &lpwhr->dwContentLength, &size, NULL))
289 lpwhr->dwContentLength = ~0u;
291 size = sizeof(encoding);
292 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
293 !strcmpiW(encoding, szChunked))
295 lpwhr->dwContentLength = ~0u;
296 lpwhr->read_chunked = TRUE;
299 if(lpwhr->decoding) {
302 static const WCHAR gzipW[] = {'g','z','i','p',0};
304 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
305 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
306 init_gzip_stream(lpwhr);
309 return lpwhr->dwContentLength;
312 /***********************************************************************
313 * HTTP_Tokenize (internal)
315 * Tokenize a string, allocating memory for the tokens.
317 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
319 LPWSTR * token_array;
326 /* empty string has no tokens */
330 for (i = 0; string[i]; i++)
332 if (!strncmpW(string+i, token_string, strlenW(token_string)))
336 /* we want to skip over separators, but not the null terminator */
337 for (j = 0; j < strlenW(token_string) - 1; j++)
345 /* add 1 for terminating NULL */
346 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
347 token_array[tokens] = NULL;
350 for (i = 0; i < tokens; i++)
353 next_token = strstrW(string, token_string);
354 if (!next_token) next_token = string+strlenW(string);
355 len = next_token - string;
356 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
357 memcpy(token_array[i], string, len*sizeof(WCHAR));
358 token_array[i][len] = '\0';
359 string = next_token+strlenW(token_string);
364 /***********************************************************************
365 * HTTP_FreeTokens (internal)
367 * Frees memory returned from HTTP_Tokenize.
369 static void HTTP_FreeTokens(LPWSTR * token_array)
372 for (i = 0; token_array[i]; i++)
373 HeapFree(GetProcessHeap(), 0, token_array[i]);
374 HeapFree(GetProcessHeap(), 0, token_array);
377 /* **********************************************************************
379 * Helper functions for the HttpSendRequest(Ex) functions
382 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
384 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
385 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
387 TRACE("%p\n", lpwhr);
389 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
390 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
391 req->dwContentLength, req->bEndRequest);
393 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
396 static void HTTP_FixURL(http_request_t *lpwhr)
398 static const WCHAR szSlash[] = { '/',0 };
399 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
401 /* If we don't have a path we set it to root */
402 if (NULL == lpwhr->lpszPath)
403 lpwhr->lpszPath = heap_strdupW(szSlash);
404 else /* remove \r and \n*/
406 int nLen = strlenW(lpwhr->lpszPath);
407 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
410 lpwhr->lpszPath[nLen]='\0';
412 /* Replace '\' with '/' */
415 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
419 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
420 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
421 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
423 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
424 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
426 strcpyW(fixurl + 1, lpwhr->lpszPath);
427 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
428 lpwhr->lpszPath = fixurl;
432 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
434 LPWSTR requestString;
440 static const WCHAR szSpace[] = { ' ',0 };
441 static const WCHAR szColon[] = { ':',' ',0 };
442 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
444 /* allocate space for an array of all the string pointers to be added */
445 len = (lpwhr->nCustHeaders)*4 + 10;
446 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
448 /* add the verb, path and HTTP version string */
456 /* Append custom request headers */
457 for (i = 0; i < lpwhr->nCustHeaders; i++)
459 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
462 req[n++] = lpwhr->pCustHeaders[i].lpszField;
464 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
466 TRACE("Adding custom header %s (%s)\n",
467 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
468 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
473 ERR("oops. buffer overrun\n");
476 requestString = HTTP_build_req( req, 4 );
477 HeapFree( GetProcessHeap(), 0, req );
480 * Set (header) termination string for request
481 * Make sure there's exactly two new lines at the end of the request
483 p = &requestString[strlenW(requestString)-1];
484 while ( (*p == '\n') || (*p == '\r') )
486 strcpyW( p+1, sztwocrlf );
488 return requestString;
491 static void HTTP_ProcessCookies( http_request_t *lpwhr )
495 LPHTTPHEADERW setCookieHeader;
497 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
499 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
501 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
504 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
508 Host = HTTP_GetHeader(lpwhr, hostW);
509 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
510 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
511 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
512 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
514 HeapFree(GetProcessHeap(), 0, buf_url);
520 static void strip_spaces(LPWSTR start)
525 while (*str == ' ' && *str != '\0')
529 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
531 end = start + strlenW(start) - 1;
532 while (end >= start && *end == ' ')
539 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
541 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
542 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
544 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
545 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
546 if (is_basic && pszRealm)
549 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
553 token = strchrW(ptr,'=');
557 while (*realm == ' ' && *realm != '\0')
559 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
560 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
563 while (*token == ' ' && *token != '\0')
567 *pszRealm = heap_strdupW(token);
568 strip_spaces(*pszRealm);
575 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
577 if (!authinfo) return;
579 if (SecIsValidHandle(&authinfo->ctx))
580 DeleteSecurityContext(&authinfo->ctx);
581 if (SecIsValidHandle(&authinfo->cred))
582 FreeCredentialsHandle(&authinfo->cred);
584 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
585 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
586 HeapFree(GetProcessHeap(), 0, authinfo);
589 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
591 authorizationData *ad;
594 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
596 EnterCriticalSection(&authcache_cs);
597 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
599 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
601 TRACE("Authorization found in cache\n");
602 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
603 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
604 rc = ad->AuthorizationLen;
608 LeaveCriticalSection(&authcache_cs);
612 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
615 authorizationData* ad = NULL;
617 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
619 EnterCriticalSection(&authcache_cs);
620 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
622 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
623 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
632 TRACE("Found match in cache, replacing\n");
633 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
634 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
635 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
636 ad->AuthorizationLen = auth_data_len;
640 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
641 ad->lpszwHost = heap_strdupW(host);
642 ad->lpszwRealm = heap_strdupW(realm);
643 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
644 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
645 ad->AuthorizationLen = auth_data_len;
646 list_add_head(&basicAuthorizationCache,&ad->entry);
647 TRACE("authorization cached\n");
649 LeaveCriticalSection(&authcache_cs);
652 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
653 struct HttpAuthInfo **ppAuthInfo,
654 LPWSTR domain_and_username, LPWSTR password,
657 SECURITY_STATUS sec_status;
658 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
660 LPWSTR szRealm = NULL;
662 TRACE("%s\n", debugstr_w(pszAuthValue));
669 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
673 SecInvalidateHandle(&pAuthInfo->cred);
674 SecInvalidateHandle(&pAuthInfo->ctx);
675 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
677 pAuthInfo->auth_data = NULL;
678 pAuthInfo->auth_data_len = 0;
679 pAuthInfo->finished = FALSE;
681 if (is_basic_auth_value(pszAuthValue,NULL))
683 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
684 pAuthInfo->scheme = heap_strdupW(szBasic);
685 if (!pAuthInfo->scheme)
687 HeapFree(GetProcessHeap(), 0, pAuthInfo);
694 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
696 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
697 if (!pAuthInfo->scheme)
699 HeapFree(GetProcessHeap(), 0, pAuthInfo);
703 if (domain_and_username)
705 WCHAR *user = strchrW(domain_and_username, '\\');
706 WCHAR *domain = domain_and_username;
708 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
710 pAuthData = &nt_auth_identity;
715 user = domain_and_username;
719 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
720 nt_auth_identity.User = user;
721 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
722 nt_auth_identity.Domain = domain;
723 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
724 nt_auth_identity.Password = password;
725 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
728 /* use default credentials */
731 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
732 SECPKG_CRED_OUTBOUND, NULL,
734 NULL, &pAuthInfo->cred,
736 if (sec_status == SEC_E_OK)
738 PSecPkgInfoW sec_pkg_info;
739 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
740 if (sec_status == SEC_E_OK)
742 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
743 FreeContextBuffer(sec_pkg_info);
746 if (sec_status != SEC_E_OK)
748 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
749 debugstr_w(pAuthInfo->scheme), sec_status);
750 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
751 HeapFree(GetProcessHeap(), 0, pAuthInfo);
755 *ppAuthInfo = pAuthInfo;
757 else if (pAuthInfo->finished)
760 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
761 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
763 ERR("authentication scheme changed from %s to %s\n",
764 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
768 if (is_basic_auth_value(pszAuthValue,&szRealm))
772 char *auth_data = NULL;
773 UINT auth_data_len = 0;
775 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
777 if (!domain_and_username)
780 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
781 if (auth_data_len == 0)
783 HeapFree(GetProcessHeap(),0,szRealm);
789 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
790 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
792 /* length includes a nul terminator, which will be re-used for the ':' */
793 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
796 HeapFree(GetProcessHeap(),0,szRealm);
800 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
801 auth_data[userlen] = ':';
802 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
803 auth_data_len = userlen + 1 + passlen;
805 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
808 pAuthInfo->auth_data = auth_data;
809 pAuthInfo->auth_data_len = auth_data_len;
810 pAuthInfo->finished = TRUE;
811 HeapFree(GetProcessHeap(),0,szRealm);
818 SecBufferDesc out_desc, in_desc;
820 unsigned char *buffer;
821 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
822 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
824 in.BufferType = SECBUFFER_TOKEN;
828 in_desc.ulVersion = 0;
829 in_desc.cBuffers = 1;
830 in_desc.pBuffers = ∈
832 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
833 if (*pszAuthData == ' ')
836 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
837 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
838 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
841 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
843 out.BufferType = SECBUFFER_TOKEN;
844 out.cbBuffer = pAuthInfo->max_token;
845 out.pvBuffer = buffer;
847 out_desc.ulVersion = 0;
848 out_desc.cBuffers = 1;
849 out_desc.pBuffers = &out;
851 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
852 first ? NULL : &pAuthInfo->ctx,
853 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
854 context_req, 0, SECURITY_NETWORK_DREP,
855 in.pvBuffer ? &in_desc : NULL,
856 0, &pAuthInfo->ctx, &out_desc,
857 &pAuthInfo->attr, &pAuthInfo->exp);
858 if (sec_status == SEC_E_OK)
860 pAuthInfo->finished = TRUE;
861 pAuthInfo->auth_data = out.pvBuffer;
862 pAuthInfo->auth_data_len = out.cbBuffer;
863 TRACE("sending last auth packet\n");
865 else if (sec_status == SEC_I_CONTINUE_NEEDED)
867 pAuthInfo->auth_data = out.pvBuffer;
868 pAuthInfo->auth_data_len = out.cbBuffer;
869 TRACE("sending next auth packet\n");
873 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
874 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
875 destroy_authinfo(pAuthInfo);
884 /***********************************************************************
885 * HTTP_HttpAddRequestHeadersW (internal)
887 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
888 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
893 BOOL bSuccess = FALSE;
896 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
898 if( dwHeaderLength == ~0U )
899 len = strlenW(lpszHeader);
901 len = dwHeaderLength;
902 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
903 lstrcpynW( buffer, lpszHeader, len + 1);
909 LPWSTR * pFieldAndValue;
913 while (*lpszEnd != '\0')
915 if (*lpszEnd == '\r' || *lpszEnd == '\n')
920 if (*lpszStart == '\0')
923 if (*lpszEnd == '\r' || *lpszEnd == '\n')
926 lpszEnd++; /* Jump over newline */
928 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
929 if (*lpszStart == '\0')
931 /* Skip 0-length headers */
936 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
939 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
941 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
942 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
943 HTTP_FreeTokens(pFieldAndValue);
949 HeapFree(GetProcessHeap(), 0, buffer);
954 /***********************************************************************
955 * HttpAddRequestHeadersW (WININET.@)
957 * Adds one or more HTTP header to the request handler
960 * On Windows if dwHeaderLength includes the trailing '\0', then
961 * HttpAddRequestHeadersW() adds it too. However this results in an
962 * invalid Http header which is rejected by some servers so we probably
963 * don't need to match Windows on that point.
970 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
971 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
973 BOOL bSuccess = FALSE;
974 http_request_t *lpwhr;
976 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
981 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
982 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
984 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
987 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
990 WININET_Release( &lpwhr->hdr );
995 /***********************************************************************
996 * HttpAddRequestHeadersA (WININET.@)
998 * Adds one or more HTTP header to the request handler
1005 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1006 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1012 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1014 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1015 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1016 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1017 if( dwHeaderLength != ~0U )
1018 dwHeaderLength = len;
1020 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1022 HeapFree( GetProcessHeap(), 0, hdr );
1027 /***********************************************************************
1028 * HttpEndRequestA (WININET.@)
1030 * Ends an HTTP request that was started by HttpSendRequestEx
1033 * TRUE if successful
1037 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
1038 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
1040 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
1044 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1048 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
1051 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
1056 INTERNET_ASYNC_RESULT iar;
1058 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1059 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1061 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
1065 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1066 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
1068 /* process cookies here. Is this right? */
1069 HTTP_ProcessCookies(lpwhr);
1071 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
1073 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
1075 DWORD dwCode,dwCodeLength = sizeof(DWORD);
1076 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
1077 (dwCode == 302 || dwCode == 301 || dwCode == 303))
1079 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
1080 dwBufferSize=sizeof(szNewLocation);
1081 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
1083 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
1085 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1086 lpwhr->lpszVerb = heap_strdupW(szGET);
1088 HTTP_DrainContent(lpwhr);
1089 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
1091 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
1092 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
1093 rc = HTTP_HandleRedirect(lpwhr, new_url);
1095 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
1096 HeapFree( GetProcessHeap(), 0, new_url );
1102 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
1103 iar.dwError = rc ? 0 : INTERNET_GetLastError();
1105 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1106 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1107 sizeof(INTERNET_ASYNC_RESULT));
1111 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
1113 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
1114 http_request_t *lpwhr = (http_request_t*)work->hdr;
1116 TRACE("%p\n", lpwhr);
1118 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
1121 /***********************************************************************
1122 * HttpEndRequestW (WININET.@)
1124 * Ends an HTTP request that was started by HttpSendRequestEx
1127 * TRUE if successful
1131 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
1132 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
1135 http_request_t *lpwhr;
1141 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1145 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
1147 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
1149 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1151 WININET_Release( &lpwhr->hdr );
1154 lpwhr->hdr.dwFlags |= dwFlags;
1156 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1159 struct WORKREQ_HTTPENDREQUESTW *request;
1161 work.asyncproc = AsyncHttpEndRequestProc;
1162 work.hdr = WININET_AddRef( &lpwhr->hdr );
1164 request = &work.u.HttpEndRequestW;
1165 request->dwFlags = dwFlags;
1166 request->dwContext = dwContext;
1168 INTERNET_AsyncCall(&work);
1169 INTERNET_SetLastError(ERROR_IO_PENDING);
1172 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1174 WININET_Release( &lpwhr->hdr );
1175 TRACE("%i <--\n",rc);
1179 /***********************************************************************
1180 * HttpOpenRequestW (WININET.@)
1182 * Open a HTTP request handle
1185 * HINTERNET a HTTP request handle on success
1189 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1190 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1191 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1192 DWORD dwFlags, DWORD_PTR dwContext)
1194 http_session_t *lpwhs;
1195 HINTERNET handle = NULL;
1197 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1198 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1199 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1200 dwFlags, dwContext);
1201 if(lpszAcceptTypes!=NULL)
1204 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1205 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1208 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1209 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1211 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1216 * My tests seem to show that the windows version does not
1217 * become asynchronous until after this point. And anyhow
1218 * if this call was asynchronous then how would you get the
1219 * necessary HINTERNET pointer returned by this function.
1222 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1223 lpszVersion, lpszReferrer, lpszAcceptTypes,
1224 dwFlags, dwContext);
1227 WININET_Release( &lpwhs->hdr );
1228 TRACE("returning %p\n", handle);
1233 /***********************************************************************
1234 * HttpOpenRequestA (WININET.@)
1236 * Open a HTTP request handle
1239 * HINTERNET a HTTP request handle on success
1243 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1244 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1245 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1246 DWORD dwFlags, DWORD_PTR dwContext)
1248 LPWSTR szVerb = NULL, szObjectName = NULL;
1249 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1250 INT acceptTypesCount;
1251 HINTERNET rc = FALSE;
1254 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1255 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1256 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1257 dwFlags, dwContext);
1261 szVerb = heap_strdupAtoW(lpszVerb);
1268 szObjectName = heap_strdupAtoW(lpszObjectName);
1269 if ( !szObjectName )
1275 szVersion = heap_strdupAtoW(lpszVersion);
1282 szReferrer = heap_strdupAtoW(lpszReferrer);
1287 if (lpszAcceptTypes)
1289 acceptTypesCount = 0;
1290 types = lpszAcceptTypes;
1295 /* find out how many there are */
1296 if (*types && **types)
1298 TRACE("accept type: %s\n", debugstr_a(*types));
1304 WARN("invalid accept type pointer\n");
1309 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1310 if (!szAcceptTypes) goto end;
1312 acceptTypesCount = 0;
1313 types = lpszAcceptTypes;
1318 if (*types && **types)
1319 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1323 /* ignore invalid pointer */
1328 szAcceptTypes[acceptTypesCount] = NULL;
1331 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1332 szVersion, szReferrer,
1333 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1338 acceptTypesCount = 0;
1339 while (szAcceptTypes[acceptTypesCount])
1341 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1344 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1346 HeapFree(GetProcessHeap(), 0, szReferrer);
1347 HeapFree(GetProcessHeap(), 0, szVersion);
1348 HeapFree(GetProcessHeap(), 0, szObjectName);
1349 HeapFree(GetProcessHeap(), 0, szVerb);
1354 /***********************************************************************
1357 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1360 static const CHAR HTTP_Base64Enc[] =
1361 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1365 /* first 6 bits, all from bin[0] */
1366 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1367 x = (bin[0] & 3) << 4;
1369 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1372 base64[n++] = HTTP_Base64Enc[x];
1377 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1378 x = ( bin[1] & 0x0f ) << 2;
1380 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1383 base64[n++] = HTTP_Base64Enc[x];
1387 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1389 /* last 6 bits, all from bin [2] */
1390 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1398 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1399 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1400 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1401 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1402 static const signed char HTTP_Base64Dec[256] =
1404 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1405 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1406 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1407 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1408 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1409 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1410 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1411 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1412 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1413 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1414 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1415 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1416 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1417 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1418 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1419 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1420 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1421 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1422 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1423 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1424 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1425 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1426 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1427 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1428 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1429 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1433 /***********************************************************************
1436 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1444 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1445 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1446 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1447 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1449 WARN("invalid base64: %s\n", debugstr_w(base64));
1453 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1456 if ((base64[2] == '=') && (base64[3] == '='))
1458 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1459 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1461 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1465 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1468 if (base64[3] == '=')
1470 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1471 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1473 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1477 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1486 /***********************************************************************
1487 * HTTP_InsertAuthorization
1489 * Insert or delete the authorization field in the request header.
1491 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1495 static const WCHAR wszSpace[] = {' ',0};
1496 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1498 WCHAR *authorization = NULL;
1500 if (pAuthInfo->auth_data_len)
1502 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1503 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1504 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1508 strcpyW(authorization, pAuthInfo->scheme);
1509 strcatW(authorization, wszSpace);
1510 HTTP_EncodeBase64(pAuthInfo->auth_data,
1511 pAuthInfo->auth_data_len,
1512 authorization+strlenW(authorization));
1514 /* clear the data as it isn't valid now that it has been sent to the
1515 * server, unless it's Basic authentication which doesn't do
1516 * connection tracking */
1517 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1519 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1520 pAuthInfo->auth_data = NULL;
1521 pAuthInfo->auth_data_len = 0;
1525 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1527 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1529 HeapFree(GetProcessHeap(), 0, authorization);
1534 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1536 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1539 size = sizeof(new_location);
1540 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1542 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1543 strcpyW( url, new_location );
1547 static const WCHAR slash[] = { '/',0 };
1548 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1549 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1550 http_session_t *session = req->lpHttpSession;
1552 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1553 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1555 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1557 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1558 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1560 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1561 if (req->lpszPath[0] != '/') strcatW( url, slash );
1562 strcatW( url, req->lpszPath );
1564 TRACE("url=%s\n", debugstr_w(url));
1568 /***********************************************************************
1569 * HTTP_DealWithProxy
1571 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1573 WCHAR buf[MAXHOSTNAME];
1574 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1575 static WCHAR szNul[] = { 0 };
1576 URL_COMPONENTSW UrlComponents;
1577 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1578 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1580 memset( &UrlComponents, 0, sizeof UrlComponents );
1581 UrlComponents.dwStructSize = sizeof UrlComponents;
1582 UrlComponents.lpszHostName = buf;
1583 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1585 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1586 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1587 sprintfW(proxy, szFormat, hIC->lpszProxy);
1589 strcpyW(proxy, hIC->lpszProxy);
1590 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1592 if( UrlComponents.dwHostNameLength == 0 )
1595 if( !lpwhr->lpszPath )
1596 lpwhr->lpszPath = szNul;
1598 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1599 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1601 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1602 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1603 lpwhs->nServerPort = UrlComponents.nPort;
1605 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1609 #ifndef INET6_ADDRSTRLEN
1610 #define INET6_ADDRSTRLEN 46
1613 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1615 char szaddr[INET6_ADDRSTRLEN];
1616 http_session_t *lpwhs = lpwhr->lpHttpSession;
1619 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1620 INTERNET_STATUS_RESOLVING_NAME,
1621 lpwhs->lpszServerName,
1622 strlenW(lpwhs->lpszServerName)+1);
1624 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1625 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1626 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1628 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1632 switch (lpwhs->socketAddress.ss_family)
1635 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1638 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1641 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1642 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1645 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1646 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1647 INTERNET_STATUS_NAME_RESOLVED,
1648 szaddr, strlen(szaddr)+1);
1650 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1655 /***********************************************************************
1656 * HTTPREQ_Destroy (internal)
1658 * Deallocate request handle
1661 static void HTTPREQ_Destroy(object_header_t *hdr)
1663 http_request_t *lpwhr = (http_request_t*) hdr;
1668 if(lpwhr->hCacheFile)
1669 CloseHandle(lpwhr->hCacheFile);
1671 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1673 DeleteCriticalSection( &lpwhr->read_section );
1674 WININET_Release(&lpwhr->lpHttpSession->hdr);
1676 destroy_authinfo(lpwhr->pAuthInfo);
1677 destroy_authinfo(lpwhr->pProxyAuthInfo);
1679 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1680 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1681 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1682 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1683 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1685 for (i = 0; i < lpwhr->nCustHeaders; i++)
1687 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1688 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1692 if(lpwhr->gzip_stream) {
1693 if(!lpwhr->gzip_stream->end_of_data)
1694 inflateEnd(&lpwhr->gzip_stream->zstream);
1695 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1699 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1700 HeapFree(GetProcessHeap(), 0, lpwhr);
1703 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1705 http_request_t *lpwhr = (http_request_t*) hdr;
1707 TRACE("%p\n",lpwhr);
1709 if (!NETCON_connected(&lpwhr->netConnection))
1712 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1713 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1715 NETCON_close(&lpwhr->netConnection);
1717 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1718 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1721 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1723 LPHTTPHEADERW host_header;
1725 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1727 host_header = HTTP_GetHeader(req, hostW);
1731 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1735 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1737 WCHAR szVersion[10];
1738 WCHAR szConnectionResponse[20];
1739 DWORD dwBufferSize = sizeof(szVersion);
1740 BOOL keepalive = FALSE;
1742 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1743 * the connection is keep-alive by default */
1744 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1745 &dwBufferSize, NULL) &&
1746 !strcmpiW(szVersion, g_szHttp1_1))
1751 dwBufferSize = sizeof(szConnectionResponse);
1752 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1753 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1755 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1761 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1763 http_request_t *req = (http_request_t*)hdr;
1766 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1768 http_session_t *lpwhs = req->lpHttpSession;
1769 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1771 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1773 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1774 return ERROR_INSUFFICIENT_BUFFER;
1775 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1776 /* FIXME: can't get a SOCKET from our connection since we don't use
1780 /* FIXME: get source port from req->netConnection */
1781 info->SourcePort = 0;
1782 info->DestPort = lpwhs->nHostPort;
1784 if (HTTP_KeepAlive(req))
1785 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1786 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1787 info->Flags |= IDSI_FLAG_PROXY;
1788 if (req->netConnection.useSSL)
1789 info->Flags |= IDSI_FLAG_SECURE;
1791 return ERROR_SUCCESS;
1794 case INTERNET_OPTION_SECURITY_FLAGS:
1796 http_session_t *lpwhs;
1797 lpwhs = req->lpHttpSession;
1799 if (*size < sizeof(ULONG))
1800 return ERROR_INSUFFICIENT_BUFFER;
1802 *size = sizeof(DWORD);
1803 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1804 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1806 *(DWORD*)buffer = 0;
1807 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1808 return ERROR_SUCCESS;
1811 case INTERNET_OPTION_HANDLE_TYPE:
1812 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1814 if (*size < sizeof(ULONG))
1815 return ERROR_INSUFFICIENT_BUFFER;
1817 *size = sizeof(DWORD);
1818 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1819 return ERROR_SUCCESS;
1821 case INTERNET_OPTION_URL: {
1822 WCHAR url[INTERNET_MAX_URL_LENGTH];
1827 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1829 TRACE("INTERNET_OPTION_URL\n");
1831 host = HTTP_GetHeader(req, hostW);
1832 strcpyW(url, httpW);
1833 strcatW(url, host->lpszValue);
1834 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1836 strcatW(url, req->lpszPath);
1838 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1841 len = (strlenW(url)+1) * sizeof(WCHAR);
1843 return ERROR_INSUFFICIENT_BUFFER;
1846 strcpyW(buffer, url);
1847 return ERROR_SUCCESS;
1849 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1851 return ERROR_INSUFFICIENT_BUFFER;
1854 return ERROR_SUCCESS;
1858 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1859 INTERNET_CACHE_ENTRY_INFOW *info;
1860 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1861 WCHAR url[INTERNET_MAX_URL_LENGTH];
1862 DWORD nbytes, error;
1865 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1867 if (*size < sizeof(*ts))
1869 *size = sizeof(*ts);
1870 return ERROR_INSUFFICIENT_BUFFER;
1873 HTTP_GetRequestURL(req, url);
1874 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1875 error = GetLastError();
1876 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1878 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1879 return ERROR_OUTOFMEMORY;
1881 GetUrlCacheEntryInfoW(url, info, &nbytes);
1883 ts->ftExpires = info->ExpireTime;
1884 ts->ftLastModified = info->LastModifiedTime;
1886 HeapFree(GetProcessHeap(), 0, info);
1887 *size = sizeof(*ts);
1888 return ERROR_SUCCESS;
1893 case INTERNET_OPTION_DATAFILE_NAME: {
1896 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1898 if(!req->lpszCacheFile) {
1900 return ERROR_INTERNET_ITEM_NOT_FOUND;
1904 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1905 if(*size < req_size)
1906 return ERROR_INSUFFICIENT_BUFFER;
1909 memcpy(buffer, req->lpszCacheFile, *size);
1910 return ERROR_SUCCESS;
1912 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1913 if (req_size > *size)
1914 return ERROR_INSUFFICIENT_BUFFER;
1916 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1917 -1, buffer, *size, NULL, NULL);
1918 return ERROR_SUCCESS;
1922 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1923 PCCERT_CONTEXT context;
1925 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1926 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1927 return ERROR_INSUFFICIENT_BUFFER;
1930 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1932 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1935 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1936 info->ftExpiry = context->pCertInfo->NotAfter;
1937 info->ftStart = context->pCertInfo->NotBefore;
1939 len = CertNameToStrW(context->dwCertEncodingType,
1940 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1941 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1942 if(info->lpszSubjectInfo)
1943 CertNameToStrW(context->dwCertEncodingType,
1944 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1945 info->lpszSubjectInfo, len);
1946 len = CertNameToStrW(context->dwCertEncodingType,
1947 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1948 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1949 if (info->lpszIssuerInfo)
1950 CertNameToStrW(context->dwCertEncodingType,
1951 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1952 info->lpszIssuerInfo, len);
1954 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1956 len = CertNameToStrA(context->dwCertEncodingType,
1957 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1958 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1959 if(infoA->lpszSubjectInfo)
1960 CertNameToStrA(context->dwCertEncodingType,
1961 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1962 infoA->lpszSubjectInfo, len);
1963 len = CertNameToStrA(context->dwCertEncodingType,
1964 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1965 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1966 if(infoA->lpszIssuerInfo)
1967 CertNameToStrA(context->dwCertEncodingType,
1968 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1969 infoA->lpszIssuerInfo, len);
1973 * Contrary to MSDN, these do not appear to be set.
1975 * lpszSignatureAlgName
1976 * lpszEncryptionAlgName
1979 CertFreeCertificateContext(context);
1980 return ERROR_SUCCESS;
1985 return INET_QueryOption(option, buffer, size, unicode);
1988 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1990 http_request_t *req = (http_request_t*)hdr;
1993 case INTERNET_OPTION_SEND_TIMEOUT:
1994 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1995 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1997 if (size != sizeof(DWORD))
1998 return ERROR_INVALID_PARAMETER;
2000 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
2003 case INTERNET_OPTION_USERNAME:
2004 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
2005 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2006 return ERROR_SUCCESS;
2008 case INTERNET_OPTION_PASSWORD:
2009 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
2010 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2011 return ERROR_SUCCESS;
2012 case INTERNET_OPTION_HTTP_DECODING:
2013 if(size != sizeof(BOOL))
2014 return ERROR_INVALID_PARAMETER;
2015 req->decoding = *(BOOL*)buffer;
2016 return ERROR_SUCCESS;
2019 return ERROR_INTERNET_INVALID_OPTION;
2022 /* read some more data into the read buffer (the read section must be held) */
2023 static BOOL read_more_data( http_request_t *req, int maxlen )
2029 /* move existing data to the start of the buffer */
2031 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2035 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2037 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2038 maxlen - req->read_size, 0, &len ))
2041 req->read_size += len;
2045 /* remove some amount of data from the read buffer (the read section must be held) */
2046 static void remove_data( http_request_t *req, int count )
2048 if (!(req->read_size -= count)) req->read_pos = 0;
2049 else req->read_pos += count;
2052 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2054 int count, bytes_read, pos = 0;
2056 EnterCriticalSection( &req->read_section );
2059 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2063 count = eol - (req->read_buf + req->read_pos);
2064 bytes_read = count + 1;
2066 else count = bytes_read = req->read_size;
2068 count = min( count, *len - pos );
2069 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2071 remove_data( req, bytes_read );
2074 if (!read_more_data( req, -1 ) || !req->read_size)
2077 TRACE( "returning empty string\n" );
2078 LeaveCriticalSection( &req->read_section );
2082 LeaveCriticalSection( &req->read_section );
2086 if (pos && buffer[pos - 1] == '\r') pos--;
2089 buffer[*len - 1] = 0;
2090 TRACE( "returning %s\n", debugstr_a(buffer));
2094 /* discard data contents until we reach end of line (the read section must be held) */
2095 static BOOL discard_eol( http_request_t *req )
2099 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2102 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2105 req->read_pos = req->read_size = 0; /* discard everything */
2106 if (!read_more_data( req, -1 )) return FALSE;
2107 } while (req->read_size);
2111 /* read the size of the next chunk (the read section must be held) */
2112 static BOOL start_next_chunk( http_request_t *req )
2114 DWORD chunk_size = 0;
2116 if (!req->dwContentLength) return TRUE;
2117 if (req->dwContentLength == req->dwContentRead)
2119 /* read terminator for the previous chunk */
2120 if (!discard_eol( req )) return FALSE;
2121 req->dwContentLength = ~0u;
2122 req->dwContentRead = 0;
2126 while (req->read_size)
2128 char ch = req->read_buf[req->read_pos];
2129 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2130 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2131 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2132 else if (ch == ';' || ch == '\r' || ch == '\n')
2134 TRACE( "reading %u byte chunk\n", chunk_size );
2135 req->dwContentLength = chunk_size;
2136 req->dwContentRead = 0;
2137 if (!discard_eol( req )) return FALSE;
2140 remove_data( req, 1 );
2142 if (!read_more_data( req, -1 )) return FALSE;
2143 if (!req->read_size)
2145 req->dwContentLength = req->dwContentRead = 0;
2151 /* check if we have reached the end of the data to read (the read section must be held) */
2152 static BOOL end_of_read_data( http_request_t *req )
2154 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2155 if (req->read_chunked) return (req->dwContentLength == 0);
2156 if (req->dwContentLength == ~0u) return FALSE;
2157 return (req->dwContentLength == req->dwContentRead);
2160 /* fetch some more data into the read buffer (the read section must be held) */
2161 static BOOL refill_buffer( http_request_t *req )
2163 int len = sizeof(req->read_buf);
2165 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2167 if (!start_next_chunk( req )) return FALSE;
2170 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2171 if (len <= req->read_size) return TRUE;
2173 if (!read_more_data( req, len )) return FALSE;
2174 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2178 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2180 DWORD ret = ERROR_SUCCESS;
2184 z_stream *zstream = &req->gzip_stream->zstream;
2187 while(read < size && !req->gzip_stream->end_of_data) {
2188 if(!req->read_size) {
2189 if(!sync || !refill_buffer(req))
2193 zstream->next_in = req->read_buf+req->read_pos;
2194 zstream->avail_in = req->read_size;
2195 zstream->next_out = buf+read;
2196 zstream->avail_out = size-read;
2197 zres = inflate(zstream, Z_FULL_FLUSH);
2198 read = size - zstream->avail_out;
2199 remove_data(req, req->read_size-zstream->avail_in);
2200 if(zres == Z_STREAM_END) {
2201 TRACE("end of data\n");
2202 req->gzip_stream->end_of_data = TRUE;
2203 inflateEnd(&req->gzip_stream->zstream);
2204 }else if(zres != Z_OK) {
2205 WARN("inflate failed %d\n", zres);
2207 ret = ERROR_INTERNET_DECODING_FAILED;
2217 static void refill_gzip_buffer(http_request_t *req)
2222 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2225 if(req->gzip_stream->buf_pos) {
2226 if(req->gzip_stream->buf_size)
2227 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2228 req->gzip_stream->buf_pos = 0;
2231 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2232 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2233 if(res == ERROR_SUCCESS)
2234 req->gzip_stream->buf_size += len;
2237 /* return the size of data available to be read immediately (the read section must be held) */
2238 static DWORD get_avail_data( http_request_t *req )
2240 if (req->gzip_stream) {
2241 refill_gzip_buffer(req);
2242 return req->gzip_stream->buf_size;
2244 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2246 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2249 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2251 INTERNET_ASYNC_RESULT iar;
2255 EnterCriticalSection( &req->read_section );
2256 if (refill_buffer( req )) {
2257 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2258 iar.dwError = first_notif ? 0 : get_avail_data(req);
2261 iar.dwError = INTERNET_GetLastError();
2263 LeaveCriticalSection( &req->read_section );
2265 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2266 sizeof(INTERNET_ASYNC_RESULT));
2269 /* read data from the http connection (the read section must be held) */
2270 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2272 BOOL finished_reading = FALSE;
2273 int len, bytes_read = 0;
2274 DWORD ret = ERROR_SUCCESS;
2276 EnterCriticalSection( &req->read_section );
2278 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2280 if (!start_next_chunk( req )) goto done;
2283 if(req->gzip_stream) {
2284 if(req->gzip_stream->buf_size) {
2285 bytes_read = min(req->gzip_stream->buf_size, size);
2286 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2287 req->gzip_stream->buf_pos += bytes_read;
2288 req->gzip_stream->buf_size -= bytes_read;
2289 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2293 if(size > bytes_read) {
2294 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2295 if(ret == ERROR_SUCCESS)
2299 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2301 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2303 if (req->read_size) {
2304 bytes_read = min( req->read_size, size );
2305 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2306 remove_data( req, bytes_read );
2309 if (size > bytes_read && (!bytes_read || sync)) {
2310 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2311 sync ? MSG_WAITALL : 0, &len))
2313 /* always return success, even if the network layer returns an error */
2316 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2319 req->dwContentRead += bytes_read;
2322 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2323 LeaveCriticalSection( &req->read_section );
2325 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2327 DWORD dwBytesWritten;
2329 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2331 WARN("WriteFile failed: %u\n", GetLastError());
2334 if(finished_reading)
2335 HTTP_FinishedReading(req);
2341 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2343 http_request_t *req = (http_request_t*)hdr;
2344 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2347 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2349 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2350 http_request_t *req = (http_request_t*)workRequest->hdr;
2351 INTERNET_ASYNC_RESULT iar;
2354 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2356 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2357 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2359 iar.dwResult = res == ERROR_SUCCESS;
2362 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2363 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2364 sizeof(INTERNET_ASYNC_RESULT));
2367 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2368 DWORD flags, DWORD_PTR context)
2370 http_request_t *req = (http_request_t*)hdr;
2373 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2374 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2376 if (buffers->dwStructSize != sizeof(*buffers))
2377 return ERROR_INVALID_PARAMETER;
2379 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2381 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2383 WORKREQUEST workRequest;
2385 if (TryEnterCriticalSection( &req->read_section ))
2387 if (get_avail_data(req))
2389 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2390 &buffers->dwBufferLength, FALSE);
2391 LeaveCriticalSection( &req->read_section );
2394 LeaveCriticalSection( &req->read_section );
2397 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2398 workRequest.hdr = WININET_AddRef(&req->hdr);
2399 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2401 INTERNET_AsyncCall(&workRequest);
2403 return ERROR_IO_PENDING;
2406 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2407 !(flags & IRF_NO_WAIT));
2410 if (res == ERROR_SUCCESS) {
2411 DWORD size = buffers->dwBufferLength;
2412 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2413 &size, sizeof(size));
2419 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2421 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2422 http_request_t *req = (http_request_t*)workRequest->hdr;
2423 INTERNET_ASYNC_RESULT iar;
2426 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2428 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2429 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2431 iar.dwResult = res == ERROR_SUCCESS;
2434 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2435 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2436 sizeof(INTERNET_ASYNC_RESULT));
2439 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2440 DWORD flags, DWORD_PTR context)
2443 http_request_t *req = (http_request_t*)hdr;
2446 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2447 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2449 if (buffers->dwStructSize != sizeof(*buffers))
2450 return ERROR_INVALID_PARAMETER;
2452 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2454 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2456 WORKREQUEST workRequest;
2458 if (TryEnterCriticalSection( &req->read_section ))
2460 if (get_avail_data(req))
2462 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2463 &buffers->dwBufferLength, FALSE);
2464 LeaveCriticalSection( &req->read_section );
2467 LeaveCriticalSection( &req->read_section );
2470 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2471 workRequest.hdr = WININET_AddRef(&req->hdr);
2472 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2474 INTERNET_AsyncCall(&workRequest);
2476 return ERROR_IO_PENDING;
2479 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2480 !(flags & IRF_NO_WAIT));
2483 if (res == ERROR_SUCCESS) {
2484 DWORD size = buffers->dwBufferLength;
2485 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2486 &size, sizeof(size));
2492 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2495 http_request_t *lpwhr = (http_request_t*)hdr;
2497 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2500 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2501 lpwhr->dwBytesWritten += *written;
2503 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2507 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2509 http_request_t *req = (http_request_t*)workRequest->hdr;
2511 HTTP_ReceiveRequestData(req, FALSE);
2514 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2516 http_request_t *req = (http_request_t*)hdr;
2518 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2520 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2522 WORKREQUEST workRequest;
2524 /* never wait, if we can't enter the section we queue an async request right away */
2525 if (TryEnterCriticalSection( &req->read_section ))
2527 if ((*available = get_avail_data( req ))) goto done;
2528 if (end_of_read_data( req )) goto done;
2529 LeaveCriticalSection( &req->read_section );
2532 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2533 workRequest.hdr = WININET_AddRef( &req->hdr );
2535 INTERNET_AsyncCall(&workRequest);
2537 return ERROR_IO_PENDING;
2540 EnterCriticalSection( &req->read_section );
2542 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2544 refill_buffer( req );
2545 *available = get_avail_data( req );
2549 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2552 if (NETCON_query_data_available(&req->netConnection, &extra))
2553 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2555 LeaveCriticalSection( &req->read_section );
2557 TRACE( "returning %u\n", *available );
2558 return ERROR_SUCCESS;
2561 static const object_vtbl_t HTTPREQVtbl = {
2563 HTTPREQ_CloseConnection,
2564 HTTPREQ_QueryOption,
2567 HTTPREQ_ReadFileExA,
2568 HTTPREQ_ReadFileExW,
2570 HTTPREQ_QueryDataAvailable,
2574 /***********************************************************************
2575 * HTTP_HttpOpenRequestW (internal)
2577 * Open a HTTP request handle
2580 * HINTERNET a HTTP request handle on success
2584 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2585 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2586 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2587 DWORD dwFlags, DWORD_PTR dwContext)
2589 appinfo_t *hIC = NULL;
2590 http_request_t *lpwhr;
2591 LPWSTR lpszHostName = NULL;
2592 HINTERNET handle = NULL;
2593 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2598 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2599 hIC = lpwhs->lpAppInfo;
2601 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2604 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2607 lpwhr->hdr.htype = WH_HHTTPREQ;
2608 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2609 lpwhr->hdr.dwFlags = dwFlags;
2610 lpwhr->hdr.dwContext = dwContext;
2611 lpwhr->hdr.refs = 1;
2612 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2613 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2614 lpwhr->dwContentLength = ~0u;
2615 InitializeCriticalSection( &lpwhr->read_section );
2617 WININET_AddRef( &lpwhs->hdr );
2618 lpwhr->lpHttpSession = lpwhs;
2619 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2621 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2622 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2623 if (NULL == lpszHostName)
2625 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2629 handle = WININET_AllocHandle( &lpwhr->hdr );
2632 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2636 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2638 InternetCloseHandle( handle );
2643 if (lpszObjectName && *lpszObjectName) {
2647 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2648 if (rc != E_POINTER)
2649 len = strlenW(lpszObjectName)+1;
2650 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2651 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2652 URL_ESCAPE_SPACES_ONLY);
2655 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2656 strcpyW(lpwhr->lpszPath,lpszObjectName);
2659 static const WCHAR slashW[] = {'/',0};
2661 lpwhr->lpszPath = heap_strdupW(slashW);
2664 if (lpszReferrer && *lpszReferrer)
2665 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2667 if (lpszAcceptTypes)
2670 for (i = 0; lpszAcceptTypes[i]; i++)
2672 if (!*lpszAcceptTypes[i]) continue;
2673 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2674 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2675 HTTP_ADDHDR_FLAG_REQ |
2676 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2680 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2681 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2683 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2684 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2685 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2687 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2688 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2689 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2692 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2693 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2695 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2696 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2697 INTERNET_DEFAULT_HTTPS_PORT :
2698 INTERNET_DEFAULT_HTTP_PORT);
2700 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2701 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2702 INTERNET_DEFAULT_HTTPS_PORT :
2703 INTERNET_DEFAULT_HTTP_PORT);
2705 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2706 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2708 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2709 INTERNET_STATUS_HANDLE_CREATED, &handle,
2713 HeapFree(GetProcessHeap(), 0, lpszHostName);
2715 WININET_Release( &lpwhr->hdr );
2717 TRACE("<-- %p (%p)\n", handle, lpwhr);
2721 /* read any content returned by the server so that the connection can be
2723 static void HTTP_DrainContent(http_request_t *req)
2727 if (!NETCON_connected(&req->netConnection)) return;
2729 if (req->dwContentLength == -1)
2731 NETCON_close(&req->netConnection);
2734 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2739 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2741 } while (bytes_read);
2744 static const LPCWSTR header_lookup[] = {
2745 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2746 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2747 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2748 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2749 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2750 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2751 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2752 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2753 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2754 szDate, /* HTTP_QUERY_DATE = 9 */
2755 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2756 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2757 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2758 szURI, /* HTTP_QUERY_URI = 13 */
2759 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2760 NULL, /* HTTP_QUERY_COST = 15 */
2761 NULL, /* HTTP_QUERY_LINK = 16 */
2762 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2763 NULL, /* HTTP_QUERY_VERSION = 18 */
2764 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2765 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2766 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2767 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2768 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2769 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2770 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2771 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2772 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2773 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2774 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2775 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2776 NULL, /* HTTP_QUERY_FROM = 31 */
2777 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2778 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2779 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2780 szReferer, /* HTTP_QUERY_REFERER = 35 */
2781 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2782 szServer, /* HTTP_QUERY_SERVER = 37 */
2783 NULL, /* HTTP_TITLE = 38 */
2784 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2785 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2786 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2787 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2788 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2789 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2790 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2791 NULL, /* HTTP_QUERY_REFRESH = 46 */
2792 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2793 szAge, /* HTTP_QUERY_AGE = 48 */
2794 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2795 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2796 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2797 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2798 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2799 szETag, /* HTTP_QUERY_ETAG = 54 */
2800 hostW, /* HTTP_QUERY_HOST = 55 */
2801 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2802 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2803 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2804 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2805 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2806 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2807 szRange, /* HTTP_QUERY_RANGE = 62 */
2808 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2809 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2810 szVary, /* HTTP_QUERY_VARY = 65 */
2811 szVia, /* HTTP_QUERY_VIA = 66 */
2812 szWarning, /* HTTP_QUERY_WARNING = 67 */
2813 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2814 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2815 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2818 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2820 /***********************************************************************
2821 * HTTP_HttpQueryInfoW (internal)
2823 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2824 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2826 LPHTTPHEADERW lphttpHdr = NULL;
2827 BOOL bSuccess = FALSE;
2828 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2829 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2830 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2833 /* Find requested header structure */
2836 case HTTP_QUERY_CUSTOM:
2837 if (!lpBuffer) return FALSE;
2838 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2840 case HTTP_QUERY_RAW_HEADERS_CRLF:
2847 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2849 headers = lpwhr->lpszRawHeaders;
2852 len = strlenW(headers) * sizeof(WCHAR);
2854 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2856 len += sizeof(WCHAR);
2857 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2863 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2866 len = strlenW(szCrLf) * sizeof(WCHAR);
2867 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2869 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2872 *lpdwBufferLength = len;
2875 HeapFree(GetProcessHeap(), 0, headers);
2878 case HTTP_QUERY_RAW_HEADERS:
2880 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2882 LPWSTR pszString = lpBuffer;
2884 for (i = 0; ppszRawHeaderLines[i]; i++)
2885 size += strlenW(ppszRawHeaderLines[i]) + 1;
2887 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2889 HTTP_FreeTokens(ppszRawHeaderLines);
2890 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2891 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2896 for (i = 0; ppszRawHeaderLines[i]; i++)
2898 DWORD len = strlenW(ppszRawHeaderLines[i]);
2899 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2903 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2905 *lpdwBufferLength = size * sizeof(WCHAR);
2906 HTTP_FreeTokens(ppszRawHeaderLines);
2910 case HTTP_QUERY_STATUS_TEXT:
2911 if (lpwhr->lpszStatusText)
2913 DWORD len = strlenW(lpwhr->lpszStatusText);
2914 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2916 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2917 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2922 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2923 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2925 *lpdwBufferLength = len * sizeof(WCHAR);
2929 case HTTP_QUERY_VERSION:
2930 if (lpwhr->lpszVersion)
2932 DWORD len = strlenW(lpwhr->lpszVersion);
2933 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2935 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2936 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2941 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2942 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2944 *lpdwBufferLength = len * sizeof(WCHAR);
2948 case HTTP_QUERY_CONTENT_ENCODING:
2949 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2950 requested_index,request_only);
2953 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2955 if (level < LAST_TABLE_HEADER && header_lookup[level])
2956 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2957 requested_index,request_only);
2961 lphttpHdr = &lpwhr->pCustHeaders[index];
2963 /* Ensure header satisfies requested attributes */
2965 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2966 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2968 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2972 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2974 /* coalesce value to requested type */
2975 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2977 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2978 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2981 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2987 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2989 tmpTM = *gmtime(&tmpTime);
2990 STHook = (SYSTEMTIME *)lpBuffer;
2991 STHook->wDay = tmpTM.tm_mday;
2992 STHook->wHour = tmpTM.tm_hour;
2993 STHook->wMilliseconds = 0;
2994 STHook->wMinute = tmpTM.tm_min;
2995 STHook->wDayOfWeek = tmpTM.tm_wday;
2996 STHook->wMonth = tmpTM.tm_mon + 1;
2997 STHook->wSecond = tmpTM.tm_sec;
2998 STHook->wYear = tmpTM.tm_year;
3001 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3002 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3003 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3005 else if (lphttpHdr->lpszValue)
3007 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3009 if (len > *lpdwBufferLength)
3011 *lpdwBufferLength = len;
3012 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
3017 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3018 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
3020 *lpdwBufferLength = len - sizeof(WCHAR);
3026 /***********************************************************************
3027 * HttpQueryInfoW (WININET.@)
3029 * Queries for information about an HTTP request
3036 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3037 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3039 BOOL bSuccess = FALSE;
3040 http_request_t *lpwhr;
3042 if (TRACE_ON(wininet)) {
3043 #define FE(x) { x, #x }
3044 static const wininet_flag_info query_flags[] = {
3045 FE(HTTP_QUERY_MIME_VERSION),
3046 FE(HTTP_QUERY_CONTENT_TYPE),
3047 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3048 FE(HTTP_QUERY_CONTENT_ID),
3049 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3050 FE(HTTP_QUERY_CONTENT_LENGTH),
3051 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3052 FE(HTTP_QUERY_ALLOW),
3053 FE(HTTP_QUERY_PUBLIC),
3054 FE(HTTP_QUERY_DATE),
3055 FE(HTTP_QUERY_EXPIRES),
3056 FE(HTTP_QUERY_LAST_MODIFIED),
3057 FE(HTTP_QUERY_MESSAGE_ID),
3059 FE(HTTP_QUERY_DERIVED_FROM),
3060 FE(HTTP_QUERY_COST),
3061 FE(HTTP_QUERY_LINK),
3062 FE(HTTP_QUERY_PRAGMA),
3063 FE(HTTP_QUERY_VERSION),
3064 FE(HTTP_QUERY_STATUS_CODE),
3065 FE(HTTP_QUERY_STATUS_TEXT),
3066 FE(HTTP_QUERY_RAW_HEADERS),
3067 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3068 FE(HTTP_QUERY_CONNECTION),
3069 FE(HTTP_QUERY_ACCEPT),
3070 FE(HTTP_QUERY_ACCEPT_CHARSET),
3071 FE(HTTP_QUERY_ACCEPT_ENCODING),
3072 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3073 FE(HTTP_QUERY_AUTHORIZATION),
3074 FE(HTTP_QUERY_CONTENT_ENCODING),
3075 FE(HTTP_QUERY_FORWARDED),
3076 FE(HTTP_QUERY_FROM),
3077 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3078 FE(HTTP_QUERY_LOCATION),
3079 FE(HTTP_QUERY_ORIG_URI),
3080 FE(HTTP_QUERY_REFERER),
3081 FE(HTTP_QUERY_RETRY_AFTER),
3082 FE(HTTP_QUERY_SERVER),
3083 FE(HTTP_QUERY_TITLE),
3084 FE(HTTP_QUERY_USER_AGENT),
3085 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3086 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3087 FE(HTTP_QUERY_ACCEPT_RANGES),
3088 FE(HTTP_QUERY_SET_COOKIE),
3089 FE(HTTP_QUERY_COOKIE),
3090 FE(HTTP_QUERY_REQUEST_METHOD),
3091 FE(HTTP_QUERY_REFRESH),
3092 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3094 FE(HTTP_QUERY_CACHE_CONTROL),
3095 FE(HTTP_QUERY_CONTENT_BASE),
3096 FE(HTTP_QUERY_CONTENT_LOCATION),
3097 FE(HTTP_QUERY_CONTENT_MD5),
3098 FE(HTTP_QUERY_CONTENT_RANGE),
3099 FE(HTTP_QUERY_ETAG),
3100 FE(HTTP_QUERY_HOST),
3101 FE(HTTP_QUERY_IF_MATCH),
3102 FE(HTTP_QUERY_IF_NONE_MATCH),
3103 FE(HTTP_QUERY_IF_RANGE),
3104 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3105 FE(HTTP_QUERY_MAX_FORWARDS),
3106 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3107 FE(HTTP_QUERY_RANGE),
3108 FE(HTTP_QUERY_TRANSFER_ENCODING),
3109 FE(HTTP_QUERY_UPGRADE),
3110 FE(HTTP_QUERY_VARY),
3112 FE(HTTP_QUERY_WARNING),
3113 FE(HTTP_QUERY_CUSTOM)
3115 static const wininet_flag_info modifier_flags[] = {
3116 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3117 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3118 FE(HTTP_QUERY_FLAG_NUMBER),
3119 FE(HTTP_QUERY_FLAG_COALESCE)
3122 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3123 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3126 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
3127 TRACE(" Attribute:");
3128 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3129 if (query_flags[i].val == info) {
3130 TRACE(" %s", query_flags[i].name);
3134 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3135 TRACE(" Unknown (%08x)", info);
3138 TRACE(" Modifier:");
3139 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3140 if (modifier_flags[i].val & info_mod) {
3141 TRACE(" %s", modifier_flags[i].name);
3142 info_mod &= ~ modifier_flags[i].val;
3147 TRACE(" Unknown (%08x)", info_mod);
3152 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3153 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3155 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3159 if (lpBuffer == NULL)
3160 *lpdwBufferLength = 0;
3161 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3162 lpBuffer, lpdwBufferLength, lpdwIndex);
3166 WININET_Release( &lpwhr->hdr );
3168 TRACE("%d <--\n", bSuccess);
3172 /***********************************************************************
3173 * HttpQueryInfoA (WININET.@)
3175 * Queries for information about an HTTP request
3182 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3183 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3189 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3190 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3192 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3193 lpdwBufferLength, lpdwIndex );
3199 len = (*lpdwBufferLength)*sizeof(WCHAR);
3200 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3202 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3208 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3209 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3210 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3211 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3218 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3222 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3223 lpBuffer, *lpdwBufferLength, NULL, NULL );
3224 *lpdwBufferLength = len - 1;
3226 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3229 /* since the strings being returned from HttpQueryInfoW should be
3230 * only ASCII characters, it is reasonable to assume that all of
3231 * the Unicode characters can be reduced to a single byte */
3232 *lpdwBufferLength = len / sizeof(WCHAR);
3234 HeapFree(GetProcessHeap(), 0, bufferW );
3239 /***********************************************************************
3240 * HttpSendRequestExA (WININET.@)
3242 * Sends the specified request to the HTTP server and allows chunked
3247 * Failure: FALSE, call GetLastError() for more information.
3249 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3250 LPINTERNET_BUFFERSA lpBuffersIn,
3251 LPINTERNET_BUFFERSA lpBuffersOut,
3252 DWORD dwFlags, DWORD_PTR dwContext)
3254 INTERNET_BUFFERSW BuffersInW;
3257 LPWSTR header = NULL;
3259 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3260 lpBuffersOut, dwFlags, dwContext);
3264 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3265 if (lpBuffersIn->lpcszHeader)
3267 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3268 lpBuffersIn->dwHeadersLength,0,0);
3269 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3270 if (!(BuffersInW.lpcszHeader = header))
3272 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3275 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3276 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3280 BuffersInW.lpcszHeader = NULL;
3281 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3282 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3283 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3284 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3285 BuffersInW.Next = NULL;
3288 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3290 HeapFree(GetProcessHeap(),0,header);
3295 /***********************************************************************
3296 * HttpSendRequestExW (WININET.@)
3298 * Sends the specified request to the HTTP server and allows chunked
3303 * Failure: FALSE, call GetLastError() for more information.
3305 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3306 LPINTERNET_BUFFERSW lpBuffersIn,
3307 LPINTERNET_BUFFERSW lpBuffersOut,
3308 DWORD dwFlags, DWORD_PTR dwContext)
3311 http_request_t *lpwhr;
3312 http_session_t *lpwhs;
3315 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3316 lpBuffersOut, dwFlags, dwContext);
3318 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3320 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3322 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3326 lpwhs = lpwhr->lpHttpSession;
3327 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3328 hIC = lpwhs->lpAppInfo;
3329 assert(hIC->hdr.htype == WH_HINIT);
3331 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3333 WORKREQUEST workRequest;
3334 struct WORKREQ_HTTPSENDREQUESTW *req;
3336 workRequest.asyncproc = AsyncHttpSendRequestProc;
3337 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3338 req = &workRequest.u.HttpSendRequestW;
3343 if (lpBuffersIn->lpcszHeader)
3345 if (lpBuffersIn->dwHeadersLength == ~0u)
3346 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3348 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3350 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3351 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3353 else req->lpszHeader = NULL;
3355 req->dwHeaderLength = size / sizeof(WCHAR);
3356 req->lpOptional = lpBuffersIn->lpvBuffer;
3357 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3358 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3362 req->lpszHeader = NULL;
3363 req->dwHeaderLength = 0;
3364 req->lpOptional = NULL;
3365 req->dwOptionalLength = 0;
3366 req->dwContentLength = 0;
3369 req->bEndRequest = FALSE;
3371 INTERNET_AsyncCall(&workRequest);
3373 * This is from windows.
3375 INTERNET_SetLastError(ERROR_IO_PENDING);
3380 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3381 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3382 lpBuffersIn->dwBufferTotal, FALSE);
3384 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3389 WININET_Release( &lpwhr->hdr );
3395 /***********************************************************************
3396 * HttpSendRequestW (WININET.@)
3398 * Sends the specified request to the HTTP server
3405 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3406 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3408 http_request_t *lpwhr;
3409 http_session_t *lpwhs = NULL;
3410 appinfo_t *hIC = NULL;
3413 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3414 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3416 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3417 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3419 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3424 lpwhs = lpwhr->lpHttpSession;
3425 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3427 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3432 hIC = lpwhs->lpAppInfo;
3433 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3435 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3440 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3442 WORKREQUEST workRequest;
3443 struct WORKREQ_HTTPSENDREQUESTW *req;
3445 workRequest.asyncproc = AsyncHttpSendRequestProc;
3446 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3447 req = &workRequest.u.HttpSendRequestW;
3452 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3453 else size = dwHeaderLength * sizeof(WCHAR);
3455 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3456 memcpy(req->lpszHeader, lpszHeaders, size);
3459 req->lpszHeader = 0;
3460 req->dwHeaderLength = dwHeaderLength;
3461 req->lpOptional = lpOptional;
3462 req->dwOptionalLength = dwOptionalLength;
3463 req->dwContentLength = dwOptionalLength;
3464 req->bEndRequest = TRUE;
3466 INTERNET_AsyncCall(&workRequest);
3468 * This is from windows.
3470 INTERNET_SetLastError(ERROR_IO_PENDING);
3475 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3476 dwHeaderLength, lpOptional, dwOptionalLength,
3477 dwOptionalLength, TRUE);
3481 WININET_Release( &lpwhr->hdr );
3485 /***********************************************************************
3486 * HttpSendRequestA (WININET.@)
3488 * Sends the specified request to the HTTP server
3495 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3496 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3499 LPWSTR szHeaders=NULL;
3500 DWORD nLen=dwHeaderLength;
3501 if(lpszHeaders!=NULL)
3503 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3504 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3505 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3507 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3508 HeapFree(GetProcessHeap(),0,szHeaders);
3512 /***********************************************************************
3513 * HTTP_GetRedirectURL (internal)
3515 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3517 static WCHAR szHttp[] = {'h','t','t','p',0};
3518 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3519 http_session_t *lpwhs = lpwhr->lpHttpSession;
3520 URL_COMPONENTSW urlComponents;
3521 DWORD url_length = 0;
3523 LPWSTR combined_url;
3525 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3526 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3527 urlComponents.dwSchemeLength = 0;
3528 urlComponents.lpszHostName = lpwhs->lpszHostName;
3529 urlComponents.dwHostNameLength = 0;
3530 urlComponents.nPort = lpwhs->nHostPort;
3531 urlComponents.lpszUserName = lpwhs->lpszUserName;
3532 urlComponents.dwUserNameLength = 0;
3533 urlComponents.lpszPassword = NULL;
3534 urlComponents.dwPasswordLength = 0;
3535 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3536 urlComponents.dwUrlPathLength = 0;
3537 urlComponents.lpszExtraInfo = NULL;
3538 urlComponents.dwExtraInfoLength = 0;
3540 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3541 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3544 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3546 /* convert from bytes to characters */
3547 url_length = url_length / sizeof(WCHAR) - 1;
3548 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3550 HeapFree(GetProcessHeap(), 0, orig_url);
3555 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3556 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3558 HeapFree(GetProcessHeap(), 0, orig_url);
3561 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3563 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3565 HeapFree(GetProcessHeap(), 0, orig_url);
3566 HeapFree(GetProcessHeap(), 0, combined_url);
3569 HeapFree(GetProcessHeap(), 0, orig_url);
3570 return combined_url;
3574 /***********************************************************************
3575 * HTTP_HandleRedirect (internal)
3577 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3579 http_session_t *lpwhs = lpwhr->lpHttpSession;
3580 appinfo_t *hIC = lpwhs->lpAppInfo;
3581 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3582 WCHAR path[INTERNET_MAX_URL_LENGTH];
3587 /* if it's an absolute path, keep the same session info */
3588 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3592 URL_COMPONENTSW urlComponents;
3593 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3594 static WCHAR szHttp[] = {'h','t','t','p',0};
3595 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3601 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3602 urlComponents.lpszScheme = protocol;
3603 urlComponents.dwSchemeLength = 32;
3604 urlComponents.lpszHostName = hostName;
3605 urlComponents.dwHostNameLength = MAXHOSTNAME;
3606 urlComponents.lpszUserName = userName;
3607 urlComponents.dwUserNameLength = 1024;
3608 urlComponents.lpszPassword = NULL;
3609 urlComponents.dwPasswordLength = 0;
3610 urlComponents.lpszUrlPath = path;
3611 urlComponents.dwUrlPathLength = 2048;
3612 urlComponents.lpszExtraInfo = NULL;
3613 urlComponents.dwExtraInfoLength = 0;
3614 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3617 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3618 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3620 TRACE("redirect from secure page to non-secure page\n");
3621 /* FIXME: warn about from secure redirect to non-secure page */
3622 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3624 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3625 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3627 TRACE("redirect from non-secure page to secure page\n");
3628 /* FIXME: notify about redirect to secure page */
3629 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3632 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3634 if (lstrlenW(protocol)>4) /*https*/
3635 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3637 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3642 * This upsets redirects to binary files on sourceforge.net
3643 * and gives an html page instead of the target file
3644 * Examination of the HTTP request sent by native wininet.dll
3645 * reveals that it doesn't send a referrer in that case.
3646 * Maybe there's a flag that enables this, or maybe a referrer
3647 * shouldn't be added in case of a redirect.
3650 /* consider the current host as the referrer */
3651 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3652 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3653 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3654 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3657 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3658 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3659 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3662 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3663 len = lstrlenW(hostName);
3664 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3665 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3666 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3669 lpwhs->lpszHostName = heap_strdupW(hostName);
3671 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3673 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3674 lpwhs->lpszUserName = NULL;
3676 lpwhs->lpszUserName = heap_strdupW(userName);
3680 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3682 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3683 lpwhs->lpszServerName = heap_strdupW(hostName);
3684 lpwhs->nServerPort = urlComponents.nPort;
3686 NETCON_close(&lpwhr->netConnection);
3687 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3688 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3689 lpwhr->read_pos = lpwhr->read_size = 0;
3690 lpwhr->read_chunked = FALSE;
3694 TRACE("Redirect through proxy\n");
3697 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3698 lpwhr->lpszPath=NULL;
3704 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3705 if (rc != E_POINTER)
3706 needed = strlenW(path)+1;
3707 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3708 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3709 URL_ESCAPE_SPACES_ONLY);
3712 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3713 strcpyW(lpwhr->lpszPath,path);
3717 /* Remove custom content-type/length headers on redirects. */
3718 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3720 HTTP_DeleteCustomHeader(lpwhr, index);
3721 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3723 HTTP_DeleteCustomHeader(lpwhr, index);
3728 /***********************************************************************
3729 * HTTP_build_req (internal)
3731 * concatenate all the strings in the request together
3733 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3738 for( t = list; *t ; t++ )
3739 len += strlenW( *t );
3742 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3745 for( t = list; *t ; t++ )
3751 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3754 LPWSTR requestString;
3760 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3761 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3762 http_session_t *lpwhs = lpwhr->lpHttpSession;
3766 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3767 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3768 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3769 HeapFree( GetProcessHeap(), 0, lpszPath );
3771 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3772 NULL, 0, NULL, NULL );
3773 len--; /* the nul terminator isn't needed */
3774 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3775 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3776 ascii_req, len, NULL, NULL );
3777 HeapFree( GetProcessHeap(), 0, requestString );
3779 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3781 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3782 HeapFree( GetProcessHeap(), 0, ascii_req );
3783 if (!ret || cnt < 0)
3786 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3793 static void HTTP_InsertCookies(http_request_t *lpwhr)
3795 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3796 LPWSTR lpszCookies, lpszUrl = NULL;
3797 DWORD nCookieSize, size;
3798 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3800 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3801 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3802 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3804 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3807 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3809 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3810 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3812 cnt += sprintfW(lpszCookies, szCookie);
3813 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3814 strcatW(lpszCookies, szCrLf);
3816 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3817 HeapFree(GetProcessHeap(), 0, lpszCookies);
3820 HeapFree(GetProcessHeap(), 0, lpszUrl);
3823 /***********************************************************************
3824 * HTTP_HttpSendRequestW (internal)
3826 * Sends the specified request to the HTTP server
3833 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3834 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3835 DWORD dwContentLength, BOOL bEndRequest)
3838 BOOL bSuccess = FALSE, redirected = FALSE;
3839 LPWSTR requestString = NULL;
3842 INTERNET_ASYNC_RESULT iar;
3843 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3844 static const WCHAR szContentLength[] =
3845 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3846 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3848 TRACE("--> %p\n", lpwhr);
3850 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3852 /* if the verb is NULL default to GET */
3853 if (!lpwhr->lpszVerb)
3854 lpwhr->lpszVerb = heap_strdupW(szGET);
3856 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3858 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3859 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3860 lpwhr->dwBytesToWrite = dwContentLength;
3862 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3864 WCHAR *agent_header;
3865 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3868 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3869 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3870 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3872 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3873 HeapFree(GetProcessHeap(), 0, agent_header);
3875 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3877 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3878 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3880 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3882 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3883 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3884 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3894 /* like native, just in case the caller forgot to call InternetReadFile
3895 * for all the data */
3896 HTTP_DrainContent(lpwhr);
3897 lpwhr->dwContentRead = 0;
3899 if (TRACE_ON(wininet))
3901 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3902 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3906 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3908 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3910 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3911 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3913 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3914 HTTP_InsertCookies(lpwhr);
3916 /* add the headers the caller supplied */
3917 if( lpszHeaders && dwHeaderLength )
3919 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3920 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3923 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3925 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3926 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3927 HeapFree(GetProcessHeap(), 0, url);
3930 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3933 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3935 /* Send the request and store the results */
3936 if (!HTTP_OpenConnection(lpwhr))
3939 /* send the request as ASCII, tack on the optional data */
3940 if (!lpOptional || redirected)
3941 dwOptionalLength = 0;
3942 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3943 NULL, 0, NULL, NULL );
3944 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3945 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3946 ascii_req, len, NULL, NULL );
3948 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3949 len = (len + dwOptionalLength - 1);
3951 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3953 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3954 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3956 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3957 HeapFree( GetProcessHeap(), 0, ascii_req );
3959 lpwhr->dwBytesWritten = dwOptionalLength;
3961 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3962 INTERNET_STATUS_REQUEST_SENT,
3963 &len, sizeof(DWORD));
3970 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3971 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3976 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3980 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3981 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3984 HTTP_ProcessCookies(lpwhr);
3986 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3988 dwBufferSize = sizeof(dwStatusCode);
3989 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3990 &dwStatusCode,&dwBufferSize,NULL))
3993 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3995 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3996 dwBufferSize=sizeof(szNewLocation);
3997 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3998 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
4000 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
4002 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
4003 lpwhr->lpszVerb = heap_strdupW(szGET);
4005 HTTP_DrainContent(lpwhr);
4006 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
4008 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4009 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4010 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
4013 HeapFree(GetProcessHeap(), 0, requestString);
4016 HeapFree( GetProcessHeap(), 0, new_url );
4021 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
4023 WCHAR szAuthValue[2048];
4025 if (dwStatusCode == HTTP_STATUS_DENIED)
4027 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
4029 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4031 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4033 lpwhr->lpHttpSession->lpszUserName,
4034 lpwhr->lpHttpSession->lpszPassword,
4042 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4045 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4047 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4048 &lpwhr->pProxyAuthInfo,
4049 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
4050 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
4066 WCHAR url[INTERNET_MAX_URL_LENGTH];
4067 WCHAR cacheFileName[MAX_PATH+1];
4070 b = HTTP_GetRequestURL(lpwhr, url);
4072 WARN("Could not get URL\n");
4076 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
4078 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
4079 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4080 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4081 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
4082 WARN("Could not create file: %u\n", GetLastError());
4083 lpwhr->hCacheFile = NULL;
4086 WARN("Could not create cache entry: %08x\n", GetLastError());
4092 HeapFree(GetProcessHeap(), 0, requestString);
4094 /* TODO: send notification for P3P header */
4096 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4100 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
4103 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4106 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4107 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4108 sizeof(INTERNET_ASYNC_RESULT));
4113 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4114 iar.dwError = INTERNET_GetLastError();
4116 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4117 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4118 sizeof(INTERNET_ASYNC_RESULT));
4123 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
4127 /***********************************************************************
4128 * HTTPSESSION_Destroy (internal)
4130 * Deallocate session handle
4133 static void HTTPSESSION_Destroy(object_header_t *hdr)
4135 http_session_t *lpwhs = (http_session_t*) hdr;
4137 TRACE("%p\n", lpwhs);
4139 WININET_Release(&lpwhs->lpAppInfo->hdr);
4141 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4142 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4143 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4144 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4145 HeapFree(GetProcessHeap(), 0, lpwhs);
4148 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4151 case INTERNET_OPTION_HANDLE_TYPE:
4152 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4154 if (*size < sizeof(ULONG))
4155 return ERROR_INSUFFICIENT_BUFFER;
4157 *size = sizeof(DWORD);
4158 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4159 return ERROR_SUCCESS;
4162 return INET_QueryOption(option, buffer, size, unicode);
4165 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4167 http_session_t *ses = (http_session_t*)hdr;
4170 case INTERNET_OPTION_USERNAME:
4172 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4173 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4174 return ERROR_SUCCESS;
4176 case INTERNET_OPTION_PASSWORD:
4178 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4179 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4180 return ERROR_SUCCESS;
4185 return ERROR_INTERNET_INVALID_OPTION;
4188 static const object_vtbl_t HTTPSESSIONVtbl = {
4189 HTTPSESSION_Destroy,
4191 HTTPSESSION_QueryOption,
4192 HTTPSESSION_SetOption,
4201 /***********************************************************************
4202 * HTTP_Connect (internal)
4204 * Create http session handle
4207 * HINTERNET a session handle on success
4211 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4212 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4213 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4214 DWORD dwInternalFlags)
4216 http_session_t *lpwhs = NULL;
4217 HINTERNET handle = NULL;
4221 if (!lpszServerName || !lpszServerName[0])
4223 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4227 assert( hIC->hdr.htype == WH_HINIT );
4229 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4232 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4237 * According to my tests. The name is not resolved until a request is sent
4240 lpwhs->hdr.htype = WH_HHTTPSESSION;
4241 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4242 lpwhs->hdr.dwFlags = dwFlags;
4243 lpwhs->hdr.dwContext = dwContext;
4244 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4245 lpwhs->hdr.refs = 1;
4246 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4248 WININET_AddRef( &hIC->hdr );
4249 lpwhs->lpAppInfo = hIC;
4250 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4252 handle = WININET_AllocHandle( &lpwhs->hdr );
4255 ERR("Failed to alloc handle\n");
4256 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4260 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4261 if(strchrW(hIC->lpszProxy, ' '))
4262 FIXME("Several proxies not implemented.\n");
4263 if(hIC->lpszProxyBypass)
4264 FIXME("Proxy bypass is ignored.\n");
4266 if (lpszServerName && lpszServerName[0])
4268 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4269 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4271 if (lpszUserName && lpszUserName[0])
4272 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4273 if (lpszPassword && lpszPassword[0])
4274 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4275 lpwhs->nServerPort = nServerPort;
4276 lpwhs->nHostPort = nServerPort;
4278 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4279 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4281 INTERNET_SendCallback(&hIC->hdr, dwContext,
4282 INTERNET_STATUS_HANDLE_CREATED, &handle,
4288 WININET_Release( &lpwhs->hdr );
4291 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4295 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4300 /***********************************************************************
4301 * HTTP_OpenConnection (internal)
4303 * Connect to a web server
4310 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4312 BOOL bSuccess = FALSE;
4313 http_session_t *lpwhs;
4314 appinfo_t *hIC = NULL;
4315 char szaddr[INET6_ADDRSTRLEN];
4317 DWORD res = ERROR_SUCCESS;
4322 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4324 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4328 if (NETCON_connected(&lpwhr->netConnection))
4333 if (!HTTP_ResolveName(lpwhr)) goto lend;
4335 lpwhs = lpwhr->lpHttpSession;
4337 hIC = lpwhs->lpAppInfo;
4338 switch (lpwhs->socketAddress.ss_family)
4341 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4344 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4347 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4348 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4351 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4352 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4353 INTERNET_STATUS_CONNECTING_TO_SERVER,
4357 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4358 if (res != ERROR_SUCCESS)
4360 WARN("Socket creation failed: %u\n", res);
4364 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4366 if(res != ERROR_SUCCESS)
4369 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4371 /* Note: we differ from Microsoft's WinINet here. they seem to have
4372 * a bug that causes no status callbacks to be sent when starting
4373 * a tunnel to a proxy server using the CONNECT verb. i believe our
4374 * behaviour to be more correct and to not cause any incompatibilities
4375 * because using a secure connection through a proxy server is a rare
4376 * case that would be hard for anyone to depend on */
4377 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4380 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4382 WARN("Couldn't connect securely to host\n");
4387 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4388 INTERNET_STATUS_CONNECTED_TO_SERVER,
4389 szaddr, strlen(szaddr)+1);
4394 lpwhr->read_pos = lpwhr->read_size = 0;
4395 lpwhr->read_chunked = FALSE;
4397 if(res != ERROR_SUCCESS)
4398 INTERNET_SetLastError(res);
4400 TRACE("%d <--\n", bSuccess);
4405 /***********************************************************************
4406 * HTTP_clear_response_headers (internal)
4408 * clear out any old response headers
4410 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4414 for( i=0; i<lpwhr->nCustHeaders; i++)
4416 if( !lpwhr->pCustHeaders[i].lpszField )
4418 if( !lpwhr->pCustHeaders[i].lpszValue )
4420 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4422 HTTP_DeleteCustomHeader( lpwhr, i );
4427 /***********************************************************************
4428 * HTTP_GetResponseHeaders (internal)
4430 * Read server response
4437 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4440 WCHAR buffer[MAX_REPLY_LEN];
4441 DWORD buflen = MAX_REPLY_LEN;
4442 BOOL bSuccess = FALSE;
4444 char bufferA[MAX_REPLY_LEN];
4445 LPWSTR status_code = NULL, status_text = NULL;
4446 DWORD cchMaxRawHeaders = 1024;
4447 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4449 DWORD cchRawHeaders = 0;
4450 BOOL codeHundred = FALSE;
4454 /* clear old response headers (eg. from a redirect response) */
4455 if (clear) HTTP_clear_response_headers( lpwhr );
4457 if (!NETCON_connected(&lpwhr->netConnection))
4461 static const WCHAR szHundred[] = {'1','0','0',0};
4463 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4465 buflen = MAX_REPLY_LEN;
4466 if (!read_line(lpwhr, bufferA, &buflen))
4469 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4470 /* check is this a status code line? */
4471 if (!strncmpW(buffer, g_szHttp1_0, 4))
4473 /* split the version from the status code */
4474 status_code = strchrW( buffer, ' ' );
4479 /* split the status code from the status text */
4480 status_text = strchrW( status_code, ' ' );
4485 TRACE("version [%s] status code [%s] status text [%s]\n",
4486 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4488 codeHundred = (!strcmpW(status_code, szHundred));
4490 else if (!codeHundred)
4492 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4495 } while (codeHundred);
4497 /* Add status code */
4498 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4499 HTTP_ADDHDR_FLAG_REPLACE);
4501 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4502 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4504 lpwhr->lpszVersion = heap_strdupW(buffer);
4505 lpwhr->lpszStatusText = heap_strdupW(status_text);
4507 /* Restore the spaces */
4508 *(status_code-1) = ' ';
4509 *(status_text-1) = ' ';
4511 /* regenerate raw headers */
4512 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4513 cchMaxRawHeaders *= 2;
4514 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4515 if (temp == NULL) goto lend;
4516 lpszRawHeaders = temp;
4517 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4518 cchRawHeaders += (buflen-1);
4519 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4520 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4521 lpszRawHeaders[cchRawHeaders] = '\0';
4523 /* Parse each response line */
4526 buflen = MAX_REPLY_LEN;
4527 if (read_line(lpwhr, bufferA, &buflen))
4529 LPWSTR * pFieldAndValue;
4531 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4533 if (!bufferA[0]) break;
4534 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4536 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4539 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4540 cchMaxRawHeaders *= 2;
4541 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4542 if (temp == NULL) goto lend;
4543 lpszRawHeaders = temp;
4544 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4545 cchRawHeaders += (buflen-1);
4546 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4547 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4548 lpszRawHeaders[cchRawHeaders] = '\0';
4550 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4551 HTTP_ADDREQ_FLAG_ADD );
4553 HTTP_FreeTokens(pFieldAndValue);
4564 /* make sure the response header is terminated with an empty line. Some apps really
4565 truly care about that empty line being there for some reason. Just add it to the
4567 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4569 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4570 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4571 if (temp == NULL) goto lend;
4572 lpszRawHeaders = temp;
4575 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4577 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4578 lpwhr->lpszRawHeaders = lpszRawHeaders;
4579 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4589 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4594 /***********************************************************************
4595 * HTTP_InterpretHttpHeader (internal)
4597 * Parse server response
4601 * Pointer to array of field, value, NULL on success.
4604 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4606 LPWSTR * pTokenPair;
4610 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4612 pszColon = strchrW(buffer, ':');
4613 /* must have two tokens */
4616 HTTP_FreeTokens(pTokenPair);
4618 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4622 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4625 HTTP_FreeTokens(pTokenPair);
4628 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4629 pTokenPair[0][pszColon - buffer] = '\0';
4633 len = strlenW(pszColon);
4634 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4637 HTTP_FreeTokens(pTokenPair);
4640 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4642 strip_spaces(pTokenPair[0]);
4643 strip_spaces(pTokenPair[1]);
4645 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4649 /***********************************************************************
4650 * HTTP_ProcessHeader (internal)
4652 * Stuff header into header tables according to <dwModifier>
4656 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4658 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4660 LPHTTPHEADERW lphttpHdr = NULL;
4661 BOOL bSuccess = FALSE;
4663 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4665 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4667 /* REPLACE wins out over ADD */
4668 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4669 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4671 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4674 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4678 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4682 lphttpHdr = &lpwhr->pCustHeaders[index];
4688 hdr.lpszField = (LPWSTR)field;
4689 hdr.lpszValue = (LPWSTR)value;
4690 hdr.wFlags = hdr.wCount = 0;
4692 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4693 hdr.wFlags |= HDR_ISREQUEST;
4695 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4697 /* no value to delete */
4700 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4701 lphttpHdr->wFlags |= HDR_ISREQUEST;
4703 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4705 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4707 HTTP_DeleteCustomHeader( lpwhr, index );
4713 hdr.lpszField = (LPWSTR)field;
4714 hdr.lpszValue = (LPWSTR)value;
4715 hdr.wFlags = hdr.wCount = 0;
4717 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4718 hdr.wFlags |= HDR_ISREQUEST;
4720 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4725 else if (dwModifier & COALESCEFLAGS)
4730 INT origlen = strlenW(lphttpHdr->lpszValue);
4731 INT valuelen = strlenW(value);
4733 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4736 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4738 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4741 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4744 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4746 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4749 lphttpHdr->lpszValue = lpsztmp;
4750 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4753 lphttpHdr->lpszValue[origlen] = ch;
4755 lphttpHdr->lpszValue[origlen] = ' ';
4759 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4760 lphttpHdr->lpszValue[len] = '\0';
4765 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4766 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4769 TRACE("<-- %d\n",bSuccess);
4774 /***********************************************************************
4775 * HTTP_FinishedReading (internal)
4777 * Called when all content from server has been read by client.
4780 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4782 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4789 HTTPREQ_CloseConnection(&lpwhr->hdr);
4792 /* FIXME: store data in the URL cache here */
4798 /***********************************************************************
4799 * HTTP_GetCustomHeaderIndex (internal)
4801 * Return index of custom header from header array
4804 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4805 int requested_index, BOOL request_only)
4809 TRACE("%s\n", debugstr_w(lpszField));
4811 for (index = 0; index < lpwhr->nCustHeaders; index++)
4813 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4816 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4819 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4822 if (requested_index == 0)
4827 if (index >= lpwhr->nCustHeaders)
4830 TRACE("Return: %d\n", index);
4835 /***********************************************************************
4836 * HTTP_InsertCustomHeader (internal)
4838 * Insert header into array
4841 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4844 LPHTTPHEADERW lph = NULL;
4847 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4848 count = lpwhr->nCustHeaders + 1;
4850 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4852 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4856 lpwhr->pCustHeaders = lph;
4857 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4858 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4859 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4860 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4861 lpwhr->nCustHeaders++;
4866 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4873 /***********************************************************************
4874 * HTTP_DeleteCustomHeader (internal)
4876 * Delete header from array
4877 * If this function is called, the indexs may change.
4879 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4881 if( lpwhr->nCustHeaders <= 0 )
4883 if( index >= lpwhr->nCustHeaders )
4885 lpwhr->nCustHeaders--;
4887 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4888 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4890 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4891 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4892 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4898 /***********************************************************************
4899 * HTTP_VerifyValidHeader (internal)
4901 * Verify the given header is not invalid for the given http request
4904 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4906 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4907 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4913 /***********************************************************************
4914 * IsHostInProxyBypassList (@)
4919 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4921 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);