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 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1705 return ERROR_SUCCESS;
1708 case INTERNET_OPTION_HANDLE_TYPE:
1709 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1711 if (*size < sizeof(ULONG))
1712 return ERROR_INSUFFICIENT_BUFFER;
1714 *size = sizeof(DWORD);
1715 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1716 return ERROR_SUCCESS;
1718 case INTERNET_OPTION_URL: {
1719 WCHAR url[INTERNET_MAX_URL_LENGTH];
1724 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1726 TRACE("INTERNET_OPTION_URL\n");
1728 host = HTTP_GetHeader(req, hostW);
1729 strcpyW(url, httpW);
1730 strcatW(url, host->lpszValue);
1731 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1733 strcatW(url, req->lpszPath);
1735 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1738 len = (strlenW(url)+1) * sizeof(WCHAR);
1740 return ERROR_INSUFFICIENT_BUFFER;
1743 strcpyW(buffer, url);
1744 return ERROR_SUCCESS;
1746 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1748 return ERROR_INSUFFICIENT_BUFFER;
1751 return ERROR_SUCCESS;
1755 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1756 INTERNET_CACHE_ENTRY_INFOW *info;
1757 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1758 WCHAR url[INTERNET_MAX_URL_LENGTH];
1759 DWORD nbytes, error;
1762 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1764 if (*size < sizeof(*ts))
1766 *size = sizeof(*ts);
1767 return ERROR_INSUFFICIENT_BUFFER;
1770 HTTP_GetRequestURL(req, url);
1771 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1772 error = GetLastError();
1773 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1775 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1776 return ERROR_OUTOFMEMORY;
1778 GetUrlCacheEntryInfoW(url, info, &nbytes);
1780 ts->ftExpires = info->ExpireTime;
1781 ts->ftLastModified = info->LastModifiedTime;
1783 HeapFree(GetProcessHeap(), 0, info);
1784 *size = sizeof(*ts);
1785 return ERROR_SUCCESS;
1790 case INTERNET_OPTION_DATAFILE_NAME: {
1793 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1795 if(!req->lpszCacheFile) {
1797 return ERROR_INTERNET_ITEM_NOT_FOUND;
1801 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1802 if(*size < req_size)
1803 return ERROR_INSUFFICIENT_BUFFER;
1806 memcpy(buffer, req->lpszCacheFile, *size);
1807 return ERROR_SUCCESS;
1809 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1810 if (req_size > *size)
1811 return ERROR_INSUFFICIENT_BUFFER;
1813 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1814 -1, buffer, *size, NULL, NULL);
1815 return ERROR_SUCCESS;
1819 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1820 PCCERT_CONTEXT context;
1822 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1823 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1824 return ERROR_INSUFFICIENT_BUFFER;
1827 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1829 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1832 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1833 info->ftExpiry = context->pCertInfo->NotAfter;
1834 info->ftStart = context->pCertInfo->NotBefore;
1836 len = CertNameToStrW(context->dwCertEncodingType,
1837 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1838 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1839 if(info->lpszSubjectInfo)
1840 CertNameToStrW(context->dwCertEncodingType,
1841 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1842 info->lpszSubjectInfo, len);
1843 len = CertNameToStrW(context->dwCertEncodingType,
1844 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1845 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1846 if (info->lpszIssuerInfo)
1847 CertNameToStrW(context->dwCertEncodingType,
1848 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1849 info->lpszIssuerInfo, len);
1851 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1853 len = CertNameToStrA(context->dwCertEncodingType,
1854 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1855 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1856 if(infoA->lpszSubjectInfo)
1857 CertNameToStrA(context->dwCertEncodingType,
1858 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1859 infoA->lpszSubjectInfo, len);
1860 len = CertNameToStrA(context->dwCertEncodingType,
1861 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1862 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1863 if(infoA->lpszIssuerInfo)
1864 CertNameToStrA(context->dwCertEncodingType,
1865 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1866 infoA->lpszIssuerInfo, len);
1870 * Contrary to MSDN, these do not appear to be set.
1872 * lpszSignatureAlgName
1873 * lpszEncryptionAlgName
1876 CertFreeCertificateContext(context);
1877 return ERROR_SUCCESS;
1882 return INET_QueryOption(hdr, option, buffer, size, unicode);
1885 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1887 http_request_t *req = (http_request_t*)hdr;
1890 case INTERNET_OPTION_SEND_TIMEOUT:
1891 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1892 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1894 if (size != sizeof(DWORD))
1895 return ERROR_INVALID_PARAMETER;
1897 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1900 case INTERNET_OPTION_USERNAME:
1901 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1902 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1903 return ERROR_SUCCESS;
1905 case INTERNET_OPTION_PASSWORD:
1906 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1907 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1908 return ERROR_SUCCESS;
1909 case INTERNET_OPTION_HTTP_DECODING:
1910 if(size != sizeof(BOOL))
1911 return ERROR_INVALID_PARAMETER;
1912 req->decoding = *(BOOL*)buffer;
1913 return ERROR_SUCCESS;
1916 return ERROR_INTERNET_INVALID_OPTION;
1919 /* read some more data into the read buffer (the read section must be held) */
1920 static DWORD read_more_data( http_request_t *req, int maxlen )
1927 /* move existing data to the start of the buffer */
1929 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1933 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1935 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1936 maxlen - req->read_size, 0, &len );
1937 if(res == ERROR_SUCCESS)
1938 req->read_size += len;
1943 /* remove some amount of data from the read buffer (the read section must be held) */
1944 static void remove_data( http_request_t *req, int count )
1946 if (!(req->read_size -= count)) req->read_pos = 0;
1947 else req->read_pos += count;
1950 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1952 int count, bytes_read, pos = 0;
1955 EnterCriticalSection( &req->read_section );
1958 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1962 count = eol - (req->read_buf + req->read_pos);
1963 bytes_read = count + 1;
1965 else count = bytes_read = req->read_size;
1967 count = min( count, *len - pos );
1968 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1970 remove_data( req, bytes_read );
1973 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1976 TRACE( "returning empty string\n" );
1977 LeaveCriticalSection( &req->read_section );
1978 INTERNET_SetLastError(res);
1982 LeaveCriticalSection( &req->read_section );
1986 if (pos && buffer[pos - 1] == '\r') pos--;
1989 buffer[*len - 1] = 0;
1990 TRACE( "returning %s\n", debugstr_a(buffer));
1994 /* discard data contents until we reach end of line (the read section must be held) */
1995 static DWORD discard_eol( http_request_t *req )
2001 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2004 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2007 req->read_pos = req->read_size = 0; /* discard everything */
2008 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2009 } while (req->read_size);
2010 return ERROR_SUCCESS;
2013 /* read the size of the next chunk (the read section must be held) */
2014 static DWORD start_next_chunk( http_request_t *req )
2016 DWORD chunk_size = 0, res;
2018 if (!req->dwContentLength) return ERROR_SUCCESS;
2019 if (req->dwContentLength == req->dwContentRead)
2021 /* read terminator for the previous chunk */
2022 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2023 req->dwContentLength = ~0u;
2024 req->dwContentRead = 0;
2028 while (req->read_size)
2030 char ch = req->read_buf[req->read_pos];
2031 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2032 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2033 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2034 else if (ch == ';' || ch == '\r' || ch == '\n')
2036 TRACE( "reading %u byte chunk\n", chunk_size );
2037 req->dwContentLength = chunk_size;
2038 req->dwContentRead = 0;
2039 return discard_eol( req );
2041 remove_data( req, 1 );
2043 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2044 if (!req->read_size)
2046 req->dwContentLength = req->dwContentRead = 0;
2047 return ERROR_SUCCESS;
2052 /* check if we have reached the end of the data to read (the read section must be held) */
2053 static BOOL end_of_read_data( http_request_t *req )
2055 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2056 if (req->read_chunked) return (req->dwContentLength == 0);
2057 if (req->dwContentLength == ~0u) return FALSE;
2058 return (req->dwContentLength == req->dwContentRead);
2061 /* fetch some more data into the read buffer (the read section must be held) */
2062 static DWORD refill_buffer( http_request_t *req )
2064 int len = sizeof(req->read_buf);
2067 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2069 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2072 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2073 if (len <= req->read_size) return ERROR_SUCCESS;
2075 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2076 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2077 return ERROR_SUCCESS;
2080 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2082 DWORD ret = ERROR_SUCCESS;
2086 z_stream *zstream = &req->gzip_stream->zstream;
2090 while(read < size && !req->gzip_stream->end_of_data) {
2091 if(!req->read_size) {
2092 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2096 if(req->dwContentRead == req->dwContentLength)
2099 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2101 zstream->next_in = req->read_buf+req->read_pos;
2102 zstream->avail_in = buf_avail;
2103 zstream->next_out = buf+read;
2104 zstream->avail_out = size-read;
2105 zres = inflate(zstream, Z_FULL_FLUSH);
2106 read = size - zstream->avail_out;
2107 req->dwContentRead += buf_avail-zstream->avail_in;
2108 remove_data(req, buf_avail-zstream->avail_in);
2109 if(zres == Z_STREAM_END) {
2110 TRACE("end of data\n");
2111 req->gzip_stream->end_of_data = TRUE;
2112 inflateEnd(&req->gzip_stream->zstream);
2113 }else if(zres != Z_OK) {
2114 WARN("inflate failed %d\n", zres);
2116 ret = ERROR_INTERNET_DECODING_FAILED;
2126 static void refill_gzip_buffer(http_request_t *req)
2131 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2134 if(req->gzip_stream->buf_pos) {
2135 if(req->gzip_stream->buf_size)
2136 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2137 req->gzip_stream->buf_pos = 0;
2140 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2141 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2142 if(res == ERROR_SUCCESS)
2143 req->gzip_stream->buf_size += len;
2146 /* return the size of data available to be read immediately (the read section must be held) */
2147 static DWORD get_avail_data( http_request_t *req )
2149 if (req->gzip_stream) {
2150 refill_gzip_buffer(req);
2151 return req->gzip_stream->buf_size;
2153 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2155 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2158 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2160 INTERNET_ASYNC_RESULT iar;
2165 EnterCriticalSection( &req->read_section );
2166 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2167 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2168 iar.dwError = first_notif ? 0 : get_avail_data(req);
2173 LeaveCriticalSection( &req->read_section );
2175 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2176 sizeof(INTERNET_ASYNC_RESULT));
2179 /* read data from the http connection (the read section must be held) */
2180 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2182 BOOL finished_reading = FALSE;
2183 int len, bytes_read = 0;
2184 DWORD ret = ERROR_SUCCESS;
2186 EnterCriticalSection( &req->read_section );
2188 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2190 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2193 if(req->gzip_stream) {
2194 if(req->gzip_stream->buf_size) {
2195 bytes_read = min(req->gzip_stream->buf_size, size);
2196 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2197 req->gzip_stream->buf_pos += bytes_read;
2198 req->gzip_stream->buf_size -= bytes_read;
2199 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2203 if(size > bytes_read) {
2204 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2205 if(ret == ERROR_SUCCESS)
2209 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2211 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2213 if (req->read_size) {
2214 bytes_read = min( req->read_size, size );
2215 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2216 remove_data( req, bytes_read );
2219 if (size > bytes_read && (!bytes_read || sync)) {
2220 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2221 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2223 /* always return success, even if the network layer returns an error */
2226 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2227 req->dwContentRead += bytes_read;
2232 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2233 LeaveCriticalSection( &req->read_section );
2235 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2237 DWORD dwBytesWritten;
2239 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2241 WARN("WriteFile failed: %u\n", GetLastError());
2244 if(finished_reading)
2245 HTTP_FinishedReading(req);
2251 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2253 http_request_t *req = (http_request_t*)hdr;
2256 EnterCriticalSection( &req->read_section );
2257 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2258 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2260 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2261 if(res == ERROR_SUCCESS)
2263 LeaveCriticalSection( &req->read_section );
2268 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2270 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2271 http_request_t *req = (http_request_t*)workRequest->hdr;
2272 INTERNET_ASYNC_RESULT iar;
2275 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2277 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2278 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2280 iar.dwResult = res == ERROR_SUCCESS;
2283 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2284 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2285 sizeof(INTERNET_ASYNC_RESULT));
2288 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2289 DWORD flags, DWORD_PTR context)
2291 http_request_t *req = (http_request_t*)hdr;
2292 DWORD res, size, read, error = ERROR_SUCCESS;
2294 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2295 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2297 if (buffers->dwStructSize != sizeof(*buffers))
2298 return ERROR_INVALID_PARAMETER;
2300 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2302 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2304 WORKREQUEST workRequest;
2306 if (TryEnterCriticalSection( &req->read_section ))
2308 if (get_avail_data(req))
2310 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2311 &buffers->dwBufferLength, FALSE);
2312 size = buffers->dwBufferLength;
2313 LeaveCriticalSection( &req->read_section );
2316 LeaveCriticalSection( &req->read_section );
2319 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2320 workRequest.hdr = WININET_AddRef(&req->hdr);
2321 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2323 INTERNET_AsyncCall(&workRequest);
2325 return ERROR_IO_PENDING;
2329 size = buffers->dwBufferLength;
2331 EnterCriticalSection( &req->read_section );
2332 if(hdr->dwError == ERROR_SUCCESS)
2333 hdr->dwError = INTERNET_HANDLE_IN_USE;
2334 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2335 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2338 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2339 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2340 if(res == ERROR_SUCCESS)
2341 read += buffers->dwBufferLength;
2345 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2346 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2348 LeaveCriticalSection( &req->read_section );
2350 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2351 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2352 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2353 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2355 EnterCriticalSection( &req->read_section );
2358 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2359 hdr->dwError = ERROR_SUCCESS;
2361 error = hdr->dwError;
2363 LeaveCriticalSection( &req->read_section );
2364 size = buffers->dwBufferLength;
2365 buffers->dwBufferLength = read;
2368 if (res == ERROR_SUCCESS) {
2369 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2370 &size, sizeof(size));
2373 return res==ERROR_SUCCESS ? error : res;
2376 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2378 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2379 http_request_t *req = (http_request_t*)workRequest->hdr;
2380 INTERNET_ASYNC_RESULT iar;
2383 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2385 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2386 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2388 iar.dwResult = res == ERROR_SUCCESS;
2391 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2392 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2393 sizeof(INTERNET_ASYNC_RESULT));
2396 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2397 DWORD flags, DWORD_PTR context)
2400 http_request_t *req = (http_request_t*)hdr;
2401 DWORD res, size, read, error = ERROR_SUCCESS;
2403 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2404 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2406 if (buffers->dwStructSize != sizeof(*buffers))
2407 return ERROR_INVALID_PARAMETER;
2409 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2411 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2413 WORKREQUEST workRequest;
2415 if (TryEnterCriticalSection( &req->read_section ))
2417 if (get_avail_data(req))
2419 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2420 &buffers->dwBufferLength, FALSE);
2421 size = buffers->dwBufferLength;
2422 LeaveCriticalSection( &req->read_section );
2425 LeaveCriticalSection( &req->read_section );
2428 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2429 workRequest.hdr = WININET_AddRef(&req->hdr);
2430 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2432 INTERNET_AsyncCall(&workRequest);
2434 return ERROR_IO_PENDING;
2438 size = buffers->dwBufferLength;
2440 EnterCriticalSection( &req->read_section );
2441 if(hdr->dwError == ERROR_SUCCESS)
2442 hdr->dwError = INTERNET_HANDLE_IN_USE;
2443 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2444 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2447 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2448 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2449 if(res == ERROR_SUCCESS)
2450 read += buffers->dwBufferLength;
2454 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2455 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2457 LeaveCriticalSection( &req->read_section );
2459 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2460 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2461 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2462 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2464 EnterCriticalSection( &req->read_section );
2467 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2468 hdr->dwError = ERROR_SUCCESS;
2470 error = hdr->dwError;
2472 LeaveCriticalSection( &req->read_section );
2473 size = buffers->dwBufferLength;
2474 buffers->dwBufferLength = read;
2477 if (res == ERROR_SUCCESS) {
2478 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2479 &size, sizeof(size));
2482 return res==ERROR_SUCCESS ? error : res;
2485 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2488 http_request_t *lpwhr = (http_request_t*)hdr;
2490 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2493 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2494 if (res == ERROR_SUCCESS)
2495 lpwhr->dwBytesWritten += *written;
2497 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2501 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2503 http_request_t *req = (http_request_t*)workRequest->hdr;
2505 HTTP_ReceiveRequestData(req, FALSE);
2508 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2510 http_request_t *req = (http_request_t*)hdr;
2512 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2514 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2516 WORKREQUEST workRequest;
2518 /* never wait, if we can't enter the section we queue an async request right away */
2519 if (TryEnterCriticalSection( &req->read_section ))
2521 if ((*available = get_avail_data( req ))) goto done;
2522 if (end_of_read_data( req )) goto done;
2523 LeaveCriticalSection( &req->read_section );
2526 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2527 workRequest.hdr = WININET_AddRef( &req->hdr );
2529 INTERNET_AsyncCall(&workRequest);
2531 return ERROR_IO_PENDING;
2534 EnterCriticalSection( &req->read_section );
2536 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2538 refill_buffer( req );
2539 *available = get_avail_data( req );
2543 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2546 if (NETCON_query_data_available(&req->netConnection, &extra))
2547 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2549 LeaveCriticalSection( &req->read_section );
2551 TRACE( "returning %u\n", *available );
2552 return ERROR_SUCCESS;
2555 static const object_vtbl_t HTTPREQVtbl = {
2557 HTTPREQ_CloseConnection,
2558 HTTPREQ_QueryOption,
2561 HTTPREQ_ReadFileExA,
2562 HTTPREQ_ReadFileExW,
2564 HTTPREQ_QueryDataAvailable,
2568 /***********************************************************************
2569 * HTTP_HttpOpenRequestW (internal)
2571 * Open a HTTP request handle
2574 * HINTERNET a HTTP request handle on success
2578 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2579 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2580 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2581 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2583 appinfo_t *hIC = NULL;
2584 http_request_t *lpwhr;
2585 LPWSTR lpszHostName = NULL;
2586 HINTERNET handle = NULL;
2587 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2592 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2593 hIC = lpwhs->lpAppInfo;
2595 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2598 res = ERROR_OUTOFMEMORY;
2601 lpwhr->hdr.htype = WH_HHTTPREQ;
2602 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2603 lpwhr->hdr.dwFlags = dwFlags;
2604 lpwhr->hdr.dwContext = dwContext;
2605 lpwhr->hdr.refs = 1;
2606 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2607 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2608 lpwhr->dwContentLength = ~0u;
2609 InitializeCriticalSection( &lpwhr->read_section );
2611 WININET_AddRef( &lpwhs->hdr );
2612 lpwhr->lpHttpSession = lpwhs;
2613 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2615 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2616 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2617 if (NULL == lpszHostName)
2619 res = ERROR_OUTOFMEMORY;
2623 handle = WININET_AllocHandle( &lpwhr->hdr );
2626 res = ERROR_OUTOFMEMORY;
2630 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2632 InternetCloseHandle( handle );
2637 if (lpszObjectName && *lpszObjectName) {
2641 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2642 if (rc != E_POINTER)
2643 len = strlenW(lpszObjectName)+1;
2644 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2645 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2646 URL_ESCAPE_SPACES_ONLY);
2649 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2650 strcpyW(lpwhr->lpszPath,lpszObjectName);
2653 static const WCHAR slashW[] = {'/',0};
2655 lpwhr->lpszPath = heap_strdupW(slashW);
2658 if (lpszReferrer && *lpszReferrer)
2659 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2661 if (lpszAcceptTypes)
2664 for (i = 0; lpszAcceptTypes[i]; i++)
2666 if (!*lpszAcceptTypes[i]) continue;
2667 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2668 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2669 HTTP_ADDHDR_FLAG_REQ |
2670 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2674 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2675 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2677 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2678 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2679 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2681 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2682 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2683 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2686 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2687 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2689 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2690 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2691 INTERNET_DEFAULT_HTTPS_PORT :
2692 INTERNET_DEFAULT_HTTP_PORT);
2694 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2695 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2696 INTERNET_DEFAULT_HTTPS_PORT :
2697 INTERNET_DEFAULT_HTTP_PORT);
2699 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2700 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2702 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2703 INTERNET_STATUS_HANDLE_CREATED, &handle,
2707 HeapFree(GetProcessHeap(), 0, lpszHostName);
2709 WININET_Release( &lpwhr->hdr );
2711 TRACE("<-- %p (%p)\n", handle, lpwhr);
2716 /***********************************************************************
2717 * HttpOpenRequestW (WININET.@)
2719 * Open a HTTP request handle
2722 * HINTERNET a HTTP request handle on success
2726 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2727 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2728 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2729 DWORD dwFlags, DWORD_PTR dwContext)
2731 http_session_t *lpwhs;
2732 HINTERNET handle = NULL;
2735 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2736 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2737 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2738 dwFlags, dwContext);
2739 if(lpszAcceptTypes!=NULL)
2742 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2743 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2746 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2747 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2749 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2754 * My tests seem to show that the windows version does not
2755 * become asynchronous until after this point. And anyhow
2756 * if this call was asynchronous then how would you get the
2757 * necessary HINTERNET pointer returned by this function.
2760 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2761 lpszVersion, lpszReferrer, lpszAcceptTypes,
2762 dwFlags, dwContext, &handle);
2765 WININET_Release( &lpwhs->hdr );
2766 TRACE("returning %p\n", handle);
2767 if(res != ERROR_SUCCESS)
2772 /* read any content returned by the server so that the connection can be
2774 static void HTTP_DrainContent(http_request_t *req)
2778 if (!NETCON_connected(&req->netConnection)) return;
2780 if (req->dwContentLength == -1)
2782 NETCON_close(&req->netConnection);
2785 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2790 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2792 } while (bytes_read);
2795 static const LPCWSTR header_lookup[] = {
2796 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2797 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2798 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2799 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2800 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2801 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2802 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2803 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2804 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2805 szDate, /* HTTP_QUERY_DATE = 9 */
2806 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2807 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2808 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2809 szURI, /* HTTP_QUERY_URI = 13 */
2810 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2811 NULL, /* HTTP_QUERY_COST = 15 */
2812 NULL, /* HTTP_QUERY_LINK = 16 */
2813 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2814 NULL, /* HTTP_QUERY_VERSION = 18 */
2815 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2816 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2817 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2818 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2819 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2820 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2821 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2822 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2823 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2824 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2825 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2826 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2827 NULL, /* HTTP_QUERY_FROM = 31 */
2828 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2829 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2830 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2831 szReferer, /* HTTP_QUERY_REFERER = 35 */
2832 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2833 szServer, /* HTTP_QUERY_SERVER = 37 */
2834 NULL, /* HTTP_TITLE = 38 */
2835 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2836 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2837 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2838 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2839 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2840 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2841 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2842 NULL, /* HTTP_QUERY_REFRESH = 46 */
2843 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2844 szAge, /* HTTP_QUERY_AGE = 48 */
2845 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2846 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2847 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2848 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2849 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2850 szETag, /* HTTP_QUERY_ETAG = 54 */
2851 hostW, /* HTTP_QUERY_HOST = 55 */
2852 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2853 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2854 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2855 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2856 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2857 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2858 szRange, /* HTTP_QUERY_RANGE = 62 */
2859 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2860 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2861 szVary, /* HTTP_QUERY_VARY = 65 */
2862 szVia, /* HTTP_QUERY_VIA = 66 */
2863 szWarning, /* HTTP_QUERY_WARNING = 67 */
2864 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2865 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2866 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2869 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2871 /***********************************************************************
2872 * HTTP_HttpQueryInfoW (internal)
2874 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2875 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2877 LPHTTPHEADERW lphttpHdr = NULL;
2878 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2879 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2880 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2883 /* Find requested header structure */
2886 case HTTP_QUERY_CUSTOM:
2887 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2888 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2890 case HTTP_QUERY_RAW_HEADERS_CRLF:
2894 DWORD res = ERROR_INVALID_PARAMETER;
2897 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2899 headers = lpwhr->lpszRawHeaders;
2902 len = strlenW(headers) * sizeof(WCHAR);
2904 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2906 len += sizeof(WCHAR);
2907 res = ERROR_INSUFFICIENT_BUFFER;
2912 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2915 len = strlenW(szCrLf) * sizeof(WCHAR);
2916 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2918 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2919 res = ERROR_SUCCESS;
2921 *lpdwBufferLength = len;
2924 HeapFree(GetProcessHeap(), 0, headers);
2927 case HTTP_QUERY_RAW_HEADERS:
2929 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2931 LPWSTR pszString = lpBuffer;
2933 for (i = 0; ppszRawHeaderLines[i]; i++)
2934 size += strlenW(ppszRawHeaderLines[i]) + 1;
2936 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2938 HTTP_FreeTokens(ppszRawHeaderLines);
2939 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2940 return ERROR_INSUFFICIENT_BUFFER;
2944 for (i = 0; ppszRawHeaderLines[i]; i++)
2946 DWORD len = strlenW(ppszRawHeaderLines[i]);
2947 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2951 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2953 *lpdwBufferLength = size * sizeof(WCHAR);
2954 HTTP_FreeTokens(ppszRawHeaderLines);
2956 return ERROR_SUCCESS;
2958 case HTTP_QUERY_STATUS_TEXT:
2959 if (lpwhr->lpszStatusText)
2961 DWORD len = strlenW(lpwhr->lpszStatusText);
2962 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2964 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2965 return ERROR_INSUFFICIENT_BUFFER;
2969 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2970 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2972 *lpdwBufferLength = len * sizeof(WCHAR);
2973 return ERROR_SUCCESS;
2976 case HTTP_QUERY_VERSION:
2977 if (lpwhr->lpszVersion)
2979 DWORD len = strlenW(lpwhr->lpszVersion);
2980 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2982 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2983 return ERROR_INSUFFICIENT_BUFFER;
2987 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2988 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2990 *lpdwBufferLength = len * sizeof(WCHAR);
2991 return ERROR_SUCCESS;
2994 case HTTP_QUERY_CONTENT_ENCODING:
2995 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2996 requested_index,request_only);
2999 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3001 if (level < LAST_TABLE_HEADER && header_lookup[level])
3002 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
3003 requested_index,request_only);
3007 lphttpHdr = &lpwhr->pCustHeaders[index];
3009 /* Ensure header satisfies requested attributes */
3011 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3012 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3014 return ERROR_HTTP_HEADER_NOT_FOUND;
3017 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3019 /* coalesce value to requested type */
3020 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3022 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3023 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3025 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3031 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3033 tmpTM = *gmtime(&tmpTime);
3034 STHook = (SYSTEMTIME *)lpBuffer;
3035 STHook->wDay = tmpTM.tm_mday;
3036 STHook->wHour = tmpTM.tm_hour;
3037 STHook->wMilliseconds = 0;
3038 STHook->wMinute = tmpTM.tm_min;
3039 STHook->wDayOfWeek = tmpTM.tm_wday;
3040 STHook->wMonth = tmpTM.tm_mon + 1;
3041 STHook->wSecond = tmpTM.tm_sec;
3042 STHook->wYear = tmpTM.tm_year;
3044 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3045 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3046 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3048 else if (lphttpHdr->lpszValue)
3050 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3052 if (len > *lpdwBufferLength)
3054 *lpdwBufferLength = len;
3055 return ERROR_INSUFFICIENT_BUFFER;
3059 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3060 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3062 *lpdwBufferLength = len - sizeof(WCHAR);
3064 return ERROR_SUCCESS;
3067 /***********************************************************************
3068 * HttpQueryInfoW (WININET.@)
3070 * Queries for information about an HTTP request
3077 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3078 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3080 http_request_t *lpwhr;
3083 if (TRACE_ON(wininet)) {
3084 #define FE(x) { x, #x }
3085 static const wininet_flag_info query_flags[] = {
3086 FE(HTTP_QUERY_MIME_VERSION),
3087 FE(HTTP_QUERY_CONTENT_TYPE),
3088 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3089 FE(HTTP_QUERY_CONTENT_ID),
3090 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3091 FE(HTTP_QUERY_CONTENT_LENGTH),
3092 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3093 FE(HTTP_QUERY_ALLOW),
3094 FE(HTTP_QUERY_PUBLIC),
3095 FE(HTTP_QUERY_DATE),
3096 FE(HTTP_QUERY_EXPIRES),
3097 FE(HTTP_QUERY_LAST_MODIFIED),
3098 FE(HTTP_QUERY_MESSAGE_ID),
3100 FE(HTTP_QUERY_DERIVED_FROM),
3101 FE(HTTP_QUERY_COST),
3102 FE(HTTP_QUERY_LINK),
3103 FE(HTTP_QUERY_PRAGMA),
3104 FE(HTTP_QUERY_VERSION),
3105 FE(HTTP_QUERY_STATUS_CODE),
3106 FE(HTTP_QUERY_STATUS_TEXT),
3107 FE(HTTP_QUERY_RAW_HEADERS),
3108 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3109 FE(HTTP_QUERY_CONNECTION),
3110 FE(HTTP_QUERY_ACCEPT),
3111 FE(HTTP_QUERY_ACCEPT_CHARSET),
3112 FE(HTTP_QUERY_ACCEPT_ENCODING),
3113 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3114 FE(HTTP_QUERY_AUTHORIZATION),
3115 FE(HTTP_QUERY_CONTENT_ENCODING),
3116 FE(HTTP_QUERY_FORWARDED),
3117 FE(HTTP_QUERY_FROM),
3118 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3119 FE(HTTP_QUERY_LOCATION),
3120 FE(HTTP_QUERY_ORIG_URI),
3121 FE(HTTP_QUERY_REFERER),
3122 FE(HTTP_QUERY_RETRY_AFTER),
3123 FE(HTTP_QUERY_SERVER),
3124 FE(HTTP_QUERY_TITLE),
3125 FE(HTTP_QUERY_USER_AGENT),
3126 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3127 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3128 FE(HTTP_QUERY_ACCEPT_RANGES),
3129 FE(HTTP_QUERY_SET_COOKIE),
3130 FE(HTTP_QUERY_COOKIE),
3131 FE(HTTP_QUERY_REQUEST_METHOD),
3132 FE(HTTP_QUERY_REFRESH),
3133 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3135 FE(HTTP_QUERY_CACHE_CONTROL),
3136 FE(HTTP_QUERY_CONTENT_BASE),
3137 FE(HTTP_QUERY_CONTENT_LOCATION),
3138 FE(HTTP_QUERY_CONTENT_MD5),
3139 FE(HTTP_QUERY_CONTENT_RANGE),
3140 FE(HTTP_QUERY_ETAG),
3141 FE(HTTP_QUERY_HOST),
3142 FE(HTTP_QUERY_IF_MATCH),
3143 FE(HTTP_QUERY_IF_NONE_MATCH),
3144 FE(HTTP_QUERY_IF_RANGE),
3145 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3146 FE(HTTP_QUERY_MAX_FORWARDS),
3147 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3148 FE(HTTP_QUERY_RANGE),
3149 FE(HTTP_QUERY_TRANSFER_ENCODING),
3150 FE(HTTP_QUERY_UPGRADE),
3151 FE(HTTP_QUERY_VARY),
3153 FE(HTTP_QUERY_WARNING),
3154 FE(HTTP_QUERY_CUSTOM)
3156 static const wininet_flag_info modifier_flags[] = {
3157 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3158 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3159 FE(HTTP_QUERY_FLAG_NUMBER),
3160 FE(HTTP_QUERY_FLAG_COALESCE)
3163 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3164 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3167 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3168 TRACE(" Attribute:");
3169 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3170 if (query_flags[i].val == info) {
3171 TRACE(" %s", query_flags[i].name);
3175 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3176 TRACE(" Unknown (%08x)", info);
3179 TRACE(" Modifier:");
3180 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3181 if (modifier_flags[i].val & info_mod) {
3182 TRACE(" %s", modifier_flags[i].name);
3183 info_mod &= ~ modifier_flags[i].val;
3188 TRACE(" Unknown (%08x)", info_mod);
3193 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3194 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3196 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3200 if (lpBuffer == NULL)
3201 *lpdwBufferLength = 0;
3202 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3203 lpBuffer, lpdwBufferLength, lpdwIndex);
3207 WININET_Release( &lpwhr->hdr );
3209 TRACE("%u <--\n", res);
3210 if(res != ERROR_SUCCESS)
3212 return res == ERROR_SUCCESS;
3215 /***********************************************************************
3216 * HttpQueryInfoA (WININET.@)
3218 * Queries for information about an HTTP request
3225 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3226 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3232 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3233 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3235 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3236 lpdwBufferLength, lpdwIndex );
3242 len = (*lpdwBufferLength)*sizeof(WCHAR);
3243 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3245 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3251 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3252 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3253 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3254 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3261 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3265 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3266 lpBuffer, *lpdwBufferLength, NULL, NULL );
3267 *lpdwBufferLength = len - 1;
3269 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3272 /* since the strings being returned from HttpQueryInfoW should be
3273 * only ASCII characters, it is reasonable to assume that all of
3274 * the Unicode characters can be reduced to a single byte */
3275 *lpdwBufferLength = len / sizeof(WCHAR);
3277 HeapFree(GetProcessHeap(), 0, bufferW );
3282 /***********************************************************************
3283 * HTTP_GetRedirectURL (internal)
3285 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3287 static WCHAR szHttp[] = {'h','t','t','p',0};
3288 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3289 http_session_t *lpwhs = lpwhr->lpHttpSession;
3290 URL_COMPONENTSW urlComponents;
3291 DWORD url_length = 0;
3293 LPWSTR combined_url;
3295 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3296 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3297 urlComponents.dwSchemeLength = 0;
3298 urlComponents.lpszHostName = lpwhs->lpszHostName;
3299 urlComponents.dwHostNameLength = 0;
3300 urlComponents.nPort = lpwhs->nHostPort;
3301 urlComponents.lpszUserName = lpwhs->lpszUserName;
3302 urlComponents.dwUserNameLength = 0;
3303 urlComponents.lpszPassword = NULL;
3304 urlComponents.dwPasswordLength = 0;
3305 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3306 urlComponents.dwUrlPathLength = 0;
3307 urlComponents.lpszExtraInfo = NULL;
3308 urlComponents.dwExtraInfoLength = 0;
3310 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3311 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3314 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3316 /* convert from bytes to characters */
3317 url_length = url_length / sizeof(WCHAR) - 1;
3318 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3320 HeapFree(GetProcessHeap(), 0, orig_url);
3325 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3326 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3328 HeapFree(GetProcessHeap(), 0, orig_url);
3331 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3333 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3335 HeapFree(GetProcessHeap(), 0, orig_url);
3336 HeapFree(GetProcessHeap(), 0, combined_url);
3339 HeapFree(GetProcessHeap(), 0, orig_url);
3340 return combined_url;
3344 /***********************************************************************
3345 * HTTP_HandleRedirect (internal)
3347 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3349 http_session_t *lpwhs = lpwhr->lpHttpSession;
3350 appinfo_t *hIC = lpwhs->lpAppInfo;
3351 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3352 WCHAR path[INTERNET_MAX_URL_LENGTH];
3357 /* if it's an absolute path, keep the same session info */
3358 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3362 URL_COMPONENTSW urlComponents;
3363 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3364 static WCHAR szHttp[] = {'h','t','t','p',0};
3365 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3371 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3372 urlComponents.lpszScheme = protocol;
3373 urlComponents.dwSchemeLength = 32;
3374 urlComponents.lpszHostName = hostName;
3375 urlComponents.dwHostNameLength = MAXHOSTNAME;
3376 urlComponents.lpszUserName = userName;
3377 urlComponents.dwUserNameLength = 1024;
3378 urlComponents.lpszPassword = NULL;
3379 urlComponents.dwPasswordLength = 0;
3380 urlComponents.lpszUrlPath = path;
3381 urlComponents.dwUrlPathLength = 2048;
3382 urlComponents.lpszExtraInfo = NULL;
3383 urlComponents.dwExtraInfoLength = 0;
3384 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3385 return INTERNET_GetLastError();
3387 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3388 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3390 TRACE("redirect from secure page to non-secure page\n");
3391 /* FIXME: warn about from secure redirect to non-secure page */
3392 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3394 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3395 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3397 TRACE("redirect from non-secure page to secure page\n");
3398 /* FIXME: notify about redirect to secure page */
3399 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3402 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3404 if (lstrlenW(protocol)>4) /*https*/
3405 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3407 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3412 * This upsets redirects to binary files on sourceforge.net
3413 * and gives an html page instead of the target file
3414 * Examination of the HTTP request sent by native wininet.dll
3415 * reveals that it doesn't send a referrer in that case.
3416 * Maybe there's a flag that enables this, or maybe a referrer
3417 * shouldn't be added in case of a redirect.
3420 /* consider the current host as the referrer */
3421 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3422 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3423 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3424 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3427 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3428 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3429 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3432 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3433 len = lstrlenW(hostName);
3434 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3435 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3436 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3439 lpwhs->lpszHostName = heap_strdupW(hostName);
3441 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3443 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3444 lpwhs->lpszUserName = NULL;
3446 lpwhs->lpszUserName = heap_strdupW(userName);
3450 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3454 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3455 lpwhs->lpszServerName = heap_strdupW(hostName);
3456 lpwhs->nServerPort = urlComponents.nPort;
3458 NETCON_close(&lpwhr->netConnection);
3459 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3462 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3463 if (res != ERROR_SUCCESS)
3466 lpwhr->read_pos = lpwhr->read_size = 0;
3467 lpwhr->read_chunked = FALSE;
3471 TRACE("Redirect through proxy\n");
3474 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3475 lpwhr->lpszPath=NULL;
3481 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3482 if (rc != E_POINTER)
3483 needed = strlenW(path)+1;
3484 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3485 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3486 URL_ESCAPE_SPACES_ONLY);
3489 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3490 strcpyW(lpwhr->lpszPath,path);
3494 /* Remove custom content-type/length headers on redirects. */
3495 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3497 HTTP_DeleteCustomHeader(lpwhr, index);
3498 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3500 HTTP_DeleteCustomHeader(lpwhr, index);
3502 return ERROR_SUCCESS;
3505 /***********************************************************************
3506 * HTTP_build_req (internal)
3508 * concatenate all the strings in the request together
3510 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3515 for( t = list; *t ; t++ )
3516 len += strlenW( *t );
3519 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3522 for( t = list; *t ; t++ )
3528 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3531 LPWSTR requestString;
3537 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3538 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3539 http_session_t *lpwhs = lpwhr->lpHttpSession;
3543 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3544 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3545 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3546 HeapFree( GetProcessHeap(), 0, lpszPath );
3548 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3549 NULL, 0, NULL, NULL );
3550 len--; /* the nul terminator isn't needed */
3551 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3552 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3553 ascii_req, len, NULL, NULL );
3554 HeapFree( GetProcessHeap(), 0, requestString );
3556 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3558 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3559 HeapFree( GetProcessHeap(), 0, ascii_req );
3560 if (res != ERROR_SUCCESS)
3563 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3565 return ERROR_HTTP_INVALID_HEADER;
3567 return ERROR_SUCCESS;
3570 static void HTTP_InsertCookies(http_request_t *lpwhr)
3572 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3573 LPWSTR lpszCookies, lpszUrl = NULL;
3574 DWORD nCookieSize, size;
3575 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3577 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3578 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3579 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3581 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3584 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3586 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3587 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3589 cnt += sprintfW(lpszCookies, szCookie);
3590 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3591 strcatW(lpszCookies, szCrLf);
3593 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3594 HeapFree(GetProcessHeap(), 0, lpszCookies);
3597 HeapFree(GetProcessHeap(), 0, lpszUrl);
3600 /***********************************************************************
3601 * HTTP_HttpSendRequestW (internal)
3603 * Sends the specified request to the HTTP server
3610 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3611 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3612 DWORD dwContentLength, BOOL bEndRequest)
3615 BOOL redirected = FALSE;
3616 LPWSTR requestString = NULL;
3619 INTERNET_ASYNC_RESULT iar;
3620 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3621 static const WCHAR szContentLength[] =
3622 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3623 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3626 TRACE("--> %p\n", lpwhr);
3628 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3630 /* if the verb is NULL default to GET */
3631 if (!lpwhr->lpszVerb)
3632 lpwhr->lpszVerb = heap_strdupW(szGET);
3634 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3636 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3637 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3638 lpwhr->dwBytesToWrite = dwContentLength;
3640 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3642 WCHAR *agent_header;
3643 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3646 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3647 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3648 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3650 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3651 HeapFree(GetProcessHeap(), 0, agent_header);
3653 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3655 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3656 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3658 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3660 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3661 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3662 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3668 BOOL reusing_connection;
3673 /* like native, just in case the caller forgot to call InternetReadFile
3674 * for all the data */
3675 HTTP_DrainContent(lpwhr);
3676 lpwhr->dwContentRead = 0;
3678 lpwhr->dwContentLength = ~0u;
3679 lpwhr->dwBytesToWrite = 0;
3682 if (TRACE_ON(wininet))
3684 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3685 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3689 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3691 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3693 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3694 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3696 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3697 HTTP_InsertCookies(lpwhr);
3699 /* add the headers the caller supplied */
3700 if( lpszHeaders && dwHeaderLength )
3702 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3703 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3706 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3708 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3709 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3710 HeapFree(GetProcessHeap(), 0, url);
3713 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3716 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3718 /* Send the request and store the results */
3719 if(NETCON_connected(&lpwhr->netConnection))
3720 reusing_connection = TRUE;
3722 reusing_connection = FALSE;
3724 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3727 /* send the request as ASCII, tack on the optional data */
3728 if (!lpOptional || redirected)
3729 dwOptionalLength = 0;
3730 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3731 NULL, 0, NULL, NULL );
3732 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3733 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3734 ascii_req, len, NULL, NULL );
3736 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3737 len = (len + dwOptionalLength - 1);
3739 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3741 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3742 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3744 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3745 HeapFree( GetProcessHeap(), 0, ascii_req );
3747 lpwhr->dwBytesWritten = dwOptionalLength;
3749 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3750 INTERNET_STATUS_REQUEST_SENT,
3751 &len, sizeof(DWORD));
3758 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3759 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3761 if (res != ERROR_SUCCESS)
3764 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3765 /* FIXME: We should know that connection is closed before sending
3766 * headers. Otherwise wrong callbacks are executed */
3767 if(!responseLen && reusing_connection) {
3768 TRACE("Connection closed by server, reconnecting\n");
3773 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3774 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3777 HTTP_ProcessCookies(lpwhr);
3779 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3781 dwBufferSize = sizeof(dwStatusCode);
3782 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3783 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3786 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3788 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3789 dwBufferSize=sizeof(szNewLocation);
3790 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3791 dwStatusCode == HTTP_STATUS_MOVED ||
3792 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3793 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3795 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3797 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3798 lpwhr->lpszVerb = heap_strdupW(szGET);
3800 HTTP_DrainContent(lpwhr);
3801 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3803 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3804 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3805 res = HTTP_HandleRedirect(lpwhr, new_url);
3806 if (res == ERROR_SUCCESS)
3808 HeapFree(GetProcessHeap(), 0, requestString);
3811 HeapFree( GetProcessHeap(), 0, new_url );
3816 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3818 WCHAR szAuthValue[2048];
3820 if (dwStatusCode == HTTP_STATUS_DENIED)
3822 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3824 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3826 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3828 lpwhr->lpHttpSession->lpszUserName,
3829 lpwhr->lpHttpSession->lpszPassword,
3832 HeapFree(GetProcessHeap(), 0, requestString);
3839 TRACE("Cleaning wrong authorization data\n");
3840 destroy_authinfo(lpwhr->pAuthInfo);
3841 lpwhr->pAuthInfo = NULL;
3844 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3847 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3849 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3850 &lpwhr->pProxyAuthInfo,
3851 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3852 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3861 TRACE("Cleaning wrong proxy authorization data\n");
3862 destroy_authinfo(lpwhr->pProxyAuthInfo);
3863 lpwhr->pProxyAuthInfo = NULL;
3869 res = ERROR_SUCCESS;
3873 if(res == ERROR_SUCCESS) {
3874 WCHAR url[INTERNET_MAX_URL_LENGTH];
3875 WCHAR cacheFileName[MAX_PATH+1];
3878 b = HTTP_GetRequestURL(lpwhr, url);
3880 WARN("Could not get URL\n");
3884 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3886 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3887 CloseHandle(lpwhr->hCacheFile);
3889 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3890 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3891 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3892 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3893 WARN("Could not create file: %u\n", GetLastError());
3894 lpwhr->hCacheFile = NULL;
3897 WARN("Could not create cache entry: %08x\n", GetLastError());
3903 HeapFree(GetProcessHeap(), 0, requestString);
3905 /* TODO: send notification for P3P header */
3907 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3909 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3910 HTTP_ReceiveRequestData(lpwhr, TRUE);
3913 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3916 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3917 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3918 sizeof(INTERNET_ASYNC_RESULT));
3926 /***********************************************************************
3928 * Helper functions for the HttpSendRequest(Ex) functions
3931 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3933 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3934 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3936 TRACE("%p\n", lpwhr);
3938 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3939 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3940 req->dwContentLength, req->bEndRequest);
3942 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3946 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3950 INTERNET_ASYNC_RESULT iar;
3951 DWORD res = ERROR_SUCCESS;
3953 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3954 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3956 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3958 res = ERROR_HTTP_HEADER_NOT_FOUND;
3960 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3961 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3963 /* process cookies here. Is this right? */
3964 HTTP_ProcessCookies(lpwhr);
3966 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3968 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3970 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3971 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3972 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3974 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3975 dwBufferSize=sizeof(szNewLocation);
3976 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3978 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3980 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3981 lpwhr->lpszVerb = heap_strdupW(szGET);
3983 HTTP_DrainContent(lpwhr);
3984 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3986 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3987 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3988 res = HTTP_HandleRedirect(lpwhr, new_url);
3989 if (res == ERROR_SUCCESS)
3990 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3991 HeapFree( GetProcessHeap(), 0, new_url );
3997 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
4000 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4001 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4002 sizeof(INTERNET_ASYNC_RESULT));
4006 /***********************************************************************
4007 * HttpEndRequestA (WININET.@)
4009 * Ends an HTTP request that was started by HttpSendRequestEx
4012 * TRUE if successful
4016 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4017 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4019 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4023 SetLastError(ERROR_INVALID_PARAMETER);
4027 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4030 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4032 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4033 http_request_t *lpwhr = (http_request_t*)work->hdr;
4035 TRACE("%p\n", lpwhr);
4037 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4040 /***********************************************************************
4041 * HttpEndRequestW (WININET.@)
4043 * Ends an HTTP request that was started by HttpSendRequestEx
4046 * TRUE if successful
4050 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4051 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4053 http_request_t *lpwhr;
4060 SetLastError(ERROR_INVALID_PARAMETER);
4064 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4066 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4068 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4070 WININET_Release( &lpwhr->hdr );
4073 lpwhr->hdr.dwFlags |= dwFlags;
4075 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4078 struct WORKREQ_HTTPENDREQUESTW *request;
4080 work.asyncproc = AsyncHttpEndRequestProc;
4081 work.hdr = WININET_AddRef( &lpwhr->hdr );
4083 request = &work.u.HttpEndRequestW;
4084 request->dwFlags = dwFlags;
4085 request->dwContext = dwContext;
4087 INTERNET_AsyncCall(&work);
4088 res = ERROR_IO_PENDING;
4091 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4093 WININET_Release( &lpwhr->hdr );
4094 TRACE("%u <--\n", res);
4095 if(res != ERROR_SUCCESS)
4097 return res == ERROR_SUCCESS;
4100 /***********************************************************************
4101 * HttpSendRequestExA (WININET.@)
4103 * Sends the specified request to the HTTP server and allows chunked
4108 * Failure: FALSE, call GetLastError() for more information.
4110 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4111 LPINTERNET_BUFFERSA lpBuffersIn,
4112 LPINTERNET_BUFFERSA lpBuffersOut,
4113 DWORD dwFlags, DWORD_PTR dwContext)
4115 INTERNET_BUFFERSW BuffersInW;
4118 LPWSTR header = NULL;
4120 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4121 lpBuffersOut, dwFlags, dwContext);
4125 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4126 if (lpBuffersIn->lpcszHeader)
4128 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4129 lpBuffersIn->dwHeadersLength,0,0);
4130 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4131 if (!(BuffersInW.lpcszHeader = header))
4133 SetLastError(ERROR_OUTOFMEMORY);
4136 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4137 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4141 BuffersInW.lpcszHeader = NULL;
4142 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4143 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4144 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4145 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4146 BuffersInW.Next = NULL;
4149 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4151 HeapFree(GetProcessHeap(),0,header);
4156 /***********************************************************************
4157 * HttpSendRequestExW (WININET.@)
4159 * Sends the specified request to the HTTP server and allows chunked
4164 * Failure: FALSE, call GetLastError() for more information.
4166 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4167 LPINTERNET_BUFFERSW lpBuffersIn,
4168 LPINTERNET_BUFFERSW lpBuffersOut,
4169 DWORD dwFlags, DWORD_PTR dwContext)
4171 http_request_t *lpwhr;
4172 http_session_t *lpwhs;
4176 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4177 lpBuffersOut, dwFlags, dwContext);
4179 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4181 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4183 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4187 lpwhs = lpwhr->lpHttpSession;
4188 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4189 hIC = lpwhs->lpAppInfo;
4190 assert(hIC->hdr.htype == WH_HINIT);
4192 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4194 WORKREQUEST workRequest;
4195 struct WORKREQ_HTTPSENDREQUESTW *req;
4197 workRequest.asyncproc = AsyncHttpSendRequestProc;
4198 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4199 req = &workRequest.u.HttpSendRequestW;
4204 if (lpBuffersIn->lpcszHeader)
4206 if (lpBuffersIn->dwHeadersLength == ~0u)
4207 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4209 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4211 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4212 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4214 else req->lpszHeader = NULL;
4216 req->dwHeaderLength = size / sizeof(WCHAR);
4217 req->lpOptional = lpBuffersIn->lpvBuffer;
4218 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4219 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4223 req->lpszHeader = NULL;
4224 req->dwHeaderLength = 0;
4225 req->lpOptional = NULL;
4226 req->dwOptionalLength = 0;
4227 req->dwContentLength = 0;
4230 req->bEndRequest = FALSE;
4232 INTERNET_AsyncCall(&workRequest);
4234 * This is from windows.
4236 res = ERROR_IO_PENDING;
4241 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4242 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4243 lpBuffersIn->dwBufferTotal, FALSE);
4245 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4250 WININET_Release( &lpwhr->hdr );
4254 return res == ERROR_SUCCESS;
4257 /***********************************************************************
4258 * HttpSendRequestW (WININET.@)
4260 * Sends the specified request to the HTTP server
4267 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4268 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4270 http_request_t *lpwhr;
4271 http_session_t *lpwhs = NULL;
4272 appinfo_t *hIC = NULL;
4273 DWORD res = ERROR_SUCCESS;
4275 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4276 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4278 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4279 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4281 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4285 lpwhs = lpwhr->lpHttpSession;
4286 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4288 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4292 hIC = lpwhs->lpAppInfo;
4293 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4295 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4299 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4301 WORKREQUEST workRequest;
4302 struct WORKREQ_HTTPSENDREQUESTW *req;
4304 workRequest.asyncproc = AsyncHttpSendRequestProc;
4305 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4306 req = &workRequest.u.HttpSendRequestW;
4311 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4312 else size = dwHeaderLength * sizeof(WCHAR);
4314 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4315 memcpy(req->lpszHeader, lpszHeaders, size);
4318 req->lpszHeader = 0;
4319 req->dwHeaderLength = dwHeaderLength;
4320 req->lpOptional = lpOptional;
4321 req->dwOptionalLength = dwOptionalLength;
4322 req->dwContentLength = dwOptionalLength;
4323 req->bEndRequest = TRUE;
4325 INTERNET_AsyncCall(&workRequest);
4327 * This is from windows.
4329 res = ERROR_IO_PENDING;
4333 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4334 dwHeaderLength, lpOptional, dwOptionalLength,
4335 dwOptionalLength, TRUE);
4339 WININET_Release( &lpwhr->hdr );
4342 return res == ERROR_SUCCESS;
4345 /***********************************************************************
4346 * HttpSendRequestA (WININET.@)
4348 * Sends the specified request to the HTTP server
4355 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4356 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4359 LPWSTR szHeaders=NULL;
4360 DWORD nLen=dwHeaderLength;
4361 if(lpszHeaders!=NULL)
4363 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4364 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4365 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4367 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4368 HeapFree(GetProcessHeap(),0,szHeaders);
4372 /***********************************************************************
4373 * HTTPSESSION_Destroy (internal)
4375 * Deallocate session handle
4378 static void HTTPSESSION_Destroy(object_header_t *hdr)
4380 http_session_t *lpwhs = (http_session_t*) hdr;
4382 TRACE("%p\n", lpwhs);
4384 WININET_Release(&lpwhs->lpAppInfo->hdr);
4386 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4387 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4388 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4389 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4390 HeapFree(GetProcessHeap(), 0, lpwhs);
4393 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4396 case INTERNET_OPTION_HANDLE_TYPE:
4397 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4399 if (*size < sizeof(ULONG))
4400 return ERROR_INSUFFICIENT_BUFFER;
4402 *size = sizeof(DWORD);
4403 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4404 return ERROR_SUCCESS;
4407 return INET_QueryOption(hdr, option, buffer, size, unicode);
4410 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4412 http_session_t *ses = (http_session_t*)hdr;
4415 case INTERNET_OPTION_USERNAME:
4417 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4418 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4419 return ERROR_SUCCESS;
4421 case INTERNET_OPTION_PASSWORD:
4423 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4424 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4425 return ERROR_SUCCESS;
4430 return ERROR_INTERNET_INVALID_OPTION;
4433 static const object_vtbl_t HTTPSESSIONVtbl = {
4434 HTTPSESSION_Destroy,
4436 HTTPSESSION_QueryOption,
4437 HTTPSESSION_SetOption,
4446 /***********************************************************************
4447 * HTTP_Connect (internal)
4449 * Create http session handle
4452 * HINTERNET a session handle on success
4456 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4457 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4458 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4459 DWORD dwInternalFlags, HINTERNET *ret)
4461 http_session_t *lpwhs = NULL;
4462 HINTERNET handle = NULL;
4463 DWORD res = ERROR_SUCCESS;
4467 if (!lpszServerName || !lpszServerName[0])
4468 return ERROR_INVALID_PARAMETER;
4470 assert( hIC->hdr.htype == WH_HINIT );
4472 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4474 return ERROR_OUTOFMEMORY;
4477 * According to my tests. The name is not resolved until a request is sent
4480 lpwhs->hdr.htype = WH_HHTTPSESSION;
4481 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4482 lpwhs->hdr.dwFlags = dwFlags;
4483 lpwhs->hdr.dwContext = dwContext;
4484 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4485 lpwhs->hdr.refs = 1;
4486 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4488 WININET_AddRef( &hIC->hdr );
4489 lpwhs->lpAppInfo = hIC;
4490 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4492 handle = WININET_AllocHandle( &lpwhs->hdr );
4495 ERR("Failed to alloc handle\n");
4496 res = ERROR_OUTOFMEMORY;
4500 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4501 if(hIC->lpszProxyBypass)
4502 FIXME("Proxy bypass is ignored.\n");
4504 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4505 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4506 if (lpszUserName && lpszUserName[0])
4507 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4508 if (lpszPassword && lpszPassword[0])
4509 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4510 lpwhs->nServerPort = nServerPort;
4511 lpwhs->nHostPort = nServerPort;
4513 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4514 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4516 INTERNET_SendCallback(&hIC->hdr, dwContext,
4517 INTERNET_STATUS_HANDLE_CREATED, &handle,
4523 WININET_Release( &lpwhs->hdr );
4526 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4530 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4532 if(res == ERROR_SUCCESS)
4538 /***********************************************************************
4539 * HTTP_OpenConnection (internal)
4541 * Connect to a web server
4548 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4550 http_session_t *lpwhs;
4551 appinfo_t *hIC = NULL;
4552 char szaddr[INET6_ADDRSTRLEN];
4554 DWORD res = ERROR_SUCCESS;
4559 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4561 res = ERROR_INVALID_PARAMETER;
4565 if (NETCON_connected(&lpwhr->netConnection))
4567 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4569 lpwhs = lpwhr->lpHttpSession;
4571 hIC = lpwhs->lpAppInfo;
4572 switch (lpwhs->socketAddress.ss_family)
4575 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4578 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4581 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4582 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4584 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4585 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4586 INTERNET_STATUS_CONNECTING_TO_SERVER,
4590 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4591 if (res != ERROR_SUCCESS)
4593 WARN("Socket creation failed: %u\n", res);
4597 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4599 if(res != ERROR_SUCCESS)
4602 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4603 INTERNET_STATUS_CONNECTED_TO_SERVER,
4604 szaddr, strlen(szaddr)+1);
4606 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4608 /* Note: we differ from Microsoft's WinINet here. they seem to have
4609 * a bug that causes no status callbacks to be sent when starting
4610 * a tunnel to a proxy server using the CONNECT verb. i believe our
4611 * behaviour to be more correct and to not cause any incompatibilities
4612 * because using a secure connection through a proxy server is a rare
4613 * case that would be hard for anyone to depend on */
4614 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4615 HTTPREQ_CloseConnection(&lpwhr->hdr);
4619 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4620 if(res != ERROR_SUCCESS)
4622 WARN("Couldn't connect securely to host\n");
4624 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4625 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4626 || res == ERROR_INTERNET_INVALID_CA
4627 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4628 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4629 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4630 || res == ERROR_INTERNET_SEC_INVALID_CERT
4631 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4632 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4634 HTTPREQ_CloseConnection(&lpwhr->hdr);
4641 lpwhr->read_pos = lpwhr->read_size = 0;
4642 lpwhr->read_chunked = FALSE;
4644 TRACE("%d <--\n", res);
4649 /***********************************************************************
4650 * HTTP_clear_response_headers (internal)
4652 * clear out any old response headers
4654 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4658 for( i=0; i<lpwhr->nCustHeaders; i++)
4660 if( !lpwhr->pCustHeaders[i].lpszField )
4662 if( !lpwhr->pCustHeaders[i].lpszValue )
4664 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4666 HTTP_DeleteCustomHeader( lpwhr, i );
4671 /***********************************************************************
4672 * HTTP_GetResponseHeaders (internal)
4674 * Read server response
4681 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4684 WCHAR buffer[MAX_REPLY_LEN];
4685 DWORD buflen = MAX_REPLY_LEN;
4686 BOOL bSuccess = FALSE;
4688 char bufferA[MAX_REPLY_LEN];
4689 LPWSTR status_code = NULL, status_text = NULL;
4690 DWORD cchMaxRawHeaders = 1024;
4691 LPWSTR lpszRawHeaders = NULL;
4693 DWORD cchRawHeaders = 0;
4694 BOOL codeHundred = FALSE;
4698 if (!NETCON_connected(&lpwhr->netConnection))
4702 static const WCHAR szHundred[] = {'1','0','0',0};
4704 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4706 buflen = MAX_REPLY_LEN;
4707 if (!read_line(lpwhr, bufferA, &buflen))
4710 /* clear old response headers (eg. from a redirect response) */
4712 HTTP_clear_response_headers( lpwhr );
4717 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4718 /* check is this a status code line? */
4719 if (!strncmpW(buffer, g_szHttp1_0, 4))
4721 /* split the version from the status code */
4722 status_code = strchrW( buffer, ' ' );
4727 /* split the status code from the status text */
4728 status_text = strchrW( status_code, ' ' );
4733 TRACE("version [%s] status code [%s] status text [%s]\n",
4734 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4736 codeHundred = (!strcmpW(status_code, szHundred));
4738 else if (!codeHundred)
4740 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4742 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4743 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4745 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4746 lpwhr->lpszStatusText = heap_strdupW(szOK);
4748 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4749 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4754 } while (codeHundred);
4756 /* Add status code */
4757 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4758 HTTP_ADDHDR_FLAG_REPLACE);
4760 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4761 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4763 lpwhr->lpszVersion = heap_strdupW(buffer);
4764 lpwhr->lpszStatusText = heap_strdupW(status_text);
4766 /* Restore the spaces */
4767 *(status_code-1) = ' ';
4768 *(status_text-1) = ' ';
4770 /* regenerate raw headers */
4771 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4772 if (!lpszRawHeaders) goto lend;
4774 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4775 cchMaxRawHeaders *= 2;
4776 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4777 if (temp == NULL) goto lend;
4778 lpszRawHeaders = temp;
4779 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4780 cchRawHeaders += (buflen-1);
4781 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4782 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4783 lpszRawHeaders[cchRawHeaders] = '\0';
4785 /* Parse each response line */
4788 buflen = MAX_REPLY_LEN;
4789 if (read_line(lpwhr, bufferA, &buflen))
4791 LPWSTR * pFieldAndValue;
4793 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4795 if (!bufferA[0]) break;
4796 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4798 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4801 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4802 cchMaxRawHeaders *= 2;
4803 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4804 if (temp == NULL) goto lend;
4805 lpszRawHeaders = temp;
4806 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4807 cchRawHeaders += (buflen-1);
4808 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4809 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4810 lpszRawHeaders[cchRawHeaders] = '\0';
4812 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4813 HTTP_ADDREQ_FLAG_ADD );
4815 HTTP_FreeTokens(pFieldAndValue);
4826 /* make sure the response header is terminated with an empty line. Some apps really
4827 truly care about that empty line being there for some reason. Just add it to the
4829 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4831 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4832 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4833 if (temp == NULL) goto lend;
4834 lpszRawHeaders = temp;
4837 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4839 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4840 lpwhr->lpszRawHeaders = lpszRawHeaders;
4841 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4851 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4856 /***********************************************************************
4857 * HTTP_InterpretHttpHeader (internal)
4859 * Parse server response
4863 * Pointer to array of field, value, NULL on success.
4866 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4868 LPWSTR * pTokenPair;
4872 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4874 pszColon = strchrW(buffer, ':');
4875 /* must have two tokens */
4878 HTTP_FreeTokens(pTokenPair);
4880 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4884 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4887 HTTP_FreeTokens(pTokenPair);
4890 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4891 pTokenPair[0][pszColon - buffer] = '\0';
4895 len = strlenW(pszColon);
4896 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4899 HTTP_FreeTokens(pTokenPair);
4902 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4904 strip_spaces(pTokenPair[0]);
4905 strip_spaces(pTokenPair[1]);
4907 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4911 /***********************************************************************
4912 * HTTP_ProcessHeader (internal)
4914 * Stuff header into header tables according to <dwModifier>
4918 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4920 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4922 LPHTTPHEADERW lphttpHdr = NULL;
4924 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4925 DWORD res = ERROR_HTTP_INVALID_HEADER;
4927 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4929 /* REPLACE wins out over ADD */
4930 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4931 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4933 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4936 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4940 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4941 return ERROR_HTTP_INVALID_HEADER;
4942 lphttpHdr = &lpwhr->pCustHeaders[index];
4948 hdr.lpszField = (LPWSTR)field;
4949 hdr.lpszValue = (LPWSTR)value;
4950 hdr.wFlags = hdr.wCount = 0;
4952 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4953 hdr.wFlags |= HDR_ISREQUEST;
4955 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4957 /* no value to delete */
4958 else return ERROR_SUCCESS;
4960 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4961 lphttpHdr->wFlags |= HDR_ISREQUEST;
4963 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4965 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4967 HTTP_DeleteCustomHeader( lpwhr, index );
4973 hdr.lpszField = (LPWSTR)field;
4974 hdr.lpszValue = (LPWSTR)value;
4975 hdr.wFlags = hdr.wCount = 0;
4977 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4978 hdr.wFlags |= HDR_ISREQUEST;
4980 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4983 return ERROR_SUCCESS;
4985 else if (dwModifier & COALESCEFLAGS)
4990 INT origlen = strlenW(lphttpHdr->lpszValue);
4991 INT valuelen = strlenW(value);
4993 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4996 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4998 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5001 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5004 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5006 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5009 lphttpHdr->lpszValue = lpsztmp;
5010 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5013 lphttpHdr->lpszValue[origlen] = ch;
5015 lphttpHdr->lpszValue[origlen] = ' ';
5019 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5020 lphttpHdr->lpszValue[len] = '\0';
5021 res = ERROR_SUCCESS;
5025 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5026 res = ERROR_OUTOFMEMORY;
5029 TRACE("<-- %d\n", res);
5034 /***********************************************************************
5035 * HTTP_FinishedReading (internal)
5037 * Called when all content from server has been read by client.
5040 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5042 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5049 HTTPREQ_CloseConnection(&lpwhr->hdr);
5052 /* FIXME: store data in the URL cache here */
5058 /***********************************************************************
5059 * HTTP_GetCustomHeaderIndex (internal)
5061 * Return index of custom header from header array
5064 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5065 int requested_index, BOOL request_only)
5069 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5071 for (index = 0; index < lpwhr->nCustHeaders; index++)
5073 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5076 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5079 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5082 if (requested_index == 0)
5087 if (index >= lpwhr->nCustHeaders)
5090 TRACE("Return: %d\n", index);
5095 /***********************************************************************
5096 * HTTP_InsertCustomHeader (internal)
5098 * Insert header into array
5101 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5104 LPHTTPHEADERW lph = NULL;
5106 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5107 count = lpwhr->nCustHeaders + 1;
5109 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5111 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5114 return ERROR_OUTOFMEMORY;
5116 lpwhr->pCustHeaders = lph;
5117 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5118 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5119 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5120 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5121 lpwhr->nCustHeaders++;
5123 return ERROR_SUCCESS;
5127 /***********************************************************************
5128 * HTTP_DeleteCustomHeader (internal)
5130 * Delete header from array
5131 * If this function is called, the indexs may change.
5133 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5135 if( lpwhr->nCustHeaders <= 0 )
5137 if( index >= lpwhr->nCustHeaders )
5139 lpwhr->nCustHeaders--;
5141 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5142 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5144 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5145 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5146 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5152 /***********************************************************************
5153 * HTTP_VerifyValidHeader (internal)
5155 * Verify the given header is not invalid for the given http request
5158 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5160 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5161 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5162 return ERROR_HTTP_INVALID_HEADER;
5164 return ERROR_SUCCESS;
5167 /***********************************************************************
5168 * IsHostInProxyBypassList (@)
5173 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5175 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);