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 szOK[] = {'O','K',0};
77 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf[] = {'\r','\n', 0};
87 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge[] = { 'A','g','e',0 };
93 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 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 };
105 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 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 };
132 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia[] = { 'V','i','a',0 };
137 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 struct gzip_stream_t {
182 typedef struct _basicAuthorizationData
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
190 } basicAuthorizationData;
192 typedef struct _authorizationData
206 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
207 static struct list authorizationCache = LIST_INIT(authorizationCache);
209 static CRITICAL_SECTION authcache_cs;
210 static CRITICAL_SECTION_DEBUG critsect_debug =
213 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
214 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
216 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
218 static DWORD HTTP_OpenConnection(http_request_t *req);
219 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
220 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
221 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
222 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
223 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
224 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
225 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
226 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
227 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
228 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
229 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
230 static void HTTP_DrainContent(http_request_t *req);
231 static BOOL HTTP_FinishedReading(http_request_t *req);
233 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
236 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
237 if (HeaderIndex == -1)
240 return &req->pCustHeaders[HeaderIndex];
245 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
247 return HeapAlloc(GetProcessHeap(), 0, items*size);
250 static void wininet_zfree(voidpf opaque, voidpf address)
252 HeapFree(GetProcessHeap(), 0, address);
255 static void init_gzip_stream(http_request_t *req)
257 gzip_stream_t *gzip_stream;
260 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
261 gzip_stream->zstream.zalloc = wininet_zalloc;
262 gzip_stream->zstream.zfree = wininet_zfree;
263 gzip_stream->zstream.opaque = NULL;
264 gzip_stream->zstream.next_in = NULL;
265 gzip_stream->zstream.avail_in = 0;
266 gzip_stream->zstream.next_out = NULL;
267 gzip_stream->zstream.avail_out = 0;
268 gzip_stream->buf_pos = 0;
269 gzip_stream->buf_size = 0;
270 gzip_stream->end_of_data = FALSE;
272 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
274 ERR("inflateInit failed: %d\n", zres);
275 HeapFree(GetProcessHeap(), 0, gzip_stream);
279 req->gzip_stream = gzip_stream;
281 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
283 HTTP_DeleteCustomHeader(req, index);
288 static void init_gzip_stream(http_request_t *req)
290 ERR("gzip stream not supported, missing zlib.\n");
295 /* set the request content length based on the headers */
296 static DWORD set_content_length( http_request_t *lpwhr )
298 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
302 size = sizeof(lpwhr->dwContentLength);
303 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
304 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
305 lpwhr->dwContentLength = ~0u;
307 size = sizeof(encoding);
308 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
309 !strcmpiW(encoding, szChunked))
311 lpwhr->dwContentLength = ~0u;
312 lpwhr->read_chunked = TRUE;
315 if(lpwhr->decoding) {
318 static const WCHAR gzipW[] = {'g','z','i','p',0};
320 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
321 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
322 init_gzip_stream(lpwhr);
325 return lpwhr->dwContentLength;
328 /***********************************************************************
329 * HTTP_Tokenize (internal)
331 * Tokenize a string, allocating memory for the tokens.
333 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
335 LPWSTR * token_array;
342 /* empty string has no tokens */
346 for (i = 0; string[i]; i++)
348 if (!strncmpW(string+i, token_string, strlenW(token_string)))
352 /* we want to skip over separators, but not the null terminator */
353 for (j = 0; j < strlenW(token_string) - 1; j++)
361 /* add 1 for terminating NULL */
362 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
363 token_array[tokens] = NULL;
366 for (i = 0; i < tokens; i++)
369 next_token = strstrW(string, token_string);
370 if (!next_token) next_token = string+strlenW(string);
371 len = next_token - string;
372 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
373 memcpy(token_array[i], string, len*sizeof(WCHAR));
374 token_array[i][len] = '\0';
375 string = next_token+strlenW(token_string);
380 /***********************************************************************
381 * HTTP_FreeTokens (internal)
383 * Frees memory returned from HTTP_Tokenize.
385 static void HTTP_FreeTokens(LPWSTR * token_array)
388 for (i = 0; token_array[i]; i++)
389 HeapFree(GetProcessHeap(), 0, token_array[i]);
390 HeapFree(GetProcessHeap(), 0, token_array);
393 static void HTTP_FixURL(http_request_t *lpwhr)
395 static const WCHAR szSlash[] = { '/',0 };
396 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
398 /* If we don't have a path we set it to root */
399 if (NULL == lpwhr->lpszPath)
400 lpwhr->lpszPath = heap_strdupW(szSlash);
401 else /* remove \r and \n*/
403 int nLen = strlenW(lpwhr->lpszPath);
404 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
407 lpwhr->lpszPath[nLen]='\0';
409 /* Replace '\' with '/' */
412 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
416 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
417 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
418 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
420 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
421 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
423 strcpyW(fixurl + 1, lpwhr->lpszPath);
424 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
425 lpwhr->lpszPath = fixurl;
429 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
431 LPWSTR requestString;
437 static const WCHAR szSpace[] = { ' ',0 };
438 static const WCHAR szColon[] = { ':',' ',0 };
439 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
441 /* allocate space for an array of all the string pointers to be added */
442 len = (lpwhr->nCustHeaders)*4 + 10;
443 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
445 /* add the verb, path and HTTP version string */
453 /* Append custom request headers */
454 for (i = 0; i < lpwhr->nCustHeaders; i++)
456 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
459 req[n++] = lpwhr->pCustHeaders[i].lpszField;
461 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
463 TRACE("Adding custom header %s (%s)\n",
464 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
465 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
470 ERR("oops. buffer overrun\n");
473 requestString = HTTP_build_req( req, 4 );
474 HeapFree( GetProcessHeap(), 0, req );
477 * Set (header) termination string for request
478 * Make sure there's exactly two new lines at the end of the request
480 p = &requestString[strlenW(requestString)-1];
481 while ( (*p == '\n') || (*p == '\r') )
483 strcpyW( p+1, sztwocrlf );
485 return requestString;
488 static void HTTP_ProcessCookies( http_request_t *lpwhr )
492 LPHTTPHEADERW setCookieHeader;
494 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
496 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
498 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
501 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
505 Host = HTTP_GetHeader(lpwhr, hostW);
506 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
507 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
508 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
509 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
511 HeapFree(GetProcessHeap(), 0, buf_url);
517 static void strip_spaces(LPWSTR start)
522 while (*str == ' ' && *str != '\0')
526 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
528 end = start + strlenW(start) - 1;
529 while (end >= start && *end == ' ')
536 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
538 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
539 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
541 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
542 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
543 if (is_basic && pszRealm)
546 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
550 token = strchrW(ptr,'=');
554 while (*realm == ' ' && *realm != '\0')
556 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
557 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
560 while (*token == ' ' && *token != '\0')
564 *pszRealm = heap_strdupW(token);
565 strip_spaces(*pszRealm);
572 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
574 if (!authinfo) return;
576 if (SecIsValidHandle(&authinfo->ctx))
577 DeleteSecurityContext(&authinfo->ctx);
578 if (SecIsValidHandle(&authinfo->cred))
579 FreeCredentialsHandle(&authinfo->cred);
581 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
582 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
583 HeapFree(GetProcessHeap(), 0, authinfo);
586 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
588 basicAuthorizationData *ad;
591 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
593 EnterCriticalSection(&authcache_cs);
594 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
596 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
598 TRACE("Authorization found in cache\n");
599 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
600 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
601 rc = ad->AuthorizationLen;
605 LeaveCriticalSection(&authcache_cs);
609 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
612 basicAuthorizationData* ad = NULL;
614 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
616 EnterCriticalSection(&authcache_cs);
617 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
619 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
620 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
629 TRACE("Found match in cache, replacing\n");
630 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
631 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
632 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
633 ad->AuthorizationLen = auth_data_len;
637 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
638 ad->lpszwHost = heap_strdupW(host);
639 ad->lpszwRealm = heap_strdupW(realm);
640 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
641 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
642 ad->AuthorizationLen = auth_data_len;
643 list_add_head(&basicAuthorizationCache,&ad->entry);
644 TRACE("authorization cached\n");
646 LeaveCriticalSection(&authcache_cs);
649 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
650 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
652 authorizationData *ad;
654 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
656 EnterCriticalSection(&authcache_cs);
657 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
658 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
659 TRACE("Authorization found in cache\n");
661 nt_auth_identity->User = heap_strdupW(ad->user);
662 nt_auth_identity->Password = heap_strdupW(ad->password);
663 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
664 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
665 (!nt_auth_identity->Domain && ad->domain_len)) {
666 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
667 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
668 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
672 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
673 nt_auth_identity->UserLength = ad->user_len;
674 nt_auth_identity->PasswordLength = ad->password_len;
675 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
676 nt_auth_identity->DomainLength = ad->domain_len;
677 LeaveCriticalSection(&authcache_cs);
681 LeaveCriticalSection(&authcache_cs);
686 static void cache_authorization(LPWSTR host, LPWSTR scheme,
687 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
689 authorizationData *ad;
692 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
694 EnterCriticalSection(&authcache_cs);
695 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
696 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
702 HeapFree(GetProcessHeap(), 0, ad->user);
703 HeapFree(GetProcessHeap(), 0, ad->password);
704 HeapFree(GetProcessHeap(), 0, ad->domain);
706 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
708 LeaveCriticalSection(&authcache_cs);
712 ad->host = heap_strdupW(host);
713 ad->scheme = heap_strdupW(scheme);
714 list_add_head(&authorizationCache, &ad->entry);
717 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
718 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
719 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
720 ad->user_len = nt_auth_identity->UserLength;
721 ad->password_len = nt_auth_identity->PasswordLength;
722 ad->domain_len = nt_auth_identity->DomainLength;
724 if(!ad->host || !ad->scheme || !ad->user || !ad->password
725 || (nt_auth_identity->Domain && !ad->domain)) {
726 HeapFree(GetProcessHeap(), 0, ad->host);
727 HeapFree(GetProcessHeap(), 0, ad->scheme);
728 HeapFree(GetProcessHeap(), 0, ad->user);
729 HeapFree(GetProcessHeap(), 0, ad->password);
730 HeapFree(GetProcessHeap(), 0, ad->domain);
731 list_remove(&ad->entry);
732 HeapFree(GetProcessHeap(), 0, ad);
735 LeaveCriticalSection(&authcache_cs);
738 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
739 struct HttpAuthInfo **ppAuthInfo,
740 LPWSTR domain_and_username, LPWSTR password,
743 SECURITY_STATUS sec_status;
744 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
746 LPWSTR szRealm = NULL;
748 TRACE("%s\n", debugstr_w(pszAuthValue));
755 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
759 SecInvalidateHandle(&pAuthInfo->cred);
760 SecInvalidateHandle(&pAuthInfo->ctx);
761 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
763 pAuthInfo->auth_data = NULL;
764 pAuthInfo->auth_data_len = 0;
765 pAuthInfo->finished = FALSE;
767 if (is_basic_auth_value(pszAuthValue,NULL))
769 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
770 pAuthInfo->scheme = heap_strdupW(szBasic);
771 if (!pAuthInfo->scheme)
773 HeapFree(GetProcessHeap(), 0, pAuthInfo);
780 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
782 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
783 if (!pAuthInfo->scheme)
785 HeapFree(GetProcessHeap(), 0, pAuthInfo);
789 if (domain_and_username)
791 WCHAR *user = strchrW(domain_and_username, '\\');
792 WCHAR *domain = domain_and_username;
794 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
796 pAuthData = &nt_auth_identity;
801 user = domain_and_username;
805 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
806 nt_auth_identity.User = user;
807 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
808 nt_auth_identity.Domain = domain;
809 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
810 nt_auth_identity.Password = password;
811 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
813 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
815 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
816 pAuthData = &nt_auth_identity;
818 /* use default credentials */
821 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
822 SECPKG_CRED_OUTBOUND, NULL,
824 NULL, &pAuthInfo->cred,
827 if(pAuthData && !domain_and_username) {
828 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
829 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
830 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
833 if (sec_status == SEC_E_OK)
835 PSecPkgInfoW sec_pkg_info;
836 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
837 if (sec_status == SEC_E_OK)
839 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
840 FreeContextBuffer(sec_pkg_info);
843 if (sec_status != SEC_E_OK)
845 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
846 debugstr_w(pAuthInfo->scheme), sec_status);
847 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
848 HeapFree(GetProcessHeap(), 0, pAuthInfo);
852 *ppAuthInfo = pAuthInfo;
854 else if (pAuthInfo->finished)
857 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
858 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
860 ERR("authentication scheme changed from %s to %s\n",
861 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
865 if (is_basic_auth_value(pszAuthValue,&szRealm))
869 char *auth_data = NULL;
870 UINT auth_data_len = 0;
872 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
874 if (!domain_and_username)
877 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
878 if (auth_data_len == 0)
880 HeapFree(GetProcessHeap(),0,szRealm);
886 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
887 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
889 /* length includes a nul terminator, which will be re-used for the ':' */
890 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
893 HeapFree(GetProcessHeap(),0,szRealm);
897 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
898 auth_data[userlen] = ':';
899 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
900 auth_data_len = userlen + 1 + passlen;
902 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
905 pAuthInfo->auth_data = auth_data;
906 pAuthInfo->auth_data_len = auth_data_len;
907 pAuthInfo->finished = TRUE;
908 HeapFree(GetProcessHeap(),0,szRealm);
915 SecBufferDesc out_desc, in_desc;
917 unsigned char *buffer;
918 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
919 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
921 in.BufferType = SECBUFFER_TOKEN;
925 in_desc.ulVersion = 0;
926 in_desc.cBuffers = 1;
927 in_desc.pBuffers = ∈
929 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
930 if (*pszAuthData == ' ')
933 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
934 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
935 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
938 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
940 out.BufferType = SECBUFFER_TOKEN;
941 out.cbBuffer = pAuthInfo->max_token;
942 out.pvBuffer = buffer;
944 out_desc.ulVersion = 0;
945 out_desc.cBuffers = 1;
946 out_desc.pBuffers = &out;
948 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
949 first ? NULL : &pAuthInfo->ctx,
950 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
951 context_req, 0, SECURITY_NETWORK_DREP,
952 in.pvBuffer ? &in_desc : NULL,
953 0, &pAuthInfo->ctx, &out_desc,
954 &pAuthInfo->attr, &pAuthInfo->exp);
955 if (sec_status == SEC_E_OK)
957 pAuthInfo->finished = TRUE;
958 pAuthInfo->auth_data = out.pvBuffer;
959 pAuthInfo->auth_data_len = out.cbBuffer;
960 TRACE("sending last auth packet\n");
962 else if (sec_status == SEC_I_CONTINUE_NEEDED)
964 pAuthInfo->auth_data = out.pvBuffer;
965 pAuthInfo->auth_data_len = out.cbBuffer;
966 TRACE("sending next auth packet\n");
970 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
971 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
972 destroy_authinfo(pAuthInfo);
981 /***********************************************************************
982 * HTTP_HttpAddRequestHeadersW (internal)
984 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
985 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
990 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
992 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
994 if( dwHeaderLength == ~0U )
995 len = strlenW(lpszHeader);
997 len = dwHeaderLength;
998 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
999 lstrcpynW( buffer, lpszHeader, len + 1);
1005 LPWSTR * pFieldAndValue;
1007 lpszEnd = lpszStart;
1009 while (*lpszEnd != '\0')
1011 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1016 if (*lpszStart == '\0')
1019 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1022 lpszEnd++; /* Jump over newline */
1024 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1025 if (*lpszStart == '\0')
1027 /* Skip 0-length headers */
1028 lpszStart = lpszEnd;
1029 res = ERROR_SUCCESS;
1032 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1035 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
1036 if (res == ERROR_SUCCESS)
1037 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
1038 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1039 HTTP_FreeTokens(pFieldAndValue);
1042 lpszStart = lpszEnd;
1043 } while (res == ERROR_SUCCESS);
1045 HeapFree(GetProcessHeap(), 0, buffer);
1050 /***********************************************************************
1051 * HttpAddRequestHeadersW (WININET.@)
1053 * Adds one or more HTTP header to the request handler
1056 * On Windows if dwHeaderLength includes the trailing '\0', then
1057 * HttpAddRequestHeadersW() adds it too. However this results in an
1058 * invalid Http header which is rejected by some servers so we probably
1059 * don't need to match Windows on that point.
1066 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1067 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1069 http_request_t *lpwhr;
1070 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1072 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1077 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
1078 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
1079 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
1081 WININET_Release( &lpwhr->hdr );
1083 if(res != ERROR_SUCCESS)
1085 return res == ERROR_SUCCESS;
1088 /***********************************************************************
1089 * HttpAddRequestHeadersA (WININET.@)
1091 * Adds one or more HTTP header to the request handler
1098 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1099 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1105 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1107 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1108 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1109 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1110 if( dwHeaderLength != ~0U )
1111 dwHeaderLength = len;
1113 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1115 HeapFree( GetProcessHeap(), 0, hdr );
1120 /***********************************************************************
1121 * HttpOpenRequestA (WININET.@)
1123 * Open a HTTP request handle
1126 * HINTERNET a HTTP request handle on success
1130 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1131 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1132 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1133 DWORD dwFlags, DWORD_PTR dwContext)
1135 LPWSTR szVerb = NULL, szObjectName = NULL;
1136 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1137 INT acceptTypesCount;
1138 HINTERNET rc = FALSE;
1141 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1142 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1143 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1144 dwFlags, dwContext);
1148 szVerb = heap_strdupAtoW(lpszVerb);
1155 szObjectName = heap_strdupAtoW(lpszObjectName);
1156 if ( !szObjectName )
1162 szVersion = heap_strdupAtoW(lpszVersion);
1169 szReferrer = heap_strdupAtoW(lpszReferrer);
1174 if (lpszAcceptTypes)
1176 acceptTypesCount = 0;
1177 types = lpszAcceptTypes;
1182 /* find out how many there are */
1183 if (*types && **types)
1185 TRACE("accept type: %s\n", debugstr_a(*types));
1191 WARN("invalid accept type pointer\n");
1196 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1197 if (!szAcceptTypes) goto end;
1199 acceptTypesCount = 0;
1200 types = lpszAcceptTypes;
1205 if (*types && **types)
1206 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1210 /* ignore invalid pointer */
1215 szAcceptTypes[acceptTypesCount] = NULL;
1218 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1219 szVersion, szReferrer,
1220 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1225 acceptTypesCount = 0;
1226 while (szAcceptTypes[acceptTypesCount])
1228 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1231 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1233 HeapFree(GetProcessHeap(), 0, szReferrer);
1234 HeapFree(GetProcessHeap(), 0, szVersion);
1235 HeapFree(GetProcessHeap(), 0, szObjectName);
1236 HeapFree(GetProcessHeap(), 0, szVerb);
1241 /***********************************************************************
1244 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1247 static const CHAR HTTP_Base64Enc[] =
1248 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1252 /* first 6 bits, all from bin[0] */
1253 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1254 x = (bin[0] & 3) << 4;
1256 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1259 base64[n++] = HTTP_Base64Enc[x];
1264 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1265 x = ( bin[1] & 0x0f ) << 2;
1267 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1270 base64[n++] = HTTP_Base64Enc[x];
1274 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1276 /* last 6 bits, all from bin [2] */
1277 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1285 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1286 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1287 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1288 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1289 static const signed char HTTP_Base64Dec[256] =
1291 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1292 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1293 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1294 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1295 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1296 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1297 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1298 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1299 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1300 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1301 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1302 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1303 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1304 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1305 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1306 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1307 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1308 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1309 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1310 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1311 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1312 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1313 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1314 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1315 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1316 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1320 /***********************************************************************
1323 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1331 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1332 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1333 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1334 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1336 WARN("invalid base64: %s\n", debugstr_w(base64));
1340 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1343 if ((base64[2] == '=') && (base64[3] == '='))
1345 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1346 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1348 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1352 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1355 if (base64[3] == '=')
1357 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1358 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1360 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1364 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1373 /***********************************************************************
1374 * HTTP_InsertAuthorization
1376 * Insert or delete the authorization field in the request header.
1378 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1382 static const WCHAR wszSpace[] = {' ',0};
1383 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1385 WCHAR *authorization = NULL;
1387 if (pAuthInfo->auth_data_len)
1389 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1390 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1391 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1395 strcpyW(authorization, pAuthInfo->scheme);
1396 strcatW(authorization, wszSpace);
1397 HTTP_EncodeBase64(pAuthInfo->auth_data,
1398 pAuthInfo->auth_data_len,
1399 authorization+strlenW(authorization));
1401 /* clear the data as it isn't valid now that it has been sent to the
1402 * server, unless it's Basic authentication which doesn't do
1403 * connection tracking */
1404 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1406 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1407 pAuthInfo->auth_data = NULL;
1408 pAuthInfo->auth_data_len = 0;
1412 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1414 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1416 HeapFree(GetProcessHeap(), 0, authorization);
1421 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1423 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1426 size = sizeof(new_location);
1427 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1429 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1430 strcpyW( url, new_location );
1434 static const WCHAR slash[] = { '/',0 };
1435 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1436 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1437 http_session_t *session = req->lpHttpSession;
1439 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1440 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1442 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1444 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1445 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1447 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1448 if (req->lpszPath[0] != '/') strcatW( url, slash );
1449 strcatW( url, req->lpszPath );
1451 TRACE("url=%s\n", debugstr_w(url));
1455 /***********************************************************************
1456 * HTTP_DealWithProxy
1458 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1460 WCHAR buf[MAXHOSTNAME];
1461 WCHAR protoProxy[MAXHOSTNAME + 15];
1462 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1463 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1464 static WCHAR szNul[] = { 0 };
1465 URL_COMPONENTSW UrlComponents;
1466 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1467 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1468 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1470 memset( &UrlComponents, 0, sizeof UrlComponents );
1471 UrlComponents.dwStructSize = sizeof UrlComponents;
1472 UrlComponents.lpszHostName = buf;
1473 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1475 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1477 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1478 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1479 sprintfW(proxy, szFormat, protoProxy);
1481 strcpyW(proxy, protoProxy);
1482 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1484 if( UrlComponents.dwHostNameLength == 0 )
1487 if( !lpwhr->lpszPath )
1488 lpwhr->lpszPath = szNul;
1490 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1491 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1493 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1494 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1495 lpwhs->nServerPort = UrlComponents.nPort;
1497 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1501 #ifndef INET6_ADDRSTRLEN
1502 #define INET6_ADDRSTRLEN 46
1505 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1507 char szaddr[INET6_ADDRSTRLEN];
1508 http_session_t *lpwhs = lpwhr->lpHttpSession;
1511 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1512 INTERNET_STATUS_RESOLVING_NAME,
1513 lpwhs->lpszServerName,
1514 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1516 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1517 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1518 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1519 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1521 switch (lpwhs->socketAddress.ss_family)
1524 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1527 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1530 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1531 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1533 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1534 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1535 INTERNET_STATUS_NAME_RESOLVED,
1536 szaddr, strlen(szaddr)+1);
1538 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1539 return ERROR_SUCCESS;
1542 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1544 LPHTTPHEADERW host_header;
1546 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1548 host_header = HTTP_GetHeader(req, hostW);
1552 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1557 /***********************************************************************
1558 * HTTPREQ_Destroy (internal)
1560 * Deallocate request handle
1563 static void HTTPREQ_Destroy(object_header_t *hdr)
1565 http_request_t *lpwhr = (http_request_t*) hdr;
1570 if(lpwhr->hCacheFile) {
1571 WCHAR url[INTERNET_MAX_URL_LENGTH];
1574 CloseHandle(lpwhr->hCacheFile);
1576 memset(&ft, 0, sizeof(FILETIME));
1577 if(HTTP_GetRequestURL(lpwhr, url)) {
1578 CommitUrlCacheEntryW(url, lpwhr->lpszCacheFile, ft, ft,
1579 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1585 DeleteCriticalSection( &lpwhr->read_section );
1586 WININET_Release(&lpwhr->lpHttpSession->hdr);
1588 destroy_authinfo(lpwhr->pAuthInfo);
1589 destroy_authinfo(lpwhr->pProxyAuthInfo);
1591 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1592 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1593 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1594 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1595 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1597 for (i = 0; i < lpwhr->nCustHeaders; i++)
1599 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1600 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1604 if(lpwhr->gzip_stream) {
1605 if(!lpwhr->gzip_stream->end_of_data)
1606 inflateEnd(&lpwhr->gzip_stream->zstream);
1607 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1611 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1612 HeapFree(GetProcessHeap(), 0, lpwhr);
1615 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1617 http_request_t *lpwhr = (http_request_t*) hdr;
1619 TRACE("%p\n",lpwhr);
1621 if (!NETCON_connected(&lpwhr->netConnection))
1624 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1625 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1627 NETCON_close(&lpwhr->netConnection);
1629 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1630 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1633 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1635 WCHAR szVersion[10];
1636 WCHAR szConnectionResponse[20];
1637 DWORD dwBufferSize = sizeof(szVersion);
1638 BOOL keepalive = FALSE;
1640 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1641 * the connection is keep-alive by default */
1642 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1643 && !strcmpiW(szVersion, g_szHttp1_1))
1648 dwBufferSize = sizeof(szConnectionResponse);
1649 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1650 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1652 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1658 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1660 http_request_t *req = (http_request_t*)hdr;
1663 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1665 http_session_t *lpwhs = req->lpHttpSession;
1666 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1668 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1670 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1671 return ERROR_INSUFFICIENT_BUFFER;
1672 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1673 /* FIXME: can't get a SOCKET from our connection since we don't use
1677 /* FIXME: get source port from req->netConnection */
1678 info->SourcePort = 0;
1679 info->DestPort = lpwhs->nHostPort;
1681 if (HTTP_KeepAlive(req))
1682 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1683 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1684 info->Flags |= IDSI_FLAG_PROXY;
1685 if (req->netConnection.useSSL)
1686 info->Flags |= IDSI_FLAG_SECURE;
1688 return ERROR_SUCCESS;
1691 case INTERNET_OPTION_SECURITY_FLAGS:
1693 http_session_t *lpwhs;
1694 lpwhs = req->lpHttpSession;
1696 if (*size < sizeof(ULONG))
1697 return ERROR_INSUFFICIENT_BUFFER;
1699 *size = sizeof(DWORD);
1700 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1701 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1703 *(DWORD*)buffer = 0;
1704 *(DWORD *)buffer |= req->netConnection.security_flags;
1705 /* FIXME: set connection cipher strength (SECURITY_FLAG_STRENGTH_*) */
1706 return ERROR_SUCCESS;
1709 case INTERNET_OPTION_HANDLE_TYPE:
1710 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1712 if (*size < sizeof(ULONG))
1713 return ERROR_INSUFFICIENT_BUFFER;
1715 *size = sizeof(DWORD);
1716 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1717 return ERROR_SUCCESS;
1719 case INTERNET_OPTION_URL: {
1720 WCHAR url[INTERNET_MAX_URL_LENGTH];
1725 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1727 TRACE("INTERNET_OPTION_URL\n");
1729 host = HTTP_GetHeader(req, hostW);
1730 strcpyW(url, httpW);
1731 strcatW(url, host->lpszValue);
1732 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1734 strcatW(url, req->lpszPath);
1736 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1739 len = (strlenW(url)+1) * sizeof(WCHAR);
1741 return ERROR_INSUFFICIENT_BUFFER;
1744 strcpyW(buffer, url);
1745 return ERROR_SUCCESS;
1747 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1749 return ERROR_INSUFFICIENT_BUFFER;
1752 return ERROR_SUCCESS;
1756 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1757 INTERNET_CACHE_ENTRY_INFOW *info;
1758 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1759 WCHAR url[INTERNET_MAX_URL_LENGTH];
1760 DWORD nbytes, error;
1763 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1765 if (*size < sizeof(*ts))
1767 *size = sizeof(*ts);
1768 return ERROR_INSUFFICIENT_BUFFER;
1771 HTTP_GetRequestURL(req, url);
1772 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1773 error = GetLastError();
1774 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1776 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1777 return ERROR_OUTOFMEMORY;
1779 GetUrlCacheEntryInfoW(url, info, &nbytes);
1781 ts->ftExpires = info->ExpireTime;
1782 ts->ftLastModified = info->LastModifiedTime;
1784 HeapFree(GetProcessHeap(), 0, info);
1785 *size = sizeof(*ts);
1786 return ERROR_SUCCESS;
1791 case INTERNET_OPTION_DATAFILE_NAME: {
1794 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1796 if(!req->lpszCacheFile) {
1798 return ERROR_INTERNET_ITEM_NOT_FOUND;
1802 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1803 if(*size < req_size)
1804 return ERROR_INSUFFICIENT_BUFFER;
1807 memcpy(buffer, req->lpszCacheFile, *size);
1808 return ERROR_SUCCESS;
1810 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1811 if (req_size > *size)
1812 return ERROR_INSUFFICIENT_BUFFER;
1814 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1815 -1, buffer, *size, NULL, NULL);
1816 return ERROR_SUCCESS;
1820 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1821 PCCERT_CONTEXT context;
1823 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1824 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1825 return ERROR_INSUFFICIENT_BUFFER;
1828 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1830 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1833 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1834 info->ftExpiry = context->pCertInfo->NotAfter;
1835 info->ftStart = context->pCertInfo->NotBefore;
1837 len = CertNameToStrW(context->dwCertEncodingType,
1838 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1839 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1840 if(info->lpszSubjectInfo)
1841 CertNameToStrW(context->dwCertEncodingType,
1842 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1843 info->lpszSubjectInfo, len);
1844 len = CertNameToStrW(context->dwCertEncodingType,
1845 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1846 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1847 if (info->lpszIssuerInfo)
1848 CertNameToStrW(context->dwCertEncodingType,
1849 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1850 info->lpszIssuerInfo, len);
1852 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1854 len = CertNameToStrA(context->dwCertEncodingType,
1855 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1856 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1857 if(infoA->lpszSubjectInfo)
1858 CertNameToStrA(context->dwCertEncodingType,
1859 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1860 infoA->lpszSubjectInfo, len);
1861 len = CertNameToStrA(context->dwCertEncodingType,
1862 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1863 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1864 if(infoA->lpszIssuerInfo)
1865 CertNameToStrA(context->dwCertEncodingType,
1866 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1867 infoA->lpszIssuerInfo, len);
1871 * Contrary to MSDN, these do not appear to be set.
1873 * lpszSignatureAlgName
1874 * lpszEncryptionAlgName
1877 CertFreeCertificateContext(context);
1878 return ERROR_SUCCESS;
1883 return INET_QueryOption(hdr, option, buffer, size, unicode);
1886 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1888 http_request_t *req = (http_request_t*)hdr;
1891 case INTERNET_OPTION_SECURITY_FLAGS:
1895 if (!buffer || size != sizeof(DWORD))
1896 return ERROR_INVALID_PARAMETER;
1897 flags = *(DWORD *)buffer;
1898 TRACE("%08x\n", flags);
1899 req->netConnection.security_flags = flags;
1900 return ERROR_SUCCESS;
1902 case INTERNET_OPTION_SEND_TIMEOUT:
1903 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1904 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1906 if (size != sizeof(DWORD))
1907 return ERROR_INVALID_PARAMETER;
1909 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1912 case INTERNET_OPTION_USERNAME:
1913 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1914 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1915 return ERROR_SUCCESS;
1917 case INTERNET_OPTION_PASSWORD:
1918 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1919 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1920 return ERROR_SUCCESS;
1921 case INTERNET_OPTION_HTTP_DECODING:
1922 if(size != sizeof(BOOL))
1923 return ERROR_INVALID_PARAMETER;
1924 req->decoding = *(BOOL*)buffer;
1925 return ERROR_SUCCESS;
1928 return ERROR_INTERNET_INVALID_OPTION;
1931 /* read some more data into the read buffer (the read section must be held) */
1932 static DWORD read_more_data( http_request_t *req, int maxlen )
1939 /* move existing data to the start of the buffer */
1941 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1945 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1947 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1948 maxlen - req->read_size, 0, &len );
1949 if(res == ERROR_SUCCESS)
1950 req->read_size += len;
1955 /* remove some amount of data from the read buffer (the read section must be held) */
1956 static void remove_data( http_request_t *req, int count )
1958 if (!(req->read_size -= count)) req->read_pos = 0;
1959 else req->read_pos += count;
1962 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1964 int count, bytes_read, pos = 0;
1967 EnterCriticalSection( &req->read_section );
1970 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1974 count = eol - (req->read_buf + req->read_pos);
1975 bytes_read = count + 1;
1977 else count = bytes_read = req->read_size;
1979 count = min( count, *len - pos );
1980 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1982 remove_data( req, bytes_read );
1985 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1988 TRACE( "returning empty string\n" );
1989 LeaveCriticalSection( &req->read_section );
1990 INTERNET_SetLastError(res);
1994 LeaveCriticalSection( &req->read_section );
1998 if (pos && buffer[pos - 1] == '\r') pos--;
2001 buffer[*len - 1] = 0;
2002 TRACE( "returning %s\n", debugstr_a(buffer));
2006 /* discard data contents until we reach end of line (the read section must be held) */
2007 static DWORD discard_eol( http_request_t *req )
2013 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2016 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2019 req->read_pos = req->read_size = 0; /* discard everything */
2020 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2021 } while (req->read_size);
2022 return ERROR_SUCCESS;
2025 /* read the size of the next chunk (the read section must be held) */
2026 static DWORD start_next_chunk( http_request_t *req )
2028 DWORD chunk_size = 0, res;
2030 if (!req->dwContentLength) return ERROR_SUCCESS;
2031 if (req->dwContentLength == req->dwContentRead)
2033 /* read terminator for the previous chunk */
2034 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2035 req->dwContentLength = ~0u;
2036 req->dwContentRead = 0;
2040 while (req->read_size)
2042 char ch = req->read_buf[req->read_pos];
2043 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2044 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2045 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2046 else if (ch == ';' || ch == '\r' || ch == '\n')
2048 TRACE( "reading %u byte chunk\n", chunk_size );
2049 req->dwContentLength = chunk_size;
2050 req->dwContentRead = 0;
2051 return discard_eol( req );
2053 remove_data( req, 1 );
2055 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2056 if (!req->read_size)
2058 req->dwContentLength = req->dwContentRead = 0;
2059 return ERROR_SUCCESS;
2064 /* check if we have reached the end of the data to read (the read section must be held) */
2065 static BOOL end_of_read_data( http_request_t *req )
2067 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2068 if (req->read_chunked) return (req->dwContentLength == 0);
2069 if (req->dwContentLength == ~0u) return FALSE;
2070 return (req->dwContentLength == req->dwContentRead);
2073 /* fetch some more data into the read buffer (the read section must be held) */
2074 static DWORD refill_buffer( http_request_t *req )
2076 int len = sizeof(req->read_buf);
2079 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2081 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2084 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2085 if (len <= req->read_size) return ERROR_SUCCESS;
2087 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2088 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2089 return ERROR_SUCCESS;
2092 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2094 DWORD ret = ERROR_SUCCESS;
2098 z_stream *zstream = &req->gzip_stream->zstream;
2102 while(read < size && !req->gzip_stream->end_of_data) {
2103 if(!req->read_size) {
2104 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2108 if(req->dwContentRead == req->dwContentLength)
2111 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2113 zstream->next_in = req->read_buf+req->read_pos;
2114 zstream->avail_in = buf_avail;
2115 zstream->next_out = buf+read;
2116 zstream->avail_out = size-read;
2117 zres = inflate(zstream, Z_FULL_FLUSH);
2118 read = size - zstream->avail_out;
2119 req->dwContentRead += buf_avail-zstream->avail_in;
2120 remove_data(req, buf_avail-zstream->avail_in);
2121 if(zres == Z_STREAM_END) {
2122 TRACE("end of data\n");
2123 req->gzip_stream->end_of_data = TRUE;
2124 inflateEnd(&req->gzip_stream->zstream);
2125 }else if(zres != Z_OK) {
2126 WARN("inflate failed %d\n", zres);
2128 ret = ERROR_INTERNET_DECODING_FAILED;
2138 static void refill_gzip_buffer(http_request_t *req)
2143 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2146 if(req->gzip_stream->buf_pos) {
2147 if(req->gzip_stream->buf_size)
2148 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2149 req->gzip_stream->buf_pos = 0;
2152 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2153 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2154 if(res == ERROR_SUCCESS)
2155 req->gzip_stream->buf_size += len;
2158 /* return the size of data available to be read immediately (the read section must be held) */
2159 static DWORD get_avail_data( http_request_t *req )
2161 if (req->gzip_stream) {
2162 refill_gzip_buffer(req);
2163 return req->gzip_stream->buf_size;
2165 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2167 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2170 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2172 INTERNET_ASYNC_RESULT iar;
2177 EnterCriticalSection( &req->read_section );
2178 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2179 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2180 iar.dwError = first_notif ? 0 : get_avail_data(req);
2185 LeaveCriticalSection( &req->read_section );
2187 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2188 sizeof(INTERNET_ASYNC_RESULT));
2191 /* read data from the http connection (the read section must be held) */
2192 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2194 BOOL finished_reading = FALSE;
2195 int len, bytes_read = 0;
2196 DWORD ret = ERROR_SUCCESS;
2198 EnterCriticalSection( &req->read_section );
2200 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2202 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2205 if(req->gzip_stream) {
2206 if(req->gzip_stream->buf_size) {
2207 bytes_read = min(req->gzip_stream->buf_size, size);
2208 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2209 req->gzip_stream->buf_pos += bytes_read;
2210 req->gzip_stream->buf_size -= bytes_read;
2211 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2215 if(size > bytes_read) {
2216 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2217 if(ret == ERROR_SUCCESS)
2221 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2223 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2225 if (req->read_size) {
2226 bytes_read = min( req->read_size, size );
2227 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2228 remove_data( req, bytes_read );
2231 if (size > bytes_read && (!bytes_read || sync)) {
2232 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2233 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2235 /* always return success, even if the network layer returns an error */
2238 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2239 req->dwContentRead += bytes_read;
2244 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2245 LeaveCriticalSection( &req->read_section );
2247 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2249 DWORD dwBytesWritten;
2251 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2253 WARN("WriteFile failed: %u\n", GetLastError());
2256 if(finished_reading)
2257 HTTP_FinishedReading(req);
2263 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2265 http_request_t *req = (http_request_t*)hdr;
2268 EnterCriticalSection( &req->read_section );
2269 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2270 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2272 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2273 if(res == ERROR_SUCCESS)
2275 LeaveCriticalSection( &req->read_section );
2280 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2282 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2283 http_request_t *req = (http_request_t*)workRequest->hdr;
2284 INTERNET_ASYNC_RESULT iar;
2287 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2289 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2290 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2292 iar.dwResult = res == ERROR_SUCCESS;
2295 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2296 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2297 sizeof(INTERNET_ASYNC_RESULT));
2300 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2301 DWORD flags, DWORD_PTR context)
2303 http_request_t *req = (http_request_t*)hdr;
2304 DWORD res, size, read, error = ERROR_SUCCESS;
2306 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2307 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2309 if (buffers->dwStructSize != sizeof(*buffers))
2310 return ERROR_INVALID_PARAMETER;
2312 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2314 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2316 WORKREQUEST workRequest;
2318 if (TryEnterCriticalSection( &req->read_section ))
2320 if (get_avail_data(req))
2322 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2323 &buffers->dwBufferLength, FALSE);
2324 size = buffers->dwBufferLength;
2325 LeaveCriticalSection( &req->read_section );
2328 LeaveCriticalSection( &req->read_section );
2331 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2332 workRequest.hdr = WININET_AddRef(&req->hdr);
2333 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2335 INTERNET_AsyncCall(&workRequest);
2337 return ERROR_IO_PENDING;
2341 size = buffers->dwBufferLength;
2343 EnterCriticalSection( &req->read_section );
2344 if(hdr->dwError == ERROR_SUCCESS)
2345 hdr->dwError = INTERNET_HANDLE_IN_USE;
2346 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2347 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2350 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2351 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2352 if(res == ERROR_SUCCESS)
2353 read += buffers->dwBufferLength;
2357 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2358 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2360 LeaveCriticalSection( &req->read_section );
2362 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2363 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2364 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2365 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2367 EnterCriticalSection( &req->read_section );
2370 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2371 hdr->dwError = ERROR_SUCCESS;
2373 error = hdr->dwError;
2375 LeaveCriticalSection( &req->read_section );
2376 size = buffers->dwBufferLength;
2377 buffers->dwBufferLength = read;
2380 if (res == ERROR_SUCCESS) {
2381 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2382 &size, sizeof(size));
2385 return res==ERROR_SUCCESS ? error : res;
2388 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2390 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2391 http_request_t *req = (http_request_t*)workRequest->hdr;
2392 INTERNET_ASYNC_RESULT iar;
2395 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2397 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2398 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2400 iar.dwResult = res == ERROR_SUCCESS;
2403 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2404 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2405 sizeof(INTERNET_ASYNC_RESULT));
2408 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2409 DWORD flags, DWORD_PTR context)
2412 http_request_t *req = (http_request_t*)hdr;
2413 DWORD res, size, read, error = ERROR_SUCCESS;
2415 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2416 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2418 if (buffers->dwStructSize != sizeof(*buffers))
2419 return ERROR_INVALID_PARAMETER;
2421 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2423 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2425 WORKREQUEST workRequest;
2427 if (TryEnterCriticalSection( &req->read_section ))
2429 if (get_avail_data(req))
2431 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2432 &buffers->dwBufferLength, FALSE);
2433 size = buffers->dwBufferLength;
2434 LeaveCriticalSection( &req->read_section );
2437 LeaveCriticalSection( &req->read_section );
2440 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2441 workRequest.hdr = WININET_AddRef(&req->hdr);
2442 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2444 INTERNET_AsyncCall(&workRequest);
2446 return ERROR_IO_PENDING;
2450 size = buffers->dwBufferLength;
2452 EnterCriticalSection( &req->read_section );
2453 if(hdr->dwError == ERROR_SUCCESS)
2454 hdr->dwError = INTERNET_HANDLE_IN_USE;
2455 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2456 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2459 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2460 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2461 if(res == ERROR_SUCCESS)
2462 read += buffers->dwBufferLength;
2466 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2467 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2469 LeaveCriticalSection( &req->read_section );
2471 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2472 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2473 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2474 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2476 EnterCriticalSection( &req->read_section );
2479 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2480 hdr->dwError = ERROR_SUCCESS;
2482 error = hdr->dwError;
2484 LeaveCriticalSection( &req->read_section );
2485 size = buffers->dwBufferLength;
2486 buffers->dwBufferLength = read;
2489 if (res == ERROR_SUCCESS) {
2490 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2491 &size, sizeof(size));
2494 return res==ERROR_SUCCESS ? error : res;
2497 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2500 http_request_t *lpwhr = (http_request_t*)hdr;
2502 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2505 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2506 if (res == ERROR_SUCCESS)
2507 lpwhr->dwBytesWritten += *written;
2509 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2513 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2515 http_request_t *req = (http_request_t*)workRequest->hdr;
2517 HTTP_ReceiveRequestData(req, FALSE);
2520 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2522 http_request_t *req = (http_request_t*)hdr;
2524 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2526 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2528 WORKREQUEST workRequest;
2530 /* never wait, if we can't enter the section we queue an async request right away */
2531 if (TryEnterCriticalSection( &req->read_section ))
2533 if ((*available = get_avail_data( req ))) goto done;
2534 if (end_of_read_data( req )) goto done;
2535 LeaveCriticalSection( &req->read_section );
2538 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2539 workRequest.hdr = WININET_AddRef( &req->hdr );
2541 INTERNET_AsyncCall(&workRequest);
2543 return ERROR_IO_PENDING;
2546 EnterCriticalSection( &req->read_section );
2548 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2550 refill_buffer( req );
2551 *available = get_avail_data( req );
2555 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2558 if (NETCON_query_data_available(&req->netConnection, &extra))
2559 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2561 LeaveCriticalSection( &req->read_section );
2563 TRACE( "returning %u\n", *available );
2564 return ERROR_SUCCESS;
2567 static const object_vtbl_t HTTPREQVtbl = {
2569 HTTPREQ_CloseConnection,
2570 HTTPREQ_QueryOption,
2573 HTTPREQ_ReadFileExA,
2574 HTTPREQ_ReadFileExW,
2576 HTTPREQ_QueryDataAvailable,
2580 /***********************************************************************
2581 * HTTP_HttpOpenRequestW (internal)
2583 * Open a HTTP request handle
2586 * HINTERNET a HTTP request handle on success
2590 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2591 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2592 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2593 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2595 appinfo_t *hIC = NULL;
2596 http_request_t *lpwhr;
2597 LPWSTR lpszHostName = NULL;
2598 HINTERNET handle = NULL;
2599 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2604 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2605 hIC = lpwhs->lpAppInfo;
2607 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2610 res = ERROR_OUTOFMEMORY;
2613 lpwhr->hdr.htype = WH_HHTTPREQ;
2614 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2615 lpwhr->hdr.dwFlags = dwFlags;
2616 lpwhr->hdr.dwContext = dwContext;
2617 lpwhr->hdr.refs = 1;
2618 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2619 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2620 lpwhr->dwContentLength = ~0u;
2621 InitializeCriticalSection( &lpwhr->read_section );
2623 WININET_AddRef( &lpwhs->hdr );
2624 lpwhr->lpHttpSession = lpwhs;
2625 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2627 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2628 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2629 if (NULL == lpszHostName)
2631 res = ERROR_OUTOFMEMORY;
2635 handle = WININET_AllocHandle( &lpwhr->hdr );
2638 res = ERROR_OUTOFMEMORY;
2642 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2644 InternetCloseHandle( handle );
2649 if (lpszObjectName && *lpszObjectName) {
2653 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2654 if (rc != E_POINTER)
2655 len = strlenW(lpszObjectName)+1;
2656 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2657 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2658 URL_ESCAPE_SPACES_ONLY);
2661 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2662 strcpyW(lpwhr->lpszPath,lpszObjectName);
2665 static const WCHAR slashW[] = {'/',0};
2667 lpwhr->lpszPath = heap_strdupW(slashW);
2670 if (lpszReferrer && *lpszReferrer)
2671 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2673 if (lpszAcceptTypes)
2676 for (i = 0; lpszAcceptTypes[i]; i++)
2678 if (!*lpszAcceptTypes[i]) continue;
2679 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2680 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2681 HTTP_ADDHDR_FLAG_REQ |
2682 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2686 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2687 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2689 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2690 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2691 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2693 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2694 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2695 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2698 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2699 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2701 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2702 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2703 INTERNET_DEFAULT_HTTPS_PORT :
2704 INTERNET_DEFAULT_HTTP_PORT);
2706 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2707 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2708 INTERNET_DEFAULT_HTTPS_PORT :
2709 INTERNET_DEFAULT_HTTP_PORT);
2711 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2712 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2714 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2715 INTERNET_STATUS_HANDLE_CREATED, &handle,
2719 HeapFree(GetProcessHeap(), 0, lpszHostName);
2721 WININET_Release( &lpwhr->hdr );
2723 TRACE("<-- %p (%p)\n", handle, lpwhr);
2728 /***********************************************************************
2729 * HttpOpenRequestW (WININET.@)
2731 * Open a HTTP request handle
2734 * HINTERNET a HTTP request handle on success
2738 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2739 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2740 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2741 DWORD dwFlags, DWORD_PTR dwContext)
2743 http_session_t *lpwhs;
2744 HINTERNET handle = NULL;
2747 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2748 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2749 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2750 dwFlags, dwContext);
2751 if(lpszAcceptTypes!=NULL)
2754 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2755 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2758 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2759 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2761 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2766 * My tests seem to show that the windows version does not
2767 * become asynchronous until after this point. And anyhow
2768 * if this call was asynchronous then how would you get the
2769 * necessary HINTERNET pointer returned by this function.
2772 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2773 lpszVersion, lpszReferrer, lpszAcceptTypes,
2774 dwFlags, dwContext, &handle);
2777 WININET_Release( &lpwhs->hdr );
2778 TRACE("returning %p\n", handle);
2779 if(res != ERROR_SUCCESS)
2784 /* read any content returned by the server so that the connection can be
2786 static void HTTP_DrainContent(http_request_t *req)
2790 if (!NETCON_connected(&req->netConnection)) return;
2792 if (req->dwContentLength == -1)
2794 NETCON_close(&req->netConnection);
2797 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2802 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2804 } while (bytes_read);
2807 static const LPCWSTR header_lookup[] = {
2808 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2809 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2810 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2811 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2812 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2813 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2814 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2815 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2816 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2817 szDate, /* HTTP_QUERY_DATE = 9 */
2818 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2819 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2820 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2821 szURI, /* HTTP_QUERY_URI = 13 */
2822 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2823 NULL, /* HTTP_QUERY_COST = 15 */
2824 NULL, /* HTTP_QUERY_LINK = 16 */
2825 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2826 NULL, /* HTTP_QUERY_VERSION = 18 */
2827 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2828 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2829 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2830 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2831 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2832 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2833 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2834 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2835 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2836 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2837 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2838 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2839 NULL, /* HTTP_QUERY_FROM = 31 */
2840 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2841 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2842 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2843 szReferer, /* HTTP_QUERY_REFERER = 35 */
2844 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2845 szServer, /* HTTP_QUERY_SERVER = 37 */
2846 NULL, /* HTTP_TITLE = 38 */
2847 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2848 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2849 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2850 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2851 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2852 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2853 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2854 NULL, /* HTTP_QUERY_REFRESH = 46 */
2855 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2856 szAge, /* HTTP_QUERY_AGE = 48 */
2857 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2858 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2859 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2860 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2861 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2862 szETag, /* HTTP_QUERY_ETAG = 54 */
2863 hostW, /* HTTP_QUERY_HOST = 55 */
2864 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2865 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2866 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2867 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2868 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2869 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2870 szRange, /* HTTP_QUERY_RANGE = 62 */
2871 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2872 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2873 szVary, /* HTTP_QUERY_VARY = 65 */
2874 szVia, /* HTTP_QUERY_VIA = 66 */
2875 szWarning, /* HTTP_QUERY_WARNING = 67 */
2876 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2877 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2878 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2881 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2883 /***********************************************************************
2884 * HTTP_HttpQueryInfoW (internal)
2886 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2887 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2889 LPHTTPHEADERW lphttpHdr = NULL;
2890 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2891 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2892 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2895 /* Find requested header structure */
2898 case HTTP_QUERY_CUSTOM:
2899 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2900 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2902 case HTTP_QUERY_RAW_HEADERS_CRLF:
2906 DWORD res = ERROR_INVALID_PARAMETER;
2909 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2911 headers = lpwhr->lpszRawHeaders;
2914 len = strlenW(headers) * sizeof(WCHAR);
2916 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2918 len += sizeof(WCHAR);
2919 res = ERROR_INSUFFICIENT_BUFFER;
2924 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2927 len = strlenW(szCrLf) * sizeof(WCHAR);
2928 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2930 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2931 res = ERROR_SUCCESS;
2933 *lpdwBufferLength = len;
2936 HeapFree(GetProcessHeap(), 0, headers);
2939 case HTTP_QUERY_RAW_HEADERS:
2941 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2943 LPWSTR pszString = lpBuffer;
2945 for (i = 0; ppszRawHeaderLines[i]; i++)
2946 size += strlenW(ppszRawHeaderLines[i]) + 1;
2948 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2950 HTTP_FreeTokens(ppszRawHeaderLines);
2951 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2952 return ERROR_INSUFFICIENT_BUFFER;
2956 for (i = 0; ppszRawHeaderLines[i]; i++)
2958 DWORD len = strlenW(ppszRawHeaderLines[i]);
2959 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2963 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2965 *lpdwBufferLength = size * sizeof(WCHAR);
2966 HTTP_FreeTokens(ppszRawHeaderLines);
2968 return ERROR_SUCCESS;
2970 case HTTP_QUERY_STATUS_TEXT:
2971 if (lpwhr->lpszStatusText)
2973 DWORD len = strlenW(lpwhr->lpszStatusText);
2974 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2976 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2977 return ERROR_INSUFFICIENT_BUFFER;
2981 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2982 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2984 *lpdwBufferLength = len * sizeof(WCHAR);
2985 return ERROR_SUCCESS;
2988 case HTTP_QUERY_VERSION:
2989 if (lpwhr->lpszVersion)
2991 DWORD len = strlenW(lpwhr->lpszVersion);
2992 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2994 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2995 return ERROR_INSUFFICIENT_BUFFER;
2999 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
3000 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3002 *lpdwBufferLength = len * sizeof(WCHAR);
3003 return ERROR_SUCCESS;
3006 case HTTP_QUERY_CONTENT_ENCODING:
3007 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
3008 requested_index,request_only);
3011 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3013 if (level < LAST_TABLE_HEADER && header_lookup[level])
3014 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
3015 requested_index,request_only);
3019 lphttpHdr = &lpwhr->pCustHeaders[index];
3021 /* Ensure header satisfies requested attributes */
3023 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3024 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3026 return ERROR_HTTP_HEADER_NOT_FOUND;
3029 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3031 /* coalesce value to requested type */
3032 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3034 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3035 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3037 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3043 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3045 tmpTM = *gmtime(&tmpTime);
3046 STHook = (SYSTEMTIME *)lpBuffer;
3047 STHook->wDay = tmpTM.tm_mday;
3048 STHook->wHour = tmpTM.tm_hour;
3049 STHook->wMilliseconds = 0;
3050 STHook->wMinute = tmpTM.tm_min;
3051 STHook->wDayOfWeek = tmpTM.tm_wday;
3052 STHook->wMonth = tmpTM.tm_mon + 1;
3053 STHook->wSecond = tmpTM.tm_sec;
3054 STHook->wYear = tmpTM.tm_year;
3056 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3057 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3058 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3060 else if (lphttpHdr->lpszValue)
3062 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3064 if (len > *lpdwBufferLength)
3066 *lpdwBufferLength = len;
3067 return ERROR_INSUFFICIENT_BUFFER;
3071 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3072 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3074 *lpdwBufferLength = len - sizeof(WCHAR);
3076 return ERROR_SUCCESS;
3079 /***********************************************************************
3080 * HttpQueryInfoW (WININET.@)
3082 * Queries for information about an HTTP request
3089 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3090 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3092 http_request_t *lpwhr;
3095 if (TRACE_ON(wininet)) {
3096 #define FE(x) { x, #x }
3097 static const wininet_flag_info query_flags[] = {
3098 FE(HTTP_QUERY_MIME_VERSION),
3099 FE(HTTP_QUERY_CONTENT_TYPE),
3100 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3101 FE(HTTP_QUERY_CONTENT_ID),
3102 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3103 FE(HTTP_QUERY_CONTENT_LENGTH),
3104 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3105 FE(HTTP_QUERY_ALLOW),
3106 FE(HTTP_QUERY_PUBLIC),
3107 FE(HTTP_QUERY_DATE),
3108 FE(HTTP_QUERY_EXPIRES),
3109 FE(HTTP_QUERY_LAST_MODIFIED),
3110 FE(HTTP_QUERY_MESSAGE_ID),
3112 FE(HTTP_QUERY_DERIVED_FROM),
3113 FE(HTTP_QUERY_COST),
3114 FE(HTTP_QUERY_LINK),
3115 FE(HTTP_QUERY_PRAGMA),
3116 FE(HTTP_QUERY_VERSION),
3117 FE(HTTP_QUERY_STATUS_CODE),
3118 FE(HTTP_QUERY_STATUS_TEXT),
3119 FE(HTTP_QUERY_RAW_HEADERS),
3120 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3121 FE(HTTP_QUERY_CONNECTION),
3122 FE(HTTP_QUERY_ACCEPT),
3123 FE(HTTP_QUERY_ACCEPT_CHARSET),
3124 FE(HTTP_QUERY_ACCEPT_ENCODING),
3125 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3126 FE(HTTP_QUERY_AUTHORIZATION),
3127 FE(HTTP_QUERY_CONTENT_ENCODING),
3128 FE(HTTP_QUERY_FORWARDED),
3129 FE(HTTP_QUERY_FROM),
3130 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3131 FE(HTTP_QUERY_LOCATION),
3132 FE(HTTP_QUERY_ORIG_URI),
3133 FE(HTTP_QUERY_REFERER),
3134 FE(HTTP_QUERY_RETRY_AFTER),
3135 FE(HTTP_QUERY_SERVER),
3136 FE(HTTP_QUERY_TITLE),
3137 FE(HTTP_QUERY_USER_AGENT),
3138 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3139 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3140 FE(HTTP_QUERY_ACCEPT_RANGES),
3141 FE(HTTP_QUERY_SET_COOKIE),
3142 FE(HTTP_QUERY_COOKIE),
3143 FE(HTTP_QUERY_REQUEST_METHOD),
3144 FE(HTTP_QUERY_REFRESH),
3145 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3147 FE(HTTP_QUERY_CACHE_CONTROL),
3148 FE(HTTP_QUERY_CONTENT_BASE),
3149 FE(HTTP_QUERY_CONTENT_LOCATION),
3150 FE(HTTP_QUERY_CONTENT_MD5),
3151 FE(HTTP_QUERY_CONTENT_RANGE),
3152 FE(HTTP_QUERY_ETAG),
3153 FE(HTTP_QUERY_HOST),
3154 FE(HTTP_QUERY_IF_MATCH),
3155 FE(HTTP_QUERY_IF_NONE_MATCH),
3156 FE(HTTP_QUERY_IF_RANGE),
3157 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3158 FE(HTTP_QUERY_MAX_FORWARDS),
3159 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3160 FE(HTTP_QUERY_RANGE),
3161 FE(HTTP_QUERY_TRANSFER_ENCODING),
3162 FE(HTTP_QUERY_UPGRADE),
3163 FE(HTTP_QUERY_VARY),
3165 FE(HTTP_QUERY_WARNING),
3166 FE(HTTP_QUERY_CUSTOM)
3168 static const wininet_flag_info modifier_flags[] = {
3169 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3170 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3171 FE(HTTP_QUERY_FLAG_NUMBER),
3172 FE(HTTP_QUERY_FLAG_COALESCE)
3175 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3176 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3179 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3180 TRACE(" Attribute:");
3181 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3182 if (query_flags[i].val == info) {
3183 TRACE(" %s", query_flags[i].name);
3187 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3188 TRACE(" Unknown (%08x)", info);
3191 TRACE(" Modifier:");
3192 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3193 if (modifier_flags[i].val & info_mod) {
3194 TRACE(" %s", modifier_flags[i].name);
3195 info_mod &= ~ modifier_flags[i].val;
3200 TRACE(" Unknown (%08x)", info_mod);
3205 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3206 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3208 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3212 if (lpBuffer == NULL)
3213 *lpdwBufferLength = 0;
3214 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3215 lpBuffer, lpdwBufferLength, lpdwIndex);
3219 WININET_Release( &lpwhr->hdr );
3221 TRACE("%u <--\n", res);
3222 if(res != ERROR_SUCCESS)
3224 return res == ERROR_SUCCESS;
3227 /***********************************************************************
3228 * HttpQueryInfoA (WININET.@)
3230 * Queries for information about an HTTP request
3237 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3238 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3244 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3245 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3247 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3248 lpdwBufferLength, lpdwIndex );
3254 len = (*lpdwBufferLength)*sizeof(WCHAR);
3255 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3257 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3263 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3264 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3265 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3266 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3273 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3277 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3278 lpBuffer, *lpdwBufferLength, NULL, NULL );
3279 *lpdwBufferLength = len - 1;
3281 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3284 /* since the strings being returned from HttpQueryInfoW should be
3285 * only ASCII characters, it is reasonable to assume that all of
3286 * the Unicode characters can be reduced to a single byte */
3287 *lpdwBufferLength = len / sizeof(WCHAR);
3289 HeapFree(GetProcessHeap(), 0, bufferW );
3294 /***********************************************************************
3295 * HTTP_GetRedirectURL (internal)
3297 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3299 static WCHAR szHttp[] = {'h','t','t','p',0};
3300 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3301 http_session_t *lpwhs = lpwhr->lpHttpSession;
3302 URL_COMPONENTSW urlComponents;
3303 DWORD url_length = 0;
3305 LPWSTR combined_url;
3307 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3308 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3309 urlComponents.dwSchemeLength = 0;
3310 urlComponents.lpszHostName = lpwhs->lpszHostName;
3311 urlComponents.dwHostNameLength = 0;
3312 urlComponents.nPort = lpwhs->nHostPort;
3313 urlComponents.lpszUserName = lpwhs->lpszUserName;
3314 urlComponents.dwUserNameLength = 0;
3315 urlComponents.lpszPassword = NULL;
3316 urlComponents.dwPasswordLength = 0;
3317 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3318 urlComponents.dwUrlPathLength = 0;
3319 urlComponents.lpszExtraInfo = NULL;
3320 urlComponents.dwExtraInfoLength = 0;
3322 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3323 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3326 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3328 /* convert from bytes to characters */
3329 url_length = url_length / sizeof(WCHAR) - 1;
3330 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3332 HeapFree(GetProcessHeap(), 0, orig_url);
3337 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3338 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3340 HeapFree(GetProcessHeap(), 0, orig_url);
3343 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3345 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3347 HeapFree(GetProcessHeap(), 0, orig_url);
3348 HeapFree(GetProcessHeap(), 0, combined_url);
3351 HeapFree(GetProcessHeap(), 0, orig_url);
3352 return combined_url;
3356 /***********************************************************************
3357 * HTTP_HandleRedirect (internal)
3359 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3361 http_session_t *lpwhs = lpwhr->lpHttpSession;
3362 appinfo_t *hIC = lpwhs->lpAppInfo;
3363 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3364 WCHAR path[INTERNET_MAX_URL_LENGTH];
3369 /* if it's an absolute path, keep the same session info */
3370 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3374 URL_COMPONENTSW urlComponents;
3375 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3376 static WCHAR szHttp[] = {'h','t','t','p',0};
3377 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3383 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3384 urlComponents.lpszScheme = protocol;
3385 urlComponents.dwSchemeLength = 32;
3386 urlComponents.lpszHostName = hostName;
3387 urlComponents.dwHostNameLength = MAXHOSTNAME;
3388 urlComponents.lpszUserName = userName;
3389 urlComponents.dwUserNameLength = 1024;
3390 urlComponents.lpszPassword = NULL;
3391 urlComponents.dwPasswordLength = 0;
3392 urlComponents.lpszUrlPath = path;
3393 urlComponents.dwUrlPathLength = 2048;
3394 urlComponents.lpszExtraInfo = NULL;
3395 urlComponents.dwExtraInfoLength = 0;
3396 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3397 return INTERNET_GetLastError();
3399 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3400 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3402 TRACE("redirect from secure page to non-secure page\n");
3403 /* FIXME: warn about from secure redirect to non-secure page */
3404 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3406 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3407 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3409 TRACE("redirect from non-secure page to secure page\n");
3410 /* FIXME: notify about redirect to secure page */
3411 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3414 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3416 if (lstrlenW(protocol)>4) /*https*/
3417 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3419 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3424 * This upsets redirects to binary files on sourceforge.net
3425 * and gives an html page instead of the target file
3426 * Examination of the HTTP request sent by native wininet.dll
3427 * reveals that it doesn't send a referrer in that case.
3428 * Maybe there's a flag that enables this, or maybe a referrer
3429 * shouldn't be added in case of a redirect.
3432 /* consider the current host as the referrer */
3433 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3434 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3435 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3436 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3439 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3440 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3441 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3444 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3445 len = lstrlenW(hostName);
3446 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3447 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3448 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3451 lpwhs->lpszHostName = heap_strdupW(hostName);
3453 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3455 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3456 lpwhs->lpszUserName = NULL;
3458 lpwhs->lpszUserName = heap_strdupW(userName);
3462 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3466 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3467 lpwhs->lpszServerName = heap_strdupW(hostName);
3468 lpwhs->nServerPort = urlComponents.nPort;
3470 NETCON_close(&lpwhr->netConnection);
3471 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3474 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3475 if (res != ERROR_SUCCESS)
3478 lpwhr->read_pos = lpwhr->read_size = 0;
3479 lpwhr->read_chunked = FALSE;
3483 TRACE("Redirect through proxy\n");
3486 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3487 lpwhr->lpszPath=NULL;
3493 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3494 if (rc != E_POINTER)
3495 needed = strlenW(path)+1;
3496 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3497 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3498 URL_ESCAPE_SPACES_ONLY);
3501 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3502 strcpyW(lpwhr->lpszPath,path);
3506 /* Remove custom content-type/length headers on redirects. */
3507 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3509 HTTP_DeleteCustomHeader(lpwhr, index);
3510 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3512 HTTP_DeleteCustomHeader(lpwhr, index);
3514 return ERROR_SUCCESS;
3517 /***********************************************************************
3518 * HTTP_build_req (internal)
3520 * concatenate all the strings in the request together
3522 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3527 for( t = list; *t ; t++ )
3528 len += strlenW( *t );
3531 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3534 for( t = list; *t ; t++ )
3540 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3543 LPWSTR requestString;
3549 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3550 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3551 http_session_t *lpwhs = lpwhr->lpHttpSession;
3555 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3556 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3557 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3558 HeapFree( GetProcessHeap(), 0, lpszPath );
3560 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3561 NULL, 0, NULL, NULL );
3562 len--; /* the nul terminator isn't needed */
3563 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3564 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3565 ascii_req, len, NULL, NULL );
3566 HeapFree( GetProcessHeap(), 0, requestString );
3568 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3570 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3571 HeapFree( GetProcessHeap(), 0, ascii_req );
3572 if (res != ERROR_SUCCESS)
3575 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3577 return ERROR_HTTP_INVALID_HEADER;
3579 return ERROR_SUCCESS;
3582 static void HTTP_InsertCookies(http_request_t *lpwhr)
3584 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3585 LPWSTR lpszCookies, lpszUrl = NULL;
3586 DWORD nCookieSize, size;
3587 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3589 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3590 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3591 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3593 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3596 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3598 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3599 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3601 cnt += sprintfW(lpszCookies, szCookie);
3602 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3603 strcatW(lpszCookies, szCrLf);
3605 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3606 HeapFree(GetProcessHeap(), 0, lpszCookies);
3609 HeapFree(GetProcessHeap(), 0, lpszUrl);
3612 /***********************************************************************
3613 * HTTP_HttpSendRequestW (internal)
3615 * Sends the specified request to the HTTP server
3622 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3623 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3624 DWORD dwContentLength, BOOL bEndRequest)
3627 BOOL redirected = FALSE;
3628 LPWSTR requestString = NULL;
3631 INTERNET_ASYNC_RESULT iar;
3632 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3633 static const WCHAR szContentLength[] =
3634 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3635 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3638 TRACE("--> %p\n", lpwhr);
3640 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3642 /* if the verb is NULL default to GET */
3643 if (!lpwhr->lpszVerb)
3644 lpwhr->lpszVerb = heap_strdupW(szGET);
3646 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3648 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3649 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3650 lpwhr->dwBytesToWrite = dwContentLength;
3652 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3654 WCHAR *agent_header;
3655 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3658 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3659 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3660 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3662 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3663 HeapFree(GetProcessHeap(), 0, agent_header);
3665 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3667 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3668 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3670 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3672 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3673 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3674 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3680 BOOL reusing_connection;
3685 /* like native, just in case the caller forgot to call InternetReadFile
3686 * for all the data */
3687 HTTP_DrainContent(lpwhr);
3688 lpwhr->dwContentRead = 0;
3690 lpwhr->dwContentLength = ~0u;
3691 lpwhr->dwBytesToWrite = 0;
3694 if (TRACE_ON(wininet))
3696 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3697 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3701 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3703 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3705 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3706 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3708 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3709 HTTP_InsertCookies(lpwhr);
3711 /* add the headers the caller supplied */
3712 if( lpszHeaders && dwHeaderLength )
3714 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3715 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3718 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3720 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3721 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3722 HeapFree(GetProcessHeap(), 0, url);
3725 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3728 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3730 /* Send the request and store the results */
3731 if(NETCON_connected(&lpwhr->netConnection))
3732 reusing_connection = TRUE;
3734 reusing_connection = FALSE;
3736 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3739 /* send the request as ASCII, tack on the optional data */
3740 if (!lpOptional || redirected)
3741 dwOptionalLength = 0;
3742 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3743 NULL, 0, NULL, NULL );
3744 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3745 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3746 ascii_req, len, NULL, NULL );
3748 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3749 len = (len + dwOptionalLength - 1);
3751 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3753 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3754 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3756 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3757 HeapFree( GetProcessHeap(), 0, ascii_req );
3759 lpwhr->dwBytesWritten = dwOptionalLength;
3761 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3762 INTERNET_STATUS_REQUEST_SENT,
3763 &len, sizeof(DWORD));
3770 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3771 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3773 if (res != ERROR_SUCCESS)
3776 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3777 /* FIXME: We should know that connection is closed before sending
3778 * headers. Otherwise wrong callbacks are executed */
3779 if(!responseLen && reusing_connection) {
3780 TRACE("Connection closed by server, reconnecting\n");
3785 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3786 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3789 HTTP_ProcessCookies(lpwhr);
3791 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3793 dwBufferSize = sizeof(dwStatusCode);
3794 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3795 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3798 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3800 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3801 dwBufferSize=sizeof(szNewLocation);
3802 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3803 dwStatusCode == HTTP_STATUS_MOVED ||
3804 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3805 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3807 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3809 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3810 lpwhr->lpszVerb = heap_strdupW(szGET);
3812 HTTP_DrainContent(lpwhr);
3813 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3815 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3816 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3817 res = HTTP_HandleRedirect(lpwhr, new_url);
3818 if (res == ERROR_SUCCESS)
3820 HeapFree(GetProcessHeap(), 0, requestString);
3823 HeapFree( GetProcessHeap(), 0, new_url );
3828 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3830 WCHAR szAuthValue[2048];
3832 if (dwStatusCode == HTTP_STATUS_DENIED)
3834 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3836 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3838 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3840 lpwhr->lpHttpSession->lpszUserName,
3841 lpwhr->lpHttpSession->lpszPassword,
3844 HeapFree(GetProcessHeap(), 0, requestString);
3851 TRACE("Cleaning wrong authorization data\n");
3852 destroy_authinfo(lpwhr->pAuthInfo);
3853 lpwhr->pAuthInfo = NULL;
3856 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3859 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3861 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3862 &lpwhr->pProxyAuthInfo,
3863 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3864 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3873 TRACE("Cleaning wrong proxy authorization data\n");
3874 destroy_authinfo(lpwhr->pProxyAuthInfo);
3875 lpwhr->pProxyAuthInfo = NULL;
3881 res = ERROR_SUCCESS;
3885 if(res == ERROR_SUCCESS) {
3886 WCHAR url[INTERNET_MAX_URL_LENGTH];
3887 WCHAR cacheFileName[MAX_PATH+1];
3890 b = HTTP_GetRequestURL(lpwhr, url);
3892 WARN("Could not get URL\n");
3896 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3898 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3899 CloseHandle(lpwhr->hCacheFile);
3901 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3902 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3903 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3904 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3905 WARN("Could not create file: %u\n", GetLastError());
3906 lpwhr->hCacheFile = NULL;
3909 WARN("Could not create cache entry: %08x\n", GetLastError());
3915 HeapFree(GetProcessHeap(), 0, requestString);
3917 /* TODO: send notification for P3P header */
3919 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3921 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3922 HTTP_ReceiveRequestData(lpwhr, TRUE);
3925 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3928 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3929 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3930 sizeof(INTERNET_ASYNC_RESULT));
3938 /***********************************************************************
3940 * Helper functions for the HttpSendRequest(Ex) functions
3943 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3945 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3946 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3948 TRACE("%p\n", lpwhr);
3950 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3951 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3952 req->dwContentLength, req->bEndRequest);
3954 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3958 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3962 INTERNET_ASYNC_RESULT iar;
3963 DWORD res = ERROR_SUCCESS;
3965 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3966 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3968 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3970 res = ERROR_HTTP_HEADER_NOT_FOUND;
3972 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3973 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3975 /* process cookies here. Is this right? */
3976 HTTP_ProcessCookies(lpwhr);
3978 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3980 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3982 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3983 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3984 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3986 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3987 dwBufferSize=sizeof(szNewLocation);
3988 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3990 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3992 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3993 lpwhr->lpszVerb = heap_strdupW(szGET);
3995 HTTP_DrainContent(lpwhr);
3996 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3998 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3999 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4000 res = HTTP_HandleRedirect(lpwhr, new_url);
4001 if (res == ERROR_SUCCESS)
4002 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
4003 HeapFree( GetProcessHeap(), 0, new_url );
4009 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
4012 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4013 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4014 sizeof(INTERNET_ASYNC_RESULT));
4018 /***********************************************************************
4019 * HttpEndRequestA (WININET.@)
4021 * Ends an HTTP request that was started by HttpSendRequestEx
4024 * TRUE if successful
4028 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4029 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4031 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4035 SetLastError(ERROR_INVALID_PARAMETER);
4039 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4042 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4044 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4045 http_request_t *lpwhr = (http_request_t*)work->hdr;
4047 TRACE("%p\n", lpwhr);
4049 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4052 /***********************************************************************
4053 * HttpEndRequestW (WININET.@)
4055 * Ends an HTTP request that was started by HttpSendRequestEx
4058 * TRUE if successful
4062 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4063 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4065 http_request_t *lpwhr;
4072 SetLastError(ERROR_INVALID_PARAMETER);
4076 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4078 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4080 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4082 WININET_Release( &lpwhr->hdr );
4085 lpwhr->hdr.dwFlags |= dwFlags;
4087 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4090 struct WORKREQ_HTTPENDREQUESTW *request;
4092 work.asyncproc = AsyncHttpEndRequestProc;
4093 work.hdr = WININET_AddRef( &lpwhr->hdr );
4095 request = &work.u.HttpEndRequestW;
4096 request->dwFlags = dwFlags;
4097 request->dwContext = dwContext;
4099 INTERNET_AsyncCall(&work);
4100 res = ERROR_IO_PENDING;
4103 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4105 WININET_Release( &lpwhr->hdr );
4106 TRACE("%u <--\n", res);
4107 if(res != ERROR_SUCCESS)
4109 return res == ERROR_SUCCESS;
4112 /***********************************************************************
4113 * HttpSendRequestExA (WININET.@)
4115 * Sends the specified request to the HTTP server and allows chunked
4120 * Failure: FALSE, call GetLastError() for more information.
4122 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4123 LPINTERNET_BUFFERSA lpBuffersIn,
4124 LPINTERNET_BUFFERSA lpBuffersOut,
4125 DWORD dwFlags, DWORD_PTR dwContext)
4127 INTERNET_BUFFERSW BuffersInW;
4130 LPWSTR header = NULL;
4132 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4133 lpBuffersOut, dwFlags, dwContext);
4137 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4138 if (lpBuffersIn->lpcszHeader)
4140 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4141 lpBuffersIn->dwHeadersLength,0,0);
4142 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4143 if (!(BuffersInW.lpcszHeader = header))
4145 SetLastError(ERROR_OUTOFMEMORY);
4148 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4149 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4153 BuffersInW.lpcszHeader = NULL;
4154 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4155 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4156 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4157 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4158 BuffersInW.Next = NULL;
4161 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4163 HeapFree(GetProcessHeap(),0,header);
4168 /***********************************************************************
4169 * HttpSendRequestExW (WININET.@)
4171 * Sends the specified request to the HTTP server and allows chunked
4176 * Failure: FALSE, call GetLastError() for more information.
4178 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4179 LPINTERNET_BUFFERSW lpBuffersIn,
4180 LPINTERNET_BUFFERSW lpBuffersOut,
4181 DWORD dwFlags, DWORD_PTR dwContext)
4183 http_request_t *lpwhr;
4184 http_session_t *lpwhs;
4188 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4189 lpBuffersOut, dwFlags, dwContext);
4191 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4193 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4195 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4199 lpwhs = lpwhr->lpHttpSession;
4200 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4201 hIC = lpwhs->lpAppInfo;
4202 assert(hIC->hdr.htype == WH_HINIT);
4204 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4206 WORKREQUEST workRequest;
4207 struct WORKREQ_HTTPSENDREQUESTW *req;
4209 workRequest.asyncproc = AsyncHttpSendRequestProc;
4210 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4211 req = &workRequest.u.HttpSendRequestW;
4216 if (lpBuffersIn->lpcszHeader)
4218 if (lpBuffersIn->dwHeadersLength == ~0u)
4219 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4221 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4223 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4224 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4226 else req->lpszHeader = NULL;
4228 req->dwHeaderLength = size / sizeof(WCHAR);
4229 req->lpOptional = lpBuffersIn->lpvBuffer;
4230 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4231 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4235 req->lpszHeader = NULL;
4236 req->dwHeaderLength = 0;
4237 req->lpOptional = NULL;
4238 req->dwOptionalLength = 0;
4239 req->dwContentLength = 0;
4242 req->bEndRequest = FALSE;
4244 INTERNET_AsyncCall(&workRequest);
4246 * This is from windows.
4248 res = ERROR_IO_PENDING;
4253 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4254 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4255 lpBuffersIn->dwBufferTotal, FALSE);
4257 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4262 WININET_Release( &lpwhr->hdr );
4266 return res == ERROR_SUCCESS;
4269 /***********************************************************************
4270 * HttpSendRequestW (WININET.@)
4272 * Sends the specified request to the HTTP server
4279 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4280 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4282 http_request_t *lpwhr;
4283 http_session_t *lpwhs = NULL;
4284 appinfo_t *hIC = NULL;
4285 DWORD res = ERROR_SUCCESS;
4287 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4288 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4290 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4291 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4293 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4297 lpwhs = lpwhr->lpHttpSession;
4298 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4300 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4304 hIC = lpwhs->lpAppInfo;
4305 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4307 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4311 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4313 WORKREQUEST workRequest;
4314 struct WORKREQ_HTTPSENDREQUESTW *req;
4316 workRequest.asyncproc = AsyncHttpSendRequestProc;
4317 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4318 req = &workRequest.u.HttpSendRequestW;
4323 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4324 else size = dwHeaderLength * sizeof(WCHAR);
4326 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4327 memcpy(req->lpszHeader, lpszHeaders, size);
4330 req->lpszHeader = 0;
4331 req->dwHeaderLength = dwHeaderLength;
4332 req->lpOptional = lpOptional;
4333 req->dwOptionalLength = dwOptionalLength;
4334 req->dwContentLength = dwOptionalLength;
4335 req->bEndRequest = TRUE;
4337 INTERNET_AsyncCall(&workRequest);
4339 * This is from windows.
4341 res = ERROR_IO_PENDING;
4345 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4346 dwHeaderLength, lpOptional, dwOptionalLength,
4347 dwOptionalLength, TRUE);
4351 WININET_Release( &lpwhr->hdr );
4354 return res == ERROR_SUCCESS;
4357 /***********************************************************************
4358 * HttpSendRequestA (WININET.@)
4360 * Sends the specified request to the HTTP server
4367 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4368 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4371 LPWSTR szHeaders=NULL;
4372 DWORD nLen=dwHeaderLength;
4373 if(lpszHeaders!=NULL)
4375 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4376 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4377 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4379 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4380 HeapFree(GetProcessHeap(),0,szHeaders);
4384 /***********************************************************************
4385 * HTTPSESSION_Destroy (internal)
4387 * Deallocate session handle
4390 static void HTTPSESSION_Destroy(object_header_t *hdr)
4392 http_session_t *lpwhs = (http_session_t*) hdr;
4394 TRACE("%p\n", lpwhs);
4396 WININET_Release(&lpwhs->lpAppInfo->hdr);
4398 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4399 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4400 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4401 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4402 HeapFree(GetProcessHeap(), 0, lpwhs);
4405 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4408 case INTERNET_OPTION_HANDLE_TYPE:
4409 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4411 if (*size < sizeof(ULONG))
4412 return ERROR_INSUFFICIENT_BUFFER;
4414 *size = sizeof(DWORD);
4415 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4416 return ERROR_SUCCESS;
4419 return INET_QueryOption(hdr, option, buffer, size, unicode);
4422 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4424 http_session_t *ses = (http_session_t*)hdr;
4427 case INTERNET_OPTION_USERNAME:
4429 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4430 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4431 return ERROR_SUCCESS;
4433 case INTERNET_OPTION_PASSWORD:
4435 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4436 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4437 return ERROR_SUCCESS;
4442 return ERROR_INTERNET_INVALID_OPTION;
4445 static const object_vtbl_t HTTPSESSIONVtbl = {
4446 HTTPSESSION_Destroy,
4448 HTTPSESSION_QueryOption,
4449 HTTPSESSION_SetOption,
4458 /***********************************************************************
4459 * HTTP_Connect (internal)
4461 * Create http session handle
4464 * HINTERNET a session handle on success
4468 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4469 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4470 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4471 DWORD dwInternalFlags, HINTERNET *ret)
4473 http_session_t *lpwhs = NULL;
4474 HINTERNET handle = NULL;
4475 DWORD res = ERROR_SUCCESS;
4479 if (!lpszServerName || !lpszServerName[0])
4480 return ERROR_INVALID_PARAMETER;
4482 assert( hIC->hdr.htype == WH_HINIT );
4484 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4486 return ERROR_OUTOFMEMORY;
4489 * According to my tests. The name is not resolved until a request is sent
4492 lpwhs->hdr.htype = WH_HHTTPSESSION;
4493 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4494 lpwhs->hdr.dwFlags = dwFlags;
4495 lpwhs->hdr.dwContext = dwContext;
4496 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4497 lpwhs->hdr.refs = 1;
4498 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4500 WININET_AddRef( &hIC->hdr );
4501 lpwhs->lpAppInfo = hIC;
4502 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4504 handle = WININET_AllocHandle( &lpwhs->hdr );
4507 ERR("Failed to alloc handle\n");
4508 res = ERROR_OUTOFMEMORY;
4512 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4513 if(hIC->lpszProxyBypass)
4514 FIXME("Proxy bypass is ignored.\n");
4516 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4517 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4518 if (lpszUserName && lpszUserName[0])
4519 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4520 if (lpszPassword && lpszPassword[0])
4521 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4522 lpwhs->nServerPort = nServerPort;
4523 lpwhs->nHostPort = nServerPort;
4525 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4526 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4528 INTERNET_SendCallback(&hIC->hdr, dwContext,
4529 INTERNET_STATUS_HANDLE_CREATED, &handle,
4535 WININET_Release( &lpwhs->hdr );
4538 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4542 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4544 if(res == ERROR_SUCCESS)
4550 /***********************************************************************
4551 * HTTP_OpenConnection (internal)
4553 * Connect to a web server
4560 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4562 http_session_t *lpwhs;
4563 appinfo_t *hIC = NULL;
4564 char szaddr[INET6_ADDRSTRLEN];
4566 DWORD res = ERROR_SUCCESS;
4571 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4573 res = ERROR_INVALID_PARAMETER;
4577 if (NETCON_connected(&lpwhr->netConnection))
4579 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4581 lpwhs = lpwhr->lpHttpSession;
4583 hIC = lpwhs->lpAppInfo;
4584 switch (lpwhs->socketAddress.ss_family)
4587 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4590 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4593 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4594 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4596 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4597 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4598 INTERNET_STATUS_CONNECTING_TO_SERVER,
4602 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4603 if (res != ERROR_SUCCESS)
4605 WARN("Socket creation failed: %u\n", res);
4609 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4611 if(res != ERROR_SUCCESS)
4614 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4615 INTERNET_STATUS_CONNECTED_TO_SERVER,
4616 szaddr, strlen(szaddr)+1);
4618 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4620 /* Note: we differ from Microsoft's WinINet here. they seem to have
4621 * a bug that causes no status callbacks to be sent when starting
4622 * a tunnel to a proxy server using the CONNECT verb. i believe our
4623 * behaviour to be more correct and to not cause any incompatibilities
4624 * because using a secure connection through a proxy server is a rare
4625 * case that would be hard for anyone to depend on */
4626 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4627 HTTPREQ_CloseConnection(&lpwhr->hdr);
4631 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4632 if(res != ERROR_SUCCESS)
4634 WARN("Couldn't connect securely to host\n");
4636 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4637 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4638 || res == ERROR_INTERNET_INVALID_CA
4639 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4640 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4641 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4642 || res == ERROR_INTERNET_SEC_INVALID_CERT
4643 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4644 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4646 HTTPREQ_CloseConnection(&lpwhr->hdr);
4653 lpwhr->read_pos = lpwhr->read_size = 0;
4654 lpwhr->read_chunked = FALSE;
4656 TRACE("%d <--\n", res);
4661 /***********************************************************************
4662 * HTTP_clear_response_headers (internal)
4664 * clear out any old response headers
4666 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4670 for( i=0; i<lpwhr->nCustHeaders; i++)
4672 if( !lpwhr->pCustHeaders[i].lpszField )
4674 if( !lpwhr->pCustHeaders[i].lpszValue )
4676 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4678 HTTP_DeleteCustomHeader( lpwhr, i );
4683 /***********************************************************************
4684 * HTTP_GetResponseHeaders (internal)
4686 * Read server response
4693 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4696 WCHAR buffer[MAX_REPLY_LEN];
4697 DWORD buflen = MAX_REPLY_LEN;
4698 BOOL bSuccess = FALSE;
4700 char bufferA[MAX_REPLY_LEN];
4701 LPWSTR status_code = NULL, status_text = NULL;
4702 DWORD cchMaxRawHeaders = 1024;
4703 LPWSTR lpszRawHeaders = NULL;
4705 DWORD cchRawHeaders = 0;
4706 BOOL codeHundred = FALSE;
4710 if (!NETCON_connected(&lpwhr->netConnection))
4714 static const WCHAR szHundred[] = {'1','0','0',0};
4716 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4718 buflen = MAX_REPLY_LEN;
4719 if (!read_line(lpwhr, bufferA, &buflen))
4722 /* clear old response headers (eg. from a redirect response) */
4724 HTTP_clear_response_headers( lpwhr );
4729 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4730 /* check is this a status code line? */
4731 if (!strncmpW(buffer, g_szHttp1_0, 4))
4733 /* split the version from the status code */
4734 status_code = strchrW( buffer, ' ' );
4739 /* split the status code from the status text */
4740 status_text = strchrW( status_code, ' ' );
4745 TRACE("version [%s] status code [%s] status text [%s]\n",
4746 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4748 codeHundred = (!strcmpW(status_code, szHundred));
4750 else if (!codeHundred)
4752 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4754 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4755 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4757 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4758 lpwhr->lpszStatusText = heap_strdupW(szOK);
4760 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4761 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4766 } while (codeHundred);
4768 /* Add status code */
4769 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4770 HTTP_ADDHDR_FLAG_REPLACE);
4772 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4773 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4775 lpwhr->lpszVersion = heap_strdupW(buffer);
4776 lpwhr->lpszStatusText = heap_strdupW(status_text);
4778 /* Restore the spaces */
4779 *(status_code-1) = ' ';
4780 *(status_text-1) = ' ';
4782 /* regenerate raw headers */
4783 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4784 if (!lpszRawHeaders) goto lend;
4786 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4787 cchMaxRawHeaders *= 2;
4788 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4789 if (temp == NULL) goto lend;
4790 lpszRawHeaders = temp;
4791 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4792 cchRawHeaders += (buflen-1);
4793 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4794 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4795 lpszRawHeaders[cchRawHeaders] = '\0';
4797 /* Parse each response line */
4800 buflen = MAX_REPLY_LEN;
4801 if (read_line(lpwhr, bufferA, &buflen))
4803 LPWSTR * pFieldAndValue;
4805 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4807 if (!bufferA[0]) break;
4808 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4810 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4813 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4814 cchMaxRawHeaders *= 2;
4815 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4816 if (temp == NULL) goto lend;
4817 lpszRawHeaders = temp;
4818 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4819 cchRawHeaders += (buflen-1);
4820 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4821 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4822 lpszRawHeaders[cchRawHeaders] = '\0';
4824 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4825 HTTP_ADDREQ_FLAG_ADD );
4827 HTTP_FreeTokens(pFieldAndValue);
4838 /* make sure the response header is terminated with an empty line. Some apps really
4839 truly care about that empty line being there for some reason. Just add it to the
4841 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4843 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4844 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4845 if (temp == NULL) goto lend;
4846 lpszRawHeaders = temp;
4849 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4851 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4852 lpwhr->lpszRawHeaders = lpszRawHeaders;
4853 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4863 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4868 /***********************************************************************
4869 * HTTP_InterpretHttpHeader (internal)
4871 * Parse server response
4875 * Pointer to array of field, value, NULL on success.
4878 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4880 LPWSTR * pTokenPair;
4884 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4886 pszColon = strchrW(buffer, ':');
4887 /* must have two tokens */
4890 HTTP_FreeTokens(pTokenPair);
4892 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4896 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4899 HTTP_FreeTokens(pTokenPair);
4902 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4903 pTokenPair[0][pszColon - buffer] = '\0';
4907 len = strlenW(pszColon);
4908 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4911 HTTP_FreeTokens(pTokenPair);
4914 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4916 strip_spaces(pTokenPair[0]);
4917 strip_spaces(pTokenPair[1]);
4919 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4923 /***********************************************************************
4924 * HTTP_ProcessHeader (internal)
4926 * Stuff header into header tables according to <dwModifier>
4930 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4932 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4934 LPHTTPHEADERW lphttpHdr = NULL;
4936 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4937 DWORD res = ERROR_HTTP_INVALID_HEADER;
4939 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4941 /* REPLACE wins out over ADD */
4942 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4943 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4945 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4948 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4952 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4953 return ERROR_HTTP_INVALID_HEADER;
4954 lphttpHdr = &lpwhr->pCustHeaders[index];
4960 hdr.lpszField = (LPWSTR)field;
4961 hdr.lpszValue = (LPWSTR)value;
4962 hdr.wFlags = hdr.wCount = 0;
4964 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4965 hdr.wFlags |= HDR_ISREQUEST;
4967 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4969 /* no value to delete */
4970 else return ERROR_SUCCESS;
4972 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4973 lphttpHdr->wFlags |= HDR_ISREQUEST;
4975 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4977 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4979 HTTP_DeleteCustomHeader( lpwhr, index );
4985 hdr.lpszField = (LPWSTR)field;
4986 hdr.lpszValue = (LPWSTR)value;
4987 hdr.wFlags = hdr.wCount = 0;
4989 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4990 hdr.wFlags |= HDR_ISREQUEST;
4992 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4995 return ERROR_SUCCESS;
4997 else if (dwModifier & COALESCEFLAGS)
5002 INT origlen = strlenW(lphttpHdr->lpszValue);
5003 INT valuelen = strlenW(value);
5005 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5008 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5010 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5013 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5016 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5018 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5021 lphttpHdr->lpszValue = lpsztmp;
5022 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5025 lphttpHdr->lpszValue[origlen] = ch;
5027 lphttpHdr->lpszValue[origlen] = ' ';
5031 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5032 lphttpHdr->lpszValue[len] = '\0';
5033 res = ERROR_SUCCESS;
5037 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5038 res = ERROR_OUTOFMEMORY;
5041 TRACE("<-- %d\n", res);
5046 /***********************************************************************
5047 * HTTP_FinishedReading (internal)
5049 * Called when all content from server has been read by client.
5052 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5054 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5061 HTTPREQ_CloseConnection(&lpwhr->hdr);
5064 /* FIXME: store data in the URL cache here */
5070 /***********************************************************************
5071 * HTTP_GetCustomHeaderIndex (internal)
5073 * Return index of custom header from header array
5076 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5077 int requested_index, BOOL request_only)
5081 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5083 for (index = 0; index < lpwhr->nCustHeaders; index++)
5085 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5088 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5091 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5094 if (requested_index == 0)
5099 if (index >= lpwhr->nCustHeaders)
5102 TRACE("Return: %d\n", index);
5107 /***********************************************************************
5108 * HTTP_InsertCustomHeader (internal)
5110 * Insert header into array
5113 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5116 LPHTTPHEADERW lph = NULL;
5118 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5119 count = lpwhr->nCustHeaders + 1;
5121 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5123 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5126 return ERROR_OUTOFMEMORY;
5128 lpwhr->pCustHeaders = lph;
5129 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5130 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5131 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5132 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5133 lpwhr->nCustHeaders++;
5135 return ERROR_SUCCESS;
5139 /***********************************************************************
5140 * HTTP_DeleteCustomHeader (internal)
5142 * Delete header from array
5143 * If this function is called, the indexs may change.
5145 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5147 if( lpwhr->nCustHeaders <= 0 )
5149 if( index >= lpwhr->nCustHeaders )
5151 lpwhr->nCustHeaders--;
5153 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5154 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5156 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5157 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5158 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5164 /***********************************************************************
5165 * HTTP_VerifyValidHeader (internal)
5167 * Verify the given header is not invalid for the given http request
5170 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5172 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5173 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5174 return ERROR_HTTP_INVALID_HEADER;
5176 return ERROR_SUCCESS;
5179 /***********************************************************************
5180 * IsHostInProxyBypassList (@)
5185 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5187 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);