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 BOOL read_more_data( http_request_t *req, int maxlen )
2025 /* move existing data to the start of the buffer */
2027 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2031 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2033 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2034 maxlen - req->read_size, 0, &len ))
2037 req->read_size += len;
2041 /* remove some amount of data from the read buffer (the read section must be held) */
2042 static void remove_data( http_request_t *req, int count )
2044 if (!(req->read_size -= count)) req->read_pos = 0;
2045 else req->read_pos += count;
2048 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2050 int count, bytes_read, pos = 0;
2052 EnterCriticalSection( &req->read_section );
2055 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2059 count = eol - (req->read_buf + req->read_pos);
2060 bytes_read = count + 1;
2062 else count = bytes_read = req->read_size;
2064 count = min( count, *len - pos );
2065 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2067 remove_data( req, bytes_read );
2070 if (!read_more_data( req, -1 ) || !req->read_size)
2073 TRACE( "returning empty string\n" );
2074 LeaveCriticalSection( &req->read_section );
2078 LeaveCriticalSection( &req->read_section );
2082 if (pos && buffer[pos - 1] == '\r') pos--;
2085 buffer[*len - 1] = 0;
2086 TRACE( "returning %s\n", debugstr_a(buffer));
2090 /* discard data contents until we reach end of line (the read section must be held) */
2091 static BOOL discard_eol( http_request_t *req )
2095 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2098 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2101 req->read_pos = req->read_size = 0; /* discard everything */
2102 if (!read_more_data( req, -1 )) return FALSE;
2103 } while (req->read_size);
2107 /* read the size of the next chunk (the read section must be held) */
2108 static BOOL start_next_chunk( http_request_t *req )
2110 DWORD chunk_size = 0;
2112 if (!req->dwContentLength) return TRUE;
2113 if (req->dwContentLength == req->dwContentRead)
2115 /* read terminator for the previous chunk */
2116 if (!discard_eol( req )) return FALSE;
2117 req->dwContentLength = ~0u;
2118 req->dwContentRead = 0;
2122 while (req->read_size)
2124 char ch = req->read_buf[req->read_pos];
2125 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2126 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2127 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2128 else if (ch == ';' || ch == '\r' || ch == '\n')
2130 TRACE( "reading %u byte chunk\n", chunk_size );
2131 req->dwContentLength = chunk_size;
2132 req->dwContentRead = 0;
2133 if (!discard_eol( req )) return FALSE;
2136 remove_data( req, 1 );
2138 if (!read_more_data( req, -1 )) return FALSE;
2139 if (!req->read_size)
2141 req->dwContentLength = req->dwContentRead = 0;
2147 /* check if we have reached the end of the data to read (the read section must be held) */
2148 static BOOL end_of_read_data( http_request_t *req )
2150 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2151 if (req->read_chunked) return (req->dwContentLength == 0);
2152 if (req->dwContentLength == ~0u) return FALSE;
2153 return (req->dwContentLength == req->dwContentRead);
2156 /* fetch some more data into the read buffer (the read section must be held) */
2157 static BOOL refill_buffer( http_request_t *req )
2159 int len = sizeof(req->read_buf);
2161 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2163 if (!start_next_chunk( req )) return FALSE;
2166 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2167 if (len <= req->read_size) return TRUE;
2169 if (!read_more_data( req, len )) return FALSE;
2170 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2174 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2176 DWORD ret = ERROR_SUCCESS;
2180 z_stream *zstream = &req->gzip_stream->zstream;
2183 while(read < size && !req->gzip_stream->end_of_data) {
2184 if(!req->read_size) {
2185 if(!sync || !refill_buffer(req))
2189 zstream->next_in = req->read_buf+req->read_pos;
2190 zstream->avail_in = req->read_size;
2191 zstream->next_out = buf+read;
2192 zstream->avail_out = size-read;
2193 zres = inflate(zstream, Z_FULL_FLUSH);
2194 read = size - zstream->avail_out;
2195 remove_data(req, req->read_size-zstream->avail_in);
2196 if(zres == Z_STREAM_END) {
2197 TRACE("end of data\n");
2198 req->gzip_stream->end_of_data = TRUE;
2199 inflateEnd(&req->gzip_stream->zstream);
2200 }else if(zres != Z_OK) {
2201 WARN("inflate failed %d\n", zres);
2203 ret = ERROR_INTERNET_DECODING_FAILED;
2213 static void refill_gzip_buffer(http_request_t *req)
2218 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2221 if(req->gzip_stream->buf_pos) {
2222 if(req->gzip_stream->buf_size)
2223 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2224 req->gzip_stream->buf_pos = 0;
2227 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2228 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2229 if(res == ERROR_SUCCESS)
2230 req->gzip_stream->buf_size += len;
2233 /* return the size of data available to be read immediately (the read section must be held) */
2234 static DWORD get_avail_data( http_request_t *req )
2236 if (req->gzip_stream) {
2237 refill_gzip_buffer(req);
2238 return req->gzip_stream->buf_size;
2240 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2242 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2245 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2247 INTERNET_ASYNC_RESULT iar;
2251 EnterCriticalSection( &req->read_section );
2252 if (refill_buffer( req )) {
2253 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2254 iar.dwError = first_notif ? 0 : get_avail_data(req);
2257 iar.dwError = INTERNET_GetLastError();
2259 LeaveCriticalSection( &req->read_section );
2261 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2262 sizeof(INTERNET_ASYNC_RESULT));
2265 /* read data from the http connection (the read section must be held) */
2266 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2268 BOOL finished_reading = FALSE;
2269 int len, bytes_read = 0;
2270 DWORD ret = ERROR_SUCCESS;
2272 EnterCriticalSection( &req->read_section );
2274 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2276 if (!start_next_chunk( req )) goto done;
2279 if(req->gzip_stream) {
2280 if(req->gzip_stream->buf_size) {
2281 bytes_read = min(req->gzip_stream->buf_size, size);
2282 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2283 req->gzip_stream->buf_pos += bytes_read;
2284 req->gzip_stream->buf_size -= bytes_read;
2285 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2289 if(size > bytes_read) {
2290 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2291 if(ret == ERROR_SUCCESS)
2295 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2297 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2299 if (req->read_size) {
2300 bytes_read = min( req->read_size, size );
2301 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2302 remove_data( req, bytes_read );
2305 if (size > bytes_read && (!bytes_read || sync)) {
2306 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2307 sync ? MSG_WAITALL : 0, &len))
2309 /* always return success, even if the network layer returns an error */
2312 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2315 req->dwContentRead += bytes_read;
2318 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2319 LeaveCriticalSection( &req->read_section );
2321 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2323 DWORD dwBytesWritten;
2325 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2327 WARN("WriteFile failed: %u\n", GetLastError());
2330 if(finished_reading)
2331 HTTP_FinishedReading(req);
2337 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2339 http_request_t *req = (http_request_t*)hdr;
2340 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2343 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2345 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2346 http_request_t *req = (http_request_t*)workRequest->hdr;
2347 INTERNET_ASYNC_RESULT iar;
2350 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2352 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2353 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2355 iar.dwResult = res == ERROR_SUCCESS;
2358 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2359 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2360 sizeof(INTERNET_ASYNC_RESULT));
2363 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2364 DWORD flags, DWORD_PTR context)
2366 http_request_t *req = (http_request_t*)hdr;
2369 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2370 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2372 if (buffers->dwStructSize != sizeof(*buffers))
2373 return ERROR_INVALID_PARAMETER;
2375 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2377 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2379 WORKREQUEST workRequest;
2381 if (TryEnterCriticalSection( &req->read_section ))
2383 if (get_avail_data(req))
2385 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2386 &buffers->dwBufferLength, FALSE);
2387 LeaveCriticalSection( &req->read_section );
2390 LeaveCriticalSection( &req->read_section );
2393 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2394 workRequest.hdr = WININET_AddRef(&req->hdr);
2395 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2397 INTERNET_AsyncCall(&workRequest);
2399 return ERROR_IO_PENDING;
2402 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2403 !(flags & IRF_NO_WAIT));
2406 if (res == ERROR_SUCCESS) {
2407 DWORD size = buffers->dwBufferLength;
2408 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2409 &size, sizeof(size));
2415 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2417 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2418 http_request_t *req = (http_request_t*)workRequest->hdr;
2419 INTERNET_ASYNC_RESULT iar;
2422 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2424 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2425 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2427 iar.dwResult = res == ERROR_SUCCESS;
2430 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2431 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2432 sizeof(INTERNET_ASYNC_RESULT));
2435 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2436 DWORD flags, DWORD_PTR context)
2439 http_request_t *req = (http_request_t*)hdr;
2442 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2443 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2445 if (buffers->dwStructSize != sizeof(*buffers))
2446 return ERROR_INVALID_PARAMETER;
2448 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2450 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2452 WORKREQUEST workRequest;
2454 if (TryEnterCriticalSection( &req->read_section ))
2456 if (get_avail_data(req))
2458 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2459 &buffers->dwBufferLength, FALSE);
2460 LeaveCriticalSection( &req->read_section );
2463 LeaveCriticalSection( &req->read_section );
2466 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2467 workRequest.hdr = WININET_AddRef(&req->hdr);
2468 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2470 INTERNET_AsyncCall(&workRequest);
2472 return ERROR_IO_PENDING;
2475 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2476 !(flags & IRF_NO_WAIT));
2479 if (res == ERROR_SUCCESS) {
2480 DWORD size = buffers->dwBufferLength;
2481 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2482 &size, sizeof(size));
2488 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2491 http_request_t *lpwhr = (http_request_t*)hdr;
2493 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2496 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2497 if (res == ERROR_SUCCESS)
2498 lpwhr->dwBytesWritten += *written;
2500 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2504 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2506 http_request_t *req = (http_request_t*)workRequest->hdr;
2508 HTTP_ReceiveRequestData(req, FALSE);
2511 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2513 http_request_t *req = (http_request_t*)hdr;
2515 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2517 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2519 WORKREQUEST workRequest;
2521 /* never wait, if we can't enter the section we queue an async request right away */
2522 if (TryEnterCriticalSection( &req->read_section ))
2524 if ((*available = get_avail_data( req ))) goto done;
2525 if (end_of_read_data( req )) goto done;
2526 LeaveCriticalSection( &req->read_section );
2529 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2530 workRequest.hdr = WININET_AddRef( &req->hdr );
2532 INTERNET_AsyncCall(&workRequest);
2534 return ERROR_IO_PENDING;
2537 EnterCriticalSection( &req->read_section );
2539 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2541 refill_buffer( req );
2542 *available = get_avail_data( req );
2546 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2549 if (NETCON_query_data_available(&req->netConnection, &extra))
2550 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2552 LeaveCriticalSection( &req->read_section );
2554 TRACE( "returning %u\n", *available );
2555 return ERROR_SUCCESS;
2558 static const object_vtbl_t HTTPREQVtbl = {
2560 HTTPREQ_CloseConnection,
2561 HTTPREQ_QueryOption,
2564 HTTPREQ_ReadFileExA,
2565 HTTPREQ_ReadFileExW,
2567 HTTPREQ_QueryDataAvailable,
2571 /***********************************************************************
2572 * HTTP_HttpOpenRequestW (internal)
2574 * Open a HTTP request handle
2577 * HINTERNET a HTTP request handle on success
2581 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2582 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2583 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2584 DWORD dwFlags, DWORD_PTR dwContext)
2586 appinfo_t *hIC = NULL;
2587 http_request_t *lpwhr;
2588 LPWSTR lpszHostName = NULL;
2589 HINTERNET handle = NULL;
2590 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2595 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2596 hIC = lpwhs->lpAppInfo;
2598 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2601 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2604 lpwhr->hdr.htype = WH_HHTTPREQ;
2605 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2606 lpwhr->hdr.dwFlags = dwFlags;
2607 lpwhr->hdr.dwContext = dwContext;
2608 lpwhr->hdr.refs = 1;
2609 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2610 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2611 lpwhr->dwContentLength = ~0u;
2612 InitializeCriticalSection( &lpwhr->read_section );
2614 WININET_AddRef( &lpwhs->hdr );
2615 lpwhr->lpHttpSession = lpwhs;
2616 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2618 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2619 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2620 if (NULL == lpszHostName)
2622 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2626 handle = WININET_AllocHandle( &lpwhr->hdr );
2629 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2633 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2635 InternetCloseHandle( handle );
2640 if (lpszObjectName && *lpszObjectName) {
2644 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2645 if (rc != E_POINTER)
2646 len = strlenW(lpszObjectName)+1;
2647 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2648 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2649 URL_ESCAPE_SPACES_ONLY);
2652 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2653 strcpyW(lpwhr->lpszPath,lpszObjectName);
2656 static const WCHAR slashW[] = {'/',0};
2658 lpwhr->lpszPath = heap_strdupW(slashW);
2661 if (lpszReferrer && *lpszReferrer)
2662 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2664 if (lpszAcceptTypes)
2667 for (i = 0; lpszAcceptTypes[i]; i++)
2669 if (!*lpszAcceptTypes[i]) continue;
2670 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2671 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2672 HTTP_ADDHDR_FLAG_REQ |
2673 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2677 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2678 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2680 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2681 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2682 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2684 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2685 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2686 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2689 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2690 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2692 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2693 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2694 INTERNET_DEFAULT_HTTPS_PORT :
2695 INTERNET_DEFAULT_HTTP_PORT);
2697 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2698 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2699 INTERNET_DEFAULT_HTTPS_PORT :
2700 INTERNET_DEFAULT_HTTP_PORT);
2702 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2703 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2705 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2706 INTERNET_STATUS_HANDLE_CREATED, &handle,
2710 HeapFree(GetProcessHeap(), 0, lpszHostName);
2712 WININET_Release( &lpwhr->hdr );
2714 TRACE("<-- %p (%p)\n", handle, lpwhr);
2718 /* read any content returned by the server so that the connection can be
2720 static void HTTP_DrainContent(http_request_t *req)
2724 if (!NETCON_connected(&req->netConnection)) return;
2726 if (req->dwContentLength == -1)
2728 NETCON_close(&req->netConnection);
2731 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2736 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2738 } while (bytes_read);
2741 static const LPCWSTR header_lookup[] = {
2742 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2743 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2744 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2745 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2746 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2747 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2748 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2749 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2750 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2751 szDate, /* HTTP_QUERY_DATE = 9 */
2752 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2753 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2754 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2755 szURI, /* HTTP_QUERY_URI = 13 */
2756 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2757 NULL, /* HTTP_QUERY_COST = 15 */
2758 NULL, /* HTTP_QUERY_LINK = 16 */
2759 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2760 NULL, /* HTTP_QUERY_VERSION = 18 */
2761 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2762 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2763 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2764 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2765 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2766 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2767 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2768 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2769 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2770 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2771 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2772 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2773 NULL, /* HTTP_QUERY_FROM = 31 */
2774 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2775 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2776 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2777 szReferer, /* HTTP_QUERY_REFERER = 35 */
2778 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2779 szServer, /* HTTP_QUERY_SERVER = 37 */
2780 NULL, /* HTTP_TITLE = 38 */
2781 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2782 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2783 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2784 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2785 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2786 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2787 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2788 NULL, /* HTTP_QUERY_REFRESH = 46 */
2789 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2790 szAge, /* HTTP_QUERY_AGE = 48 */
2791 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2792 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2793 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2794 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2795 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2796 szETag, /* HTTP_QUERY_ETAG = 54 */
2797 hostW, /* HTTP_QUERY_HOST = 55 */
2798 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2799 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2800 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2801 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2802 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2803 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2804 szRange, /* HTTP_QUERY_RANGE = 62 */
2805 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2806 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2807 szVary, /* HTTP_QUERY_VARY = 65 */
2808 szVia, /* HTTP_QUERY_VIA = 66 */
2809 szWarning, /* HTTP_QUERY_WARNING = 67 */
2810 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2811 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2812 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2815 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2817 /***********************************************************************
2818 * HTTP_HttpQueryInfoW (internal)
2820 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2821 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2823 LPHTTPHEADERW lphttpHdr = NULL;
2824 BOOL bSuccess = FALSE;
2825 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2826 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2827 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2830 /* Find requested header structure */
2833 case HTTP_QUERY_CUSTOM:
2834 if (!lpBuffer) return FALSE;
2835 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2837 case HTTP_QUERY_RAW_HEADERS_CRLF:
2844 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2846 headers = lpwhr->lpszRawHeaders;
2849 len = strlenW(headers) * sizeof(WCHAR);
2851 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2853 len += sizeof(WCHAR);
2854 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2860 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2863 len = strlenW(szCrLf) * sizeof(WCHAR);
2864 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2866 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2869 *lpdwBufferLength = len;
2872 HeapFree(GetProcessHeap(), 0, headers);
2875 case HTTP_QUERY_RAW_HEADERS:
2877 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2879 LPWSTR pszString = lpBuffer;
2881 for (i = 0; ppszRawHeaderLines[i]; i++)
2882 size += strlenW(ppszRawHeaderLines[i]) + 1;
2884 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2886 HTTP_FreeTokens(ppszRawHeaderLines);
2887 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2888 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2893 for (i = 0; ppszRawHeaderLines[i]; i++)
2895 DWORD len = strlenW(ppszRawHeaderLines[i]);
2896 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2900 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2902 *lpdwBufferLength = size * sizeof(WCHAR);
2903 HTTP_FreeTokens(ppszRawHeaderLines);
2907 case HTTP_QUERY_STATUS_TEXT:
2908 if (lpwhr->lpszStatusText)
2910 DWORD len = strlenW(lpwhr->lpszStatusText);
2911 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2913 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2914 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2919 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2920 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2922 *lpdwBufferLength = len * sizeof(WCHAR);
2926 case HTTP_QUERY_VERSION:
2927 if (lpwhr->lpszVersion)
2929 DWORD len = strlenW(lpwhr->lpszVersion);
2930 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2932 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2933 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2938 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2939 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2941 *lpdwBufferLength = len * sizeof(WCHAR);
2945 case HTTP_QUERY_CONTENT_ENCODING:
2946 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2947 requested_index,request_only);
2950 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2952 if (level < LAST_TABLE_HEADER && header_lookup[level])
2953 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2954 requested_index,request_only);
2958 lphttpHdr = &lpwhr->pCustHeaders[index];
2960 /* Ensure header satisfies requested attributes */
2962 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2963 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2965 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2969 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2971 /* coalesce value to requested type */
2972 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2974 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2975 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2978 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2984 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2986 tmpTM = *gmtime(&tmpTime);
2987 STHook = (SYSTEMTIME *)lpBuffer;
2988 STHook->wDay = tmpTM.tm_mday;
2989 STHook->wHour = tmpTM.tm_hour;
2990 STHook->wMilliseconds = 0;
2991 STHook->wMinute = tmpTM.tm_min;
2992 STHook->wDayOfWeek = tmpTM.tm_wday;
2993 STHook->wMonth = tmpTM.tm_mon + 1;
2994 STHook->wSecond = tmpTM.tm_sec;
2995 STHook->wYear = tmpTM.tm_year;
2998 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2999 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3000 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3002 else if (lphttpHdr->lpszValue)
3004 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3006 if (len > *lpdwBufferLength)
3008 *lpdwBufferLength = len;
3009 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
3014 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3015 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
3017 *lpdwBufferLength = len - sizeof(WCHAR);
3023 /***********************************************************************
3024 * HttpQueryInfoW (WININET.@)
3026 * Queries for information about an HTTP request
3033 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3034 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3036 BOOL bSuccess = FALSE;
3037 http_request_t *lpwhr;
3039 if (TRACE_ON(wininet)) {
3040 #define FE(x) { x, #x }
3041 static const wininet_flag_info query_flags[] = {
3042 FE(HTTP_QUERY_MIME_VERSION),
3043 FE(HTTP_QUERY_CONTENT_TYPE),
3044 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3045 FE(HTTP_QUERY_CONTENT_ID),
3046 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3047 FE(HTTP_QUERY_CONTENT_LENGTH),
3048 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3049 FE(HTTP_QUERY_ALLOW),
3050 FE(HTTP_QUERY_PUBLIC),
3051 FE(HTTP_QUERY_DATE),
3052 FE(HTTP_QUERY_EXPIRES),
3053 FE(HTTP_QUERY_LAST_MODIFIED),
3054 FE(HTTP_QUERY_MESSAGE_ID),
3056 FE(HTTP_QUERY_DERIVED_FROM),
3057 FE(HTTP_QUERY_COST),
3058 FE(HTTP_QUERY_LINK),
3059 FE(HTTP_QUERY_PRAGMA),
3060 FE(HTTP_QUERY_VERSION),
3061 FE(HTTP_QUERY_STATUS_CODE),
3062 FE(HTTP_QUERY_STATUS_TEXT),
3063 FE(HTTP_QUERY_RAW_HEADERS),
3064 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3065 FE(HTTP_QUERY_CONNECTION),
3066 FE(HTTP_QUERY_ACCEPT),
3067 FE(HTTP_QUERY_ACCEPT_CHARSET),
3068 FE(HTTP_QUERY_ACCEPT_ENCODING),
3069 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3070 FE(HTTP_QUERY_AUTHORIZATION),
3071 FE(HTTP_QUERY_CONTENT_ENCODING),
3072 FE(HTTP_QUERY_FORWARDED),
3073 FE(HTTP_QUERY_FROM),
3074 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3075 FE(HTTP_QUERY_LOCATION),
3076 FE(HTTP_QUERY_ORIG_URI),
3077 FE(HTTP_QUERY_REFERER),
3078 FE(HTTP_QUERY_RETRY_AFTER),
3079 FE(HTTP_QUERY_SERVER),
3080 FE(HTTP_QUERY_TITLE),
3081 FE(HTTP_QUERY_USER_AGENT),
3082 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3083 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3084 FE(HTTP_QUERY_ACCEPT_RANGES),
3085 FE(HTTP_QUERY_SET_COOKIE),
3086 FE(HTTP_QUERY_COOKIE),
3087 FE(HTTP_QUERY_REQUEST_METHOD),
3088 FE(HTTP_QUERY_REFRESH),
3089 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3091 FE(HTTP_QUERY_CACHE_CONTROL),
3092 FE(HTTP_QUERY_CONTENT_BASE),
3093 FE(HTTP_QUERY_CONTENT_LOCATION),
3094 FE(HTTP_QUERY_CONTENT_MD5),
3095 FE(HTTP_QUERY_CONTENT_RANGE),
3096 FE(HTTP_QUERY_ETAG),
3097 FE(HTTP_QUERY_HOST),
3098 FE(HTTP_QUERY_IF_MATCH),
3099 FE(HTTP_QUERY_IF_NONE_MATCH),
3100 FE(HTTP_QUERY_IF_RANGE),
3101 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3102 FE(HTTP_QUERY_MAX_FORWARDS),
3103 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3104 FE(HTTP_QUERY_RANGE),
3105 FE(HTTP_QUERY_TRANSFER_ENCODING),
3106 FE(HTTP_QUERY_UPGRADE),
3107 FE(HTTP_QUERY_VARY),
3109 FE(HTTP_QUERY_WARNING),
3110 FE(HTTP_QUERY_CUSTOM)
3112 static const wininet_flag_info modifier_flags[] = {
3113 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3114 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3115 FE(HTTP_QUERY_FLAG_NUMBER),
3116 FE(HTTP_QUERY_FLAG_COALESCE)
3119 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3120 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3123 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
3124 TRACE(" Attribute:");
3125 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3126 if (query_flags[i].val == info) {
3127 TRACE(" %s", query_flags[i].name);
3131 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3132 TRACE(" Unknown (%08x)", info);
3135 TRACE(" Modifier:");
3136 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3137 if (modifier_flags[i].val & info_mod) {
3138 TRACE(" %s", modifier_flags[i].name);
3139 info_mod &= ~ modifier_flags[i].val;
3144 TRACE(" Unknown (%08x)", info_mod);
3149 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3150 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3152 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3156 if (lpBuffer == NULL)
3157 *lpdwBufferLength = 0;
3158 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3159 lpBuffer, lpdwBufferLength, lpdwIndex);
3163 WININET_Release( &lpwhr->hdr );
3165 TRACE("%d <--\n", bSuccess);
3169 /***********************************************************************
3170 * HttpQueryInfoA (WININET.@)
3172 * Queries for information about an HTTP request
3179 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3180 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3186 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3187 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3189 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3190 lpdwBufferLength, lpdwIndex );
3196 len = (*lpdwBufferLength)*sizeof(WCHAR);
3197 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3199 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3205 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3206 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3207 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3208 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3215 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3219 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3220 lpBuffer, *lpdwBufferLength, NULL, NULL );
3221 *lpdwBufferLength = len - 1;
3223 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3226 /* since the strings being returned from HttpQueryInfoW should be
3227 * only ASCII characters, it is reasonable to assume that all of
3228 * the Unicode characters can be reduced to a single byte */
3229 *lpdwBufferLength = len / sizeof(WCHAR);
3231 HeapFree(GetProcessHeap(), 0, bufferW );
3236 /***********************************************************************
3237 * HttpSendRequestExA (WININET.@)
3239 * Sends the specified request to the HTTP server and allows chunked
3244 * Failure: FALSE, call GetLastError() for more information.
3246 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3247 LPINTERNET_BUFFERSA lpBuffersIn,
3248 LPINTERNET_BUFFERSA lpBuffersOut,
3249 DWORD dwFlags, DWORD_PTR dwContext)
3251 INTERNET_BUFFERSW BuffersInW;
3254 LPWSTR header = NULL;
3256 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3257 lpBuffersOut, dwFlags, dwContext);
3261 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3262 if (lpBuffersIn->lpcszHeader)
3264 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3265 lpBuffersIn->dwHeadersLength,0,0);
3266 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3267 if (!(BuffersInW.lpcszHeader = header))
3269 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3272 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3273 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3277 BuffersInW.lpcszHeader = NULL;
3278 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3279 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3280 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3281 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3282 BuffersInW.Next = NULL;
3285 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3287 HeapFree(GetProcessHeap(),0,header);
3292 /***********************************************************************
3293 * HttpSendRequestExW (WININET.@)
3295 * Sends the specified request to the HTTP server and allows chunked
3300 * Failure: FALSE, call GetLastError() for more information.
3302 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3303 LPINTERNET_BUFFERSW lpBuffersIn,
3304 LPINTERNET_BUFFERSW lpBuffersOut,
3305 DWORD dwFlags, DWORD_PTR dwContext)
3308 http_request_t *lpwhr;
3309 http_session_t *lpwhs;
3312 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3313 lpBuffersOut, dwFlags, dwContext);
3315 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3317 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3319 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3323 lpwhs = lpwhr->lpHttpSession;
3324 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3325 hIC = lpwhs->lpAppInfo;
3326 assert(hIC->hdr.htype == WH_HINIT);
3328 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3330 WORKREQUEST workRequest;
3331 struct WORKREQ_HTTPSENDREQUESTW *req;
3333 workRequest.asyncproc = AsyncHttpSendRequestProc;
3334 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3335 req = &workRequest.u.HttpSendRequestW;
3340 if (lpBuffersIn->lpcszHeader)
3342 if (lpBuffersIn->dwHeadersLength == ~0u)
3343 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3345 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3347 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3348 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3350 else req->lpszHeader = NULL;
3352 req->dwHeaderLength = size / sizeof(WCHAR);
3353 req->lpOptional = lpBuffersIn->lpvBuffer;
3354 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3355 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3359 req->lpszHeader = NULL;
3360 req->dwHeaderLength = 0;
3361 req->lpOptional = NULL;
3362 req->dwOptionalLength = 0;
3363 req->dwContentLength = 0;
3366 req->bEndRequest = FALSE;
3368 INTERNET_AsyncCall(&workRequest);
3370 * This is from windows.
3372 INTERNET_SetLastError(ERROR_IO_PENDING);
3377 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3378 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3379 lpBuffersIn->dwBufferTotal, FALSE);
3381 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3386 WININET_Release( &lpwhr->hdr );
3392 /***********************************************************************
3393 * HttpSendRequestW (WININET.@)
3395 * Sends the specified request to the HTTP server
3402 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3403 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3405 http_request_t *lpwhr;
3406 http_session_t *lpwhs = NULL;
3407 appinfo_t *hIC = NULL;
3408 DWORD res = ERROR_SUCCESS;
3410 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3411 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3413 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3414 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3416 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3420 lpwhs = lpwhr->lpHttpSession;
3421 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3423 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3427 hIC = lpwhs->lpAppInfo;
3428 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3430 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3434 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3436 WORKREQUEST workRequest;
3437 struct WORKREQ_HTTPSENDREQUESTW *req;
3439 workRequest.asyncproc = AsyncHttpSendRequestProc;
3440 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3441 req = &workRequest.u.HttpSendRequestW;
3446 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3447 else size = dwHeaderLength * sizeof(WCHAR);
3449 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3450 memcpy(req->lpszHeader, lpszHeaders, size);
3453 req->lpszHeader = 0;
3454 req->dwHeaderLength = dwHeaderLength;
3455 req->lpOptional = lpOptional;
3456 req->dwOptionalLength = dwOptionalLength;
3457 req->dwContentLength = dwOptionalLength;
3458 req->bEndRequest = TRUE;
3460 INTERNET_AsyncCall(&workRequest);
3462 * This is from windows.
3464 res = ERROR_IO_PENDING;
3468 BOOL r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3469 dwHeaderLength, lpOptional, dwOptionalLength,
3470 dwOptionalLength, TRUE);
3472 res = INTERNET_GetLastError();
3476 WININET_Release( &lpwhr->hdr );
3478 if(res != ERROR_SUCCESS)
3480 return res == ERROR_SUCCESS;
3483 /***********************************************************************
3484 * HttpSendRequestA (WININET.@)
3486 * Sends the specified request to the HTTP server
3493 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3494 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3497 LPWSTR szHeaders=NULL;
3498 DWORD nLen=dwHeaderLength;
3499 if(lpszHeaders!=NULL)
3501 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3502 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3503 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3505 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3506 HeapFree(GetProcessHeap(),0,szHeaders);
3510 /***********************************************************************
3511 * HTTP_GetRedirectURL (internal)
3513 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3515 static WCHAR szHttp[] = {'h','t','t','p',0};
3516 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3517 http_session_t *lpwhs = lpwhr->lpHttpSession;
3518 URL_COMPONENTSW urlComponents;
3519 DWORD url_length = 0;
3521 LPWSTR combined_url;
3523 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3524 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3525 urlComponents.dwSchemeLength = 0;
3526 urlComponents.lpszHostName = lpwhs->lpszHostName;
3527 urlComponents.dwHostNameLength = 0;
3528 urlComponents.nPort = lpwhs->nHostPort;
3529 urlComponents.lpszUserName = lpwhs->lpszUserName;
3530 urlComponents.dwUserNameLength = 0;
3531 urlComponents.lpszPassword = NULL;
3532 urlComponents.dwPasswordLength = 0;
3533 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3534 urlComponents.dwUrlPathLength = 0;
3535 urlComponents.lpszExtraInfo = NULL;
3536 urlComponents.dwExtraInfoLength = 0;
3538 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3539 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3542 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3544 /* convert from bytes to characters */
3545 url_length = url_length / sizeof(WCHAR) - 1;
3546 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3548 HeapFree(GetProcessHeap(), 0, orig_url);
3553 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3554 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3556 HeapFree(GetProcessHeap(), 0, orig_url);
3559 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3561 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3563 HeapFree(GetProcessHeap(), 0, orig_url);
3564 HeapFree(GetProcessHeap(), 0, combined_url);
3567 HeapFree(GetProcessHeap(), 0, orig_url);
3568 return combined_url;
3572 /***********************************************************************
3573 * HTTP_HandleRedirect (internal)
3575 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3577 http_session_t *lpwhs = lpwhr->lpHttpSession;
3578 appinfo_t *hIC = lpwhs->lpAppInfo;
3579 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3580 WCHAR path[INTERNET_MAX_URL_LENGTH];
3585 /* if it's an absolute path, keep the same session info */
3586 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3590 URL_COMPONENTSW urlComponents;
3591 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3592 static WCHAR szHttp[] = {'h','t','t','p',0};
3593 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3599 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3600 urlComponents.lpszScheme = protocol;
3601 urlComponents.dwSchemeLength = 32;
3602 urlComponents.lpszHostName = hostName;
3603 urlComponents.dwHostNameLength = MAXHOSTNAME;
3604 urlComponents.lpszUserName = userName;
3605 urlComponents.dwUserNameLength = 1024;
3606 urlComponents.lpszPassword = NULL;
3607 urlComponents.dwPasswordLength = 0;
3608 urlComponents.lpszUrlPath = path;
3609 urlComponents.dwUrlPathLength = 2048;
3610 urlComponents.lpszExtraInfo = NULL;
3611 urlComponents.dwExtraInfoLength = 0;
3612 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3615 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3616 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3618 TRACE("redirect from secure page to non-secure page\n");
3619 /* FIXME: warn about from secure redirect to non-secure page */
3620 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3622 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3623 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3625 TRACE("redirect from non-secure page to secure page\n");
3626 /* FIXME: notify about redirect to secure page */
3627 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3630 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3632 if (lstrlenW(protocol)>4) /*https*/
3633 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3635 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3640 * This upsets redirects to binary files on sourceforge.net
3641 * and gives an html page instead of the target file
3642 * Examination of the HTTP request sent by native wininet.dll
3643 * reveals that it doesn't send a referrer in that case.
3644 * Maybe there's a flag that enables this, or maybe a referrer
3645 * shouldn't be added in case of a redirect.
3648 /* consider the current host as the referrer */
3649 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3650 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3651 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3652 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3655 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3656 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3657 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3660 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3661 len = lstrlenW(hostName);
3662 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3663 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3664 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3667 lpwhs->lpszHostName = heap_strdupW(hostName);
3669 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3671 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3672 lpwhs->lpszUserName = NULL;
3674 lpwhs->lpszUserName = heap_strdupW(userName);
3678 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3682 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3683 lpwhs->lpszServerName = heap_strdupW(hostName);
3684 lpwhs->nServerPort = urlComponents.nPort;
3686 NETCON_close(&lpwhr->netConnection);
3687 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) {
3688 INTERNET_SetLastError(res);
3691 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3692 lpwhr->read_pos = lpwhr->read_size = 0;
3693 lpwhr->read_chunked = FALSE;
3697 TRACE("Redirect through proxy\n");
3700 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3701 lpwhr->lpszPath=NULL;
3707 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3708 if (rc != E_POINTER)
3709 needed = strlenW(path)+1;
3710 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3711 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3712 URL_ESCAPE_SPACES_ONLY);
3715 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3716 strcpyW(lpwhr->lpszPath,path);
3720 /* Remove custom content-type/length headers on redirects. */
3721 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3723 HTTP_DeleteCustomHeader(lpwhr, index);
3724 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3726 HTTP_DeleteCustomHeader(lpwhr, index);
3731 /***********************************************************************
3732 * HTTP_build_req (internal)
3734 * concatenate all the strings in the request together
3736 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3741 for( t = list; *t ; t++ )
3742 len += strlenW( *t );
3745 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3748 for( t = list; *t ; t++ )
3754 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3757 LPWSTR requestString;
3763 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3764 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3765 http_session_t *lpwhs = lpwhr->lpHttpSession;
3769 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3770 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3771 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3772 HeapFree( GetProcessHeap(), 0, lpszPath );
3774 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3775 NULL, 0, NULL, NULL );
3776 len--; /* the nul terminator isn't needed */
3777 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3778 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3779 ascii_req, len, NULL, NULL );
3780 HeapFree( GetProcessHeap(), 0, requestString );
3782 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3784 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3785 HeapFree( GetProcessHeap(), 0, ascii_req );
3786 if (res != ERROR_SUCCESS)
3789 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3791 return ERROR_HTTP_INVALID_HEADER;
3793 return ERROR_SUCCESS;
3796 static void HTTP_InsertCookies(http_request_t *lpwhr)
3798 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3799 LPWSTR lpszCookies, lpszUrl = NULL;
3800 DWORD nCookieSize, size;
3801 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3803 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3804 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3805 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3807 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3810 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3812 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3813 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3815 cnt += sprintfW(lpszCookies, szCookie);
3816 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3817 strcatW(lpszCookies, szCrLf);
3819 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3820 HeapFree(GetProcessHeap(), 0, lpszCookies);
3823 HeapFree(GetProcessHeap(), 0, lpszUrl);
3826 /***********************************************************************
3827 * HTTP_HttpSendRequestW (internal)
3829 * Sends the specified request to the HTTP server
3836 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3837 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3838 DWORD dwContentLength, BOOL bEndRequest)
3841 BOOL bSuccess = FALSE, redirected = FALSE;
3842 LPWSTR requestString = NULL;
3845 INTERNET_ASYNC_RESULT iar;
3846 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3847 static const WCHAR szContentLength[] =
3848 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3849 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3852 TRACE("--> %p\n", lpwhr);
3854 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3856 /* if the verb is NULL default to GET */
3857 if (!lpwhr->lpszVerb)
3858 lpwhr->lpszVerb = heap_strdupW(szGET);
3860 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3862 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3863 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3864 lpwhr->dwBytesToWrite = dwContentLength;
3866 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3868 WCHAR *agent_header;
3869 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3872 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3873 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3874 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3876 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3877 HeapFree(GetProcessHeap(), 0, agent_header);
3879 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3881 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3882 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3884 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3886 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3887 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3888 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3898 /* like native, just in case the caller forgot to call InternetReadFile
3899 * for all the data */
3900 HTTP_DrainContent(lpwhr);
3901 lpwhr->dwContentRead = 0;
3903 if (TRACE_ON(wininet))
3905 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3906 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3910 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3912 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3914 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3915 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3917 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3918 HTTP_InsertCookies(lpwhr);
3920 /* add the headers the caller supplied */
3921 if( lpszHeaders && dwHeaderLength )
3923 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3924 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3927 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3929 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3930 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3931 HeapFree(GetProcessHeap(), 0, url);
3934 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3937 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3939 /* Send the request and store the results */
3940 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS) {
3941 INTERNET_SetLastError(res);
3945 /* send the request as ASCII, tack on the optional data */
3946 if (!lpOptional || redirected)
3947 dwOptionalLength = 0;
3948 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3949 NULL, 0, NULL, NULL );
3950 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3951 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3952 ascii_req, len, NULL, NULL );
3954 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3955 len = (len + dwOptionalLength - 1);
3957 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3959 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3960 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3962 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3963 HeapFree( GetProcessHeap(), 0, ascii_req );
3965 lpwhr->dwBytesWritten = dwOptionalLength;
3967 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3968 INTERNET_STATUS_REQUEST_SENT,
3969 &len, sizeof(DWORD));
3976 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3977 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3982 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3986 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3987 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3990 HTTP_ProcessCookies(lpwhr);
3992 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3994 dwBufferSize = sizeof(dwStatusCode);
3995 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3996 &dwStatusCode,&dwBufferSize,NULL))
3999 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
4001 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4002 dwBufferSize=sizeof(szNewLocation);
4003 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
4004 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
4006 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
4008 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
4009 lpwhr->lpszVerb = heap_strdupW(szGET);
4011 HTTP_DrainContent(lpwhr);
4012 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
4014 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4015 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4016 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
4019 HeapFree(GetProcessHeap(), 0, requestString);
4022 HeapFree( GetProcessHeap(), 0, new_url );
4027 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
4029 WCHAR szAuthValue[2048];
4031 if (dwStatusCode == HTTP_STATUS_DENIED)
4033 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
4035 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4037 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4039 lpwhr->lpHttpSession->lpszUserName,
4040 lpwhr->lpHttpSession->lpszPassword,
4048 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4051 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4053 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4054 &lpwhr->pProxyAuthInfo,
4055 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
4056 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
4072 WCHAR url[INTERNET_MAX_URL_LENGTH];
4073 WCHAR cacheFileName[MAX_PATH+1];
4076 b = HTTP_GetRequestURL(lpwhr, url);
4078 WARN("Could not get URL\n");
4082 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
4084 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
4085 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4086 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4087 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
4088 WARN("Could not create file: %u\n", GetLastError());
4089 lpwhr->hCacheFile = NULL;
4092 WARN("Could not create cache entry: %08x\n", GetLastError());
4098 HeapFree(GetProcessHeap(), 0, requestString);
4100 /* TODO: send notification for P3P header */
4102 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4106 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
4109 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4112 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4113 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4114 sizeof(INTERNET_ASYNC_RESULT));
4119 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4120 iar.dwError = INTERNET_GetLastError();
4122 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4123 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4124 sizeof(INTERNET_ASYNC_RESULT));
4129 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
4133 /***********************************************************************
4134 * HTTPSESSION_Destroy (internal)
4136 * Deallocate session handle
4139 static void HTTPSESSION_Destroy(object_header_t *hdr)
4141 http_session_t *lpwhs = (http_session_t*) hdr;
4143 TRACE("%p\n", lpwhs);
4145 WININET_Release(&lpwhs->lpAppInfo->hdr);
4147 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4148 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4149 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4150 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4151 HeapFree(GetProcessHeap(), 0, lpwhs);
4154 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4157 case INTERNET_OPTION_HANDLE_TYPE:
4158 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4160 if (*size < sizeof(ULONG))
4161 return ERROR_INSUFFICIENT_BUFFER;
4163 *size = sizeof(DWORD);
4164 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4165 return ERROR_SUCCESS;
4168 return INET_QueryOption(option, buffer, size, unicode);
4171 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4173 http_session_t *ses = (http_session_t*)hdr;
4176 case INTERNET_OPTION_USERNAME:
4178 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4179 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4180 return ERROR_SUCCESS;
4182 case INTERNET_OPTION_PASSWORD:
4184 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4185 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4186 return ERROR_SUCCESS;
4191 return ERROR_INTERNET_INVALID_OPTION;
4194 static const object_vtbl_t HTTPSESSIONVtbl = {
4195 HTTPSESSION_Destroy,
4197 HTTPSESSION_QueryOption,
4198 HTTPSESSION_SetOption,
4207 /***********************************************************************
4208 * HTTP_Connect (internal)
4210 * Create http session handle
4213 * HINTERNET a session handle on success
4217 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4218 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4219 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4220 DWORD dwInternalFlags)
4222 http_session_t *lpwhs = NULL;
4223 HINTERNET handle = NULL;
4227 if (!lpszServerName || !lpszServerName[0])
4229 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4233 assert( hIC->hdr.htype == WH_HINIT );
4235 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4238 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4243 * According to my tests. The name is not resolved until a request is sent
4246 lpwhs->hdr.htype = WH_HHTTPSESSION;
4247 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4248 lpwhs->hdr.dwFlags = dwFlags;
4249 lpwhs->hdr.dwContext = dwContext;
4250 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4251 lpwhs->hdr.refs = 1;
4252 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4254 WININET_AddRef( &hIC->hdr );
4255 lpwhs->lpAppInfo = hIC;
4256 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4258 handle = WININET_AllocHandle( &lpwhs->hdr );
4261 ERR("Failed to alloc handle\n");
4262 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4266 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4267 if(strchrW(hIC->lpszProxy, ' '))
4268 FIXME("Several proxies not implemented.\n");
4269 if(hIC->lpszProxyBypass)
4270 FIXME("Proxy bypass is ignored.\n");
4272 if (lpszServerName && lpszServerName[0])
4274 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4275 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4277 if (lpszUserName && lpszUserName[0])
4278 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4279 if (lpszPassword && lpszPassword[0])
4280 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4281 lpwhs->nServerPort = nServerPort;
4282 lpwhs->nHostPort = nServerPort;
4284 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4285 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4287 INTERNET_SendCallback(&hIC->hdr, dwContext,
4288 INTERNET_STATUS_HANDLE_CREATED, &handle,
4294 WININET_Release( &lpwhs->hdr );
4297 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4301 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4306 /***********************************************************************
4307 * HTTP_OpenConnection (internal)
4309 * Connect to a web server
4316 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4318 http_session_t *lpwhs;
4319 appinfo_t *hIC = NULL;
4320 char szaddr[INET6_ADDRSTRLEN];
4322 DWORD res = ERROR_SUCCESS;
4327 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4329 res = ERROR_INVALID_PARAMETER;
4333 if (NETCON_connected(&lpwhr->netConnection))
4335 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4337 lpwhs = lpwhr->lpHttpSession;
4339 hIC = lpwhs->lpAppInfo;
4340 switch (lpwhs->socketAddress.ss_family)
4343 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4346 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4349 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4350 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4352 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4353 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4354 INTERNET_STATUS_CONNECTING_TO_SERVER,
4358 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4359 if (res != ERROR_SUCCESS)
4361 WARN("Socket creation failed: %u\n", res);
4365 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4367 if(res != ERROR_SUCCESS)
4370 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4372 /* Note: we differ from Microsoft's WinINet here. they seem to have
4373 * a bug that causes no status callbacks to be sent when starting
4374 * a tunnel to a proxy server using the CONNECT verb. i believe our
4375 * behaviour to be more correct and to not cause any incompatibilities
4376 * because using a secure connection through a proxy server is a rare
4377 * case that would be hard for anyone to depend on */
4378 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4381 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4382 if(res != ERROR_SUCCESS)
4384 WARN("Couldn't connect securely to host\n");
4389 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4390 INTERNET_STATUS_CONNECTED_TO_SERVER,
4391 szaddr, strlen(szaddr)+1);
4394 lpwhr->read_pos = lpwhr->read_size = 0;
4395 lpwhr->read_chunked = FALSE;
4397 TRACE("%d <--\n", res);
4402 /***********************************************************************
4403 * HTTP_clear_response_headers (internal)
4405 * clear out any old response headers
4407 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4411 for( i=0; i<lpwhr->nCustHeaders; i++)
4413 if( !lpwhr->pCustHeaders[i].lpszField )
4415 if( !lpwhr->pCustHeaders[i].lpszValue )
4417 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4419 HTTP_DeleteCustomHeader( lpwhr, i );
4424 /***********************************************************************
4425 * HTTP_GetResponseHeaders (internal)
4427 * Read server response
4434 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4437 WCHAR buffer[MAX_REPLY_LEN];
4438 DWORD buflen = MAX_REPLY_LEN;
4439 BOOL bSuccess = FALSE;
4441 char bufferA[MAX_REPLY_LEN];
4442 LPWSTR status_code = NULL, status_text = NULL;
4443 DWORD cchMaxRawHeaders = 1024;
4444 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4446 DWORD cchRawHeaders = 0;
4447 BOOL codeHundred = FALSE;
4451 /* clear old response headers (eg. from a redirect response) */
4452 if (clear) HTTP_clear_response_headers( lpwhr );
4454 if (!NETCON_connected(&lpwhr->netConnection))
4458 static const WCHAR szHundred[] = {'1','0','0',0};
4460 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4462 buflen = MAX_REPLY_LEN;
4463 if (!read_line(lpwhr, bufferA, &buflen))
4466 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4467 /* check is this a status code line? */
4468 if (!strncmpW(buffer, g_szHttp1_0, 4))
4470 /* split the version from the status code */
4471 status_code = strchrW( buffer, ' ' );
4476 /* split the status code from the status text */
4477 status_text = strchrW( status_code, ' ' );
4482 TRACE("version [%s] status code [%s] status text [%s]\n",
4483 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4485 codeHundred = (!strcmpW(status_code, szHundred));
4487 else if (!codeHundred)
4489 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4492 } while (codeHundred);
4494 /* Add status code */
4495 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4496 HTTP_ADDHDR_FLAG_REPLACE);
4498 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4499 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4501 lpwhr->lpszVersion = heap_strdupW(buffer);
4502 lpwhr->lpszStatusText = heap_strdupW(status_text);
4504 /* Restore the spaces */
4505 *(status_code-1) = ' ';
4506 *(status_text-1) = ' ';
4508 /* regenerate raw headers */
4509 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4510 cchMaxRawHeaders *= 2;
4511 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4512 if (temp == NULL) goto lend;
4513 lpszRawHeaders = temp;
4514 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4515 cchRawHeaders += (buflen-1);
4516 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4517 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4518 lpszRawHeaders[cchRawHeaders] = '\0';
4520 /* Parse each response line */
4523 buflen = MAX_REPLY_LEN;
4524 if (read_line(lpwhr, bufferA, &buflen))
4526 LPWSTR * pFieldAndValue;
4528 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4530 if (!bufferA[0]) break;
4531 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4533 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4536 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4537 cchMaxRawHeaders *= 2;
4538 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4539 if (temp == NULL) goto lend;
4540 lpszRawHeaders = temp;
4541 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4542 cchRawHeaders += (buflen-1);
4543 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4544 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4545 lpszRawHeaders[cchRawHeaders] = '\0';
4547 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4548 HTTP_ADDREQ_FLAG_ADD );
4550 HTTP_FreeTokens(pFieldAndValue);
4561 /* make sure the response header is terminated with an empty line. Some apps really
4562 truly care about that empty line being there for some reason. Just add it to the
4564 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4566 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4567 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4568 if (temp == NULL) goto lend;
4569 lpszRawHeaders = temp;
4572 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4574 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4575 lpwhr->lpszRawHeaders = lpszRawHeaders;
4576 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4586 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4591 /***********************************************************************
4592 * HTTP_InterpretHttpHeader (internal)
4594 * Parse server response
4598 * Pointer to array of field, value, NULL on success.
4601 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4603 LPWSTR * pTokenPair;
4607 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4609 pszColon = strchrW(buffer, ':');
4610 /* must have two tokens */
4613 HTTP_FreeTokens(pTokenPair);
4615 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4619 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4622 HTTP_FreeTokens(pTokenPair);
4625 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4626 pTokenPair[0][pszColon - buffer] = '\0';
4630 len = strlenW(pszColon);
4631 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4634 HTTP_FreeTokens(pTokenPair);
4637 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4639 strip_spaces(pTokenPair[0]);
4640 strip_spaces(pTokenPair[1]);
4642 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4646 /***********************************************************************
4647 * HTTP_ProcessHeader (internal)
4649 * Stuff header into header tables according to <dwModifier>
4653 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4655 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4657 LPHTTPHEADERW lphttpHdr = NULL;
4658 BOOL bSuccess = FALSE;
4660 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4662 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4664 /* REPLACE wins out over ADD */
4665 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4666 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4668 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4671 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4675 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4679 lphttpHdr = &lpwhr->pCustHeaders[index];
4685 hdr.lpszField = (LPWSTR)field;
4686 hdr.lpszValue = (LPWSTR)value;
4687 hdr.wFlags = hdr.wCount = 0;
4689 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4690 hdr.wFlags |= HDR_ISREQUEST;
4692 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4694 /* no value to delete */
4697 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4698 lphttpHdr->wFlags |= HDR_ISREQUEST;
4700 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4702 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4704 HTTP_DeleteCustomHeader( lpwhr, index );
4710 hdr.lpszField = (LPWSTR)field;
4711 hdr.lpszValue = (LPWSTR)value;
4712 hdr.wFlags = hdr.wCount = 0;
4714 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4715 hdr.wFlags |= HDR_ISREQUEST;
4717 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4722 else if (dwModifier & COALESCEFLAGS)
4727 INT origlen = strlenW(lphttpHdr->lpszValue);
4728 INT valuelen = strlenW(value);
4730 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4733 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4735 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4738 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4741 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4743 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4746 lphttpHdr->lpszValue = lpsztmp;
4747 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4750 lphttpHdr->lpszValue[origlen] = ch;
4752 lphttpHdr->lpszValue[origlen] = ' ';
4756 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4757 lphttpHdr->lpszValue[len] = '\0';
4762 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4763 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4766 TRACE("<-- %d\n",bSuccess);
4771 /***********************************************************************
4772 * HTTP_FinishedReading (internal)
4774 * Called when all content from server has been read by client.
4777 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4779 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4786 HTTPREQ_CloseConnection(&lpwhr->hdr);
4789 /* FIXME: store data in the URL cache here */
4795 /***********************************************************************
4796 * HTTP_GetCustomHeaderIndex (internal)
4798 * Return index of custom header from header array
4801 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4802 int requested_index, BOOL request_only)
4806 TRACE("%s\n", debugstr_w(lpszField));
4808 for (index = 0; index < lpwhr->nCustHeaders; index++)
4810 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4813 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4816 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4819 if (requested_index == 0)
4824 if (index >= lpwhr->nCustHeaders)
4827 TRACE("Return: %d\n", index);
4832 /***********************************************************************
4833 * HTTP_InsertCustomHeader (internal)
4835 * Insert header into array
4838 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4841 LPHTTPHEADERW lph = NULL;
4844 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4845 count = lpwhr->nCustHeaders + 1;
4847 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4849 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4853 lpwhr->pCustHeaders = lph;
4854 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4855 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4856 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4857 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4858 lpwhr->nCustHeaders++;
4863 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4870 /***********************************************************************
4871 * HTTP_DeleteCustomHeader (internal)
4873 * Delete header from array
4874 * If this function is called, the indexs may change.
4876 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4878 if( lpwhr->nCustHeaders <= 0 )
4880 if( index >= lpwhr->nCustHeaders )
4882 lpwhr->nCustHeaders--;
4884 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4885 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4887 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4888 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4889 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4895 /***********************************************************************
4896 * HTTP_VerifyValidHeader (internal)
4898 * Verify the given header is not invalid for the given http request
4901 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4903 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4904 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4910 /***********************************************************************
4911 * IsHostInProxyBypassList (@)
4916 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4918 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);