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 DWORD 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 DWORD 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))
1627 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1629 switch (lpwhs->socketAddress.ss_family)
1632 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1635 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1638 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1639 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1641 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1642 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1643 INTERNET_STATUS_NAME_RESOLVED,
1644 szaddr, strlen(szaddr)+1);
1646 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1647 return ERROR_SUCCESS;
1651 /***********************************************************************
1652 * HTTPREQ_Destroy (internal)
1654 * Deallocate request handle
1657 static void HTTPREQ_Destroy(object_header_t *hdr)
1659 http_request_t *lpwhr = (http_request_t*) hdr;
1664 if(lpwhr->hCacheFile)
1665 CloseHandle(lpwhr->hCacheFile);
1667 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1669 DeleteCriticalSection( &lpwhr->read_section );
1670 WININET_Release(&lpwhr->lpHttpSession->hdr);
1672 destroy_authinfo(lpwhr->pAuthInfo);
1673 destroy_authinfo(lpwhr->pProxyAuthInfo);
1675 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1676 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1677 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1678 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1679 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1681 for (i = 0; i < lpwhr->nCustHeaders; i++)
1683 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1684 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1688 if(lpwhr->gzip_stream) {
1689 if(!lpwhr->gzip_stream->end_of_data)
1690 inflateEnd(&lpwhr->gzip_stream->zstream);
1691 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1695 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1696 HeapFree(GetProcessHeap(), 0, lpwhr);
1699 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1701 http_request_t *lpwhr = (http_request_t*) hdr;
1703 TRACE("%p\n",lpwhr);
1705 if (!NETCON_connected(&lpwhr->netConnection))
1708 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1709 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1711 NETCON_close(&lpwhr->netConnection);
1713 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1714 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1717 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1719 LPHTTPHEADERW host_header;
1721 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1723 host_header = HTTP_GetHeader(req, hostW);
1727 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1731 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1733 WCHAR szVersion[10];
1734 WCHAR szConnectionResponse[20];
1735 DWORD dwBufferSize = sizeof(szVersion);
1736 BOOL keepalive = FALSE;
1738 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1739 * the connection is keep-alive by default */
1740 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1741 &dwBufferSize, NULL) &&
1742 !strcmpiW(szVersion, g_szHttp1_1))
1747 dwBufferSize = sizeof(szConnectionResponse);
1748 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1749 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1751 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1757 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1759 http_request_t *req = (http_request_t*)hdr;
1762 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1764 http_session_t *lpwhs = req->lpHttpSession;
1765 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1767 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1769 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1770 return ERROR_INSUFFICIENT_BUFFER;
1771 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1772 /* FIXME: can't get a SOCKET from our connection since we don't use
1776 /* FIXME: get source port from req->netConnection */
1777 info->SourcePort = 0;
1778 info->DestPort = lpwhs->nHostPort;
1780 if (HTTP_KeepAlive(req))
1781 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1782 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1783 info->Flags |= IDSI_FLAG_PROXY;
1784 if (req->netConnection.useSSL)
1785 info->Flags |= IDSI_FLAG_SECURE;
1787 return ERROR_SUCCESS;
1790 case INTERNET_OPTION_SECURITY_FLAGS:
1792 http_session_t *lpwhs;
1793 lpwhs = req->lpHttpSession;
1795 if (*size < sizeof(ULONG))
1796 return ERROR_INSUFFICIENT_BUFFER;
1798 *size = sizeof(DWORD);
1799 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1800 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1802 *(DWORD*)buffer = 0;
1803 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1804 return ERROR_SUCCESS;
1807 case INTERNET_OPTION_HANDLE_TYPE:
1808 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1810 if (*size < sizeof(ULONG))
1811 return ERROR_INSUFFICIENT_BUFFER;
1813 *size = sizeof(DWORD);
1814 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1815 return ERROR_SUCCESS;
1817 case INTERNET_OPTION_URL: {
1818 WCHAR url[INTERNET_MAX_URL_LENGTH];
1823 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1825 TRACE("INTERNET_OPTION_URL\n");
1827 host = HTTP_GetHeader(req, hostW);
1828 strcpyW(url, httpW);
1829 strcatW(url, host->lpszValue);
1830 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1832 strcatW(url, req->lpszPath);
1834 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1837 len = (strlenW(url)+1) * sizeof(WCHAR);
1839 return ERROR_INSUFFICIENT_BUFFER;
1842 strcpyW(buffer, url);
1843 return ERROR_SUCCESS;
1845 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1847 return ERROR_INSUFFICIENT_BUFFER;
1850 return ERROR_SUCCESS;
1854 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1855 INTERNET_CACHE_ENTRY_INFOW *info;
1856 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1857 WCHAR url[INTERNET_MAX_URL_LENGTH];
1858 DWORD nbytes, error;
1861 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1863 if (*size < sizeof(*ts))
1865 *size = sizeof(*ts);
1866 return ERROR_INSUFFICIENT_BUFFER;
1869 HTTP_GetRequestURL(req, url);
1870 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1871 error = GetLastError();
1872 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1874 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1875 return ERROR_OUTOFMEMORY;
1877 GetUrlCacheEntryInfoW(url, info, &nbytes);
1879 ts->ftExpires = info->ExpireTime;
1880 ts->ftLastModified = info->LastModifiedTime;
1882 HeapFree(GetProcessHeap(), 0, info);
1883 *size = sizeof(*ts);
1884 return ERROR_SUCCESS;
1889 case INTERNET_OPTION_DATAFILE_NAME: {
1892 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1894 if(!req->lpszCacheFile) {
1896 return ERROR_INTERNET_ITEM_NOT_FOUND;
1900 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1901 if(*size < req_size)
1902 return ERROR_INSUFFICIENT_BUFFER;
1905 memcpy(buffer, req->lpszCacheFile, *size);
1906 return ERROR_SUCCESS;
1908 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1909 if (req_size > *size)
1910 return ERROR_INSUFFICIENT_BUFFER;
1912 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1913 -1, buffer, *size, NULL, NULL);
1914 return ERROR_SUCCESS;
1918 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1919 PCCERT_CONTEXT context;
1921 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1922 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1923 return ERROR_INSUFFICIENT_BUFFER;
1926 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1928 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1931 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1932 info->ftExpiry = context->pCertInfo->NotAfter;
1933 info->ftStart = context->pCertInfo->NotBefore;
1935 len = CertNameToStrW(context->dwCertEncodingType,
1936 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1937 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1938 if(info->lpszSubjectInfo)
1939 CertNameToStrW(context->dwCertEncodingType,
1940 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1941 info->lpszSubjectInfo, len);
1942 len = CertNameToStrW(context->dwCertEncodingType,
1943 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1944 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1945 if (info->lpszIssuerInfo)
1946 CertNameToStrW(context->dwCertEncodingType,
1947 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1948 info->lpszIssuerInfo, len);
1950 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1952 len = CertNameToStrA(context->dwCertEncodingType,
1953 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1954 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1955 if(infoA->lpszSubjectInfo)
1956 CertNameToStrA(context->dwCertEncodingType,
1957 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1958 infoA->lpszSubjectInfo, len);
1959 len = CertNameToStrA(context->dwCertEncodingType,
1960 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1961 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1962 if(infoA->lpszIssuerInfo)
1963 CertNameToStrA(context->dwCertEncodingType,
1964 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1965 infoA->lpszIssuerInfo, len);
1969 * Contrary to MSDN, these do not appear to be set.
1971 * lpszSignatureAlgName
1972 * lpszEncryptionAlgName
1975 CertFreeCertificateContext(context);
1976 return ERROR_SUCCESS;
1981 return INET_QueryOption(option, buffer, size, unicode);
1984 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1986 http_request_t *req = (http_request_t*)hdr;
1989 case INTERNET_OPTION_SEND_TIMEOUT:
1990 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1991 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1993 if (size != sizeof(DWORD))
1994 return ERROR_INVALID_PARAMETER;
1996 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1999 case INTERNET_OPTION_USERNAME:
2000 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
2001 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2002 return ERROR_SUCCESS;
2004 case INTERNET_OPTION_PASSWORD:
2005 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
2006 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2007 return ERROR_SUCCESS;
2008 case INTERNET_OPTION_HTTP_DECODING:
2009 if(size != sizeof(BOOL))
2010 return ERROR_INVALID_PARAMETER;
2011 req->decoding = *(BOOL*)buffer;
2012 return ERROR_SUCCESS;
2015 return ERROR_INTERNET_INVALID_OPTION;
2018 /* read some more data into the read buffer (the read section must be held) */
2019 static DWORD read_more_data( http_request_t *req, int maxlen )
2026 /* move existing data to the start of the buffer */
2028 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2032 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2034 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2035 maxlen - req->read_size, 0, &len );
2036 if(res == ERROR_SUCCESS)
2037 req->read_size += len;
2042 /* remove some amount of data from the read buffer (the read section must be held) */
2043 static void remove_data( http_request_t *req, int count )
2045 if (!(req->read_size -= count)) req->read_pos = 0;
2046 else req->read_pos += count;
2049 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2051 int count, bytes_read, pos = 0;
2054 EnterCriticalSection( &req->read_section );
2057 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2061 count = eol - (req->read_buf + req->read_pos);
2062 bytes_read = count + 1;
2064 else count = bytes_read = req->read_size;
2066 count = min( count, *len - pos );
2067 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2069 remove_data( req, bytes_read );
2072 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2075 TRACE( "returning empty string\n" );
2076 LeaveCriticalSection( &req->read_section );
2077 INTERNET_SetLastError(res);
2081 LeaveCriticalSection( &req->read_section );
2085 if (pos && buffer[pos - 1] == '\r') pos--;
2088 buffer[*len - 1] = 0;
2089 TRACE( "returning %s\n", debugstr_a(buffer));
2093 /* discard data contents until we reach end of line (the read section must be held) */
2094 static DWORD discard_eol( http_request_t *req )
2100 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2103 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2106 req->read_pos = req->read_size = 0; /* discard everything */
2107 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2108 } while (req->read_size);
2109 return ERROR_SUCCESS;
2112 /* read the size of the next chunk (the read section must be held) */
2113 static DWORD start_next_chunk( http_request_t *req )
2115 DWORD chunk_size = 0, res;
2117 if (!req->dwContentLength) return ERROR_SUCCESS;
2118 if (req->dwContentLength == req->dwContentRead)
2120 /* read terminator for the previous chunk */
2121 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2122 req->dwContentLength = ~0u;
2123 req->dwContentRead = 0;
2127 while (req->read_size)
2129 char ch = req->read_buf[req->read_pos];
2130 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2131 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2132 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2133 else if (ch == ';' || ch == '\r' || ch == '\n')
2135 TRACE( "reading %u byte chunk\n", chunk_size );
2136 req->dwContentLength = chunk_size;
2137 req->dwContentRead = 0;
2138 return discard_eol( req );
2140 remove_data( req, 1 );
2142 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2143 if (!req->read_size)
2145 req->dwContentLength = req->dwContentRead = 0;
2146 return ERROR_SUCCESS;
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 DWORD refill_buffer( http_request_t *req )
2163 int len = sizeof(req->read_buf);
2166 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2168 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2171 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2172 if (len <= req->read_size) return ERROR_SUCCESS;
2174 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2175 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2176 return ERROR_SUCCESS;
2179 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2181 DWORD ret = ERROR_SUCCESS;
2185 z_stream *zstream = &req->gzip_stream->zstream;
2188 while(read < size && !req->gzip_stream->end_of_data) {
2189 if(!req->read_size) {
2190 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2194 zstream->next_in = req->read_buf+req->read_pos;
2195 zstream->avail_in = req->read_size;
2196 zstream->next_out = buf+read;
2197 zstream->avail_out = size-read;
2198 zres = inflate(zstream, Z_FULL_FLUSH);
2199 read = size - zstream->avail_out;
2200 remove_data(req, req->read_size-zstream->avail_in);
2201 if(zres == Z_STREAM_END) {
2202 TRACE("end of data\n");
2203 req->gzip_stream->end_of_data = TRUE;
2204 inflateEnd(&req->gzip_stream->zstream);
2205 }else if(zres != Z_OK) {
2206 WARN("inflate failed %d\n", zres);
2208 ret = ERROR_INTERNET_DECODING_FAILED;
2218 static void refill_gzip_buffer(http_request_t *req)
2223 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2226 if(req->gzip_stream->buf_pos) {
2227 if(req->gzip_stream->buf_size)
2228 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2229 req->gzip_stream->buf_pos = 0;
2232 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2233 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2234 if(res == ERROR_SUCCESS)
2235 req->gzip_stream->buf_size += len;
2238 /* return the size of data available to be read immediately (the read section must be held) */
2239 static DWORD get_avail_data( http_request_t *req )
2241 if (req->gzip_stream) {
2242 refill_gzip_buffer(req);
2243 return req->gzip_stream->buf_size;
2245 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2247 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2250 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2252 INTERNET_ASYNC_RESULT iar;
2257 EnterCriticalSection( &req->read_section );
2258 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2259 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2260 iar.dwError = first_notif ? 0 : get_avail_data(req);
2265 LeaveCriticalSection( &req->read_section );
2267 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2268 sizeof(INTERNET_ASYNC_RESULT));
2271 /* read data from the http connection (the read section must be held) */
2272 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2274 BOOL finished_reading = FALSE;
2275 int len, bytes_read = 0;
2276 DWORD ret = ERROR_SUCCESS;
2278 EnterCriticalSection( &req->read_section );
2280 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2282 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2285 if(req->gzip_stream) {
2286 if(req->gzip_stream->buf_size) {
2287 bytes_read = min(req->gzip_stream->buf_size, size);
2288 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2289 req->gzip_stream->buf_pos += bytes_read;
2290 req->gzip_stream->buf_size -= bytes_read;
2291 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2295 if(size > bytes_read) {
2296 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2297 if(ret == ERROR_SUCCESS)
2301 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2303 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2305 if (req->read_size) {
2306 bytes_read = min( req->read_size, size );
2307 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2308 remove_data( req, bytes_read );
2311 if (size > bytes_read && (!bytes_read || sync)) {
2312 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2313 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2315 /* always return success, even if the network layer returns an error */
2318 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2321 req->dwContentRead += bytes_read;
2324 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2325 LeaveCriticalSection( &req->read_section );
2327 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2329 DWORD dwBytesWritten;
2331 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2333 WARN("WriteFile failed: %u\n", GetLastError());
2336 if(finished_reading)
2337 HTTP_FinishedReading(req);
2343 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2345 http_request_t *req = (http_request_t*)hdr;
2346 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2349 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2351 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2352 http_request_t *req = (http_request_t*)workRequest->hdr;
2353 INTERNET_ASYNC_RESULT iar;
2356 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2358 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2359 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2361 iar.dwResult = res == ERROR_SUCCESS;
2364 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2365 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2366 sizeof(INTERNET_ASYNC_RESULT));
2369 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2370 DWORD flags, DWORD_PTR context)
2372 http_request_t *req = (http_request_t*)hdr;
2375 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2376 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2378 if (buffers->dwStructSize != sizeof(*buffers))
2379 return ERROR_INVALID_PARAMETER;
2381 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2383 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2385 WORKREQUEST workRequest;
2387 if (TryEnterCriticalSection( &req->read_section ))
2389 if (get_avail_data(req))
2391 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2392 &buffers->dwBufferLength, FALSE);
2393 LeaveCriticalSection( &req->read_section );
2396 LeaveCriticalSection( &req->read_section );
2399 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2400 workRequest.hdr = WININET_AddRef(&req->hdr);
2401 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2403 INTERNET_AsyncCall(&workRequest);
2405 return ERROR_IO_PENDING;
2408 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2409 !(flags & IRF_NO_WAIT));
2412 if (res == ERROR_SUCCESS) {
2413 DWORD size = buffers->dwBufferLength;
2414 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2415 &size, sizeof(size));
2421 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2423 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2424 http_request_t *req = (http_request_t*)workRequest->hdr;
2425 INTERNET_ASYNC_RESULT iar;
2428 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2430 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2431 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2433 iar.dwResult = res == ERROR_SUCCESS;
2436 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2437 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2438 sizeof(INTERNET_ASYNC_RESULT));
2441 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2442 DWORD flags, DWORD_PTR context)
2445 http_request_t *req = (http_request_t*)hdr;
2448 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2449 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2451 if (buffers->dwStructSize != sizeof(*buffers))
2452 return ERROR_INVALID_PARAMETER;
2454 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2456 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2458 WORKREQUEST workRequest;
2460 if (TryEnterCriticalSection( &req->read_section ))
2462 if (get_avail_data(req))
2464 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2465 &buffers->dwBufferLength, FALSE);
2466 LeaveCriticalSection( &req->read_section );
2469 LeaveCriticalSection( &req->read_section );
2472 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2473 workRequest.hdr = WININET_AddRef(&req->hdr);
2474 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2476 INTERNET_AsyncCall(&workRequest);
2478 return ERROR_IO_PENDING;
2481 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2482 !(flags & IRF_NO_WAIT));
2485 if (res == ERROR_SUCCESS) {
2486 DWORD size = buffers->dwBufferLength;
2487 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2488 &size, sizeof(size));
2494 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2497 http_request_t *lpwhr = (http_request_t*)hdr;
2499 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2502 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2503 if (res == ERROR_SUCCESS)
2504 lpwhr->dwBytesWritten += *written;
2506 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2510 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2512 http_request_t *req = (http_request_t*)workRequest->hdr;
2514 HTTP_ReceiveRequestData(req, FALSE);
2517 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2519 http_request_t *req = (http_request_t*)hdr;
2521 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2523 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2525 WORKREQUEST workRequest;
2527 /* never wait, if we can't enter the section we queue an async request right away */
2528 if (TryEnterCriticalSection( &req->read_section ))
2530 if ((*available = get_avail_data( req ))) goto done;
2531 if (end_of_read_data( req )) goto done;
2532 LeaveCriticalSection( &req->read_section );
2535 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2536 workRequest.hdr = WININET_AddRef( &req->hdr );
2538 INTERNET_AsyncCall(&workRequest);
2540 return ERROR_IO_PENDING;
2543 EnterCriticalSection( &req->read_section );
2545 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2547 refill_buffer( req );
2548 *available = get_avail_data( req );
2552 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2555 if (NETCON_query_data_available(&req->netConnection, &extra))
2556 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2558 LeaveCriticalSection( &req->read_section );
2560 TRACE( "returning %u\n", *available );
2561 return ERROR_SUCCESS;
2564 static const object_vtbl_t HTTPREQVtbl = {
2566 HTTPREQ_CloseConnection,
2567 HTTPREQ_QueryOption,
2570 HTTPREQ_ReadFileExA,
2571 HTTPREQ_ReadFileExW,
2573 HTTPREQ_QueryDataAvailable,
2577 /***********************************************************************
2578 * HTTP_HttpOpenRequestW (internal)
2580 * Open a HTTP request handle
2583 * HINTERNET a HTTP request handle on success
2587 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2588 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2589 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2590 DWORD dwFlags, DWORD_PTR dwContext)
2592 appinfo_t *hIC = NULL;
2593 http_request_t *lpwhr;
2594 LPWSTR lpszHostName = NULL;
2595 HINTERNET handle = NULL;
2596 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2601 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2602 hIC = lpwhs->lpAppInfo;
2604 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2607 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2610 lpwhr->hdr.htype = WH_HHTTPREQ;
2611 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2612 lpwhr->hdr.dwFlags = dwFlags;
2613 lpwhr->hdr.dwContext = dwContext;
2614 lpwhr->hdr.refs = 1;
2615 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2616 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2617 lpwhr->dwContentLength = ~0u;
2618 InitializeCriticalSection( &lpwhr->read_section );
2620 WININET_AddRef( &lpwhs->hdr );
2621 lpwhr->lpHttpSession = lpwhs;
2622 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2624 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2625 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2626 if (NULL == lpszHostName)
2628 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2632 handle = WININET_AllocHandle( &lpwhr->hdr );
2635 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2639 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2641 InternetCloseHandle( handle );
2646 if (lpszObjectName && *lpszObjectName) {
2650 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2651 if (rc != E_POINTER)
2652 len = strlenW(lpszObjectName)+1;
2653 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2654 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2655 URL_ESCAPE_SPACES_ONLY);
2658 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2659 strcpyW(lpwhr->lpszPath,lpszObjectName);
2662 static const WCHAR slashW[] = {'/',0};
2664 lpwhr->lpszPath = heap_strdupW(slashW);
2667 if (lpszReferrer && *lpszReferrer)
2668 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2670 if (lpszAcceptTypes)
2673 for (i = 0; lpszAcceptTypes[i]; i++)
2675 if (!*lpszAcceptTypes[i]) continue;
2676 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2677 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2678 HTTP_ADDHDR_FLAG_REQ |
2679 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2683 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2684 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2686 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2687 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2688 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2690 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2691 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2692 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2695 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2696 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2698 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2699 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2700 INTERNET_DEFAULT_HTTPS_PORT :
2701 INTERNET_DEFAULT_HTTP_PORT);
2703 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2704 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2705 INTERNET_DEFAULT_HTTPS_PORT :
2706 INTERNET_DEFAULT_HTTP_PORT);
2708 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2709 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2711 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2712 INTERNET_STATUS_HANDLE_CREATED, &handle,
2716 HeapFree(GetProcessHeap(), 0, lpszHostName);
2718 WININET_Release( &lpwhr->hdr );
2720 TRACE("<-- %p (%p)\n", handle, lpwhr);
2724 /* read any content returned by the server so that the connection can be
2726 static void HTTP_DrainContent(http_request_t *req)
2730 if (!NETCON_connected(&req->netConnection)) return;
2732 if (req->dwContentLength == -1)
2734 NETCON_close(&req->netConnection);
2737 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2742 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2744 } while (bytes_read);
2747 static const LPCWSTR header_lookup[] = {
2748 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2749 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2750 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2751 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2752 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2753 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2754 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2755 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2756 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2757 szDate, /* HTTP_QUERY_DATE = 9 */
2758 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2759 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2760 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2761 szURI, /* HTTP_QUERY_URI = 13 */
2762 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2763 NULL, /* HTTP_QUERY_COST = 15 */
2764 NULL, /* HTTP_QUERY_LINK = 16 */
2765 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2766 NULL, /* HTTP_QUERY_VERSION = 18 */
2767 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2768 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2769 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2770 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2771 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2772 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2773 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2774 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2775 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2776 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2777 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2778 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2779 NULL, /* HTTP_QUERY_FROM = 31 */
2780 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2781 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2782 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2783 szReferer, /* HTTP_QUERY_REFERER = 35 */
2784 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2785 szServer, /* HTTP_QUERY_SERVER = 37 */
2786 NULL, /* HTTP_TITLE = 38 */
2787 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2788 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2789 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2790 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2791 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2792 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2793 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2794 NULL, /* HTTP_QUERY_REFRESH = 46 */
2795 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2796 szAge, /* HTTP_QUERY_AGE = 48 */
2797 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2798 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2799 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2800 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2801 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2802 szETag, /* HTTP_QUERY_ETAG = 54 */
2803 hostW, /* HTTP_QUERY_HOST = 55 */
2804 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2805 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2806 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2807 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2808 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2809 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2810 szRange, /* HTTP_QUERY_RANGE = 62 */
2811 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2812 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2813 szVary, /* HTTP_QUERY_VARY = 65 */
2814 szVia, /* HTTP_QUERY_VIA = 66 */
2815 szWarning, /* HTTP_QUERY_WARNING = 67 */
2816 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2817 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2818 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2821 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2823 /***********************************************************************
2824 * HTTP_HttpQueryInfoW (internal)
2826 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2827 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2829 LPHTTPHEADERW lphttpHdr = NULL;
2830 BOOL bSuccess = FALSE;
2831 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2832 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2833 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2836 /* Find requested header structure */
2839 case HTTP_QUERY_CUSTOM:
2840 if (!lpBuffer) return FALSE;
2841 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2843 case HTTP_QUERY_RAW_HEADERS_CRLF:
2850 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2852 headers = lpwhr->lpszRawHeaders;
2855 len = strlenW(headers) * sizeof(WCHAR);
2857 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2859 len += sizeof(WCHAR);
2860 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2866 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2869 len = strlenW(szCrLf) * sizeof(WCHAR);
2870 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2872 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2875 *lpdwBufferLength = len;
2878 HeapFree(GetProcessHeap(), 0, headers);
2881 case HTTP_QUERY_RAW_HEADERS:
2883 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2885 LPWSTR pszString = lpBuffer;
2887 for (i = 0; ppszRawHeaderLines[i]; i++)
2888 size += strlenW(ppszRawHeaderLines[i]) + 1;
2890 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2892 HTTP_FreeTokens(ppszRawHeaderLines);
2893 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2894 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2899 for (i = 0; ppszRawHeaderLines[i]; i++)
2901 DWORD len = strlenW(ppszRawHeaderLines[i]);
2902 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2906 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2908 *lpdwBufferLength = size * sizeof(WCHAR);
2909 HTTP_FreeTokens(ppszRawHeaderLines);
2913 case HTTP_QUERY_STATUS_TEXT:
2914 if (lpwhr->lpszStatusText)
2916 DWORD len = strlenW(lpwhr->lpszStatusText);
2917 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2919 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2920 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2925 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2926 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2928 *lpdwBufferLength = len * sizeof(WCHAR);
2932 case HTTP_QUERY_VERSION:
2933 if (lpwhr->lpszVersion)
2935 DWORD len = strlenW(lpwhr->lpszVersion);
2936 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2938 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2939 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2944 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2945 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2947 *lpdwBufferLength = len * sizeof(WCHAR);
2951 case HTTP_QUERY_CONTENT_ENCODING:
2952 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2953 requested_index,request_only);
2956 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2958 if (level < LAST_TABLE_HEADER && header_lookup[level])
2959 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2960 requested_index,request_only);
2964 lphttpHdr = &lpwhr->pCustHeaders[index];
2966 /* Ensure header satisfies requested attributes */
2968 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2969 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2971 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2975 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2977 /* coalesce value to requested type */
2978 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2980 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2981 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2984 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2990 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2992 tmpTM = *gmtime(&tmpTime);
2993 STHook = (SYSTEMTIME *)lpBuffer;
2994 STHook->wDay = tmpTM.tm_mday;
2995 STHook->wHour = tmpTM.tm_hour;
2996 STHook->wMilliseconds = 0;
2997 STHook->wMinute = tmpTM.tm_min;
2998 STHook->wDayOfWeek = tmpTM.tm_wday;
2999 STHook->wMonth = tmpTM.tm_mon + 1;
3000 STHook->wSecond = tmpTM.tm_sec;
3001 STHook->wYear = tmpTM.tm_year;
3004 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3005 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3006 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3008 else if (lphttpHdr->lpszValue)
3010 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3012 if (len > *lpdwBufferLength)
3014 *lpdwBufferLength = len;
3015 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
3020 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3021 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
3023 *lpdwBufferLength = len - sizeof(WCHAR);
3029 /***********************************************************************
3030 * HttpQueryInfoW (WININET.@)
3032 * Queries for information about an HTTP request
3039 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3040 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3042 BOOL bSuccess = FALSE;
3043 http_request_t *lpwhr;
3045 if (TRACE_ON(wininet)) {
3046 #define FE(x) { x, #x }
3047 static const wininet_flag_info query_flags[] = {
3048 FE(HTTP_QUERY_MIME_VERSION),
3049 FE(HTTP_QUERY_CONTENT_TYPE),
3050 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3051 FE(HTTP_QUERY_CONTENT_ID),
3052 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3053 FE(HTTP_QUERY_CONTENT_LENGTH),
3054 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3055 FE(HTTP_QUERY_ALLOW),
3056 FE(HTTP_QUERY_PUBLIC),
3057 FE(HTTP_QUERY_DATE),
3058 FE(HTTP_QUERY_EXPIRES),
3059 FE(HTTP_QUERY_LAST_MODIFIED),
3060 FE(HTTP_QUERY_MESSAGE_ID),
3062 FE(HTTP_QUERY_DERIVED_FROM),
3063 FE(HTTP_QUERY_COST),
3064 FE(HTTP_QUERY_LINK),
3065 FE(HTTP_QUERY_PRAGMA),
3066 FE(HTTP_QUERY_VERSION),
3067 FE(HTTP_QUERY_STATUS_CODE),
3068 FE(HTTP_QUERY_STATUS_TEXT),
3069 FE(HTTP_QUERY_RAW_HEADERS),
3070 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3071 FE(HTTP_QUERY_CONNECTION),
3072 FE(HTTP_QUERY_ACCEPT),
3073 FE(HTTP_QUERY_ACCEPT_CHARSET),
3074 FE(HTTP_QUERY_ACCEPT_ENCODING),
3075 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3076 FE(HTTP_QUERY_AUTHORIZATION),
3077 FE(HTTP_QUERY_CONTENT_ENCODING),
3078 FE(HTTP_QUERY_FORWARDED),
3079 FE(HTTP_QUERY_FROM),
3080 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3081 FE(HTTP_QUERY_LOCATION),
3082 FE(HTTP_QUERY_ORIG_URI),
3083 FE(HTTP_QUERY_REFERER),
3084 FE(HTTP_QUERY_RETRY_AFTER),
3085 FE(HTTP_QUERY_SERVER),
3086 FE(HTTP_QUERY_TITLE),
3087 FE(HTTP_QUERY_USER_AGENT),
3088 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3089 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3090 FE(HTTP_QUERY_ACCEPT_RANGES),
3091 FE(HTTP_QUERY_SET_COOKIE),
3092 FE(HTTP_QUERY_COOKIE),
3093 FE(HTTP_QUERY_REQUEST_METHOD),
3094 FE(HTTP_QUERY_REFRESH),
3095 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3097 FE(HTTP_QUERY_CACHE_CONTROL),
3098 FE(HTTP_QUERY_CONTENT_BASE),
3099 FE(HTTP_QUERY_CONTENT_LOCATION),
3100 FE(HTTP_QUERY_CONTENT_MD5),
3101 FE(HTTP_QUERY_CONTENT_RANGE),
3102 FE(HTTP_QUERY_ETAG),
3103 FE(HTTP_QUERY_HOST),
3104 FE(HTTP_QUERY_IF_MATCH),
3105 FE(HTTP_QUERY_IF_NONE_MATCH),
3106 FE(HTTP_QUERY_IF_RANGE),
3107 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3108 FE(HTTP_QUERY_MAX_FORWARDS),
3109 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3110 FE(HTTP_QUERY_RANGE),
3111 FE(HTTP_QUERY_TRANSFER_ENCODING),
3112 FE(HTTP_QUERY_UPGRADE),
3113 FE(HTTP_QUERY_VARY),
3115 FE(HTTP_QUERY_WARNING),
3116 FE(HTTP_QUERY_CUSTOM)
3118 static const wininet_flag_info modifier_flags[] = {
3119 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3120 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3121 FE(HTTP_QUERY_FLAG_NUMBER),
3122 FE(HTTP_QUERY_FLAG_COALESCE)
3125 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3126 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3129 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
3130 TRACE(" Attribute:");
3131 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3132 if (query_flags[i].val == info) {
3133 TRACE(" %s", query_flags[i].name);
3137 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3138 TRACE(" Unknown (%08x)", info);
3141 TRACE(" Modifier:");
3142 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3143 if (modifier_flags[i].val & info_mod) {
3144 TRACE(" %s", modifier_flags[i].name);
3145 info_mod &= ~ modifier_flags[i].val;
3150 TRACE(" Unknown (%08x)", info_mod);
3155 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3156 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3158 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3162 if (lpBuffer == NULL)
3163 *lpdwBufferLength = 0;
3164 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3165 lpBuffer, lpdwBufferLength, lpdwIndex);
3169 WININET_Release( &lpwhr->hdr );
3171 TRACE("%d <--\n", bSuccess);
3175 /***********************************************************************
3176 * HttpQueryInfoA (WININET.@)
3178 * Queries for information about an HTTP request
3185 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3186 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3192 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3193 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3195 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3196 lpdwBufferLength, lpdwIndex );
3202 len = (*lpdwBufferLength)*sizeof(WCHAR);
3203 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3205 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3211 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3212 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3213 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3214 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3221 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3225 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3226 lpBuffer, *lpdwBufferLength, NULL, NULL );
3227 *lpdwBufferLength = len - 1;
3229 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3232 /* since the strings being returned from HttpQueryInfoW should be
3233 * only ASCII characters, it is reasonable to assume that all of
3234 * the Unicode characters can be reduced to a single byte */
3235 *lpdwBufferLength = len / sizeof(WCHAR);
3237 HeapFree(GetProcessHeap(), 0, bufferW );
3242 /***********************************************************************
3243 * HttpSendRequestExA (WININET.@)
3245 * Sends the specified request to the HTTP server and allows chunked
3250 * Failure: FALSE, call GetLastError() for more information.
3252 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3253 LPINTERNET_BUFFERSA lpBuffersIn,
3254 LPINTERNET_BUFFERSA lpBuffersOut,
3255 DWORD dwFlags, DWORD_PTR dwContext)
3257 INTERNET_BUFFERSW BuffersInW;
3260 LPWSTR header = NULL;
3262 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3263 lpBuffersOut, dwFlags, dwContext);
3267 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3268 if (lpBuffersIn->lpcszHeader)
3270 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3271 lpBuffersIn->dwHeadersLength,0,0);
3272 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3273 if (!(BuffersInW.lpcszHeader = header))
3275 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3278 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3279 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3283 BuffersInW.lpcszHeader = NULL;
3284 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3285 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3286 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3287 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3288 BuffersInW.Next = NULL;
3291 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3293 HeapFree(GetProcessHeap(),0,header);
3298 /***********************************************************************
3299 * HttpSendRequestExW (WININET.@)
3301 * Sends the specified request to the HTTP server and allows chunked
3306 * Failure: FALSE, call GetLastError() for more information.
3308 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3309 LPINTERNET_BUFFERSW lpBuffersIn,
3310 LPINTERNET_BUFFERSW lpBuffersOut,
3311 DWORD dwFlags, DWORD_PTR dwContext)
3314 http_request_t *lpwhr;
3315 http_session_t *lpwhs;
3318 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3319 lpBuffersOut, dwFlags, dwContext);
3321 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3323 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3325 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3329 lpwhs = lpwhr->lpHttpSession;
3330 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3331 hIC = lpwhs->lpAppInfo;
3332 assert(hIC->hdr.htype == WH_HINIT);
3334 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3336 WORKREQUEST workRequest;
3337 struct WORKREQ_HTTPSENDREQUESTW *req;
3339 workRequest.asyncproc = AsyncHttpSendRequestProc;
3340 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3341 req = &workRequest.u.HttpSendRequestW;
3346 if (lpBuffersIn->lpcszHeader)
3348 if (lpBuffersIn->dwHeadersLength == ~0u)
3349 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3351 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3353 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3354 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3356 else req->lpszHeader = NULL;
3358 req->dwHeaderLength = size / sizeof(WCHAR);
3359 req->lpOptional = lpBuffersIn->lpvBuffer;
3360 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3361 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3365 req->lpszHeader = NULL;
3366 req->dwHeaderLength = 0;
3367 req->lpOptional = NULL;
3368 req->dwOptionalLength = 0;
3369 req->dwContentLength = 0;
3372 req->bEndRequest = FALSE;
3374 INTERNET_AsyncCall(&workRequest);
3376 * This is from windows.
3378 INTERNET_SetLastError(ERROR_IO_PENDING);
3383 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3384 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3385 lpBuffersIn->dwBufferTotal, FALSE);
3387 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3392 WININET_Release( &lpwhr->hdr );
3398 /***********************************************************************
3399 * HttpSendRequestW (WININET.@)
3401 * Sends the specified request to the HTTP server
3408 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3409 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3411 http_request_t *lpwhr;
3412 http_session_t *lpwhs = NULL;
3413 appinfo_t *hIC = NULL;
3414 DWORD res = ERROR_SUCCESS;
3416 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3417 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3419 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3420 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3422 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3426 lpwhs = lpwhr->lpHttpSession;
3427 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3429 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3433 hIC = lpwhs->lpAppInfo;
3434 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3436 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3440 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3442 WORKREQUEST workRequest;
3443 struct WORKREQ_HTTPSENDREQUESTW *req;
3445 workRequest.asyncproc = AsyncHttpSendRequestProc;
3446 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3447 req = &workRequest.u.HttpSendRequestW;
3452 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3453 else size = dwHeaderLength * sizeof(WCHAR);
3455 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3456 memcpy(req->lpszHeader, lpszHeaders, size);
3459 req->lpszHeader = 0;
3460 req->dwHeaderLength = dwHeaderLength;
3461 req->lpOptional = lpOptional;
3462 req->dwOptionalLength = dwOptionalLength;
3463 req->dwContentLength = dwOptionalLength;
3464 req->bEndRequest = TRUE;
3466 INTERNET_AsyncCall(&workRequest);
3468 * This is from windows.
3470 res = ERROR_IO_PENDING;
3474 BOOL r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3475 dwHeaderLength, lpOptional, dwOptionalLength,
3476 dwOptionalLength, TRUE);
3478 res = INTERNET_GetLastError();
3482 WININET_Release( &lpwhr->hdr );
3484 if(res != ERROR_SUCCESS)
3486 return res == ERROR_SUCCESS;
3489 /***********************************************************************
3490 * HttpSendRequestA (WININET.@)
3492 * Sends the specified request to the HTTP server
3499 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3500 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3503 LPWSTR szHeaders=NULL;
3504 DWORD nLen=dwHeaderLength;
3505 if(lpszHeaders!=NULL)
3507 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3508 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3509 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3511 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3512 HeapFree(GetProcessHeap(),0,szHeaders);
3516 /***********************************************************************
3517 * HTTP_GetRedirectURL (internal)
3519 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3521 static WCHAR szHttp[] = {'h','t','t','p',0};
3522 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3523 http_session_t *lpwhs = lpwhr->lpHttpSession;
3524 URL_COMPONENTSW urlComponents;
3525 DWORD url_length = 0;
3527 LPWSTR combined_url;
3529 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3530 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3531 urlComponents.dwSchemeLength = 0;
3532 urlComponents.lpszHostName = lpwhs->lpszHostName;
3533 urlComponents.dwHostNameLength = 0;
3534 urlComponents.nPort = lpwhs->nHostPort;
3535 urlComponents.lpszUserName = lpwhs->lpszUserName;
3536 urlComponents.dwUserNameLength = 0;
3537 urlComponents.lpszPassword = NULL;
3538 urlComponents.dwPasswordLength = 0;
3539 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3540 urlComponents.dwUrlPathLength = 0;
3541 urlComponents.lpszExtraInfo = NULL;
3542 urlComponents.dwExtraInfoLength = 0;
3544 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3545 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3548 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3550 /* convert from bytes to characters */
3551 url_length = url_length / sizeof(WCHAR) - 1;
3552 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3554 HeapFree(GetProcessHeap(), 0, orig_url);
3559 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3560 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3562 HeapFree(GetProcessHeap(), 0, orig_url);
3565 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3567 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3569 HeapFree(GetProcessHeap(), 0, orig_url);
3570 HeapFree(GetProcessHeap(), 0, combined_url);
3573 HeapFree(GetProcessHeap(), 0, orig_url);
3574 return combined_url;
3578 /***********************************************************************
3579 * HTTP_HandleRedirect (internal)
3581 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3583 http_session_t *lpwhs = lpwhr->lpHttpSession;
3584 appinfo_t *hIC = lpwhs->lpAppInfo;
3585 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3586 WCHAR path[INTERNET_MAX_URL_LENGTH];
3591 /* if it's an absolute path, keep the same session info */
3592 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3596 URL_COMPONENTSW urlComponents;
3597 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3598 static WCHAR szHttp[] = {'h','t','t','p',0};
3599 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3605 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3606 urlComponents.lpszScheme = protocol;
3607 urlComponents.dwSchemeLength = 32;
3608 urlComponents.lpszHostName = hostName;
3609 urlComponents.dwHostNameLength = MAXHOSTNAME;
3610 urlComponents.lpszUserName = userName;
3611 urlComponents.dwUserNameLength = 1024;
3612 urlComponents.lpszPassword = NULL;
3613 urlComponents.dwPasswordLength = 0;
3614 urlComponents.lpszUrlPath = path;
3615 urlComponents.dwUrlPathLength = 2048;
3616 urlComponents.lpszExtraInfo = NULL;
3617 urlComponents.dwExtraInfoLength = 0;
3618 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3621 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3622 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3624 TRACE("redirect from secure page to non-secure page\n");
3625 /* FIXME: warn about from secure redirect to non-secure page */
3626 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3628 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3629 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3631 TRACE("redirect from non-secure page to secure page\n");
3632 /* FIXME: notify about redirect to secure page */
3633 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3636 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3638 if (lstrlenW(protocol)>4) /*https*/
3639 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3641 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3646 * This upsets redirects to binary files on sourceforge.net
3647 * and gives an html page instead of the target file
3648 * Examination of the HTTP request sent by native wininet.dll
3649 * reveals that it doesn't send a referrer in that case.
3650 * Maybe there's a flag that enables this, or maybe a referrer
3651 * shouldn't be added in case of a redirect.
3654 /* consider the current host as the referrer */
3655 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3656 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3657 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3658 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3661 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3662 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3663 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3666 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3667 len = lstrlenW(hostName);
3668 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3669 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3670 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3673 lpwhs->lpszHostName = heap_strdupW(hostName);
3675 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3677 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3678 lpwhs->lpszUserName = NULL;
3680 lpwhs->lpszUserName = heap_strdupW(userName);
3684 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3688 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3689 lpwhs->lpszServerName = heap_strdupW(hostName);
3690 lpwhs->nServerPort = urlComponents.nPort;
3692 NETCON_close(&lpwhr->netConnection);
3693 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) {
3694 INTERNET_SetLastError(res);
3697 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3698 lpwhr->read_pos = lpwhr->read_size = 0;
3699 lpwhr->read_chunked = FALSE;
3703 TRACE("Redirect through proxy\n");
3706 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3707 lpwhr->lpszPath=NULL;
3713 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3714 if (rc != E_POINTER)
3715 needed = strlenW(path)+1;
3716 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3717 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3718 URL_ESCAPE_SPACES_ONLY);
3721 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3722 strcpyW(lpwhr->lpszPath,path);
3726 /* Remove custom content-type/length headers on redirects. */
3727 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3729 HTTP_DeleteCustomHeader(lpwhr, index);
3730 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3732 HTTP_DeleteCustomHeader(lpwhr, index);
3737 /***********************************************************************
3738 * HTTP_build_req (internal)
3740 * concatenate all the strings in the request together
3742 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3747 for( t = list; *t ; t++ )
3748 len += strlenW( *t );
3751 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3754 for( t = list; *t ; t++ )
3760 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3763 LPWSTR requestString;
3769 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3770 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3771 http_session_t *lpwhs = lpwhr->lpHttpSession;
3775 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3776 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3777 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3778 HeapFree( GetProcessHeap(), 0, lpszPath );
3780 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3781 NULL, 0, NULL, NULL );
3782 len--; /* the nul terminator isn't needed */
3783 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3784 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3785 ascii_req, len, NULL, NULL );
3786 HeapFree( GetProcessHeap(), 0, requestString );
3788 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3790 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3791 HeapFree( GetProcessHeap(), 0, ascii_req );
3792 if (res != ERROR_SUCCESS)
3795 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3797 return ERROR_HTTP_INVALID_HEADER;
3799 return ERROR_SUCCESS;
3802 static void HTTP_InsertCookies(http_request_t *lpwhr)
3804 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3805 LPWSTR lpszCookies, lpszUrl = NULL;
3806 DWORD nCookieSize, size;
3807 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3809 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3810 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3811 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3813 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3816 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3818 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3819 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3821 cnt += sprintfW(lpszCookies, szCookie);
3822 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3823 strcatW(lpszCookies, szCrLf);
3825 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3826 HeapFree(GetProcessHeap(), 0, lpszCookies);
3829 HeapFree(GetProcessHeap(), 0, lpszUrl);
3832 /***********************************************************************
3833 * HTTP_HttpSendRequestW (internal)
3835 * Sends the specified request to the HTTP server
3842 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3843 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3844 DWORD dwContentLength, BOOL bEndRequest)
3847 BOOL bSuccess = FALSE, redirected = FALSE;
3848 LPWSTR requestString = NULL;
3851 INTERNET_ASYNC_RESULT iar;
3852 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3853 static const WCHAR szContentLength[] =
3854 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3855 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3858 TRACE("--> %p\n", lpwhr);
3860 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3862 /* if the verb is NULL default to GET */
3863 if (!lpwhr->lpszVerb)
3864 lpwhr->lpszVerb = heap_strdupW(szGET);
3866 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3868 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3869 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3870 lpwhr->dwBytesToWrite = dwContentLength;
3872 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3874 WCHAR *agent_header;
3875 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3878 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3879 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3880 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3882 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3883 HeapFree(GetProcessHeap(), 0, agent_header);
3885 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3887 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3888 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3890 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3892 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3893 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3894 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3904 /* like native, just in case the caller forgot to call InternetReadFile
3905 * for all the data */
3906 HTTP_DrainContent(lpwhr);
3907 lpwhr->dwContentRead = 0;
3909 if (TRACE_ON(wininet))
3911 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3912 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3916 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3918 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3920 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3921 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3923 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3924 HTTP_InsertCookies(lpwhr);
3926 /* add the headers the caller supplied */
3927 if( lpszHeaders && dwHeaderLength )
3929 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3930 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3933 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3935 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3936 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3937 HeapFree(GetProcessHeap(), 0, url);
3940 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3943 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3945 /* Send the request and store the results */
3946 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS) {
3947 INTERNET_SetLastError(res);
3951 /* send the request as ASCII, tack on the optional data */
3952 if (!lpOptional || redirected)
3953 dwOptionalLength = 0;
3954 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3955 NULL, 0, NULL, NULL );
3956 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3957 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3958 ascii_req, len, NULL, NULL );
3960 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3961 len = (len + dwOptionalLength - 1);
3963 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3965 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3966 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3968 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3969 HeapFree( GetProcessHeap(), 0, ascii_req );
3971 lpwhr->dwBytesWritten = dwOptionalLength;
3973 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3974 INTERNET_STATUS_REQUEST_SENT,
3975 &len, sizeof(DWORD));
3982 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3983 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3988 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3992 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3993 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3996 HTTP_ProcessCookies(lpwhr);
3998 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
4000 dwBufferSize = sizeof(dwStatusCode);
4001 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4002 &dwStatusCode,&dwBufferSize,NULL))
4005 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
4007 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4008 dwBufferSize=sizeof(szNewLocation);
4009 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
4010 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
4012 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
4014 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
4015 lpwhr->lpszVerb = heap_strdupW(szGET);
4017 HTTP_DrainContent(lpwhr);
4018 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
4020 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4021 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4022 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
4025 HeapFree(GetProcessHeap(), 0, requestString);
4028 HeapFree( GetProcessHeap(), 0, new_url );
4033 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
4035 WCHAR szAuthValue[2048];
4037 if (dwStatusCode == HTTP_STATUS_DENIED)
4039 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
4041 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4043 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4045 lpwhr->lpHttpSession->lpszUserName,
4046 lpwhr->lpHttpSession->lpszPassword,
4054 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4057 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4059 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4060 &lpwhr->pProxyAuthInfo,
4061 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
4062 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
4078 WCHAR url[INTERNET_MAX_URL_LENGTH];
4079 WCHAR cacheFileName[MAX_PATH+1];
4082 b = HTTP_GetRequestURL(lpwhr, url);
4084 WARN("Could not get URL\n");
4088 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
4090 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
4091 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4092 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4093 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
4094 WARN("Could not create file: %u\n", GetLastError());
4095 lpwhr->hCacheFile = NULL;
4098 WARN("Could not create cache entry: %08x\n", GetLastError());
4104 HeapFree(GetProcessHeap(), 0, requestString);
4106 /* TODO: send notification for P3P header */
4108 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4112 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
4115 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4118 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4119 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4120 sizeof(INTERNET_ASYNC_RESULT));
4125 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4126 iar.dwError = INTERNET_GetLastError();
4128 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4129 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4130 sizeof(INTERNET_ASYNC_RESULT));
4135 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
4139 /***********************************************************************
4140 * HTTPSESSION_Destroy (internal)
4142 * Deallocate session handle
4145 static void HTTPSESSION_Destroy(object_header_t *hdr)
4147 http_session_t *lpwhs = (http_session_t*) hdr;
4149 TRACE("%p\n", lpwhs);
4151 WININET_Release(&lpwhs->lpAppInfo->hdr);
4153 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4154 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4155 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4156 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4157 HeapFree(GetProcessHeap(), 0, lpwhs);
4160 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4163 case INTERNET_OPTION_HANDLE_TYPE:
4164 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4166 if (*size < sizeof(ULONG))
4167 return ERROR_INSUFFICIENT_BUFFER;
4169 *size = sizeof(DWORD);
4170 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4171 return ERROR_SUCCESS;
4174 return INET_QueryOption(option, buffer, size, unicode);
4177 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4179 http_session_t *ses = (http_session_t*)hdr;
4182 case INTERNET_OPTION_USERNAME:
4184 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4185 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4186 return ERROR_SUCCESS;
4188 case INTERNET_OPTION_PASSWORD:
4190 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4191 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4192 return ERROR_SUCCESS;
4197 return ERROR_INTERNET_INVALID_OPTION;
4200 static const object_vtbl_t HTTPSESSIONVtbl = {
4201 HTTPSESSION_Destroy,
4203 HTTPSESSION_QueryOption,
4204 HTTPSESSION_SetOption,
4213 /***********************************************************************
4214 * HTTP_Connect (internal)
4216 * Create http session handle
4219 * HINTERNET a session handle on success
4223 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4224 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4225 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4226 DWORD dwInternalFlags)
4228 http_session_t *lpwhs = NULL;
4229 HINTERNET handle = NULL;
4233 if (!lpszServerName || !lpszServerName[0])
4235 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4239 assert( hIC->hdr.htype == WH_HINIT );
4241 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4244 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4249 * According to my tests. The name is not resolved until a request is sent
4252 lpwhs->hdr.htype = WH_HHTTPSESSION;
4253 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4254 lpwhs->hdr.dwFlags = dwFlags;
4255 lpwhs->hdr.dwContext = dwContext;
4256 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4257 lpwhs->hdr.refs = 1;
4258 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4260 WININET_AddRef( &hIC->hdr );
4261 lpwhs->lpAppInfo = hIC;
4262 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4264 handle = WININET_AllocHandle( &lpwhs->hdr );
4267 ERR("Failed to alloc handle\n");
4268 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4272 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4273 if(strchrW(hIC->lpszProxy, ' '))
4274 FIXME("Several proxies not implemented.\n");
4275 if(hIC->lpszProxyBypass)
4276 FIXME("Proxy bypass is ignored.\n");
4278 if (lpszServerName && lpszServerName[0])
4280 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4281 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4283 if (lpszUserName && lpszUserName[0])
4284 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4285 if (lpszPassword && lpszPassword[0])
4286 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4287 lpwhs->nServerPort = nServerPort;
4288 lpwhs->nHostPort = nServerPort;
4290 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4291 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4293 INTERNET_SendCallback(&hIC->hdr, dwContext,
4294 INTERNET_STATUS_HANDLE_CREATED, &handle,
4300 WININET_Release( &lpwhs->hdr );
4303 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4307 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4312 /***********************************************************************
4313 * HTTP_OpenConnection (internal)
4315 * Connect to a web server
4322 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4324 http_session_t *lpwhs;
4325 appinfo_t *hIC = NULL;
4326 char szaddr[INET6_ADDRSTRLEN];
4328 DWORD res = ERROR_SUCCESS;
4333 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4335 res = ERROR_INVALID_PARAMETER;
4339 if (NETCON_connected(&lpwhr->netConnection))
4341 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4343 lpwhs = lpwhr->lpHttpSession;
4345 hIC = lpwhs->lpAppInfo;
4346 switch (lpwhs->socketAddress.ss_family)
4349 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4352 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4355 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4356 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4358 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4359 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4360 INTERNET_STATUS_CONNECTING_TO_SERVER,
4364 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4365 if (res != ERROR_SUCCESS)
4367 WARN("Socket creation failed: %u\n", res);
4371 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4373 if(res != ERROR_SUCCESS)
4376 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4378 /* Note: we differ from Microsoft's WinINet here. they seem to have
4379 * a bug that causes no status callbacks to be sent when starting
4380 * a tunnel to a proxy server using the CONNECT verb. i believe our
4381 * behaviour to be more correct and to not cause any incompatibilities
4382 * because using a secure connection through a proxy server is a rare
4383 * case that would be hard for anyone to depend on */
4384 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4387 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4388 if(res != ERROR_SUCCESS)
4390 WARN("Couldn't connect securely to host\n");
4395 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4396 INTERNET_STATUS_CONNECTED_TO_SERVER,
4397 szaddr, strlen(szaddr)+1);
4400 lpwhr->read_pos = lpwhr->read_size = 0;
4401 lpwhr->read_chunked = FALSE;
4403 TRACE("%d <--\n", res);
4408 /***********************************************************************
4409 * HTTP_clear_response_headers (internal)
4411 * clear out any old response headers
4413 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4417 for( i=0; i<lpwhr->nCustHeaders; i++)
4419 if( !lpwhr->pCustHeaders[i].lpszField )
4421 if( !lpwhr->pCustHeaders[i].lpszValue )
4423 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4425 HTTP_DeleteCustomHeader( lpwhr, i );
4430 /***********************************************************************
4431 * HTTP_GetResponseHeaders (internal)
4433 * Read server response
4440 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4443 WCHAR buffer[MAX_REPLY_LEN];
4444 DWORD buflen = MAX_REPLY_LEN;
4445 BOOL bSuccess = FALSE;
4447 char bufferA[MAX_REPLY_LEN];
4448 LPWSTR status_code = NULL, status_text = NULL;
4449 DWORD cchMaxRawHeaders = 1024;
4450 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4452 DWORD cchRawHeaders = 0;
4453 BOOL codeHundred = FALSE;
4457 /* clear old response headers (eg. from a redirect response) */
4458 if (clear) HTTP_clear_response_headers( lpwhr );
4460 if (!NETCON_connected(&lpwhr->netConnection))
4464 static const WCHAR szHundred[] = {'1','0','0',0};
4466 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4468 buflen = MAX_REPLY_LEN;
4469 if (!read_line(lpwhr, bufferA, &buflen))
4472 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4473 /* check is this a status code line? */
4474 if (!strncmpW(buffer, g_szHttp1_0, 4))
4476 /* split the version from the status code */
4477 status_code = strchrW( buffer, ' ' );
4482 /* split the status code from the status text */
4483 status_text = strchrW( status_code, ' ' );
4488 TRACE("version [%s] status code [%s] status text [%s]\n",
4489 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4491 codeHundred = (!strcmpW(status_code, szHundred));
4493 else if (!codeHundred)
4495 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4498 } while (codeHundred);
4500 /* Add status code */
4501 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4502 HTTP_ADDHDR_FLAG_REPLACE);
4504 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4505 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4507 lpwhr->lpszVersion = heap_strdupW(buffer);
4508 lpwhr->lpszStatusText = heap_strdupW(status_text);
4510 /* Restore the spaces */
4511 *(status_code-1) = ' ';
4512 *(status_text-1) = ' ';
4514 /* regenerate raw headers */
4515 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4516 cchMaxRawHeaders *= 2;
4517 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4518 if (temp == NULL) goto lend;
4519 lpszRawHeaders = temp;
4520 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4521 cchRawHeaders += (buflen-1);
4522 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4523 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4524 lpszRawHeaders[cchRawHeaders] = '\0';
4526 /* Parse each response line */
4529 buflen = MAX_REPLY_LEN;
4530 if (read_line(lpwhr, bufferA, &buflen))
4532 LPWSTR * pFieldAndValue;
4534 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4536 if (!bufferA[0]) break;
4537 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4539 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4542 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4543 cchMaxRawHeaders *= 2;
4544 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4545 if (temp == NULL) goto lend;
4546 lpszRawHeaders = temp;
4547 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4548 cchRawHeaders += (buflen-1);
4549 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4550 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4551 lpszRawHeaders[cchRawHeaders] = '\0';
4553 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4554 HTTP_ADDREQ_FLAG_ADD );
4556 HTTP_FreeTokens(pFieldAndValue);
4567 /* make sure the response header is terminated with an empty line. Some apps really
4568 truly care about that empty line being there for some reason. Just add it to the
4570 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4572 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4573 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4574 if (temp == NULL) goto lend;
4575 lpszRawHeaders = temp;
4578 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4580 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4581 lpwhr->lpszRawHeaders = lpszRawHeaders;
4582 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4592 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4597 /***********************************************************************
4598 * HTTP_InterpretHttpHeader (internal)
4600 * Parse server response
4604 * Pointer to array of field, value, NULL on success.
4607 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4609 LPWSTR * pTokenPair;
4613 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4615 pszColon = strchrW(buffer, ':');
4616 /* must have two tokens */
4619 HTTP_FreeTokens(pTokenPair);
4621 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4625 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4628 HTTP_FreeTokens(pTokenPair);
4631 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4632 pTokenPair[0][pszColon - buffer] = '\0';
4636 len = strlenW(pszColon);
4637 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4640 HTTP_FreeTokens(pTokenPair);
4643 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4645 strip_spaces(pTokenPair[0]);
4646 strip_spaces(pTokenPair[1]);
4648 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4652 /***********************************************************************
4653 * HTTP_ProcessHeader (internal)
4655 * Stuff header into header tables according to <dwModifier>
4659 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4661 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4663 LPHTTPHEADERW lphttpHdr = NULL;
4664 BOOL bSuccess = FALSE;
4666 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4668 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4670 /* REPLACE wins out over ADD */
4671 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4672 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4674 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4677 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4681 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4685 lphttpHdr = &lpwhr->pCustHeaders[index];
4691 hdr.lpszField = (LPWSTR)field;
4692 hdr.lpszValue = (LPWSTR)value;
4693 hdr.wFlags = hdr.wCount = 0;
4695 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4696 hdr.wFlags |= HDR_ISREQUEST;
4698 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4700 /* no value to delete */
4703 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4704 lphttpHdr->wFlags |= HDR_ISREQUEST;
4706 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4708 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4710 HTTP_DeleteCustomHeader( lpwhr, index );
4716 hdr.lpszField = (LPWSTR)field;
4717 hdr.lpszValue = (LPWSTR)value;
4718 hdr.wFlags = hdr.wCount = 0;
4720 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4721 hdr.wFlags |= HDR_ISREQUEST;
4723 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4728 else if (dwModifier & COALESCEFLAGS)
4733 INT origlen = strlenW(lphttpHdr->lpszValue);
4734 INT valuelen = strlenW(value);
4736 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4739 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4741 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4744 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4747 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4749 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4752 lphttpHdr->lpszValue = lpsztmp;
4753 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4756 lphttpHdr->lpszValue[origlen] = ch;
4758 lphttpHdr->lpszValue[origlen] = ' ';
4762 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4763 lphttpHdr->lpszValue[len] = '\0';
4768 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4769 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4772 TRACE("<-- %d\n",bSuccess);
4777 /***********************************************************************
4778 * HTTP_FinishedReading (internal)
4780 * Called when all content from server has been read by client.
4783 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4785 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4792 HTTPREQ_CloseConnection(&lpwhr->hdr);
4795 /* FIXME: store data in the URL cache here */
4801 /***********************************************************************
4802 * HTTP_GetCustomHeaderIndex (internal)
4804 * Return index of custom header from header array
4807 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4808 int requested_index, BOOL request_only)
4812 TRACE("%s\n", debugstr_w(lpszField));
4814 for (index = 0; index < lpwhr->nCustHeaders; index++)
4816 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4819 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4822 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4825 if (requested_index == 0)
4830 if (index >= lpwhr->nCustHeaders)
4833 TRACE("Return: %d\n", index);
4838 /***********************************************************************
4839 * HTTP_InsertCustomHeader (internal)
4841 * Insert header into array
4844 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4847 LPHTTPHEADERW lph = NULL;
4850 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4851 count = lpwhr->nCustHeaders + 1;
4853 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4855 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4859 lpwhr->pCustHeaders = lph;
4860 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4861 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4862 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4863 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4864 lpwhr->nCustHeaders++;
4869 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4876 /***********************************************************************
4877 * HTTP_DeleteCustomHeader (internal)
4879 * Delete header from array
4880 * If this function is called, the indexs may change.
4882 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4884 if( lpwhr->nCustHeaders <= 0 )
4886 if( index >= lpwhr->nCustHeaders )
4888 lpwhr->nCustHeaders--;
4890 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4891 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4893 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4894 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4895 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4901 /***********************************************************************
4902 * HTTP_VerifyValidHeader (internal)
4904 * Verify the given header is not invalid for the given http request
4907 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4909 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4910 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4916 /***********************************************************************
4917 * IsHostInProxyBypassList (@)
4922 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4924 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);