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->dwHeadersLength == ~0u)
3344 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3346 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3348 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3349 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3351 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3352 req->lpOptional = lpBuffersIn->lpvBuffer;
3353 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3354 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3358 req->lpszHeader = NULL;
3359 req->dwHeaderLength = 0;
3360 req->lpOptional = NULL;
3361 req->dwOptionalLength = 0;
3362 req->dwContentLength = 0;
3365 req->bEndRequest = FALSE;
3367 INTERNET_AsyncCall(&workRequest);
3369 * This is from windows.
3371 INTERNET_SetLastError(ERROR_IO_PENDING);
3376 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3377 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3378 lpBuffersIn->dwBufferTotal, FALSE);
3380 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3385 WININET_Release( &lpwhr->hdr );
3391 /***********************************************************************
3392 * HttpSendRequestW (WININET.@)
3394 * Sends the specified request to the HTTP server
3401 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3402 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3404 http_request_t *lpwhr;
3405 http_session_t *lpwhs = NULL;
3406 appinfo_t *hIC = NULL;
3409 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3410 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3412 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3413 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3415 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3420 lpwhs = lpwhr->lpHttpSession;
3421 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3423 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3428 hIC = lpwhs->lpAppInfo;
3429 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3431 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3436 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3438 WORKREQUEST workRequest;
3439 struct WORKREQ_HTTPSENDREQUESTW *req;
3441 workRequest.asyncproc = AsyncHttpSendRequestProc;
3442 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3443 req = &workRequest.u.HttpSendRequestW;
3448 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3449 else size = dwHeaderLength * sizeof(WCHAR);
3451 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3452 memcpy(req->lpszHeader, lpszHeaders, size);
3455 req->lpszHeader = 0;
3456 req->dwHeaderLength = dwHeaderLength;
3457 req->lpOptional = lpOptional;
3458 req->dwOptionalLength = dwOptionalLength;
3459 req->dwContentLength = dwOptionalLength;
3460 req->bEndRequest = TRUE;
3462 INTERNET_AsyncCall(&workRequest);
3464 * This is from windows.
3466 INTERNET_SetLastError(ERROR_IO_PENDING);
3471 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3472 dwHeaderLength, lpOptional, dwOptionalLength,
3473 dwOptionalLength, TRUE);
3477 WININET_Release( &lpwhr->hdr );
3481 /***********************************************************************
3482 * HttpSendRequestA (WININET.@)
3484 * Sends the specified request to the HTTP server
3491 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3492 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3495 LPWSTR szHeaders=NULL;
3496 DWORD nLen=dwHeaderLength;
3497 if(lpszHeaders!=NULL)
3499 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3500 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3501 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3503 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3504 HeapFree(GetProcessHeap(),0,szHeaders);
3508 /***********************************************************************
3509 * HTTP_GetRedirectURL (internal)
3511 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3513 static WCHAR szHttp[] = {'h','t','t','p',0};
3514 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3515 http_session_t *lpwhs = lpwhr->lpHttpSession;
3516 URL_COMPONENTSW urlComponents;
3517 DWORD url_length = 0;
3519 LPWSTR combined_url;
3521 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3522 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3523 urlComponents.dwSchemeLength = 0;
3524 urlComponents.lpszHostName = lpwhs->lpszHostName;
3525 urlComponents.dwHostNameLength = 0;
3526 urlComponents.nPort = lpwhs->nHostPort;
3527 urlComponents.lpszUserName = lpwhs->lpszUserName;
3528 urlComponents.dwUserNameLength = 0;
3529 urlComponents.lpszPassword = NULL;
3530 urlComponents.dwPasswordLength = 0;
3531 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3532 urlComponents.dwUrlPathLength = 0;
3533 urlComponents.lpszExtraInfo = NULL;
3534 urlComponents.dwExtraInfoLength = 0;
3536 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3537 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3540 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3542 /* convert from bytes to characters */
3543 url_length = url_length / sizeof(WCHAR) - 1;
3544 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3546 HeapFree(GetProcessHeap(), 0, orig_url);
3551 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3552 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3554 HeapFree(GetProcessHeap(), 0, orig_url);
3557 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3559 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3561 HeapFree(GetProcessHeap(), 0, orig_url);
3562 HeapFree(GetProcessHeap(), 0, combined_url);
3565 HeapFree(GetProcessHeap(), 0, orig_url);
3566 return combined_url;
3570 /***********************************************************************
3571 * HTTP_HandleRedirect (internal)
3573 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3575 http_session_t *lpwhs = lpwhr->lpHttpSession;
3576 appinfo_t *hIC = lpwhs->lpAppInfo;
3577 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3578 WCHAR path[INTERNET_MAX_URL_LENGTH];
3583 /* if it's an absolute path, keep the same session info */
3584 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3588 URL_COMPONENTSW urlComponents;
3589 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3590 static WCHAR szHttp[] = {'h','t','t','p',0};
3591 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3597 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3598 urlComponents.lpszScheme = protocol;
3599 urlComponents.dwSchemeLength = 32;
3600 urlComponents.lpszHostName = hostName;
3601 urlComponents.dwHostNameLength = MAXHOSTNAME;
3602 urlComponents.lpszUserName = userName;
3603 urlComponents.dwUserNameLength = 1024;
3604 urlComponents.lpszPassword = NULL;
3605 urlComponents.dwPasswordLength = 0;
3606 urlComponents.lpszUrlPath = path;
3607 urlComponents.dwUrlPathLength = 2048;
3608 urlComponents.lpszExtraInfo = NULL;
3609 urlComponents.dwExtraInfoLength = 0;
3610 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3613 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3614 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3616 TRACE("redirect from secure page to non-secure page\n");
3617 /* FIXME: warn about from secure redirect to non-secure page */
3618 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3620 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3621 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3623 TRACE("redirect from non-secure page to secure page\n");
3624 /* FIXME: notify about redirect to secure page */
3625 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3628 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3630 if (lstrlenW(protocol)>4) /*https*/
3631 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3633 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3638 * This upsets redirects to binary files on sourceforge.net
3639 * and gives an html page instead of the target file
3640 * Examination of the HTTP request sent by native wininet.dll
3641 * reveals that it doesn't send a referrer in that case.
3642 * Maybe there's a flag that enables this, or maybe a referrer
3643 * shouldn't be added in case of a redirect.
3646 /* consider the current host as the referrer */
3647 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3648 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3649 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3650 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3653 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3654 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3655 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3658 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3659 len = lstrlenW(hostName);
3660 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3661 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3662 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3665 lpwhs->lpszHostName = heap_strdupW(hostName);
3667 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3669 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3670 lpwhs->lpszUserName = NULL;
3672 lpwhs->lpszUserName = heap_strdupW(userName);
3676 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3678 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3679 lpwhs->lpszServerName = heap_strdupW(hostName);
3680 lpwhs->nServerPort = urlComponents.nPort;
3682 NETCON_close(&lpwhr->netConnection);
3683 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3684 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3685 lpwhr->read_pos = lpwhr->read_size = 0;
3686 lpwhr->read_chunked = FALSE;
3690 TRACE("Redirect through proxy\n");
3693 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3694 lpwhr->lpszPath=NULL;
3700 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3701 if (rc != E_POINTER)
3702 needed = strlenW(path)+1;
3703 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3704 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3705 URL_ESCAPE_SPACES_ONLY);
3708 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3709 strcpyW(lpwhr->lpszPath,path);
3713 /* Remove custom content-type/length headers on redirects. */
3714 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3716 HTTP_DeleteCustomHeader(lpwhr, index);
3717 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3719 HTTP_DeleteCustomHeader(lpwhr, index);
3724 /***********************************************************************
3725 * HTTP_build_req (internal)
3727 * concatenate all the strings in the request together
3729 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3734 for( t = list; *t ; t++ )
3735 len += strlenW( *t );
3738 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3741 for( t = list; *t ; t++ )
3747 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3750 LPWSTR requestString;
3756 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3757 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3758 http_session_t *lpwhs = lpwhr->lpHttpSession;
3762 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3763 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3764 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3765 HeapFree( GetProcessHeap(), 0, lpszPath );
3767 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3768 NULL, 0, NULL, NULL );
3769 len--; /* the nul terminator isn't needed */
3770 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3771 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3772 ascii_req, len, NULL, NULL );
3773 HeapFree( GetProcessHeap(), 0, requestString );
3775 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3777 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3778 HeapFree( GetProcessHeap(), 0, ascii_req );
3779 if (!ret || cnt < 0)
3782 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3789 static void HTTP_InsertCookies(http_request_t *lpwhr)
3791 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3792 LPWSTR lpszCookies, lpszUrl = NULL;
3793 DWORD nCookieSize, size;
3794 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3796 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3797 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3798 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3800 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3803 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3805 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3806 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3808 cnt += sprintfW(lpszCookies, szCookie);
3809 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3810 strcatW(lpszCookies, szCrLf);
3812 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3813 HeapFree(GetProcessHeap(), 0, lpszCookies);
3816 HeapFree(GetProcessHeap(), 0, lpszUrl);
3819 /***********************************************************************
3820 * HTTP_HttpSendRequestW (internal)
3822 * Sends the specified request to the HTTP server
3829 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3830 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3831 DWORD dwContentLength, BOOL bEndRequest)
3834 BOOL bSuccess = FALSE, redirected = FALSE;
3835 LPWSTR requestString = NULL;
3838 INTERNET_ASYNC_RESULT iar;
3839 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3840 static const WCHAR szContentLength[] =
3841 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3842 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3844 TRACE("--> %p\n", lpwhr);
3846 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3848 /* if the verb is NULL default to GET */
3849 if (!lpwhr->lpszVerb)
3850 lpwhr->lpszVerb = heap_strdupW(szGET);
3852 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3854 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3855 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3856 lpwhr->dwBytesToWrite = dwContentLength;
3858 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3860 WCHAR *agent_header;
3861 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3864 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3865 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3866 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3868 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3869 HeapFree(GetProcessHeap(), 0, agent_header);
3871 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3873 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3874 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3876 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3878 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3879 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3880 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3890 /* like native, just in case the caller forgot to call InternetReadFile
3891 * for all the data */
3892 HTTP_DrainContent(lpwhr);
3893 lpwhr->dwContentRead = 0;
3895 if (TRACE_ON(wininet))
3897 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3898 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3902 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3904 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3906 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3907 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3909 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3910 HTTP_InsertCookies(lpwhr);
3912 /* add the headers the caller supplied */
3913 if( lpszHeaders && dwHeaderLength )
3915 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3916 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3919 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3921 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3922 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3923 HeapFree(GetProcessHeap(), 0, url);
3926 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3929 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3931 /* Send the request and store the results */
3932 if (!HTTP_OpenConnection(lpwhr))
3935 /* send the request as ASCII, tack on the optional data */
3936 if (!lpOptional || redirected)
3937 dwOptionalLength = 0;
3938 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3939 NULL, 0, NULL, NULL );
3940 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3941 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3942 ascii_req, len, NULL, NULL );
3944 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3945 len = (len + dwOptionalLength - 1);
3947 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3949 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3950 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3952 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3953 HeapFree( GetProcessHeap(), 0, ascii_req );
3955 lpwhr->dwBytesWritten = dwOptionalLength;
3957 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3958 INTERNET_STATUS_REQUEST_SENT,
3959 &len, sizeof(DWORD));
3966 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3967 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3972 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3976 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3977 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3980 HTTP_ProcessCookies(lpwhr);
3982 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3984 dwBufferSize = sizeof(dwStatusCode);
3985 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3986 &dwStatusCode,&dwBufferSize,NULL))
3989 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3991 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3992 dwBufferSize=sizeof(szNewLocation);
3993 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3994 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3996 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3998 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3999 lpwhr->lpszVerb = heap_strdupW(szGET);
4001 HTTP_DrainContent(lpwhr);
4002 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
4004 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4005 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4006 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
4009 HeapFree(GetProcessHeap(), 0, requestString);
4012 HeapFree( GetProcessHeap(), 0, new_url );
4017 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
4019 WCHAR szAuthValue[2048];
4021 if (dwStatusCode == HTTP_STATUS_DENIED)
4023 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
4025 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4027 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4029 lpwhr->lpHttpSession->lpszUserName,
4030 lpwhr->lpHttpSession->lpszPassword,
4038 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4041 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4043 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4044 &lpwhr->pProxyAuthInfo,
4045 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
4046 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
4062 WCHAR url[INTERNET_MAX_URL_LENGTH];
4063 WCHAR cacheFileName[MAX_PATH+1];
4066 b = HTTP_GetRequestURL(lpwhr, url);
4068 WARN("Could not get URL\n");
4072 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
4074 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
4075 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4076 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4077 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
4078 WARN("Could not create file: %u\n", GetLastError());
4079 lpwhr->hCacheFile = NULL;
4082 WARN("Could not create cache entry: %08x\n", GetLastError());
4088 HeapFree(GetProcessHeap(), 0, requestString);
4090 /* TODO: send notification for P3P header */
4092 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4096 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
4099 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4102 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4103 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4104 sizeof(INTERNET_ASYNC_RESULT));
4109 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4110 iar.dwError = INTERNET_GetLastError();
4112 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4113 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4114 sizeof(INTERNET_ASYNC_RESULT));
4119 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
4123 /***********************************************************************
4124 * HTTPSESSION_Destroy (internal)
4126 * Deallocate session handle
4129 static void HTTPSESSION_Destroy(object_header_t *hdr)
4131 http_session_t *lpwhs = (http_session_t*) hdr;
4133 TRACE("%p\n", lpwhs);
4135 WININET_Release(&lpwhs->lpAppInfo->hdr);
4137 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4138 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4139 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4140 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4141 HeapFree(GetProcessHeap(), 0, lpwhs);
4144 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4147 case INTERNET_OPTION_HANDLE_TYPE:
4148 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4150 if (*size < sizeof(ULONG))
4151 return ERROR_INSUFFICIENT_BUFFER;
4153 *size = sizeof(DWORD);
4154 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4155 return ERROR_SUCCESS;
4158 return INET_QueryOption(option, buffer, size, unicode);
4161 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4163 http_session_t *ses = (http_session_t*)hdr;
4166 case INTERNET_OPTION_USERNAME:
4168 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4169 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4170 return ERROR_SUCCESS;
4172 case INTERNET_OPTION_PASSWORD:
4174 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4175 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4176 return ERROR_SUCCESS;
4181 return ERROR_INTERNET_INVALID_OPTION;
4184 static const object_vtbl_t HTTPSESSIONVtbl = {
4185 HTTPSESSION_Destroy,
4187 HTTPSESSION_QueryOption,
4188 HTTPSESSION_SetOption,
4197 /***********************************************************************
4198 * HTTP_Connect (internal)
4200 * Create http session handle
4203 * HINTERNET a session handle on success
4207 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4208 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4209 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4210 DWORD dwInternalFlags)
4212 http_session_t *lpwhs = NULL;
4213 HINTERNET handle = NULL;
4217 if (!lpszServerName || !lpszServerName[0])
4219 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4223 assert( hIC->hdr.htype == WH_HINIT );
4225 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4228 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4233 * According to my tests. The name is not resolved until a request is sent
4236 lpwhs->hdr.htype = WH_HHTTPSESSION;
4237 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4238 lpwhs->hdr.dwFlags = dwFlags;
4239 lpwhs->hdr.dwContext = dwContext;
4240 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4241 lpwhs->hdr.refs = 1;
4242 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4244 WININET_AddRef( &hIC->hdr );
4245 lpwhs->lpAppInfo = hIC;
4246 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4248 handle = WININET_AllocHandle( &lpwhs->hdr );
4251 ERR("Failed to alloc handle\n");
4252 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4256 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4257 if(strchrW(hIC->lpszProxy, ' '))
4258 FIXME("Several proxies not implemented.\n");
4259 if(hIC->lpszProxyBypass)
4260 FIXME("Proxy bypass is ignored.\n");
4262 if (lpszServerName && lpszServerName[0])
4264 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4265 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4267 if (lpszUserName && lpszUserName[0])
4268 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4269 if (lpszPassword && lpszPassword[0])
4270 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4271 lpwhs->nServerPort = nServerPort;
4272 lpwhs->nHostPort = nServerPort;
4274 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4275 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4277 INTERNET_SendCallback(&hIC->hdr, dwContext,
4278 INTERNET_STATUS_HANDLE_CREATED, &handle,
4284 WININET_Release( &lpwhs->hdr );
4287 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4291 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4296 /***********************************************************************
4297 * HTTP_OpenConnection (internal)
4299 * Connect to a web server
4306 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4308 BOOL bSuccess = FALSE;
4309 http_session_t *lpwhs;
4310 appinfo_t *hIC = NULL;
4311 char szaddr[INET6_ADDRSTRLEN];
4317 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4319 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4323 if (NETCON_connected(&lpwhr->netConnection))
4328 if (!HTTP_ResolveName(lpwhr)) goto lend;
4330 lpwhs = lpwhr->lpHttpSession;
4332 hIC = lpwhs->lpAppInfo;
4333 switch (lpwhs->socketAddress.ss_family)
4336 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4339 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4342 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4343 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4346 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4347 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4348 INTERNET_STATUS_CONNECTING_TO_SERVER,
4352 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4355 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4359 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4363 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4365 /* Note: we differ from Microsoft's WinINet here. they seem to have
4366 * a bug that causes no status callbacks to be sent when starting
4367 * a tunnel to a proxy server using the CONNECT verb. i believe our
4368 * behaviour to be more correct and to not cause any incompatibilities
4369 * because using a secure connection through a proxy server is a rare
4370 * case that would be hard for anyone to depend on */
4371 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4374 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4376 WARN("Couldn't connect securely to host\n");
4381 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4382 INTERNET_STATUS_CONNECTED_TO_SERVER,
4383 szaddr, strlen(szaddr)+1);
4388 lpwhr->read_pos = lpwhr->read_size = 0;
4389 lpwhr->read_chunked = FALSE;
4391 TRACE("%d <--\n", bSuccess);
4396 /***********************************************************************
4397 * HTTP_clear_response_headers (internal)
4399 * clear out any old response headers
4401 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4405 for( i=0; i<lpwhr->nCustHeaders; i++)
4407 if( !lpwhr->pCustHeaders[i].lpszField )
4409 if( !lpwhr->pCustHeaders[i].lpszValue )
4411 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4413 HTTP_DeleteCustomHeader( lpwhr, i );
4418 /***********************************************************************
4419 * HTTP_GetResponseHeaders (internal)
4421 * Read server response
4428 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4431 WCHAR buffer[MAX_REPLY_LEN];
4432 DWORD buflen = MAX_REPLY_LEN;
4433 BOOL bSuccess = FALSE;
4435 char bufferA[MAX_REPLY_LEN];
4436 LPWSTR status_code = NULL, status_text = NULL;
4437 DWORD cchMaxRawHeaders = 1024;
4438 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4440 DWORD cchRawHeaders = 0;
4441 BOOL codeHundred = FALSE;
4445 /* clear old response headers (eg. from a redirect response) */
4446 if (clear) HTTP_clear_response_headers( lpwhr );
4448 if (!NETCON_connected(&lpwhr->netConnection))
4452 static const WCHAR szHundred[] = {'1','0','0',0};
4454 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4456 buflen = MAX_REPLY_LEN;
4457 if (!read_line(lpwhr, bufferA, &buflen))
4460 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4461 /* check is this a status code line? */
4462 if (!strncmpW(buffer, g_szHttp1_0, 4))
4464 /* split the version from the status code */
4465 status_code = strchrW( buffer, ' ' );
4470 /* split the status code from the status text */
4471 status_text = strchrW( status_code, ' ' );
4476 TRACE("version [%s] status code [%s] status text [%s]\n",
4477 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4479 codeHundred = (!strcmpW(status_code, szHundred));
4481 else if (!codeHundred)
4483 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4486 } while (codeHundred);
4488 /* Add status code */
4489 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4490 HTTP_ADDHDR_FLAG_REPLACE);
4492 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4493 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4495 lpwhr->lpszVersion = heap_strdupW(buffer);
4496 lpwhr->lpszStatusText = heap_strdupW(status_text);
4498 /* Restore the spaces */
4499 *(status_code-1) = ' ';
4500 *(status_text-1) = ' ';
4502 /* regenerate raw headers */
4503 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4504 cchMaxRawHeaders *= 2;
4505 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4506 if (temp == NULL) goto lend;
4507 lpszRawHeaders = temp;
4508 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4509 cchRawHeaders += (buflen-1);
4510 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4511 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4512 lpszRawHeaders[cchRawHeaders] = '\0';
4514 /* Parse each response line */
4517 buflen = MAX_REPLY_LEN;
4518 if (read_line(lpwhr, bufferA, &buflen))
4520 LPWSTR * pFieldAndValue;
4522 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4524 if (!bufferA[0]) break;
4525 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4527 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4530 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4531 cchMaxRawHeaders *= 2;
4532 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4533 if (temp == NULL) goto lend;
4534 lpszRawHeaders = temp;
4535 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4536 cchRawHeaders += (buflen-1);
4537 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4538 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4539 lpszRawHeaders[cchRawHeaders] = '\0';
4541 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4542 HTTP_ADDREQ_FLAG_ADD );
4544 HTTP_FreeTokens(pFieldAndValue);
4555 /* make sure the response header is terminated with an empty line. Some apps really
4556 truly care about that empty line being there for some reason. Just add it to the
4558 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4560 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4561 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4562 if (temp == NULL) goto lend;
4563 lpszRawHeaders = temp;
4566 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4568 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4569 lpwhr->lpszRawHeaders = lpszRawHeaders;
4570 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4580 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4585 /***********************************************************************
4586 * HTTP_InterpretHttpHeader (internal)
4588 * Parse server response
4592 * Pointer to array of field, value, NULL on success.
4595 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4597 LPWSTR * pTokenPair;
4601 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4603 pszColon = strchrW(buffer, ':');
4604 /* must have two tokens */
4607 HTTP_FreeTokens(pTokenPair);
4609 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4613 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4616 HTTP_FreeTokens(pTokenPair);
4619 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4620 pTokenPair[0][pszColon - buffer] = '\0';
4624 len = strlenW(pszColon);
4625 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4628 HTTP_FreeTokens(pTokenPair);
4631 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4633 strip_spaces(pTokenPair[0]);
4634 strip_spaces(pTokenPair[1]);
4636 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4640 /***********************************************************************
4641 * HTTP_ProcessHeader (internal)
4643 * Stuff header into header tables according to <dwModifier>
4647 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4649 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4651 LPHTTPHEADERW lphttpHdr = NULL;
4652 BOOL bSuccess = FALSE;
4654 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4656 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4658 /* REPLACE wins out over ADD */
4659 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4660 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4662 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4665 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4669 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4673 lphttpHdr = &lpwhr->pCustHeaders[index];
4679 hdr.lpszField = (LPWSTR)field;
4680 hdr.lpszValue = (LPWSTR)value;
4681 hdr.wFlags = hdr.wCount = 0;
4683 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4684 hdr.wFlags |= HDR_ISREQUEST;
4686 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4688 /* no value to delete */
4691 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4692 lphttpHdr->wFlags |= HDR_ISREQUEST;
4694 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4696 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4698 HTTP_DeleteCustomHeader( lpwhr, index );
4704 hdr.lpszField = (LPWSTR)field;
4705 hdr.lpszValue = (LPWSTR)value;
4706 hdr.wFlags = hdr.wCount = 0;
4708 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4709 hdr.wFlags |= HDR_ISREQUEST;
4711 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4716 else if (dwModifier & COALESCEFLAGS)
4721 INT origlen = strlenW(lphttpHdr->lpszValue);
4722 INT valuelen = strlenW(value);
4724 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4727 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4729 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4732 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4735 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4737 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4740 lphttpHdr->lpszValue = lpsztmp;
4741 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4744 lphttpHdr->lpszValue[origlen] = ch;
4746 lphttpHdr->lpszValue[origlen] = ' ';
4750 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4751 lphttpHdr->lpszValue[len] = '\0';
4756 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4757 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4760 TRACE("<-- %d\n",bSuccess);
4765 /***********************************************************************
4766 * HTTP_FinishedReading (internal)
4768 * Called when all content from server has been read by client.
4771 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4773 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4780 HTTPREQ_CloseConnection(&lpwhr->hdr);
4783 /* FIXME: store data in the URL cache here */
4789 /***********************************************************************
4790 * HTTP_GetCustomHeaderIndex (internal)
4792 * Return index of custom header from header array
4795 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4796 int requested_index, BOOL request_only)
4800 TRACE("%s\n", debugstr_w(lpszField));
4802 for (index = 0; index < lpwhr->nCustHeaders; index++)
4804 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4807 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4810 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4813 if (requested_index == 0)
4818 if (index >= lpwhr->nCustHeaders)
4821 TRACE("Return: %d\n", index);
4826 /***********************************************************************
4827 * HTTP_InsertCustomHeader (internal)
4829 * Insert header into array
4832 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4835 LPHTTPHEADERW lph = NULL;
4838 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4839 count = lpwhr->nCustHeaders + 1;
4841 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4843 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4847 lpwhr->pCustHeaders = lph;
4848 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4849 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4850 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4851 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4852 lpwhr->nCustHeaders++;
4857 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4864 /***********************************************************************
4865 * HTTP_DeleteCustomHeader (internal)
4867 * Delete header from array
4868 * If this function is called, the indexs may change.
4870 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4872 if( lpwhr->nCustHeaders <= 0 )
4874 if( index >= lpwhr->nCustHeaders )
4876 lpwhr->nCustHeaders--;
4878 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4879 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4881 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4882 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4883 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4889 /***********************************************************************
4890 * HTTP_VerifyValidHeader (internal)
4892 * Verify the given header is not invalid for the given http request
4895 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4897 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4898 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4904 /***********************************************************************
4905 * IsHostInProxyBypassList (@)
4910 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4912 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);