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;
1543 /***********************************************************************
1544 * HTTPREQ_Destroy (internal)
1546 * Deallocate request handle
1549 static void HTTPREQ_Destroy(object_header_t *hdr)
1551 http_request_t *lpwhr = (http_request_t*) hdr;
1556 if(lpwhr->hCacheFile)
1557 CloseHandle(lpwhr->hCacheFile);
1559 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1561 DeleteCriticalSection( &lpwhr->read_section );
1562 WININET_Release(&lpwhr->lpHttpSession->hdr);
1564 destroy_authinfo(lpwhr->pAuthInfo);
1565 destroy_authinfo(lpwhr->pProxyAuthInfo);
1567 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1568 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1569 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1570 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1571 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1573 for (i = 0; i < lpwhr->nCustHeaders; i++)
1575 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1576 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1580 if(lpwhr->gzip_stream) {
1581 if(!lpwhr->gzip_stream->end_of_data)
1582 inflateEnd(&lpwhr->gzip_stream->zstream);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1587 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1588 HeapFree(GetProcessHeap(), 0, lpwhr);
1591 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1593 http_request_t *lpwhr = (http_request_t*) hdr;
1595 TRACE("%p\n",lpwhr);
1597 if (!NETCON_connected(&lpwhr->netConnection))
1600 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1601 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1603 NETCON_close(&lpwhr->netConnection);
1605 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1606 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1609 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1611 LPHTTPHEADERW host_header;
1613 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1615 host_header = HTTP_GetHeader(req, hostW);
1619 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1623 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1625 WCHAR szVersion[10];
1626 WCHAR szConnectionResponse[20];
1627 DWORD dwBufferSize = sizeof(szVersion);
1628 BOOL keepalive = FALSE;
1630 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1631 * the connection is keep-alive by default */
1632 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1633 && !strcmpiW(szVersion, g_szHttp1_1))
1638 dwBufferSize = sizeof(szConnectionResponse);
1639 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1640 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1642 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1648 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1650 http_request_t *req = (http_request_t*)hdr;
1653 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1655 http_session_t *lpwhs = req->lpHttpSession;
1656 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1658 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1660 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1661 return ERROR_INSUFFICIENT_BUFFER;
1662 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1663 /* FIXME: can't get a SOCKET from our connection since we don't use
1667 /* FIXME: get source port from req->netConnection */
1668 info->SourcePort = 0;
1669 info->DestPort = lpwhs->nHostPort;
1671 if (HTTP_KeepAlive(req))
1672 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1673 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1674 info->Flags |= IDSI_FLAG_PROXY;
1675 if (req->netConnection.useSSL)
1676 info->Flags |= IDSI_FLAG_SECURE;
1678 return ERROR_SUCCESS;
1681 case INTERNET_OPTION_SECURITY_FLAGS:
1683 http_session_t *lpwhs;
1684 lpwhs = req->lpHttpSession;
1686 if (*size < sizeof(ULONG))
1687 return ERROR_INSUFFICIENT_BUFFER;
1689 *size = sizeof(DWORD);
1690 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1691 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1693 *(DWORD*)buffer = 0;
1694 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1695 return ERROR_SUCCESS;
1698 case INTERNET_OPTION_HANDLE_TYPE:
1699 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1701 if (*size < sizeof(ULONG))
1702 return ERROR_INSUFFICIENT_BUFFER;
1704 *size = sizeof(DWORD);
1705 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1706 return ERROR_SUCCESS;
1708 case INTERNET_OPTION_URL: {
1709 WCHAR url[INTERNET_MAX_URL_LENGTH];
1714 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1716 TRACE("INTERNET_OPTION_URL\n");
1718 host = HTTP_GetHeader(req, hostW);
1719 strcpyW(url, httpW);
1720 strcatW(url, host->lpszValue);
1721 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1723 strcatW(url, req->lpszPath);
1725 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1728 len = (strlenW(url)+1) * sizeof(WCHAR);
1730 return ERROR_INSUFFICIENT_BUFFER;
1733 strcpyW(buffer, url);
1734 return ERROR_SUCCESS;
1736 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1738 return ERROR_INSUFFICIENT_BUFFER;
1741 return ERROR_SUCCESS;
1745 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1746 INTERNET_CACHE_ENTRY_INFOW *info;
1747 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1748 WCHAR url[INTERNET_MAX_URL_LENGTH];
1749 DWORD nbytes, error;
1752 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1754 if (*size < sizeof(*ts))
1756 *size = sizeof(*ts);
1757 return ERROR_INSUFFICIENT_BUFFER;
1760 HTTP_GetRequestURL(req, url);
1761 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1762 error = GetLastError();
1763 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1765 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1766 return ERROR_OUTOFMEMORY;
1768 GetUrlCacheEntryInfoW(url, info, &nbytes);
1770 ts->ftExpires = info->ExpireTime;
1771 ts->ftLastModified = info->LastModifiedTime;
1773 HeapFree(GetProcessHeap(), 0, info);
1774 *size = sizeof(*ts);
1775 return ERROR_SUCCESS;
1780 case INTERNET_OPTION_DATAFILE_NAME: {
1783 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1785 if(!req->lpszCacheFile) {
1787 return ERROR_INTERNET_ITEM_NOT_FOUND;
1791 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1792 if(*size < req_size)
1793 return ERROR_INSUFFICIENT_BUFFER;
1796 memcpy(buffer, req->lpszCacheFile, *size);
1797 return ERROR_SUCCESS;
1799 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1800 if (req_size > *size)
1801 return ERROR_INSUFFICIENT_BUFFER;
1803 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1804 -1, buffer, *size, NULL, NULL);
1805 return ERROR_SUCCESS;
1809 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1810 PCCERT_CONTEXT context;
1812 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1813 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1814 return ERROR_INSUFFICIENT_BUFFER;
1817 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1819 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1822 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1823 info->ftExpiry = context->pCertInfo->NotAfter;
1824 info->ftStart = context->pCertInfo->NotBefore;
1826 len = CertNameToStrW(context->dwCertEncodingType,
1827 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1828 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1829 if(info->lpszSubjectInfo)
1830 CertNameToStrW(context->dwCertEncodingType,
1831 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1832 info->lpszSubjectInfo, len);
1833 len = CertNameToStrW(context->dwCertEncodingType,
1834 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1835 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1836 if (info->lpszIssuerInfo)
1837 CertNameToStrW(context->dwCertEncodingType,
1838 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1839 info->lpszIssuerInfo, len);
1841 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1843 len = CertNameToStrA(context->dwCertEncodingType,
1844 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1845 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1846 if(infoA->lpszSubjectInfo)
1847 CertNameToStrA(context->dwCertEncodingType,
1848 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1849 infoA->lpszSubjectInfo, len);
1850 len = CertNameToStrA(context->dwCertEncodingType,
1851 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1852 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1853 if(infoA->lpszIssuerInfo)
1854 CertNameToStrA(context->dwCertEncodingType,
1855 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1856 infoA->lpszIssuerInfo, len);
1860 * Contrary to MSDN, these do not appear to be set.
1862 * lpszSignatureAlgName
1863 * lpszEncryptionAlgName
1866 CertFreeCertificateContext(context);
1867 return ERROR_SUCCESS;
1872 return INET_QueryOption(hdr, option, buffer, size, unicode);
1875 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1877 http_request_t *req = (http_request_t*)hdr;
1880 case INTERNET_OPTION_SEND_TIMEOUT:
1881 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1882 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1884 if (size != sizeof(DWORD))
1885 return ERROR_INVALID_PARAMETER;
1887 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1890 case INTERNET_OPTION_USERNAME:
1891 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1892 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1893 return ERROR_SUCCESS;
1895 case INTERNET_OPTION_PASSWORD:
1896 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1897 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1898 return ERROR_SUCCESS;
1899 case INTERNET_OPTION_HTTP_DECODING:
1900 if(size != sizeof(BOOL))
1901 return ERROR_INVALID_PARAMETER;
1902 req->decoding = *(BOOL*)buffer;
1903 return ERROR_SUCCESS;
1906 return ERROR_INTERNET_INVALID_OPTION;
1909 /* read some more data into the read buffer (the read section must be held) */
1910 static DWORD read_more_data( http_request_t *req, int maxlen )
1917 /* move existing data to the start of the buffer */
1919 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1923 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1925 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1926 maxlen - req->read_size, 0, &len );
1927 if(res == ERROR_SUCCESS)
1928 req->read_size += len;
1933 /* remove some amount of data from the read buffer (the read section must be held) */
1934 static void remove_data( http_request_t *req, int count )
1936 if (!(req->read_size -= count)) req->read_pos = 0;
1937 else req->read_pos += count;
1940 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1942 int count, bytes_read, pos = 0;
1945 EnterCriticalSection( &req->read_section );
1948 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1952 count = eol - (req->read_buf + req->read_pos);
1953 bytes_read = count + 1;
1955 else count = bytes_read = req->read_size;
1957 count = min( count, *len - pos );
1958 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1960 remove_data( req, bytes_read );
1963 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1966 TRACE( "returning empty string\n" );
1967 LeaveCriticalSection( &req->read_section );
1968 INTERNET_SetLastError(res);
1972 LeaveCriticalSection( &req->read_section );
1976 if (pos && buffer[pos - 1] == '\r') pos--;
1979 buffer[*len - 1] = 0;
1980 TRACE( "returning %s\n", debugstr_a(buffer));
1984 /* discard data contents until we reach end of line (the read section must be held) */
1985 static DWORD discard_eol( http_request_t *req )
1991 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1994 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1997 req->read_pos = req->read_size = 0; /* discard everything */
1998 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1999 } while (req->read_size);
2000 return ERROR_SUCCESS;
2003 /* read the size of the next chunk (the read section must be held) */
2004 static DWORD start_next_chunk( http_request_t *req )
2006 DWORD chunk_size = 0, res;
2008 if (!req->dwContentLength) return ERROR_SUCCESS;
2009 if (req->dwContentLength == req->dwContentRead)
2011 /* read terminator for the previous chunk */
2012 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2013 req->dwContentLength = ~0u;
2014 req->dwContentRead = 0;
2018 while (req->read_size)
2020 char ch = req->read_buf[req->read_pos];
2021 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2022 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2023 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2024 else if (ch == ';' || ch == '\r' || ch == '\n')
2026 TRACE( "reading %u byte chunk\n", chunk_size );
2027 req->dwContentLength = chunk_size;
2028 req->dwContentRead = 0;
2029 return discard_eol( req );
2031 remove_data( req, 1 );
2033 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2034 if (!req->read_size)
2036 req->dwContentLength = req->dwContentRead = 0;
2037 return ERROR_SUCCESS;
2042 /* check if we have reached the end of the data to read (the read section must be held) */
2043 static BOOL end_of_read_data( http_request_t *req )
2045 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2046 if (req->read_chunked) return (req->dwContentLength == 0);
2047 if (req->dwContentLength == ~0u) return FALSE;
2048 return (req->dwContentLength == req->dwContentRead);
2051 /* fetch some more data into the read buffer (the read section must be held) */
2052 static DWORD refill_buffer( http_request_t *req )
2054 int len = sizeof(req->read_buf);
2057 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2059 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2062 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2063 if (len <= req->read_size) return ERROR_SUCCESS;
2065 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2066 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2067 return ERROR_SUCCESS;
2070 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2072 DWORD ret = ERROR_SUCCESS;
2076 z_stream *zstream = &req->gzip_stream->zstream;
2080 while(read < size && !req->gzip_stream->end_of_data) {
2081 if(!req->read_size) {
2082 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2086 if(req->dwContentRead == req->dwContentLength)
2089 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2091 zstream->next_in = req->read_buf+req->read_pos;
2092 zstream->avail_in = buf_avail;
2093 zstream->next_out = buf+read;
2094 zstream->avail_out = size-read;
2095 zres = inflate(zstream, Z_FULL_FLUSH);
2096 read = size - zstream->avail_out;
2097 req->dwContentRead += buf_avail-zstream->avail_in;
2098 remove_data(req, buf_avail-zstream->avail_in);
2099 if(zres == Z_STREAM_END) {
2100 TRACE("end of data\n");
2101 req->gzip_stream->end_of_data = TRUE;
2102 inflateEnd(&req->gzip_stream->zstream);
2103 }else if(zres != Z_OK) {
2104 WARN("inflate failed %d\n", zres);
2106 ret = ERROR_INTERNET_DECODING_FAILED;
2116 static void refill_gzip_buffer(http_request_t *req)
2121 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2124 if(req->gzip_stream->buf_pos) {
2125 if(req->gzip_stream->buf_size)
2126 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2127 req->gzip_stream->buf_pos = 0;
2130 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2131 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2132 if(res == ERROR_SUCCESS)
2133 req->gzip_stream->buf_size += len;
2136 /* return the size of data available to be read immediately (the read section must be held) */
2137 static DWORD get_avail_data( http_request_t *req )
2139 if (req->gzip_stream) {
2140 refill_gzip_buffer(req);
2141 return req->gzip_stream->buf_size;
2143 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2145 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2148 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2150 INTERNET_ASYNC_RESULT iar;
2155 EnterCriticalSection( &req->read_section );
2156 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2157 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2158 iar.dwError = first_notif ? 0 : get_avail_data(req);
2163 LeaveCriticalSection( &req->read_section );
2165 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2166 sizeof(INTERNET_ASYNC_RESULT));
2169 /* read data from the http connection (the read section must be held) */
2170 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2172 BOOL finished_reading = FALSE;
2173 int len, bytes_read = 0;
2174 DWORD ret = ERROR_SUCCESS;
2176 EnterCriticalSection( &req->read_section );
2178 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2180 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2183 if(req->gzip_stream) {
2184 if(req->gzip_stream->buf_size) {
2185 bytes_read = min(req->gzip_stream->buf_size, size);
2186 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2187 req->gzip_stream->buf_pos += bytes_read;
2188 req->gzip_stream->buf_size -= bytes_read;
2189 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2193 if(size > bytes_read) {
2194 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2195 if(ret == ERROR_SUCCESS)
2199 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2201 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2203 if (req->read_size) {
2204 bytes_read = min( req->read_size, size );
2205 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2206 remove_data( req, bytes_read );
2209 if (size > bytes_read && (!bytes_read || sync)) {
2210 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2211 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2213 /* always return success, even if the network layer returns an error */
2216 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2217 req->dwContentRead += bytes_read;
2222 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2223 LeaveCriticalSection( &req->read_section );
2225 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2227 DWORD dwBytesWritten;
2229 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2231 WARN("WriteFile failed: %u\n", GetLastError());
2234 if(finished_reading)
2235 HTTP_FinishedReading(req);
2241 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2243 http_request_t *req = (http_request_t*)hdr;
2246 EnterCriticalSection( &req->read_section );
2247 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2248 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2250 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2251 if(res == ERROR_SUCCESS)
2253 LeaveCriticalSection( &req->read_section );
2258 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2260 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2261 http_request_t *req = (http_request_t*)workRequest->hdr;
2262 INTERNET_ASYNC_RESULT iar;
2265 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2267 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2268 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2270 iar.dwResult = res == ERROR_SUCCESS;
2273 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2274 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2275 sizeof(INTERNET_ASYNC_RESULT));
2278 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2279 DWORD flags, DWORD_PTR context)
2281 http_request_t *req = (http_request_t*)hdr;
2282 DWORD res, size, read, error = ERROR_SUCCESS;
2284 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2285 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2287 if (buffers->dwStructSize != sizeof(*buffers))
2288 return ERROR_INVALID_PARAMETER;
2290 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2292 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2294 WORKREQUEST workRequest;
2296 if (TryEnterCriticalSection( &req->read_section ))
2298 if (get_avail_data(req))
2300 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2301 &buffers->dwBufferLength, FALSE);
2302 size = buffers->dwBufferLength;
2303 LeaveCriticalSection( &req->read_section );
2306 LeaveCriticalSection( &req->read_section );
2309 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2310 workRequest.hdr = WININET_AddRef(&req->hdr);
2311 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2313 INTERNET_AsyncCall(&workRequest);
2315 return ERROR_IO_PENDING;
2319 size = buffers->dwBufferLength;
2321 EnterCriticalSection( &req->read_section );
2322 if(hdr->dwError == ERROR_SUCCESS)
2323 hdr->dwError = INTERNET_HANDLE_IN_USE;
2324 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2325 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2328 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2329 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2330 if(res == ERROR_SUCCESS)
2331 read += buffers->dwBufferLength;
2335 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2336 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2338 LeaveCriticalSection( &req->read_section );
2340 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2341 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2342 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2343 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2345 EnterCriticalSection( &req->read_section );
2348 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2349 hdr->dwError = ERROR_SUCCESS;
2351 error = hdr->dwError;
2353 LeaveCriticalSection( &req->read_section );
2354 size = buffers->dwBufferLength;
2355 buffers->dwBufferLength = read;
2358 if (res == ERROR_SUCCESS) {
2359 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2360 &size, sizeof(size));
2363 return res==ERROR_SUCCESS ? error : res;
2366 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2368 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2369 http_request_t *req = (http_request_t*)workRequest->hdr;
2370 INTERNET_ASYNC_RESULT iar;
2373 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2375 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2376 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2378 iar.dwResult = res == ERROR_SUCCESS;
2381 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2382 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2383 sizeof(INTERNET_ASYNC_RESULT));
2386 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2387 DWORD flags, DWORD_PTR context)
2390 http_request_t *req = (http_request_t*)hdr;
2391 DWORD res, size, read, error = ERROR_SUCCESS;
2393 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2394 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2396 if (buffers->dwStructSize != sizeof(*buffers))
2397 return ERROR_INVALID_PARAMETER;
2399 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2401 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2403 WORKREQUEST workRequest;
2405 if (TryEnterCriticalSection( &req->read_section ))
2407 if (get_avail_data(req))
2409 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2410 &buffers->dwBufferLength, FALSE);
2411 size = buffers->dwBufferLength;
2412 LeaveCriticalSection( &req->read_section );
2415 LeaveCriticalSection( &req->read_section );
2418 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2419 workRequest.hdr = WININET_AddRef(&req->hdr);
2420 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2422 INTERNET_AsyncCall(&workRequest);
2424 return ERROR_IO_PENDING;
2428 size = buffers->dwBufferLength;
2430 EnterCriticalSection( &req->read_section );
2431 if(hdr->dwError == ERROR_SUCCESS)
2432 hdr->dwError = INTERNET_HANDLE_IN_USE;
2433 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2434 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2437 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2438 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2439 if(res == ERROR_SUCCESS)
2440 read += buffers->dwBufferLength;
2444 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2445 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2447 LeaveCriticalSection( &req->read_section );
2449 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2450 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2451 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2452 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2454 EnterCriticalSection( &req->read_section );
2457 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2458 hdr->dwError = ERROR_SUCCESS;
2460 error = hdr->dwError;
2462 LeaveCriticalSection( &req->read_section );
2463 size = buffers->dwBufferLength;
2464 buffers->dwBufferLength = read;
2467 if (res == ERROR_SUCCESS) {
2468 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2469 &size, sizeof(size));
2472 return res==ERROR_SUCCESS ? error : res;
2475 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2478 http_request_t *lpwhr = (http_request_t*)hdr;
2480 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2483 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2484 if (res == ERROR_SUCCESS)
2485 lpwhr->dwBytesWritten += *written;
2487 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2491 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2493 http_request_t *req = (http_request_t*)workRequest->hdr;
2495 HTTP_ReceiveRequestData(req, FALSE);
2498 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2500 http_request_t *req = (http_request_t*)hdr;
2502 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2504 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2506 WORKREQUEST workRequest;
2508 /* never wait, if we can't enter the section we queue an async request right away */
2509 if (TryEnterCriticalSection( &req->read_section ))
2511 if ((*available = get_avail_data( req ))) goto done;
2512 if (end_of_read_data( req )) goto done;
2513 LeaveCriticalSection( &req->read_section );
2516 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2517 workRequest.hdr = WININET_AddRef( &req->hdr );
2519 INTERNET_AsyncCall(&workRequest);
2521 return ERROR_IO_PENDING;
2524 EnterCriticalSection( &req->read_section );
2526 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2528 refill_buffer( req );
2529 *available = get_avail_data( req );
2533 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2536 if (NETCON_query_data_available(&req->netConnection, &extra))
2537 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2539 LeaveCriticalSection( &req->read_section );
2541 TRACE( "returning %u\n", *available );
2542 return ERROR_SUCCESS;
2545 static const object_vtbl_t HTTPREQVtbl = {
2547 HTTPREQ_CloseConnection,
2548 HTTPREQ_QueryOption,
2551 HTTPREQ_ReadFileExA,
2552 HTTPREQ_ReadFileExW,
2554 HTTPREQ_QueryDataAvailable,
2558 /***********************************************************************
2559 * HTTP_HttpOpenRequestW (internal)
2561 * Open a HTTP request handle
2564 * HINTERNET a HTTP request handle on success
2568 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2569 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2570 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2571 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2573 appinfo_t *hIC = NULL;
2574 http_request_t *lpwhr;
2575 LPWSTR lpszHostName = NULL;
2576 HINTERNET handle = NULL;
2577 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2582 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2583 hIC = lpwhs->lpAppInfo;
2585 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2588 res = ERROR_OUTOFMEMORY;
2591 lpwhr->hdr.htype = WH_HHTTPREQ;
2592 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2593 lpwhr->hdr.dwFlags = dwFlags;
2594 lpwhr->hdr.dwContext = dwContext;
2595 lpwhr->hdr.refs = 1;
2596 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2597 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2598 lpwhr->dwContentLength = ~0u;
2599 InitializeCriticalSection( &lpwhr->read_section );
2601 WININET_AddRef( &lpwhs->hdr );
2602 lpwhr->lpHttpSession = lpwhs;
2603 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2605 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2606 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2607 if (NULL == lpszHostName)
2609 res = ERROR_OUTOFMEMORY;
2613 handle = WININET_AllocHandle( &lpwhr->hdr );
2616 res = ERROR_OUTOFMEMORY;
2620 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2622 InternetCloseHandle( handle );
2627 if (lpszObjectName && *lpszObjectName) {
2631 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2632 if (rc != E_POINTER)
2633 len = strlenW(lpszObjectName)+1;
2634 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2635 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2636 URL_ESCAPE_SPACES_ONLY);
2639 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2640 strcpyW(lpwhr->lpszPath,lpszObjectName);
2643 static const WCHAR slashW[] = {'/',0};
2645 lpwhr->lpszPath = heap_strdupW(slashW);
2648 if (lpszReferrer && *lpszReferrer)
2649 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2651 if (lpszAcceptTypes)
2654 for (i = 0; lpszAcceptTypes[i]; i++)
2656 if (!*lpszAcceptTypes[i]) continue;
2657 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2658 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2659 HTTP_ADDHDR_FLAG_REQ |
2660 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2664 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2665 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2667 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2668 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2669 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2671 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2672 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2673 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2676 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2677 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2679 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2680 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2681 INTERNET_DEFAULT_HTTPS_PORT :
2682 INTERNET_DEFAULT_HTTP_PORT);
2684 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2685 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2686 INTERNET_DEFAULT_HTTPS_PORT :
2687 INTERNET_DEFAULT_HTTP_PORT);
2689 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2690 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2692 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2693 INTERNET_STATUS_HANDLE_CREATED, &handle,
2697 HeapFree(GetProcessHeap(), 0, lpszHostName);
2699 WININET_Release( &lpwhr->hdr );
2701 TRACE("<-- %p (%p)\n", handle, lpwhr);
2706 /***********************************************************************
2707 * HttpOpenRequestW (WININET.@)
2709 * Open a HTTP request handle
2712 * HINTERNET a HTTP request handle on success
2716 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2717 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2718 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2719 DWORD dwFlags, DWORD_PTR dwContext)
2721 http_session_t *lpwhs;
2722 HINTERNET handle = NULL;
2725 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2726 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2727 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2728 dwFlags, dwContext);
2729 if(lpszAcceptTypes!=NULL)
2732 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2733 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2736 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2737 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2739 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2744 * My tests seem to show that the windows version does not
2745 * become asynchronous until after this point. And anyhow
2746 * if this call was asynchronous then how would you get the
2747 * necessary HINTERNET pointer returned by this function.
2750 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2751 lpszVersion, lpszReferrer, lpszAcceptTypes,
2752 dwFlags, dwContext, &handle);
2755 WININET_Release( &lpwhs->hdr );
2756 TRACE("returning %p\n", handle);
2757 if(res != ERROR_SUCCESS)
2762 /* read any content returned by the server so that the connection can be
2764 static void HTTP_DrainContent(http_request_t *req)
2768 if (!NETCON_connected(&req->netConnection)) return;
2770 if (req->dwContentLength == -1)
2772 NETCON_close(&req->netConnection);
2775 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2780 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2782 } while (bytes_read);
2785 static const LPCWSTR header_lookup[] = {
2786 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2787 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2788 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2789 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2790 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2791 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2792 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2793 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2794 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2795 szDate, /* HTTP_QUERY_DATE = 9 */
2796 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2797 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2798 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2799 szURI, /* HTTP_QUERY_URI = 13 */
2800 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2801 NULL, /* HTTP_QUERY_COST = 15 */
2802 NULL, /* HTTP_QUERY_LINK = 16 */
2803 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2804 NULL, /* HTTP_QUERY_VERSION = 18 */
2805 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2806 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2807 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2808 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2809 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2810 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2811 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2812 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2813 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2814 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2815 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2816 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2817 NULL, /* HTTP_QUERY_FROM = 31 */
2818 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2819 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2820 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2821 szReferer, /* HTTP_QUERY_REFERER = 35 */
2822 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2823 szServer, /* HTTP_QUERY_SERVER = 37 */
2824 NULL, /* HTTP_TITLE = 38 */
2825 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2826 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2827 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2828 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2829 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2830 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2831 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2832 NULL, /* HTTP_QUERY_REFRESH = 46 */
2833 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2834 szAge, /* HTTP_QUERY_AGE = 48 */
2835 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2836 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2837 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2838 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2839 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2840 szETag, /* HTTP_QUERY_ETAG = 54 */
2841 hostW, /* HTTP_QUERY_HOST = 55 */
2842 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2843 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2844 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2845 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2846 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2847 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2848 szRange, /* HTTP_QUERY_RANGE = 62 */
2849 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2850 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2851 szVary, /* HTTP_QUERY_VARY = 65 */
2852 szVia, /* HTTP_QUERY_VIA = 66 */
2853 szWarning, /* HTTP_QUERY_WARNING = 67 */
2854 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2855 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2856 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2859 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2861 /***********************************************************************
2862 * HTTP_HttpQueryInfoW (internal)
2864 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2865 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2867 LPHTTPHEADERW lphttpHdr = NULL;
2868 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2869 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2870 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2873 /* Find requested header structure */
2876 case HTTP_QUERY_CUSTOM:
2877 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2878 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2880 case HTTP_QUERY_RAW_HEADERS_CRLF:
2884 DWORD res = ERROR_INVALID_PARAMETER;
2887 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2889 headers = lpwhr->lpszRawHeaders;
2892 len = strlenW(headers) * sizeof(WCHAR);
2894 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2896 len += sizeof(WCHAR);
2897 res = ERROR_INSUFFICIENT_BUFFER;
2902 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2905 len = strlenW(szCrLf) * sizeof(WCHAR);
2906 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2908 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2909 res = ERROR_SUCCESS;
2911 *lpdwBufferLength = len;
2914 HeapFree(GetProcessHeap(), 0, headers);
2917 case HTTP_QUERY_RAW_HEADERS:
2919 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2921 LPWSTR pszString = lpBuffer;
2923 for (i = 0; ppszRawHeaderLines[i]; i++)
2924 size += strlenW(ppszRawHeaderLines[i]) + 1;
2926 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2928 HTTP_FreeTokens(ppszRawHeaderLines);
2929 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2930 return ERROR_INSUFFICIENT_BUFFER;
2934 for (i = 0; ppszRawHeaderLines[i]; i++)
2936 DWORD len = strlenW(ppszRawHeaderLines[i]);
2937 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2941 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2943 *lpdwBufferLength = size * sizeof(WCHAR);
2944 HTTP_FreeTokens(ppszRawHeaderLines);
2946 return ERROR_SUCCESS;
2948 case HTTP_QUERY_STATUS_TEXT:
2949 if (lpwhr->lpszStatusText)
2951 DWORD len = strlenW(lpwhr->lpszStatusText);
2952 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2954 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2955 return ERROR_INSUFFICIENT_BUFFER;
2959 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2960 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2962 *lpdwBufferLength = len * sizeof(WCHAR);
2963 return ERROR_SUCCESS;
2966 case HTTP_QUERY_VERSION:
2967 if (lpwhr->lpszVersion)
2969 DWORD len = strlenW(lpwhr->lpszVersion);
2970 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2972 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2973 return ERROR_INSUFFICIENT_BUFFER;
2977 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2978 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2980 *lpdwBufferLength = len * sizeof(WCHAR);
2981 return ERROR_SUCCESS;
2984 case HTTP_QUERY_CONTENT_ENCODING:
2985 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2986 requested_index,request_only);
2989 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2991 if (level < LAST_TABLE_HEADER && header_lookup[level])
2992 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2993 requested_index,request_only);
2997 lphttpHdr = &lpwhr->pCustHeaders[index];
2999 /* Ensure header satisfies requested attributes */
3001 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3002 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3004 return ERROR_HTTP_HEADER_NOT_FOUND;
3007 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3009 /* coalesce value to requested type */
3010 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3012 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3013 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3015 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3021 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3023 tmpTM = *gmtime(&tmpTime);
3024 STHook = (SYSTEMTIME *)lpBuffer;
3025 STHook->wDay = tmpTM.tm_mday;
3026 STHook->wHour = tmpTM.tm_hour;
3027 STHook->wMilliseconds = 0;
3028 STHook->wMinute = tmpTM.tm_min;
3029 STHook->wDayOfWeek = tmpTM.tm_wday;
3030 STHook->wMonth = tmpTM.tm_mon + 1;
3031 STHook->wSecond = tmpTM.tm_sec;
3032 STHook->wYear = tmpTM.tm_year;
3034 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3035 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3036 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3038 else if (lphttpHdr->lpszValue)
3040 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3042 if (len > *lpdwBufferLength)
3044 *lpdwBufferLength = len;
3045 return ERROR_INSUFFICIENT_BUFFER;
3049 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3050 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3052 *lpdwBufferLength = len - sizeof(WCHAR);
3054 return ERROR_SUCCESS;
3057 /***********************************************************************
3058 * HttpQueryInfoW (WININET.@)
3060 * Queries for information about an HTTP request
3067 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3068 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3070 http_request_t *lpwhr;
3073 if (TRACE_ON(wininet)) {
3074 #define FE(x) { x, #x }
3075 static const wininet_flag_info query_flags[] = {
3076 FE(HTTP_QUERY_MIME_VERSION),
3077 FE(HTTP_QUERY_CONTENT_TYPE),
3078 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3079 FE(HTTP_QUERY_CONTENT_ID),
3080 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3081 FE(HTTP_QUERY_CONTENT_LENGTH),
3082 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3083 FE(HTTP_QUERY_ALLOW),
3084 FE(HTTP_QUERY_PUBLIC),
3085 FE(HTTP_QUERY_DATE),
3086 FE(HTTP_QUERY_EXPIRES),
3087 FE(HTTP_QUERY_LAST_MODIFIED),
3088 FE(HTTP_QUERY_MESSAGE_ID),
3090 FE(HTTP_QUERY_DERIVED_FROM),
3091 FE(HTTP_QUERY_COST),
3092 FE(HTTP_QUERY_LINK),
3093 FE(HTTP_QUERY_PRAGMA),
3094 FE(HTTP_QUERY_VERSION),
3095 FE(HTTP_QUERY_STATUS_CODE),
3096 FE(HTTP_QUERY_STATUS_TEXT),
3097 FE(HTTP_QUERY_RAW_HEADERS),
3098 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3099 FE(HTTP_QUERY_CONNECTION),
3100 FE(HTTP_QUERY_ACCEPT),
3101 FE(HTTP_QUERY_ACCEPT_CHARSET),
3102 FE(HTTP_QUERY_ACCEPT_ENCODING),
3103 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3104 FE(HTTP_QUERY_AUTHORIZATION),
3105 FE(HTTP_QUERY_CONTENT_ENCODING),
3106 FE(HTTP_QUERY_FORWARDED),
3107 FE(HTTP_QUERY_FROM),
3108 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3109 FE(HTTP_QUERY_LOCATION),
3110 FE(HTTP_QUERY_ORIG_URI),
3111 FE(HTTP_QUERY_REFERER),
3112 FE(HTTP_QUERY_RETRY_AFTER),
3113 FE(HTTP_QUERY_SERVER),
3114 FE(HTTP_QUERY_TITLE),
3115 FE(HTTP_QUERY_USER_AGENT),
3116 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3117 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3118 FE(HTTP_QUERY_ACCEPT_RANGES),
3119 FE(HTTP_QUERY_SET_COOKIE),
3120 FE(HTTP_QUERY_COOKIE),
3121 FE(HTTP_QUERY_REQUEST_METHOD),
3122 FE(HTTP_QUERY_REFRESH),
3123 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3125 FE(HTTP_QUERY_CACHE_CONTROL),
3126 FE(HTTP_QUERY_CONTENT_BASE),
3127 FE(HTTP_QUERY_CONTENT_LOCATION),
3128 FE(HTTP_QUERY_CONTENT_MD5),
3129 FE(HTTP_QUERY_CONTENT_RANGE),
3130 FE(HTTP_QUERY_ETAG),
3131 FE(HTTP_QUERY_HOST),
3132 FE(HTTP_QUERY_IF_MATCH),
3133 FE(HTTP_QUERY_IF_NONE_MATCH),
3134 FE(HTTP_QUERY_IF_RANGE),
3135 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3136 FE(HTTP_QUERY_MAX_FORWARDS),
3137 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3138 FE(HTTP_QUERY_RANGE),
3139 FE(HTTP_QUERY_TRANSFER_ENCODING),
3140 FE(HTTP_QUERY_UPGRADE),
3141 FE(HTTP_QUERY_VARY),
3143 FE(HTTP_QUERY_WARNING),
3144 FE(HTTP_QUERY_CUSTOM)
3146 static const wininet_flag_info modifier_flags[] = {
3147 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3148 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3149 FE(HTTP_QUERY_FLAG_NUMBER),
3150 FE(HTTP_QUERY_FLAG_COALESCE)
3153 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3154 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3157 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3158 TRACE(" Attribute:");
3159 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3160 if (query_flags[i].val == info) {
3161 TRACE(" %s", query_flags[i].name);
3165 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3166 TRACE(" Unknown (%08x)", info);
3169 TRACE(" Modifier:");
3170 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3171 if (modifier_flags[i].val & info_mod) {
3172 TRACE(" %s", modifier_flags[i].name);
3173 info_mod &= ~ modifier_flags[i].val;
3178 TRACE(" Unknown (%08x)", info_mod);
3183 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3184 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3186 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3190 if (lpBuffer == NULL)
3191 *lpdwBufferLength = 0;
3192 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3193 lpBuffer, lpdwBufferLength, lpdwIndex);
3197 WININET_Release( &lpwhr->hdr );
3199 TRACE("%u <--\n", res);
3200 if(res != ERROR_SUCCESS)
3202 return res == ERROR_SUCCESS;
3205 /***********************************************************************
3206 * HttpQueryInfoA (WININET.@)
3208 * Queries for information about an HTTP request
3215 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3216 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3222 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3223 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3225 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3226 lpdwBufferLength, lpdwIndex );
3232 len = (*lpdwBufferLength)*sizeof(WCHAR);
3233 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3235 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3241 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3242 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3243 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3244 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3251 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3255 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3256 lpBuffer, *lpdwBufferLength, NULL, NULL );
3257 *lpdwBufferLength = len - 1;
3259 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3262 /* since the strings being returned from HttpQueryInfoW should be
3263 * only ASCII characters, it is reasonable to assume that all of
3264 * the Unicode characters can be reduced to a single byte */
3265 *lpdwBufferLength = len / sizeof(WCHAR);
3267 HeapFree(GetProcessHeap(), 0, bufferW );
3272 /***********************************************************************
3273 * HTTP_GetRedirectURL (internal)
3275 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3277 static WCHAR szHttp[] = {'h','t','t','p',0};
3278 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3279 http_session_t *lpwhs = lpwhr->lpHttpSession;
3280 URL_COMPONENTSW urlComponents;
3281 DWORD url_length = 0;
3283 LPWSTR combined_url;
3285 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3286 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3287 urlComponents.dwSchemeLength = 0;
3288 urlComponents.lpszHostName = lpwhs->lpszHostName;
3289 urlComponents.dwHostNameLength = 0;
3290 urlComponents.nPort = lpwhs->nHostPort;
3291 urlComponents.lpszUserName = lpwhs->lpszUserName;
3292 urlComponents.dwUserNameLength = 0;
3293 urlComponents.lpszPassword = NULL;
3294 urlComponents.dwPasswordLength = 0;
3295 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3296 urlComponents.dwUrlPathLength = 0;
3297 urlComponents.lpszExtraInfo = NULL;
3298 urlComponents.dwExtraInfoLength = 0;
3300 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3301 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3304 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3306 /* convert from bytes to characters */
3307 url_length = url_length / sizeof(WCHAR) - 1;
3308 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3310 HeapFree(GetProcessHeap(), 0, orig_url);
3315 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3316 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3318 HeapFree(GetProcessHeap(), 0, orig_url);
3321 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3323 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3325 HeapFree(GetProcessHeap(), 0, orig_url);
3326 HeapFree(GetProcessHeap(), 0, combined_url);
3329 HeapFree(GetProcessHeap(), 0, orig_url);
3330 return combined_url;
3334 /***********************************************************************
3335 * HTTP_HandleRedirect (internal)
3337 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3339 http_session_t *lpwhs = lpwhr->lpHttpSession;
3340 appinfo_t *hIC = lpwhs->lpAppInfo;
3341 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3342 WCHAR path[INTERNET_MAX_URL_LENGTH];
3347 /* if it's an absolute path, keep the same session info */
3348 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3352 URL_COMPONENTSW urlComponents;
3353 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3354 static WCHAR szHttp[] = {'h','t','t','p',0};
3355 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3361 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3362 urlComponents.lpszScheme = protocol;
3363 urlComponents.dwSchemeLength = 32;
3364 urlComponents.lpszHostName = hostName;
3365 urlComponents.dwHostNameLength = MAXHOSTNAME;
3366 urlComponents.lpszUserName = userName;
3367 urlComponents.dwUserNameLength = 1024;
3368 urlComponents.lpszPassword = NULL;
3369 urlComponents.dwPasswordLength = 0;
3370 urlComponents.lpszUrlPath = path;
3371 urlComponents.dwUrlPathLength = 2048;
3372 urlComponents.lpszExtraInfo = NULL;
3373 urlComponents.dwExtraInfoLength = 0;
3374 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3375 return INTERNET_GetLastError();
3377 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3378 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3380 TRACE("redirect from secure page to non-secure page\n");
3381 /* FIXME: warn about from secure redirect to non-secure page */
3382 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3384 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3385 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3387 TRACE("redirect from non-secure page to secure page\n");
3388 /* FIXME: notify about redirect to secure page */
3389 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3392 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3394 if (lstrlenW(protocol)>4) /*https*/
3395 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3397 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3402 * This upsets redirects to binary files on sourceforge.net
3403 * and gives an html page instead of the target file
3404 * Examination of the HTTP request sent by native wininet.dll
3405 * reveals that it doesn't send a referrer in that case.
3406 * Maybe there's a flag that enables this, or maybe a referrer
3407 * shouldn't be added in case of a redirect.
3410 /* consider the current host as the referrer */
3411 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3412 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3413 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3414 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3417 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3418 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3419 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3422 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3423 len = lstrlenW(hostName);
3424 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3425 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3426 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3429 lpwhs->lpszHostName = heap_strdupW(hostName);
3431 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3433 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3434 lpwhs->lpszUserName = NULL;
3436 lpwhs->lpszUserName = heap_strdupW(userName);
3440 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3444 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3445 lpwhs->lpszServerName = heap_strdupW(hostName);
3446 lpwhs->nServerPort = urlComponents.nPort;
3448 NETCON_close(&lpwhr->netConnection);
3449 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3452 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3453 if (res != ERROR_SUCCESS)
3456 lpwhr->read_pos = lpwhr->read_size = 0;
3457 lpwhr->read_chunked = FALSE;
3461 TRACE("Redirect through proxy\n");
3464 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3465 lpwhr->lpszPath=NULL;
3471 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3472 if (rc != E_POINTER)
3473 needed = strlenW(path)+1;
3474 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3475 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3476 URL_ESCAPE_SPACES_ONLY);
3479 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3480 strcpyW(lpwhr->lpszPath,path);
3484 /* Remove custom content-type/length headers on redirects. */
3485 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3487 HTTP_DeleteCustomHeader(lpwhr, index);
3488 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3490 HTTP_DeleteCustomHeader(lpwhr, index);
3492 return ERROR_SUCCESS;
3495 /***********************************************************************
3496 * HTTP_build_req (internal)
3498 * concatenate all the strings in the request together
3500 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3505 for( t = list; *t ; t++ )
3506 len += strlenW( *t );
3509 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3512 for( t = list; *t ; t++ )
3518 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3521 LPWSTR requestString;
3527 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3528 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3529 http_session_t *lpwhs = lpwhr->lpHttpSession;
3533 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3534 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3535 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3536 HeapFree( GetProcessHeap(), 0, lpszPath );
3538 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3539 NULL, 0, NULL, NULL );
3540 len--; /* the nul terminator isn't needed */
3541 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3542 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3543 ascii_req, len, NULL, NULL );
3544 HeapFree( GetProcessHeap(), 0, requestString );
3546 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3548 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3549 HeapFree( GetProcessHeap(), 0, ascii_req );
3550 if (res != ERROR_SUCCESS)
3553 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3555 return ERROR_HTTP_INVALID_HEADER;
3557 return ERROR_SUCCESS;
3560 static void HTTP_InsertCookies(http_request_t *lpwhr)
3562 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3563 LPWSTR lpszCookies, lpszUrl = NULL;
3564 DWORD nCookieSize, size;
3565 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3567 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3568 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3569 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3571 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3574 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3576 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3577 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3579 cnt += sprintfW(lpszCookies, szCookie);
3580 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3581 strcatW(lpszCookies, szCrLf);
3583 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3584 HeapFree(GetProcessHeap(), 0, lpszCookies);
3587 HeapFree(GetProcessHeap(), 0, lpszUrl);
3590 /***********************************************************************
3591 * HTTP_HttpSendRequestW (internal)
3593 * Sends the specified request to the HTTP server
3600 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3601 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3602 DWORD dwContentLength, BOOL bEndRequest)
3605 BOOL redirected = FALSE;
3606 LPWSTR requestString = NULL;
3609 INTERNET_ASYNC_RESULT iar;
3610 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3611 static const WCHAR szContentLength[] =
3612 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3613 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3616 TRACE("--> %p\n", lpwhr);
3618 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3620 /* if the verb is NULL default to GET */
3621 if (!lpwhr->lpszVerb)
3622 lpwhr->lpszVerb = heap_strdupW(szGET);
3624 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3626 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3627 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3628 lpwhr->dwBytesToWrite = dwContentLength;
3630 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3632 WCHAR *agent_header;
3633 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3636 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3637 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3638 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3640 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3641 HeapFree(GetProcessHeap(), 0, agent_header);
3643 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3645 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3646 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3648 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3650 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3651 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3652 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3658 BOOL reusing_connection;
3663 /* like native, just in case the caller forgot to call InternetReadFile
3664 * for all the data */
3665 HTTP_DrainContent(lpwhr);
3666 lpwhr->dwContentRead = 0;
3668 lpwhr->dwContentLength = ~0u;
3669 lpwhr->dwBytesToWrite = 0;
3672 if (TRACE_ON(wininet))
3674 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3675 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3679 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3681 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3683 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3684 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3686 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3687 HTTP_InsertCookies(lpwhr);
3689 /* add the headers the caller supplied */
3690 if( lpszHeaders && dwHeaderLength )
3692 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3693 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3696 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3698 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3699 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3700 HeapFree(GetProcessHeap(), 0, url);
3703 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3706 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3708 /* Send the request and store the results */
3709 if(NETCON_connected(&lpwhr->netConnection))
3710 reusing_connection = TRUE;
3712 reusing_connection = FALSE;
3714 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3717 /* send the request as ASCII, tack on the optional data */
3718 if (!lpOptional || redirected)
3719 dwOptionalLength = 0;
3720 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3721 NULL, 0, NULL, NULL );
3722 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3723 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3724 ascii_req, len, NULL, NULL );
3726 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3727 len = (len + dwOptionalLength - 1);
3729 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3731 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3732 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3734 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3735 HeapFree( GetProcessHeap(), 0, ascii_req );
3737 lpwhr->dwBytesWritten = dwOptionalLength;
3739 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3740 INTERNET_STATUS_REQUEST_SENT,
3741 &len, sizeof(DWORD));
3748 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3749 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3751 if (res != ERROR_SUCCESS)
3754 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3755 /* FIXME: We should know that connection is closed before sending
3756 * headers. Otherwise wrong callbacks are executed */
3757 if(!responseLen && reusing_connection) {
3758 TRACE("Connection closed by server, reconnecting\n");
3763 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3764 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3767 HTTP_ProcessCookies(lpwhr);
3769 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3771 dwBufferSize = sizeof(dwStatusCode);
3772 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3773 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3776 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3778 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3779 dwBufferSize=sizeof(szNewLocation);
3780 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3781 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3783 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3785 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3786 lpwhr->lpszVerb = heap_strdupW(szGET);
3788 HTTP_DrainContent(lpwhr);
3789 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3791 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3792 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3793 res = HTTP_HandleRedirect(lpwhr, new_url);
3794 if (res == ERROR_SUCCESS)
3796 HeapFree(GetProcessHeap(), 0, requestString);
3799 HeapFree( GetProcessHeap(), 0, new_url );
3804 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3806 WCHAR szAuthValue[2048];
3808 if (dwStatusCode == HTTP_STATUS_DENIED)
3810 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3812 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3814 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3816 lpwhr->lpHttpSession->lpszUserName,
3817 lpwhr->lpHttpSession->lpszPassword,
3820 HeapFree(GetProcessHeap(), 0, requestString);
3827 TRACE("Cleaning wrong authorization data\n");
3828 destroy_authinfo(lpwhr->pAuthInfo);
3829 lpwhr->pAuthInfo = NULL;
3832 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3835 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3837 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3838 &lpwhr->pProxyAuthInfo,
3839 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3840 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3849 TRACE("Cleaning wrong proxy authorization data\n");
3850 destroy_authinfo(lpwhr->pProxyAuthInfo);
3851 lpwhr->pProxyAuthInfo = NULL;
3857 res = ERROR_SUCCESS;
3861 if(res == ERROR_SUCCESS) {
3862 WCHAR url[INTERNET_MAX_URL_LENGTH];
3863 WCHAR cacheFileName[MAX_PATH+1];
3866 b = HTTP_GetRequestURL(lpwhr, url);
3868 WARN("Could not get URL\n");
3872 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3874 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3875 CloseHandle(lpwhr->hCacheFile);
3877 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3878 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3879 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3880 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3881 WARN("Could not create file: %u\n", GetLastError());
3882 lpwhr->hCacheFile = NULL;
3885 WARN("Could not create cache entry: %08x\n", GetLastError());
3891 HeapFree(GetProcessHeap(), 0, requestString);
3893 /* TODO: send notification for P3P header */
3895 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3897 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3898 HTTP_ReceiveRequestData(lpwhr, TRUE);
3901 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3904 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3905 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3906 sizeof(INTERNET_ASYNC_RESULT));
3914 /***********************************************************************
3916 * Helper functions for the HttpSendRequest(Ex) functions
3919 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3921 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3922 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3924 TRACE("%p\n", lpwhr);
3926 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3927 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3928 req->dwContentLength, req->bEndRequest);
3930 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3934 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3938 INTERNET_ASYNC_RESULT iar;
3939 DWORD res = ERROR_SUCCESS;
3941 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3942 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3944 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3946 res = ERROR_HTTP_HEADER_NOT_FOUND;
3948 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3949 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3951 /* process cookies here. Is this right? */
3952 HTTP_ProcessCookies(lpwhr);
3954 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3956 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3958 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3959 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3960 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3962 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3963 dwBufferSize=sizeof(szNewLocation);
3964 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3966 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3968 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3969 lpwhr->lpszVerb = heap_strdupW(szGET);
3971 HTTP_DrainContent(lpwhr);
3972 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3974 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3975 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3976 res = HTTP_HandleRedirect(lpwhr, new_url);
3977 if (res == ERROR_SUCCESS)
3978 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3979 HeapFree( GetProcessHeap(), 0, new_url );
3985 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3988 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3989 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3990 sizeof(INTERNET_ASYNC_RESULT));
3994 /***********************************************************************
3995 * HttpEndRequestA (WININET.@)
3997 * Ends an HTTP request that was started by HttpSendRequestEx
4000 * TRUE if successful
4004 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4005 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4007 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4011 SetLastError(ERROR_INVALID_PARAMETER);
4015 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4018 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4020 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4021 http_request_t *lpwhr = (http_request_t*)work->hdr;
4023 TRACE("%p\n", lpwhr);
4025 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4028 /***********************************************************************
4029 * HttpEndRequestW (WININET.@)
4031 * Ends an HTTP request that was started by HttpSendRequestEx
4034 * TRUE if successful
4038 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4039 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4041 http_request_t *lpwhr;
4048 SetLastError(ERROR_INVALID_PARAMETER);
4052 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4054 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4056 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4058 WININET_Release( &lpwhr->hdr );
4061 lpwhr->hdr.dwFlags |= dwFlags;
4063 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4066 struct WORKREQ_HTTPENDREQUESTW *request;
4068 work.asyncproc = AsyncHttpEndRequestProc;
4069 work.hdr = WININET_AddRef( &lpwhr->hdr );
4071 request = &work.u.HttpEndRequestW;
4072 request->dwFlags = dwFlags;
4073 request->dwContext = dwContext;
4075 INTERNET_AsyncCall(&work);
4076 res = ERROR_IO_PENDING;
4079 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4081 WININET_Release( &lpwhr->hdr );
4082 TRACE("%u <--\n", res);
4083 if(res != ERROR_SUCCESS)
4085 return res == ERROR_SUCCESS;
4088 /***********************************************************************
4089 * HttpSendRequestExA (WININET.@)
4091 * Sends the specified request to the HTTP server and allows chunked
4096 * Failure: FALSE, call GetLastError() for more information.
4098 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4099 LPINTERNET_BUFFERSA lpBuffersIn,
4100 LPINTERNET_BUFFERSA lpBuffersOut,
4101 DWORD dwFlags, DWORD_PTR dwContext)
4103 INTERNET_BUFFERSW BuffersInW;
4106 LPWSTR header = NULL;
4108 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4109 lpBuffersOut, dwFlags, dwContext);
4113 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4114 if (lpBuffersIn->lpcszHeader)
4116 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4117 lpBuffersIn->dwHeadersLength,0,0);
4118 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4119 if (!(BuffersInW.lpcszHeader = header))
4121 SetLastError(ERROR_OUTOFMEMORY);
4124 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4125 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4129 BuffersInW.lpcszHeader = NULL;
4130 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4131 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4132 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4133 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4134 BuffersInW.Next = NULL;
4137 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4139 HeapFree(GetProcessHeap(),0,header);
4144 /***********************************************************************
4145 * HttpSendRequestExW (WININET.@)
4147 * Sends the specified request to the HTTP server and allows chunked
4152 * Failure: FALSE, call GetLastError() for more information.
4154 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4155 LPINTERNET_BUFFERSW lpBuffersIn,
4156 LPINTERNET_BUFFERSW lpBuffersOut,
4157 DWORD dwFlags, DWORD_PTR dwContext)
4159 http_request_t *lpwhr;
4160 http_session_t *lpwhs;
4164 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4165 lpBuffersOut, dwFlags, dwContext);
4167 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4169 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4171 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4175 lpwhs = lpwhr->lpHttpSession;
4176 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4177 hIC = lpwhs->lpAppInfo;
4178 assert(hIC->hdr.htype == WH_HINIT);
4180 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4182 WORKREQUEST workRequest;
4183 struct WORKREQ_HTTPSENDREQUESTW *req;
4185 workRequest.asyncproc = AsyncHttpSendRequestProc;
4186 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4187 req = &workRequest.u.HttpSendRequestW;
4192 if (lpBuffersIn->lpcszHeader)
4194 if (lpBuffersIn->dwHeadersLength == ~0u)
4195 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4197 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4199 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4200 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4202 else req->lpszHeader = NULL;
4204 req->dwHeaderLength = size / sizeof(WCHAR);
4205 req->lpOptional = lpBuffersIn->lpvBuffer;
4206 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4207 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4211 req->lpszHeader = NULL;
4212 req->dwHeaderLength = 0;
4213 req->lpOptional = NULL;
4214 req->dwOptionalLength = 0;
4215 req->dwContentLength = 0;
4218 req->bEndRequest = FALSE;
4220 INTERNET_AsyncCall(&workRequest);
4222 * This is from windows.
4224 res = ERROR_IO_PENDING;
4229 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4230 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4231 lpBuffersIn->dwBufferTotal, FALSE);
4233 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4238 WININET_Release( &lpwhr->hdr );
4242 return res == ERROR_SUCCESS;
4245 /***********************************************************************
4246 * HttpSendRequestW (WININET.@)
4248 * Sends the specified request to the HTTP server
4255 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4256 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4258 http_request_t *lpwhr;
4259 http_session_t *lpwhs = NULL;
4260 appinfo_t *hIC = NULL;
4261 DWORD res = ERROR_SUCCESS;
4263 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4264 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4266 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4267 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4269 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4273 lpwhs = lpwhr->lpHttpSession;
4274 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4276 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4280 hIC = lpwhs->lpAppInfo;
4281 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4283 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4287 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4289 WORKREQUEST workRequest;
4290 struct WORKREQ_HTTPSENDREQUESTW *req;
4292 workRequest.asyncproc = AsyncHttpSendRequestProc;
4293 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4294 req = &workRequest.u.HttpSendRequestW;
4299 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4300 else size = dwHeaderLength * sizeof(WCHAR);
4302 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4303 memcpy(req->lpszHeader, lpszHeaders, size);
4306 req->lpszHeader = 0;
4307 req->dwHeaderLength = dwHeaderLength;
4308 req->lpOptional = lpOptional;
4309 req->dwOptionalLength = dwOptionalLength;
4310 req->dwContentLength = dwOptionalLength;
4311 req->bEndRequest = TRUE;
4313 INTERNET_AsyncCall(&workRequest);
4315 * This is from windows.
4317 res = ERROR_IO_PENDING;
4321 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4322 dwHeaderLength, lpOptional, dwOptionalLength,
4323 dwOptionalLength, TRUE);
4327 WININET_Release( &lpwhr->hdr );
4330 return res == ERROR_SUCCESS;
4333 /***********************************************************************
4334 * HttpSendRequestA (WININET.@)
4336 * Sends the specified request to the HTTP server
4343 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4344 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4347 LPWSTR szHeaders=NULL;
4348 DWORD nLen=dwHeaderLength;
4349 if(lpszHeaders!=NULL)
4351 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4352 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4353 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4355 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4356 HeapFree(GetProcessHeap(),0,szHeaders);
4360 /***********************************************************************
4361 * HTTPSESSION_Destroy (internal)
4363 * Deallocate session handle
4366 static void HTTPSESSION_Destroy(object_header_t *hdr)
4368 http_session_t *lpwhs = (http_session_t*) hdr;
4370 TRACE("%p\n", lpwhs);
4372 WININET_Release(&lpwhs->lpAppInfo->hdr);
4374 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4375 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4376 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4377 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4378 HeapFree(GetProcessHeap(), 0, lpwhs);
4381 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4384 case INTERNET_OPTION_HANDLE_TYPE:
4385 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4387 if (*size < sizeof(ULONG))
4388 return ERROR_INSUFFICIENT_BUFFER;
4390 *size = sizeof(DWORD);
4391 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4392 return ERROR_SUCCESS;
4395 return INET_QueryOption(hdr, option, buffer, size, unicode);
4398 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4400 http_session_t *ses = (http_session_t*)hdr;
4403 case INTERNET_OPTION_USERNAME:
4405 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4406 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4407 return ERROR_SUCCESS;
4409 case INTERNET_OPTION_PASSWORD:
4411 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4412 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4413 return ERROR_SUCCESS;
4418 return ERROR_INTERNET_INVALID_OPTION;
4421 static const object_vtbl_t HTTPSESSIONVtbl = {
4422 HTTPSESSION_Destroy,
4424 HTTPSESSION_QueryOption,
4425 HTTPSESSION_SetOption,
4434 /***********************************************************************
4435 * HTTP_Connect (internal)
4437 * Create http session handle
4440 * HINTERNET a session handle on success
4444 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4445 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4446 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4447 DWORD dwInternalFlags, HINTERNET *ret)
4449 http_session_t *lpwhs = NULL;
4450 HINTERNET handle = NULL;
4451 DWORD res = ERROR_SUCCESS;
4455 if (!lpszServerName || !lpszServerName[0])
4456 return ERROR_INVALID_PARAMETER;
4458 assert( hIC->hdr.htype == WH_HINIT );
4460 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4462 return ERROR_OUTOFMEMORY;
4465 * According to my tests. The name is not resolved until a request is sent
4468 lpwhs->hdr.htype = WH_HHTTPSESSION;
4469 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4470 lpwhs->hdr.dwFlags = dwFlags;
4471 lpwhs->hdr.dwContext = dwContext;
4472 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4473 lpwhs->hdr.refs = 1;
4474 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4476 WININET_AddRef( &hIC->hdr );
4477 lpwhs->lpAppInfo = hIC;
4478 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4480 handle = WININET_AllocHandle( &lpwhs->hdr );
4483 ERR("Failed to alloc handle\n");
4484 res = ERROR_OUTOFMEMORY;
4488 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4489 if(hIC->lpszProxyBypass)
4490 FIXME("Proxy bypass is ignored.\n");
4492 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4493 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4494 if (lpszUserName && lpszUserName[0])
4495 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4496 if (lpszPassword && lpszPassword[0])
4497 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4498 lpwhs->nServerPort = nServerPort;
4499 lpwhs->nHostPort = nServerPort;
4501 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4502 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4504 INTERNET_SendCallback(&hIC->hdr, dwContext,
4505 INTERNET_STATUS_HANDLE_CREATED, &handle,
4511 WININET_Release( &lpwhs->hdr );
4514 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4518 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4520 if(res == ERROR_SUCCESS)
4526 /***********************************************************************
4527 * HTTP_OpenConnection (internal)
4529 * Connect to a web server
4536 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4538 http_session_t *lpwhs;
4539 appinfo_t *hIC = NULL;
4540 char szaddr[INET6_ADDRSTRLEN];
4542 DWORD res = ERROR_SUCCESS;
4547 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4549 res = ERROR_INVALID_PARAMETER;
4553 if (NETCON_connected(&lpwhr->netConnection))
4555 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4557 lpwhs = lpwhr->lpHttpSession;
4559 hIC = lpwhs->lpAppInfo;
4560 switch (lpwhs->socketAddress.ss_family)
4563 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4566 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4569 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4570 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4572 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4573 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4574 INTERNET_STATUS_CONNECTING_TO_SERVER,
4578 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4579 if (res != ERROR_SUCCESS)
4581 WARN("Socket creation failed: %u\n", res);
4585 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4587 if(res != ERROR_SUCCESS)
4590 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4591 INTERNET_STATUS_CONNECTED_TO_SERVER,
4592 szaddr, strlen(szaddr)+1);
4594 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4596 /* Note: we differ from Microsoft's WinINet here. they seem to have
4597 * a bug that causes no status callbacks to be sent when starting
4598 * a tunnel to a proxy server using the CONNECT verb. i believe our
4599 * behaviour to be more correct and to not cause any incompatibilities
4600 * because using a secure connection through a proxy server is a rare
4601 * case that would be hard for anyone to depend on */
4602 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4603 HTTPREQ_CloseConnection(&lpwhr->hdr);
4607 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4608 if(res != ERROR_SUCCESS)
4610 WARN("Couldn't connect securely to host\n");
4612 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4613 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4614 || res == ERROR_INTERNET_INVALID_CA
4615 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4616 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4617 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4618 || res == ERROR_INTERNET_SEC_INVALID_CERT
4619 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4620 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4622 HTTPREQ_CloseConnection(&lpwhr->hdr);
4629 lpwhr->read_pos = lpwhr->read_size = 0;
4630 lpwhr->read_chunked = FALSE;
4632 TRACE("%d <--\n", res);
4637 /***********************************************************************
4638 * HTTP_clear_response_headers (internal)
4640 * clear out any old response headers
4642 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4646 for( i=0; i<lpwhr->nCustHeaders; i++)
4648 if( !lpwhr->pCustHeaders[i].lpszField )
4650 if( !lpwhr->pCustHeaders[i].lpszValue )
4652 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4654 HTTP_DeleteCustomHeader( lpwhr, i );
4659 /***********************************************************************
4660 * HTTP_GetResponseHeaders (internal)
4662 * Read server response
4669 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4672 WCHAR buffer[MAX_REPLY_LEN];
4673 DWORD buflen = MAX_REPLY_LEN;
4674 BOOL bSuccess = FALSE;
4676 char bufferA[MAX_REPLY_LEN];
4677 LPWSTR status_code = NULL, status_text = NULL;
4678 DWORD cchMaxRawHeaders = 1024;
4679 LPWSTR lpszRawHeaders = NULL;
4681 DWORD cchRawHeaders = 0;
4682 BOOL codeHundred = FALSE;
4686 if (!NETCON_connected(&lpwhr->netConnection))
4690 static const WCHAR szHundred[] = {'1','0','0',0};
4692 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4694 buflen = MAX_REPLY_LEN;
4695 if (!read_line(lpwhr, bufferA, &buflen))
4698 /* clear old response headers (eg. from a redirect response) */
4700 HTTP_clear_response_headers( lpwhr );
4705 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4706 /* check is this a status code line? */
4707 if (!strncmpW(buffer, g_szHttp1_0, 4))
4709 /* split the version from the status code */
4710 status_code = strchrW( buffer, ' ' );
4715 /* split the status code from the status text */
4716 status_text = strchrW( status_code, ' ' );
4721 TRACE("version [%s] status code [%s] status text [%s]\n",
4722 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4724 codeHundred = (!strcmpW(status_code, szHundred));
4726 else if (!codeHundred)
4728 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4730 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4731 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4733 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4734 lpwhr->lpszStatusText = heap_strdupW(szOK);
4736 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4737 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4742 } while (codeHundred);
4744 /* Add status code */
4745 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4746 HTTP_ADDHDR_FLAG_REPLACE);
4748 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4749 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4751 lpwhr->lpszVersion = heap_strdupW(buffer);
4752 lpwhr->lpszStatusText = heap_strdupW(status_text);
4754 /* Restore the spaces */
4755 *(status_code-1) = ' ';
4756 *(status_text-1) = ' ';
4758 /* regenerate raw headers */
4759 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4760 if (!lpszRawHeaders) goto lend;
4762 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4763 cchMaxRawHeaders *= 2;
4764 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4765 if (temp == NULL) goto lend;
4766 lpszRawHeaders = temp;
4767 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4768 cchRawHeaders += (buflen-1);
4769 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4770 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4771 lpszRawHeaders[cchRawHeaders] = '\0';
4773 /* Parse each response line */
4776 buflen = MAX_REPLY_LEN;
4777 if (read_line(lpwhr, bufferA, &buflen))
4779 LPWSTR * pFieldAndValue;
4781 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4783 if (!bufferA[0]) break;
4784 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4786 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4789 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4790 cchMaxRawHeaders *= 2;
4791 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4792 if (temp == NULL) goto lend;
4793 lpszRawHeaders = temp;
4794 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4795 cchRawHeaders += (buflen-1);
4796 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4797 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4798 lpszRawHeaders[cchRawHeaders] = '\0';
4800 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4801 HTTP_ADDREQ_FLAG_ADD );
4803 HTTP_FreeTokens(pFieldAndValue);
4814 /* make sure the response header is terminated with an empty line. Some apps really
4815 truly care about that empty line being there for some reason. Just add it to the
4817 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4819 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4820 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4821 if (temp == NULL) goto lend;
4822 lpszRawHeaders = temp;
4825 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4827 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4828 lpwhr->lpszRawHeaders = lpszRawHeaders;
4829 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4839 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4844 /***********************************************************************
4845 * HTTP_InterpretHttpHeader (internal)
4847 * Parse server response
4851 * Pointer to array of field, value, NULL on success.
4854 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4856 LPWSTR * pTokenPair;
4860 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4862 pszColon = strchrW(buffer, ':');
4863 /* must have two tokens */
4866 HTTP_FreeTokens(pTokenPair);
4868 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4872 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4875 HTTP_FreeTokens(pTokenPair);
4878 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4879 pTokenPair[0][pszColon - buffer] = '\0';
4883 len = strlenW(pszColon);
4884 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4887 HTTP_FreeTokens(pTokenPair);
4890 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4892 strip_spaces(pTokenPair[0]);
4893 strip_spaces(pTokenPair[1]);
4895 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4899 /***********************************************************************
4900 * HTTP_ProcessHeader (internal)
4902 * Stuff header into header tables according to <dwModifier>
4906 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4908 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4910 LPHTTPHEADERW lphttpHdr = NULL;
4912 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4913 DWORD res = ERROR_HTTP_INVALID_HEADER;
4915 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4917 /* REPLACE wins out over ADD */
4918 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4919 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4921 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4924 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4928 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4929 return ERROR_HTTP_INVALID_HEADER;
4930 lphttpHdr = &lpwhr->pCustHeaders[index];
4936 hdr.lpszField = (LPWSTR)field;
4937 hdr.lpszValue = (LPWSTR)value;
4938 hdr.wFlags = hdr.wCount = 0;
4940 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4941 hdr.wFlags |= HDR_ISREQUEST;
4943 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4945 /* no value to delete */
4946 else return ERROR_SUCCESS;
4948 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4949 lphttpHdr->wFlags |= HDR_ISREQUEST;
4951 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4953 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4955 HTTP_DeleteCustomHeader( lpwhr, index );
4961 hdr.lpszField = (LPWSTR)field;
4962 hdr.lpszValue = (LPWSTR)value;
4963 hdr.wFlags = hdr.wCount = 0;
4965 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4966 hdr.wFlags |= HDR_ISREQUEST;
4968 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4971 return ERROR_SUCCESS;
4973 else if (dwModifier & COALESCEFLAGS)
4978 INT origlen = strlenW(lphttpHdr->lpszValue);
4979 INT valuelen = strlenW(value);
4981 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4984 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4986 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4989 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4992 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4994 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4997 lphttpHdr->lpszValue = lpsztmp;
4998 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5001 lphttpHdr->lpszValue[origlen] = ch;
5003 lphttpHdr->lpszValue[origlen] = ' ';
5007 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5008 lphttpHdr->lpszValue[len] = '\0';
5009 res = ERROR_SUCCESS;
5013 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5014 res = ERROR_OUTOFMEMORY;
5017 TRACE("<-- %d\n", res);
5022 /***********************************************************************
5023 * HTTP_FinishedReading (internal)
5025 * Called when all content from server has been read by client.
5028 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5030 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5037 HTTPREQ_CloseConnection(&lpwhr->hdr);
5040 /* FIXME: store data in the URL cache here */
5046 /***********************************************************************
5047 * HTTP_GetCustomHeaderIndex (internal)
5049 * Return index of custom header from header array
5052 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5053 int requested_index, BOOL request_only)
5057 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5059 for (index = 0; index < lpwhr->nCustHeaders; index++)
5061 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5064 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5067 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5070 if (requested_index == 0)
5075 if (index >= lpwhr->nCustHeaders)
5078 TRACE("Return: %d\n", index);
5083 /***********************************************************************
5084 * HTTP_InsertCustomHeader (internal)
5086 * Insert header into array
5089 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5092 LPHTTPHEADERW lph = NULL;
5094 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5095 count = lpwhr->nCustHeaders + 1;
5097 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5099 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5102 return ERROR_OUTOFMEMORY;
5104 lpwhr->pCustHeaders = lph;
5105 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5106 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5107 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5108 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5109 lpwhr->nCustHeaders++;
5111 return ERROR_SUCCESS;
5115 /***********************************************************************
5116 * HTTP_DeleteCustomHeader (internal)
5118 * Delete header from array
5119 * If this function is called, the indexs may change.
5121 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5123 if( lpwhr->nCustHeaders <= 0 )
5125 if( index >= lpwhr->nCustHeaders )
5127 lpwhr->nCustHeaders--;
5129 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5130 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5132 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5133 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5134 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5140 /***********************************************************************
5141 * HTTP_VerifyValidHeader (internal)
5143 * Verify the given header is not invalid for the given http request
5146 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5148 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5149 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5150 return ERROR_HTTP_INVALID_HEADER;
5152 return ERROR_SUCCESS;
5155 /***********************************************************************
5156 * IsHostInProxyBypassList (@)
5161 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5163 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);