cabinet: Call FCI function pointers explicitly instead of hiding them inside macros.
[wine.git] / dlls / wininet / http.c
blob9215c348b4b08446da8426cd6ac2b463342e9290
1 /*
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
11 * Ulrich Czekalla
12 * David Hammerton
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
29 #include "config.h"
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
67 #include "internet.h"
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]))
158 struct HttpAuthInfo
160 LPWSTR scheme;
161 CredHandle cred;
162 CtxtHandle ctx;
163 TimeStamp exp;
164 ULONG attr;
165 ULONG max_token;
166 void *auth_data;
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 struct gzip_stream_t {
173 #ifdef HAVE_ZLIB
174 z_stream zstream;
175 #endif
176 BYTE buf[8192];
177 DWORD buf_size;
178 DWORD buf_pos;
179 BOOL end_of_data;
182 typedef struct _basicAuthorizationData
184 struct list entry;
186 LPWSTR lpszwHost;
187 LPWSTR lpszwRealm;
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
190 } basicAuthorizationData;
192 typedef struct _authorizationData
194 struct list entry;
196 LPWSTR host;
197 LPWSTR scheme;
198 LPWSTR domain;
199 UINT domain_len;
200 LPWSTR user;
201 UINT user_len;
202 LPWSTR password;
203 UINT password_len;
204 } 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 =
212 0, 0, &authcache_cs,
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)
235 int HeaderIndex = 0;
236 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
237 if (HeaderIndex == -1)
238 return NULL;
239 else
240 return &req->pCustHeaders[HeaderIndex];
243 #ifdef HAVE_ZLIB
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;
258 int index, zres;
260 gzip_stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(gzip_stream_t));
261 gzip_stream->zstream.zalloc = wininet_zalloc;
262 gzip_stream->zstream.zfree = wininet_zfree;
264 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
265 if(zres != Z_OK) {
266 ERR("inflateInit failed: %d\n", zres);
267 HeapFree(GetProcessHeap(), 0, gzip_stream);
268 return;
271 req->gzip_stream = gzip_stream;
273 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
274 if(index != -1)
275 HTTP_DeleteCustomHeader(req, index);
278 #else
280 static void init_gzip_stream(http_request_t *req)
282 ERR("gzip stream not supported, missing zlib.\n");
285 #endif
287 /* set the request content length based on the headers */
288 static DWORD set_content_length( http_request_t *lpwhr )
290 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
291 WCHAR encoding[20];
292 DWORD size;
294 size = sizeof(lpwhr->dwContentLength);
295 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
296 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
297 lpwhr->dwContentLength = ~0u;
299 size = sizeof(encoding);
300 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
301 !strcmpiW(encoding, szChunked))
303 lpwhr->dwContentLength = ~0u;
304 lpwhr->read_chunked = TRUE;
307 if(lpwhr->decoding) {
308 int encoding_idx;
310 static const WCHAR gzipW[] = {'g','z','i','p',0};
312 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
313 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
314 init_gzip_stream(lpwhr);
317 return lpwhr->dwContentLength;
320 /***********************************************************************
321 * HTTP_Tokenize (internal)
323 * Tokenize a string, allocating memory for the tokens.
325 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
327 LPWSTR * token_array;
328 int tokens = 0;
329 int i;
330 LPCWSTR next_token;
332 if (string)
334 /* empty string has no tokens */
335 if (*string)
336 tokens++;
337 /* count tokens */
338 for (i = 0; string[i]; i++)
340 if (!strncmpW(string+i, token_string, strlenW(token_string)))
342 DWORD j;
343 tokens++;
344 /* we want to skip over separators, but not the null terminator */
345 for (j = 0; j < strlenW(token_string) - 1; j++)
346 if (!string[i+j])
347 break;
348 i += j;
353 /* add 1 for terminating NULL */
354 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
355 token_array[tokens] = NULL;
356 if (!tokens)
357 return token_array;
358 for (i = 0; i < tokens; i++)
360 int len;
361 next_token = strstrW(string, token_string);
362 if (!next_token) next_token = string+strlenW(string);
363 len = next_token - string;
364 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
365 memcpy(token_array[i], string, len*sizeof(WCHAR));
366 token_array[i][len] = '\0';
367 string = next_token+strlenW(token_string);
369 return token_array;
372 /***********************************************************************
373 * HTTP_FreeTokens (internal)
375 * Frees memory returned from HTTP_Tokenize.
377 static void HTTP_FreeTokens(LPWSTR * token_array)
379 int i;
380 for (i = 0; token_array[i]; i++)
381 HeapFree(GetProcessHeap(), 0, token_array[i]);
382 HeapFree(GetProcessHeap(), 0, token_array);
385 static void HTTP_FixURL(http_request_t *lpwhr)
387 static const WCHAR szSlash[] = { '/',0 };
388 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
390 /* If we don't have a path we set it to root */
391 if (NULL == lpwhr->lpszPath)
392 lpwhr->lpszPath = heap_strdupW(szSlash);
393 else /* remove \r and \n*/
395 int nLen = strlenW(lpwhr->lpszPath);
396 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
398 nLen--;
399 lpwhr->lpszPath[nLen]='\0';
401 /* Replace '\' with '/' */
402 while (nLen>0) {
403 nLen--;
404 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
408 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
409 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
410 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
412 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
413 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
414 *fixurl = '/';
415 strcpyW(fixurl + 1, lpwhr->lpszPath);
416 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
417 lpwhr->lpszPath = fixurl;
421 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
423 LPWSTR requestString;
424 DWORD len, n;
425 LPCWSTR *req;
426 UINT i;
427 LPWSTR p;
429 static const WCHAR szSpace[] = { ' ',0 };
430 static const WCHAR szColon[] = { ':',' ',0 };
431 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
433 /* allocate space for an array of all the string pointers to be added */
434 len = (lpwhr->nCustHeaders)*4 + 10;
435 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
437 /* add the verb, path and HTTP version string */
438 n = 0;
439 req[n++] = verb;
440 req[n++] = szSpace;
441 req[n++] = path;
442 req[n++] = szSpace;
443 req[n++] = version;
445 /* Append custom request headers */
446 for (i = 0; i < lpwhr->nCustHeaders; i++)
448 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
450 req[n++] = szCrLf;
451 req[n++] = lpwhr->pCustHeaders[i].lpszField;
452 req[n++] = szColon;
453 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
455 TRACE("Adding custom header %s (%s)\n",
456 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
457 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
461 if( n >= len )
462 ERR("oops. buffer overrun\n");
464 req[n] = NULL;
465 requestString = HTTP_build_req( req, 4 );
466 HeapFree( GetProcessHeap(), 0, req );
469 * Set (header) termination string for request
470 * Make sure there's exactly two new lines at the end of the request
472 p = &requestString[strlenW(requestString)-1];
473 while ( (*p == '\n') || (*p == '\r') )
474 p--;
475 strcpyW( p+1, sztwocrlf );
477 return requestString;
480 static void HTTP_ProcessCookies( http_request_t *lpwhr )
482 int HeaderIndex;
483 int numCookies = 0;
484 LPHTTPHEADERW setCookieHeader;
486 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
488 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
490 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
492 int len;
493 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
494 LPWSTR buf_url;
495 LPHTTPHEADERW Host;
497 Host = HTTP_GetHeader(lpwhr, hostW);
498 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
499 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
500 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
501 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
503 HeapFree(GetProcessHeap(), 0, buf_url);
505 numCookies++;
509 static void strip_spaces(LPWSTR start)
511 LPWSTR str = start;
512 LPWSTR end;
514 while (*str == ' ' && *str != '\0')
515 str++;
517 if (str != start)
518 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
520 end = start + strlenW(start) - 1;
521 while (end >= start && *end == ' ')
523 *end = '\0';
524 end--;
528 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
530 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
531 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
532 BOOL is_basic;
533 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
534 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
535 if (is_basic && pszRealm)
537 LPCWSTR token;
538 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
539 LPCWSTR realm;
540 ptr++;
541 *pszRealm=NULL;
542 token = strchrW(ptr,'=');
543 if (!token)
544 return TRUE;
545 realm = ptr;
546 while (*realm == ' ' && *realm != '\0')
547 realm++;
548 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
549 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
551 token++;
552 while (*token == ' ' && *token != '\0')
553 token++;
554 if (*token == '\0')
555 return TRUE;
556 *pszRealm = heap_strdupW(token);
557 strip_spaces(*pszRealm);
561 return is_basic;
564 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
566 if (!authinfo) return;
568 if (SecIsValidHandle(&authinfo->ctx))
569 DeleteSecurityContext(&authinfo->ctx);
570 if (SecIsValidHandle(&authinfo->cred))
571 FreeCredentialsHandle(&authinfo->cred);
573 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
574 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
575 HeapFree(GetProcessHeap(), 0, authinfo);
578 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
580 basicAuthorizationData *ad;
581 UINT rc = 0;
583 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
585 EnterCriticalSection(&authcache_cs);
586 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
588 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
590 TRACE("Authorization found in cache\n");
591 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
592 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
593 rc = ad->AuthorizationLen;
594 break;
597 LeaveCriticalSection(&authcache_cs);
598 return rc;
601 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
603 struct list *cursor;
604 basicAuthorizationData* ad = NULL;
606 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
608 EnterCriticalSection(&authcache_cs);
609 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
611 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
612 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
614 ad = check;
615 break;
619 if (ad)
621 TRACE("Found match in cache, replacing\n");
622 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
627 else
629 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
630 ad->lpszwHost = heap_strdupW(host);
631 ad->lpszwRealm = heap_strdupW(realm);
632 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
633 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
634 ad->AuthorizationLen = auth_data_len;
635 list_add_head(&basicAuthorizationCache,&ad->entry);
636 TRACE("authorization cached\n");
638 LeaveCriticalSection(&authcache_cs);
641 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
642 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
644 authorizationData *ad;
646 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
648 EnterCriticalSection(&authcache_cs);
649 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
650 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
651 TRACE("Authorization found in cache\n");
653 nt_auth_identity->User = heap_strdupW(ad->user);
654 nt_auth_identity->Password = heap_strdupW(ad->password);
655 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
656 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
657 (!nt_auth_identity->Domain && ad->domain_len)) {
658 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
659 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
660 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
661 break;
664 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
665 nt_auth_identity->UserLength = ad->user_len;
666 nt_auth_identity->PasswordLength = ad->password_len;
667 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
668 nt_auth_identity->DomainLength = ad->domain_len;
669 LeaveCriticalSection(&authcache_cs);
670 return TRUE;
673 LeaveCriticalSection(&authcache_cs);
675 return FALSE;
678 static void cache_authorization(LPWSTR host, LPWSTR scheme,
679 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
681 authorizationData *ad;
682 BOOL found = FALSE;
684 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
686 EnterCriticalSection(&authcache_cs);
687 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
688 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
689 found = TRUE;
690 break;
693 if(found) {
694 HeapFree(GetProcessHeap(), 0, ad->user);
695 HeapFree(GetProcessHeap(), 0, ad->password);
696 HeapFree(GetProcessHeap(), 0, ad->domain);
697 } else {
698 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
699 if(!ad) {
700 LeaveCriticalSection(&authcache_cs);
701 return;
704 ad->host = heap_strdupW(host);
705 ad->scheme = heap_strdupW(scheme);
706 list_add_head(&authorizationCache, &ad->entry);
709 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
710 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
711 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
712 ad->user_len = nt_auth_identity->UserLength;
713 ad->password_len = nt_auth_identity->PasswordLength;
714 ad->domain_len = nt_auth_identity->DomainLength;
716 if(!ad->host || !ad->scheme || !ad->user || !ad->password
717 || (nt_auth_identity->Domain && !ad->domain)) {
718 HeapFree(GetProcessHeap(), 0, ad->host);
719 HeapFree(GetProcessHeap(), 0, ad->scheme);
720 HeapFree(GetProcessHeap(), 0, ad->user);
721 HeapFree(GetProcessHeap(), 0, ad->password);
722 HeapFree(GetProcessHeap(), 0, ad->domain);
723 list_remove(&ad->entry);
724 HeapFree(GetProcessHeap(), 0, ad);
727 LeaveCriticalSection(&authcache_cs);
730 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
731 struct HttpAuthInfo **ppAuthInfo,
732 LPWSTR domain_and_username, LPWSTR password,
733 LPWSTR host )
735 SECURITY_STATUS sec_status;
736 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
737 BOOL first = FALSE;
738 LPWSTR szRealm = NULL;
740 TRACE("%s\n", debugstr_w(pszAuthValue));
742 if (!pAuthInfo)
744 TimeStamp exp;
746 first = TRUE;
747 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
748 if (!pAuthInfo)
749 return FALSE;
751 SecInvalidateHandle(&pAuthInfo->cred);
752 SecInvalidateHandle(&pAuthInfo->ctx);
753 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
754 pAuthInfo->attr = 0;
755 pAuthInfo->auth_data = NULL;
756 pAuthInfo->auth_data_len = 0;
757 pAuthInfo->finished = FALSE;
759 if (is_basic_auth_value(pszAuthValue,NULL))
761 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
762 pAuthInfo->scheme = heap_strdupW(szBasic);
763 if (!pAuthInfo->scheme)
765 HeapFree(GetProcessHeap(), 0, pAuthInfo);
766 return FALSE;
769 else
771 PVOID pAuthData;
772 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
774 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
775 if (!pAuthInfo->scheme)
777 HeapFree(GetProcessHeap(), 0, pAuthInfo);
778 return FALSE;
781 if (domain_and_username)
783 WCHAR *user = strchrW(domain_and_username, '\\');
784 WCHAR *domain = domain_and_username;
786 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
788 pAuthData = &nt_auth_identity;
790 if (user) user++;
791 else
793 user = domain_and_username;
794 domain = NULL;
797 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
798 nt_auth_identity.User = user;
799 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
800 nt_auth_identity.Domain = domain;
801 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
802 nt_auth_identity.Password = password;
803 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
805 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
807 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
808 pAuthData = &nt_auth_identity;
809 else
810 /* use default credentials */
811 pAuthData = NULL;
813 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
814 SECPKG_CRED_OUTBOUND, NULL,
815 pAuthData, NULL,
816 NULL, &pAuthInfo->cred,
817 &exp);
819 if(pAuthData && !domain_and_username) {
820 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
821 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
822 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
825 if (sec_status == SEC_E_OK)
827 PSecPkgInfoW sec_pkg_info;
828 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
829 if (sec_status == SEC_E_OK)
831 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
832 FreeContextBuffer(sec_pkg_info);
835 if (sec_status != SEC_E_OK)
837 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
838 debugstr_w(pAuthInfo->scheme), sec_status);
839 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
840 HeapFree(GetProcessHeap(), 0, pAuthInfo);
841 return FALSE;
844 *ppAuthInfo = pAuthInfo;
846 else if (pAuthInfo->finished)
847 return FALSE;
849 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
850 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
852 ERR("authentication scheme changed from %s to %s\n",
853 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
854 return FALSE;
857 if (is_basic_auth_value(pszAuthValue,&szRealm))
859 int userlen;
860 int passlen;
861 char *auth_data = NULL;
862 UINT auth_data_len = 0;
864 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
866 if (!domain_and_username)
868 if (host && szRealm)
869 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
870 if (auth_data_len == 0)
872 HeapFree(GetProcessHeap(),0,szRealm);
873 return FALSE;
876 else
878 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
879 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
881 /* length includes a nul terminator, which will be re-used for the ':' */
882 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
883 if (!auth_data)
885 HeapFree(GetProcessHeap(),0,szRealm);
886 return FALSE;
889 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
890 auth_data[userlen] = ':';
891 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
892 auth_data_len = userlen + 1 + passlen;
893 if (host && szRealm)
894 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
897 pAuthInfo->auth_data = auth_data;
898 pAuthInfo->auth_data_len = auth_data_len;
899 pAuthInfo->finished = TRUE;
900 HeapFree(GetProcessHeap(),0,szRealm);
902 return TRUE;
904 else
906 LPCWSTR pszAuthData;
907 SecBufferDesc out_desc, in_desc;
908 SecBuffer out, in;
909 unsigned char *buffer;
910 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
911 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
913 in.BufferType = SECBUFFER_TOKEN;
914 in.cbBuffer = 0;
915 in.pvBuffer = NULL;
917 in_desc.ulVersion = 0;
918 in_desc.cBuffers = 1;
919 in_desc.pBuffers = &in;
921 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
922 if (*pszAuthData == ' ')
924 pszAuthData++;
925 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
926 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
927 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
930 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
932 out.BufferType = SECBUFFER_TOKEN;
933 out.cbBuffer = pAuthInfo->max_token;
934 out.pvBuffer = buffer;
936 out_desc.ulVersion = 0;
937 out_desc.cBuffers = 1;
938 out_desc.pBuffers = &out;
940 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
941 first ? NULL : &pAuthInfo->ctx,
942 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
943 context_req, 0, SECURITY_NETWORK_DREP,
944 in.pvBuffer ? &in_desc : NULL,
945 0, &pAuthInfo->ctx, &out_desc,
946 &pAuthInfo->attr, &pAuthInfo->exp);
947 if (sec_status == SEC_E_OK)
949 pAuthInfo->finished = TRUE;
950 pAuthInfo->auth_data = out.pvBuffer;
951 pAuthInfo->auth_data_len = out.cbBuffer;
952 TRACE("sending last auth packet\n");
954 else if (sec_status == SEC_I_CONTINUE_NEEDED)
956 pAuthInfo->auth_data = out.pvBuffer;
957 pAuthInfo->auth_data_len = out.cbBuffer;
958 TRACE("sending next auth packet\n");
960 else
962 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
963 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
964 destroy_authinfo(pAuthInfo);
965 *ppAuthInfo = NULL;
966 return FALSE;
970 return TRUE;
973 /***********************************************************************
974 * HTTP_HttpAddRequestHeadersW (internal)
976 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
977 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
979 LPWSTR lpszStart;
980 LPWSTR lpszEnd;
981 LPWSTR buffer;
982 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
984 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
986 if( dwHeaderLength == ~0U )
987 len = strlenW(lpszHeader);
988 else
989 len = dwHeaderLength;
990 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
991 lstrcpynW( buffer, lpszHeader, len + 1);
993 lpszStart = buffer;
997 LPWSTR * pFieldAndValue;
999 lpszEnd = lpszStart;
1001 while (*lpszEnd != '\0')
1003 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1004 break;
1005 lpszEnd++;
1008 if (*lpszStart == '\0')
1009 break;
1011 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1013 *lpszEnd = '\0';
1014 lpszEnd++; /* Jump over newline */
1016 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1017 if (*lpszStart == '\0')
1019 /* Skip 0-length headers */
1020 lpszStart = lpszEnd;
1021 res = ERROR_SUCCESS;
1022 continue;
1024 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1025 if (pFieldAndValue)
1027 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
1028 if (res == ERROR_SUCCESS)
1029 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
1030 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1031 HTTP_FreeTokens(pFieldAndValue);
1034 lpszStart = lpszEnd;
1035 } while (res == ERROR_SUCCESS);
1037 HeapFree(GetProcessHeap(), 0, buffer);
1039 return res;
1042 /***********************************************************************
1043 * HttpAddRequestHeadersW (WININET.@)
1045 * Adds one or more HTTP header to the request handler
1047 * NOTE
1048 * On Windows if dwHeaderLength includes the trailing '\0', then
1049 * HttpAddRequestHeadersW() adds it too. However this results in an
1050 * invalid Http header which is rejected by some servers so we probably
1051 * don't need to match Windows on that point.
1053 * RETURNS
1054 * TRUE on success
1055 * FALSE on failure
1058 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1059 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1061 http_request_t *lpwhr;
1062 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1064 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1066 if (!lpszHeader)
1067 return TRUE;
1069 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
1070 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
1071 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
1072 if( lpwhr )
1073 WININET_Release( &lpwhr->hdr );
1075 if(res != ERROR_SUCCESS)
1076 SetLastError(res);
1077 return res == ERROR_SUCCESS;
1080 /***********************************************************************
1081 * HttpAddRequestHeadersA (WININET.@)
1083 * Adds one or more HTTP header to the request handler
1085 * RETURNS
1086 * TRUE on success
1087 * FALSE on failure
1090 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1091 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1093 DWORD len;
1094 LPWSTR hdr;
1095 BOOL r;
1097 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1099 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1100 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1101 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1102 if( dwHeaderLength != ~0U )
1103 dwHeaderLength = len;
1105 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1107 HeapFree( GetProcessHeap(), 0, hdr );
1109 return r;
1112 /***********************************************************************
1113 * HttpOpenRequestA (WININET.@)
1115 * Open a HTTP request handle
1117 * RETURNS
1118 * HINTERNET a HTTP request handle on success
1119 * NULL on failure
1122 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1123 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1124 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1125 DWORD dwFlags, DWORD_PTR dwContext)
1127 LPWSTR szVerb = NULL, szObjectName = NULL;
1128 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1129 INT acceptTypesCount;
1130 HINTERNET rc = FALSE;
1131 LPCSTR *types;
1133 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1134 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1135 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1136 dwFlags, dwContext);
1138 if (lpszVerb)
1140 szVerb = heap_strdupAtoW(lpszVerb);
1141 if ( !szVerb )
1142 goto end;
1145 if (lpszObjectName)
1147 szObjectName = heap_strdupAtoW(lpszObjectName);
1148 if ( !szObjectName )
1149 goto end;
1152 if (lpszVersion)
1154 szVersion = heap_strdupAtoW(lpszVersion);
1155 if ( !szVersion )
1156 goto end;
1159 if (lpszReferrer)
1161 szReferrer = heap_strdupAtoW(lpszReferrer);
1162 if ( !szReferrer )
1163 goto end;
1166 if (lpszAcceptTypes)
1168 acceptTypesCount = 0;
1169 types = lpszAcceptTypes;
1170 while (*types)
1172 __TRY
1174 /* find out how many there are */
1175 if (*types && **types)
1177 TRACE("accept type: %s\n", debugstr_a(*types));
1178 acceptTypesCount++;
1181 __EXCEPT_PAGE_FAULT
1183 WARN("invalid accept type pointer\n");
1185 __ENDTRY;
1186 types++;
1188 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1189 if (!szAcceptTypes) goto end;
1191 acceptTypesCount = 0;
1192 types = lpszAcceptTypes;
1193 while (*types)
1195 __TRY
1197 if (*types && **types)
1198 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1200 __EXCEPT_PAGE_FAULT
1202 /* ignore invalid pointer */
1204 __ENDTRY;
1205 types++;
1207 szAcceptTypes[acceptTypesCount] = NULL;
1210 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1211 szVersion, szReferrer,
1212 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1214 end:
1215 if (szAcceptTypes)
1217 acceptTypesCount = 0;
1218 while (szAcceptTypes[acceptTypesCount])
1220 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1221 acceptTypesCount++;
1223 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1225 HeapFree(GetProcessHeap(), 0, szReferrer);
1226 HeapFree(GetProcessHeap(), 0, szVersion);
1227 HeapFree(GetProcessHeap(), 0, szObjectName);
1228 HeapFree(GetProcessHeap(), 0, szVerb);
1230 return rc;
1233 /***********************************************************************
1234 * HTTP_EncodeBase64
1236 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1238 UINT n = 0, x;
1239 static const CHAR HTTP_Base64Enc[] =
1240 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1242 while( len > 0 )
1244 /* first 6 bits, all from bin[0] */
1245 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1246 x = (bin[0] & 3) << 4;
1248 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1249 if( len == 1 )
1251 base64[n++] = HTTP_Base64Enc[x];
1252 base64[n++] = '=';
1253 base64[n++] = '=';
1254 break;
1256 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1257 x = ( bin[1] & 0x0f ) << 2;
1259 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1260 if( len == 2 )
1262 base64[n++] = HTTP_Base64Enc[x];
1263 base64[n++] = '=';
1264 break;
1266 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1268 /* last 6 bits, all from bin [2] */
1269 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1270 bin += 3;
1271 len -= 3;
1273 base64[n] = 0;
1274 return n;
1277 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1278 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1279 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1280 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1281 static const signed char HTTP_Base64Dec[256] =
1283 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1284 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1285 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1286 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1287 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1288 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1289 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1290 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1291 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1292 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1293 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1294 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1295 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1296 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1297 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1298 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1299 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1300 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1301 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1302 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1303 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1304 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1305 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1306 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1307 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1308 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1310 #undef CH
1312 /***********************************************************************
1313 * HTTP_DecodeBase64
1315 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1317 unsigned int n = 0;
1319 while(*base64)
1321 signed char in[4];
1323 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1324 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1325 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1326 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1328 WARN("invalid base64: %s\n", debugstr_w(base64));
1329 return 0;
1331 if (bin)
1332 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1333 n++;
1335 if ((base64[2] == '=') && (base64[3] == '='))
1336 break;
1337 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1338 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1340 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1341 return 0;
1343 if (bin)
1344 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1345 n++;
1347 if (base64[3] == '=')
1348 break;
1349 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1350 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1352 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1353 return 0;
1355 if (bin)
1356 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1357 n++;
1359 base64 += 4;
1362 return n;
1365 /***********************************************************************
1366 * HTTP_InsertAuthorization
1368 * Insert or delete the authorization field in the request header.
1370 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1372 if (pAuthInfo)
1374 static const WCHAR wszSpace[] = {' ',0};
1375 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1376 unsigned int len;
1377 WCHAR *authorization = NULL;
1379 if (pAuthInfo->auth_data_len)
1381 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1382 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1383 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1384 if (!authorization)
1385 return FALSE;
1387 strcpyW(authorization, pAuthInfo->scheme);
1388 strcatW(authorization, wszSpace);
1389 HTTP_EncodeBase64(pAuthInfo->auth_data,
1390 pAuthInfo->auth_data_len,
1391 authorization+strlenW(authorization));
1393 /* clear the data as it isn't valid now that it has been sent to the
1394 * server, unless it's Basic authentication which doesn't do
1395 * connection tracking */
1396 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1398 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1399 pAuthInfo->auth_data = NULL;
1400 pAuthInfo->auth_data_len = 0;
1404 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1406 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1408 HeapFree(GetProcessHeap(), 0, authorization);
1410 return TRUE;
1413 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1415 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1416 DWORD size;
1418 size = sizeof(new_location);
1419 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1421 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1422 strcpyW( url, new_location );
1424 else
1426 static const WCHAR slash[] = { '/',0 };
1427 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1428 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1429 http_session_t *session = req->lpHttpSession;
1431 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1432 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1434 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1436 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1437 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1438 else
1439 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1440 if (req->lpszPath[0] != '/') strcatW( url, slash );
1441 strcatW( url, req->lpszPath );
1443 TRACE("url=%s\n", debugstr_w(url));
1444 return url;
1447 /***********************************************************************
1448 * HTTP_DealWithProxy
1450 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1452 WCHAR buf[MAXHOSTNAME];
1453 WCHAR protoProxy[MAXHOSTNAME + 15];
1454 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1455 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1456 static WCHAR szNul[] = { 0 };
1457 URL_COMPONENTSW UrlComponents;
1458 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1459 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1460 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1462 memset( &UrlComponents, 0, sizeof UrlComponents );
1463 UrlComponents.dwStructSize = sizeof UrlComponents;
1464 UrlComponents.lpszHostName = buf;
1465 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1467 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1468 return FALSE;
1469 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1470 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1471 sprintfW(proxy, szFormat, protoProxy);
1472 else
1473 strcpyW(proxy, protoProxy);
1474 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1475 return FALSE;
1476 if( UrlComponents.dwHostNameLength == 0 )
1477 return FALSE;
1479 if( !lpwhr->lpszPath )
1480 lpwhr->lpszPath = szNul;
1482 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1483 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1485 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1486 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1487 lpwhs->nServerPort = UrlComponents.nPort;
1489 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1490 return TRUE;
1493 #ifndef INET6_ADDRSTRLEN
1494 #define INET6_ADDRSTRLEN 46
1495 #endif
1497 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1499 char szaddr[INET6_ADDRSTRLEN];
1500 http_session_t *lpwhs = lpwhr->lpHttpSession;
1501 const void *addr;
1503 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1504 INTERNET_STATUS_RESOLVING_NAME,
1505 lpwhs->lpszServerName,
1506 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1508 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1509 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1510 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1511 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1513 switch (lpwhs->socketAddress.ss_family)
1515 case AF_INET:
1516 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1517 break;
1518 case AF_INET6:
1519 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1520 break;
1521 default:
1522 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1523 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1525 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1526 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1527 INTERNET_STATUS_NAME_RESOLVED,
1528 szaddr, strlen(szaddr)+1);
1530 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1531 return ERROR_SUCCESS;
1534 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1536 LPHTTPHEADERW host_header;
1538 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1540 host_header = HTTP_GetHeader(req, hostW);
1541 if(!host_header)
1542 return FALSE;
1544 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1545 return TRUE;
1549 /***********************************************************************
1550 * HTTPREQ_Destroy (internal)
1552 * Deallocate request handle
1555 static void HTTPREQ_Destroy(object_header_t *hdr)
1557 http_request_t *lpwhr = (http_request_t*) hdr;
1558 DWORD i;
1560 TRACE("\n");
1562 if(lpwhr->hCacheFile) {
1563 WCHAR url[INTERNET_MAX_URL_LENGTH];
1564 FILETIME ft;
1566 CloseHandle(lpwhr->hCacheFile);
1568 memset(&ft, 0, sizeof(FILETIME));
1569 if(HTTP_GetRequestURL(lpwhr, url)) {
1570 CommitUrlCacheEntryW(url, lpwhr->lpszCacheFile, ft, ft,
1571 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1575 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1577 DeleteCriticalSection( &lpwhr->read_section );
1578 WININET_Release(&lpwhr->lpHttpSession->hdr);
1580 destroy_authinfo(lpwhr->pAuthInfo);
1581 destroy_authinfo(lpwhr->pProxyAuthInfo);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1584 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1585 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1586 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1587 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1589 for (i = 0; i < lpwhr->nCustHeaders; i++)
1591 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1592 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1595 #ifdef HAVE_ZLIB
1596 if(lpwhr->gzip_stream) {
1597 if(!lpwhr->gzip_stream->end_of_data)
1598 inflateEnd(&lpwhr->gzip_stream->zstream);
1599 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1601 #endif
1603 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1604 HeapFree(GetProcessHeap(), 0, lpwhr);
1607 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1609 http_request_t *lpwhr = (http_request_t*) hdr;
1611 TRACE("%p\n",lpwhr);
1613 if (!NETCON_connected(&lpwhr->netConnection))
1614 return;
1616 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1617 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1619 NETCON_close(&lpwhr->netConnection);
1621 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1622 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1625 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1627 WCHAR szVersion[10];
1628 WCHAR szConnectionResponse[20];
1629 DWORD dwBufferSize = sizeof(szVersion);
1630 BOOL keepalive = FALSE;
1632 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1633 * the connection is keep-alive by default */
1634 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1635 && !strcmpiW(szVersion, g_szHttp1_1))
1637 keepalive = TRUE;
1640 dwBufferSize = sizeof(szConnectionResponse);
1641 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1642 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1644 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1647 return keepalive;
1650 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1652 http_request_t *req = (http_request_t*)hdr;
1654 switch(option) {
1655 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1657 http_session_t *lpwhs = req->lpHttpSession;
1658 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1660 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1662 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1663 return ERROR_INSUFFICIENT_BUFFER;
1664 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1665 /* FIXME: can't get a SOCKET from our connection since we don't use
1666 * winsock
1668 info->Socket = 0;
1669 /* FIXME: get source port from req->netConnection */
1670 info->SourcePort = 0;
1671 info->DestPort = lpwhs->nHostPort;
1672 info->Flags = 0;
1673 if (HTTP_KeepAlive(req))
1674 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1675 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1676 info->Flags |= IDSI_FLAG_PROXY;
1677 if (req->netConnection.useSSL)
1678 info->Flags |= IDSI_FLAG_SECURE;
1680 return ERROR_SUCCESS;
1683 case INTERNET_OPTION_SECURITY_FLAGS:
1685 DWORD flags;
1686 int bits;
1688 if (*size < sizeof(ULONG))
1689 return ERROR_INSUFFICIENT_BUFFER;
1691 *size = sizeof(DWORD);
1692 flags = 0;
1693 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1694 flags |= SECURITY_FLAG_SECURE;
1695 flags |= req->netConnection.security_flags;
1696 bits = NETCON_GetCipherStrength(&req->netConnection);
1697 if (bits >= 128)
1698 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1699 else if (bits >= 56)
1700 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1701 else
1702 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1703 *(DWORD *)buffer = flags;
1704 return ERROR_SUCCESS;
1707 case INTERNET_OPTION_HANDLE_TYPE:
1708 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1710 if (*size < sizeof(ULONG))
1711 return ERROR_INSUFFICIENT_BUFFER;
1713 *size = sizeof(DWORD);
1714 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1715 return ERROR_SUCCESS;
1717 case INTERNET_OPTION_URL: {
1718 WCHAR url[INTERNET_MAX_URL_LENGTH];
1719 HTTPHEADERW *host;
1720 DWORD len;
1721 WCHAR *pch;
1723 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1725 TRACE("INTERNET_OPTION_URL\n");
1727 host = HTTP_GetHeader(req, hostW);
1728 strcpyW(url, httpW);
1729 strcatW(url, host->lpszValue);
1730 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1731 *pch = 0;
1732 strcatW(url, req->lpszPath);
1734 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1736 if(unicode) {
1737 len = (strlenW(url)+1) * sizeof(WCHAR);
1738 if(*size < len)
1739 return ERROR_INSUFFICIENT_BUFFER;
1741 *size = len;
1742 strcpyW(buffer, url);
1743 return ERROR_SUCCESS;
1744 }else {
1745 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1746 if(len > *size)
1747 return ERROR_INSUFFICIENT_BUFFER;
1749 *size = len;
1750 return ERROR_SUCCESS;
1754 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1755 INTERNET_CACHE_ENTRY_INFOW *info;
1756 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1757 WCHAR url[INTERNET_MAX_URL_LENGTH];
1758 DWORD nbytes, error;
1759 BOOL ret;
1761 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1763 if (*size < sizeof(*ts))
1765 *size = sizeof(*ts);
1766 return ERROR_INSUFFICIENT_BUFFER;
1768 nbytes = 0;
1769 HTTP_GetRequestURL(req, url);
1770 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1771 error = GetLastError();
1772 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1774 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1775 return ERROR_OUTOFMEMORY;
1777 GetUrlCacheEntryInfoW(url, info, &nbytes);
1779 ts->ftExpires = info->ExpireTime;
1780 ts->ftLastModified = info->LastModifiedTime;
1782 HeapFree(GetProcessHeap(), 0, info);
1783 *size = sizeof(*ts);
1784 return ERROR_SUCCESS;
1786 return error;
1789 case INTERNET_OPTION_DATAFILE_NAME: {
1790 DWORD req_size;
1792 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1794 if(!req->lpszCacheFile) {
1795 *size = 0;
1796 return ERROR_INTERNET_ITEM_NOT_FOUND;
1799 if(unicode) {
1800 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1801 if(*size < req_size)
1802 return ERROR_INSUFFICIENT_BUFFER;
1804 *size = req_size;
1805 memcpy(buffer, req->lpszCacheFile, *size);
1806 return ERROR_SUCCESS;
1807 }else {
1808 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1809 if (req_size > *size)
1810 return ERROR_INSUFFICIENT_BUFFER;
1812 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1813 -1, buffer, *size, NULL, NULL);
1814 return ERROR_SUCCESS;
1818 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1819 PCCERT_CONTEXT context;
1821 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1822 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1823 return ERROR_INSUFFICIENT_BUFFER;
1826 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1827 if(context) {
1828 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1829 DWORD len;
1831 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1832 info->ftExpiry = context->pCertInfo->NotAfter;
1833 info->ftStart = context->pCertInfo->NotBefore;
1834 len = CertNameToStrA(context->dwCertEncodingType,
1835 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1836 info->lpszSubjectInfo = LocalAlloc(0, len);
1837 if(info->lpszSubjectInfo)
1838 CertNameToStrA(context->dwCertEncodingType,
1839 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1840 info->lpszSubjectInfo, len);
1841 len = CertNameToStrA(context->dwCertEncodingType,
1842 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1843 info->lpszIssuerInfo = LocalAlloc(0, len);
1844 if(info->lpszIssuerInfo)
1845 CertNameToStrA(context->dwCertEncodingType,
1846 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1847 info->lpszIssuerInfo, len);
1848 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1849 CertFreeCertificateContext(context);
1850 return ERROR_SUCCESS;
1855 return INET_QueryOption(hdr, option, buffer, size, unicode);
1858 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1860 http_request_t *req = (http_request_t*)hdr;
1862 switch(option) {
1863 case INTERNET_OPTION_SECURITY_FLAGS:
1865 DWORD flags;
1867 if (!buffer || size != sizeof(DWORD))
1868 return ERROR_INVALID_PARAMETER;
1869 flags = *(DWORD *)buffer;
1870 TRACE("%08x\n", flags);
1871 req->netConnection.security_flags = flags;
1872 return ERROR_SUCCESS;
1874 case INTERNET_OPTION_SEND_TIMEOUT:
1875 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1876 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1878 if (size != sizeof(DWORD))
1879 return ERROR_INVALID_PARAMETER;
1881 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1882 *(DWORD*)buffer);
1884 case INTERNET_OPTION_USERNAME:
1885 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1886 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1887 return ERROR_SUCCESS;
1889 case INTERNET_OPTION_PASSWORD:
1890 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1891 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1892 return ERROR_SUCCESS;
1893 case INTERNET_OPTION_HTTP_DECODING:
1894 if(size != sizeof(BOOL))
1895 return ERROR_INVALID_PARAMETER;
1896 req->decoding = *(BOOL*)buffer;
1897 return ERROR_SUCCESS;
1900 return ERROR_INTERNET_INVALID_OPTION;
1903 /* read some more data into the read buffer (the read section must be held) */
1904 static DWORD read_more_data( http_request_t *req, int maxlen )
1906 DWORD res;
1907 int len;
1909 if (req->read_pos)
1911 /* move existing data to the start of the buffer */
1912 if(req->read_size)
1913 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1914 req->read_pos = 0;
1917 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1919 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1920 maxlen - req->read_size, 0, &len );
1921 if(res == ERROR_SUCCESS)
1922 req->read_size += len;
1924 return res;
1927 /* remove some amount of data from the read buffer (the read section must be held) */
1928 static void remove_data( http_request_t *req, int count )
1930 if (!(req->read_size -= count)) req->read_pos = 0;
1931 else req->read_pos += count;
1934 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1936 int count, bytes_read, pos = 0;
1937 DWORD res;
1939 EnterCriticalSection( &req->read_section );
1940 for (;;)
1942 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1944 if (eol)
1946 count = eol - (req->read_buf + req->read_pos);
1947 bytes_read = count + 1;
1949 else count = bytes_read = req->read_size;
1951 count = min( count, *len - pos );
1952 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1953 pos += count;
1954 remove_data( req, bytes_read );
1955 if (eol) break;
1957 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1959 *len = 0;
1960 TRACE( "returning empty string\n" );
1961 LeaveCriticalSection( &req->read_section );
1962 INTERNET_SetLastError(res);
1963 return FALSE;
1966 LeaveCriticalSection( &req->read_section );
1968 if (pos < *len)
1970 if (pos && buffer[pos - 1] == '\r') pos--;
1971 *len = pos + 1;
1973 buffer[*len - 1] = 0;
1974 TRACE( "returning %s\n", debugstr_a(buffer));
1975 return TRUE;
1978 /* discard data contents until we reach end of line (the read section must be held) */
1979 static DWORD discard_eol( http_request_t *req )
1981 DWORD res;
1985 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1986 if (eol)
1988 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1989 break;
1991 req->read_pos = req->read_size = 0; /* discard everything */
1992 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1993 } while (req->read_size);
1994 return ERROR_SUCCESS;
1997 /* read the size of the next chunk (the read section must be held) */
1998 static DWORD start_next_chunk( http_request_t *req )
2000 DWORD chunk_size = 0, res;
2002 if (!req->dwContentLength) return ERROR_SUCCESS;
2003 if (req->dwContentLength == req->dwContentRead)
2005 /* read terminator for the previous chunk */
2006 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2007 req->dwContentLength = ~0u;
2008 req->dwContentRead = 0;
2010 for (;;)
2012 while (req->read_size)
2014 char ch = req->read_buf[req->read_pos];
2015 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2016 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2017 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2018 else if (ch == ';' || ch == '\r' || ch == '\n')
2020 TRACE( "reading %u byte chunk\n", chunk_size );
2021 req->dwContentLength = chunk_size;
2022 req->dwContentRead = 0;
2023 return discard_eol( req );
2025 remove_data( req, 1 );
2027 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2028 if (!req->read_size)
2030 req->dwContentLength = req->dwContentRead = 0;
2031 return ERROR_SUCCESS;
2036 /* check if we have reached the end of the data to read (the read section must be held) */
2037 static BOOL end_of_read_data( http_request_t *req )
2039 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2040 if (req->read_chunked) return (req->dwContentLength == 0);
2041 if (req->dwContentLength == ~0u) return FALSE;
2042 return (req->dwContentLength == req->dwContentRead);
2045 /* fetch some more data into the read buffer (the read section must be held) */
2046 static DWORD refill_buffer( http_request_t *req )
2048 int len = sizeof(req->read_buf);
2049 DWORD res;
2051 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2053 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2056 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2057 if (len <= req->read_size) return ERROR_SUCCESS;
2059 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2060 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2061 return ERROR_SUCCESS;
2064 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2066 DWORD ret = ERROR_SUCCESS;
2067 int read = 0;
2069 #ifdef HAVE_ZLIB
2070 z_stream *zstream = &req->gzip_stream->zstream;
2071 DWORD buf_avail;
2072 int zres;
2074 while(read < size && !req->gzip_stream->end_of_data) {
2075 if(!req->read_size) {
2076 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2077 break;
2080 if(req->dwContentRead == req->dwContentLength)
2081 break;
2083 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2085 zstream->next_in = req->read_buf+req->read_pos;
2086 zstream->avail_in = buf_avail;
2087 zstream->next_out = buf+read;
2088 zstream->avail_out = size-read;
2089 zres = inflate(zstream, Z_FULL_FLUSH);
2090 read = size - zstream->avail_out;
2091 req->dwContentRead += buf_avail-zstream->avail_in;
2092 remove_data(req, buf_avail-zstream->avail_in);
2093 if(zres == Z_STREAM_END) {
2094 TRACE("end of data\n");
2095 req->gzip_stream->end_of_data = TRUE;
2096 inflateEnd(&req->gzip_stream->zstream);
2097 }else if(zres != Z_OK) {
2098 WARN("inflate failed %d\n", zres);
2099 if(!read)
2100 ret = ERROR_INTERNET_DECODING_FAILED;
2101 break;
2104 #endif
2106 *read_ret = read;
2107 return ret;
2110 static void refill_gzip_buffer(http_request_t *req)
2112 DWORD res;
2113 int len;
2115 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2116 return;
2118 if(req->gzip_stream->buf_pos) {
2119 if(req->gzip_stream->buf_size)
2120 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2121 req->gzip_stream->buf_pos = 0;
2124 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2125 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2126 if(res == ERROR_SUCCESS)
2127 req->gzip_stream->buf_size += len;
2130 /* return the size of data available to be read immediately (the read section must be held) */
2131 static DWORD get_avail_data( http_request_t *req )
2133 if (req->gzip_stream) {
2134 refill_gzip_buffer(req);
2135 return req->gzip_stream->buf_size;
2137 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2138 return 0;
2139 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2142 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2144 INTERNET_ASYNC_RESULT iar;
2145 DWORD res;
2147 TRACE("%p\n", req);
2149 EnterCriticalSection( &req->read_section );
2150 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2151 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2152 iar.dwError = first_notif ? 0 : get_avail_data(req);
2153 }else {
2154 iar.dwResult = 0;
2155 iar.dwError = res;
2157 LeaveCriticalSection( &req->read_section );
2159 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2160 sizeof(INTERNET_ASYNC_RESULT));
2163 /* read data from the http connection (the read section must be held) */
2164 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2166 BOOL finished_reading = FALSE;
2167 int len, bytes_read = 0;
2168 DWORD ret = ERROR_SUCCESS;
2170 EnterCriticalSection( &req->read_section );
2172 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2174 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2177 if(req->gzip_stream) {
2178 if(req->gzip_stream->buf_size) {
2179 bytes_read = min(req->gzip_stream->buf_size, size);
2180 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2181 req->gzip_stream->buf_pos += bytes_read;
2182 req->gzip_stream->buf_size -= bytes_read;
2183 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2184 refill_buffer(req);
2187 if(size > bytes_read) {
2188 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2189 if(ret == ERROR_SUCCESS)
2190 bytes_read += len;
2193 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2194 }else {
2195 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2197 if (req->read_size) {
2198 bytes_read = min( req->read_size, size );
2199 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2200 remove_data( req, bytes_read );
2203 if (size > bytes_read && (!bytes_read || sync)) {
2204 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2205 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2206 bytes_read += len;
2207 /* always return success, even if the network layer returns an error */
2210 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2211 req->dwContentRead += bytes_read;
2213 done:
2214 *read = bytes_read;
2216 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2217 LeaveCriticalSection( &req->read_section );
2219 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2220 BOOL res;
2221 DWORD dwBytesWritten;
2223 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2224 if(!res)
2225 WARN("WriteFile failed: %u\n", GetLastError());
2228 if(finished_reading)
2229 HTTP_FinishedReading(req);
2231 return ret;
2235 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2237 http_request_t *req = (http_request_t*)hdr;
2238 DWORD res;
2240 EnterCriticalSection( &req->read_section );
2241 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2242 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2244 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2245 if(res == ERROR_SUCCESS)
2246 res = hdr->dwError;
2247 LeaveCriticalSection( &req->read_section );
2249 return res;
2252 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2254 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2255 http_request_t *req = (http_request_t*)workRequest->hdr;
2256 INTERNET_ASYNC_RESULT iar;
2257 DWORD res;
2259 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2261 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2262 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2264 iar.dwResult = res == ERROR_SUCCESS;
2265 iar.dwError = res;
2267 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2268 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2269 sizeof(INTERNET_ASYNC_RESULT));
2272 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2273 DWORD flags, DWORD_PTR context)
2275 http_request_t *req = (http_request_t*)hdr;
2276 DWORD res, size, read, error = ERROR_SUCCESS;
2278 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2279 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2281 if (buffers->dwStructSize != sizeof(*buffers))
2282 return ERROR_INVALID_PARAMETER;
2284 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2286 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2288 WORKREQUEST workRequest;
2290 if (TryEnterCriticalSection( &req->read_section ))
2292 if (get_avail_data(req))
2294 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2295 &buffers->dwBufferLength, FALSE);
2296 size = buffers->dwBufferLength;
2297 LeaveCriticalSection( &req->read_section );
2298 goto done;
2300 LeaveCriticalSection( &req->read_section );
2303 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2304 workRequest.hdr = WININET_AddRef(&req->hdr);
2305 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2307 INTERNET_AsyncCall(&workRequest);
2309 return ERROR_IO_PENDING;
2312 read = 0;
2313 size = buffers->dwBufferLength;
2315 EnterCriticalSection( &req->read_section );
2316 if(hdr->dwError == ERROR_SUCCESS)
2317 hdr->dwError = INTERNET_HANDLE_IN_USE;
2318 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2319 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2321 while(1) {
2322 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2323 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2324 if(res == ERROR_SUCCESS)
2325 read += buffers->dwBufferLength;
2326 else
2327 break;
2329 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2330 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2331 break;
2332 LeaveCriticalSection( &req->read_section );
2334 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2335 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2336 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2337 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2339 EnterCriticalSection( &req->read_section );
2342 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2343 hdr->dwError = ERROR_SUCCESS;
2344 else
2345 error = hdr->dwError;
2347 LeaveCriticalSection( &req->read_section );
2348 size = buffers->dwBufferLength;
2349 buffers->dwBufferLength = read;
2351 done:
2352 if (res == ERROR_SUCCESS) {
2353 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2354 &size, sizeof(size));
2357 return res==ERROR_SUCCESS ? error : res;
2360 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2362 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2363 http_request_t *req = (http_request_t*)workRequest->hdr;
2364 INTERNET_ASYNC_RESULT iar;
2365 DWORD res;
2367 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2369 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2370 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2372 iar.dwResult = res == ERROR_SUCCESS;
2373 iar.dwError = res;
2375 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2376 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2377 sizeof(INTERNET_ASYNC_RESULT));
2380 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2381 DWORD flags, DWORD_PTR context)
2384 http_request_t *req = (http_request_t*)hdr;
2385 DWORD res, size, read, error = ERROR_SUCCESS;
2387 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2388 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2390 if (buffers->dwStructSize != sizeof(*buffers))
2391 return ERROR_INVALID_PARAMETER;
2393 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2395 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2397 WORKREQUEST workRequest;
2399 if (TryEnterCriticalSection( &req->read_section ))
2401 if (get_avail_data(req))
2403 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2404 &buffers->dwBufferLength, FALSE);
2405 size = buffers->dwBufferLength;
2406 LeaveCriticalSection( &req->read_section );
2407 goto done;
2409 LeaveCriticalSection( &req->read_section );
2412 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2413 workRequest.hdr = WININET_AddRef(&req->hdr);
2414 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2416 INTERNET_AsyncCall(&workRequest);
2418 return ERROR_IO_PENDING;
2421 read = 0;
2422 size = buffers->dwBufferLength;
2424 EnterCriticalSection( &req->read_section );
2425 if(hdr->dwError == ERROR_SUCCESS)
2426 hdr->dwError = INTERNET_HANDLE_IN_USE;
2427 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2428 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2430 while(1) {
2431 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2432 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2433 if(res == ERROR_SUCCESS)
2434 read += buffers->dwBufferLength;
2435 else
2436 break;
2438 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2439 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2440 break;
2441 LeaveCriticalSection( &req->read_section );
2443 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2444 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2445 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2446 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2448 EnterCriticalSection( &req->read_section );
2451 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2452 hdr->dwError = ERROR_SUCCESS;
2453 else
2454 error = hdr->dwError;
2456 LeaveCriticalSection( &req->read_section );
2457 size = buffers->dwBufferLength;
2458 buffers->dwBufferLength = read;
2460 done:
2461 if (res == ERROR_SUCCESS) {
2462 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2463 &size, sizeof(size));
2466 return res==ERROR_SUCCESS ? error : res;
2469 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2471 DWORD res;
2472 http_request_t *lpwhr = (http_request_t*)hdr;
2474 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2476 *written = 0;
2477 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2478 if (res == ERROR_SUCCESS)
2479 lpwhr->dwBytesWritten += *written;
2481 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2482 return res;
2485 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2487 http_request_t *req = (http_request_t*)workRequest->hdr;
2489 HTTP_ReceiveRequestData(req, FALSE);
2492 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2494 http_request_t *req = (http_request_t*)hdr;
2496 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2498 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2500 WORKREQUEST workRequest;
2502 /* never wait, if we can't enter the section we queue an async request right away */
2503 if (TryEnterCriticalSection( &req->read_section ))
2505 if ((*available = get_avail_data( req ))) goto done;
2506 if (end_of_read_data( req )) goto done;
2507 LeaveCriticalSection( &req->read_section );
2510 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2511 workRequest.hdr = WININET_AddRef( &req->hdr );
2513 INTERNET_AsyncCall(&workRequest);
2515 return ERROR_IO_PENDING;
2518 EnterCriticalSection( &req->read_section );
2520 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2522 refill_buffer( req );
2523 *available = get_avail_data( req );
2526 done:
2527 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2529 DWORD extra;
2530 if (NETCON_query_data_available(&req->netConnection, &extra))
2531 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2533 LeaveCriticalSection( &req->read_section );
2535 TRACE( "returning %u\n", *available );
2536 return ERROR_SUCCESS;
2539 static const object_vtbl_t HTTPREQVtbl = {
2540 HTTPREQ_Destroy,
2541 HTTPREQ_CloseConnection,
2542 HTTPREQ_QueryOption,
2543 HTTPREQ_SetOption,
2544 HTTPREQ_ReadFile,
2545 HTTPREQ_ReadFileExA,
2546 HTTPREQ_ReadFileExW,
2547 HTTPREQ_WriteFile,
2548 HTTPREQ_QueryDataAvailable,
2549 NULL
2552 /***********************************************************************
2553 * HTTP_HttpOpenRequestW (internal)
2555 * Open a HTTP request handle
2557 * RETURNS
2558 * HINTERNET a HTTP request handle on success
2559 * NULL on failure
2562 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2563 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2564 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2565 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2567 appinfo_t *hIC = NULL;
2568 http_request_t *lpwhr;
2569 LPWSTR lpszHostName = NULL;
2570 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2571 DWORD len, res;
2573 TRACE("-->\n");
2575 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2576 hIC = lpwhs->lpAppInfo;
2578 lpwhr = alloc_object(&lpwhs->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2579 if(!lpwhr)
2580 return ERROR_OUTOFMEMORY;
2582 lpwhr->hdr.htype = WH_HHTTPREQ;
2583 lpwhr->hdr.dwFlags = dwFlags;
2584 lpwhr->hdr.dwContext = dwContext;
2585 lpwhr->dwContentLength = ~0u;
2587 InitializeCriticalSection( &lpwhr->read_section );
2589 WININET_AddRef( &lpwhs->hdr );
2590 lpwhr->lpHttpSession = lpwhs;
2591 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2593 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2594 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2595 if (NULL == lpszHostName)
2597 res = ERROR_OUTOFMEMORY;
2598 goto lend;
2601 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2602 goto lend;
2604 if (lpszObjectName && *lpszObjectName) {
2605 HRESULT rc;
2607 len = 0;
2608 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2609 if (rc != E_POINTER)
2610 len = strlenW(lpszObjectName)+1;
2611 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2612 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2613 URL_ESCAPE_SPACES_ONLY);
2614 if (rc != S_OK)
2616 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2617 strcpyW(lpwhr->lpszPath,lpszObjectName);
2619 }else {
2620 static const WCHAR slashW[] = {'/',0};
2622 lpwhr->lpszPath = heap_strdupW(slashW);
2625 if (lpszReferrer && *lpszReferrer)
2626 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2628 if (lpszAcceptTypes)
2630 int i;
2631 for (i = 0; lpszAcceptTypes[i]; i++)
2633 if (!*lpszAcceptTypes[i]) continue;
2634 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2635 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2636 HTTP_ADDHDR_FLAG_REQ |
2637 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2641 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2642 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2644 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2645 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2646 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2648 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2649 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2650 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2652 else
2653 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2654 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2656 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2657 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2658 INTERNET_DEFAULT_HTTPS_PORT :
2659 INTERNET_DEFAULT_HTTP_PORT);
2661 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2662 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2663 INTERNET_DEFAULT_HTTPS_PORT :
2664 INTERNET_DEFAULT_HTTP_PORT);
2666 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2667 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2669 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2670 INTERNET_STATUS_HANDLE_CREATED, &lpwhr->hdr.hInternet,
2671 sizeof(HINTERNET));
2673 lend:
2674 TRACE("<-- %u (%p)\n", res, lpwhr);
2676 HeapFree(GetProcessHeap(), 0, lpszHostName);
2677 if(res != ERROR_SUCCESS) {
2678 WININET_Release( &lpwhr->hdr );
2679 *ret = NULL;
2680 return res;
2683 *ret = lpwhr->hdr.hInternet;
2684 return ERROR_SUCCESS;
2687 /***********************************************************************
2688 * HttpOpenRequestW (WININET.@)
2690 * Open a HTTP request handle
2692 * RETURNS
2693 * HINTERNET a HTTP request handle on success
2694 * NULL on failure
2697 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2698 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2699 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2700 DWORD dwFlags, DWORD_PTR dwContext)
2702 http_session_t *lpwhs;
2703 HINTERNET handle = NULL;
2704 DWORD res;
2706 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2707 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2708 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2709 dwFlags, dwContext);
2710 if(lpszAcceptTypes!=NULL)
2712 int i;
2713 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2714 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2717 lpwhs = (http_session_t*) get_handle_object( hHttpSession );
2718 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2720 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2721 goto lend;
2725 * My tests seem to show that the windows version does not
2726 * become asynchronous until after this point. And anyhow
2727 * if this call was asynchronous then how would you get the
2728 * necessary HINTERNET pointer returned by this function.
2731 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2732 lpszVersion, lpszReferrer, lpszAcceptTypes,
2733 dwFlags, dwContext, &handle);
2734 lend:
2735 if( lpwhs )
2736 WININET_Release( &lpwhs->hdr );
2737 TRACE("returning %p\n", handle);
2738 if(res != ERROR_SUCCESS)
2739 SetLastError(res);
2740 return handle;
2743 /* read any content returned by the server so that the connection can be
2744 * reused */
2745 static void HTTP_DrainContent(http_request_t *req)
2747 DWORD bytes_read;
2749 if (!NETCON_connected(&req->netConnection)) return;
2751 if (req->dwContentLength == -1)
2753 NETCON_close(&req->netConnection);
2754 return;
2756 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2760 char buffer[2048];
2761 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2762 return;
2763 } while (bytes_read);
2766 static const LPCWSTR header_lookup[] = {
2767 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2768 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2769 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2770 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2771 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2772 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2773 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2774 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2775 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2776 szDate, /* HTTP_QUERY_DATE = 9 */
2777 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2778 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2779 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2780 szURI, /* HTTP_QUERY_URI = 13 */
2781 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2782 NULL, /* HTTP_QUERY_COST = 15 */
2783 NULL, /* HTTP_QUERY_LINK = 16 */
2784 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2785 NULL, /* HTTP_QUERY_VERSION = 18 */
2786 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2787 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2788 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2789 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2790 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2791 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2792 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2793 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2794 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2795 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2796 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2797 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2798 NULL, /* HTTP_QUERY_FROM = 31 */
2799 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2800 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2801 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2802 szReferer, /* HTTP_QUERY_REFERER = 35 */
2803 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2804 szServer, /* HTTP_QUERY_SERVER = 37 */
2805 NULL, /* HTTP_TITLE = 38 */
2806 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2807 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2808 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2809 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2810 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2811 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2812 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2813 NULL, /* HTTP_QUERY_REFRESH = 46 */
2814 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2815 szAge, /* HTTP_QUERY_AGE = 48 */
2816 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2817 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2818 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2819 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2820 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2821 szETag, /* HTTP_QUERY_ETAG = 54 */
2822 hostW, /* HTTP_QUERY_HOST = 55 */
2823 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2824 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2825 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2826 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2827 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2828 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2829 szRange, /* HTTP_QUERY_RANGE = 62 */
2830 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2831 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2832 szVary, /* HTTP_QUERY_VARY = 65 */
2833 szVia, /* HTTP_QUERY_VIA = 66 */
2834 szWarning, /* HTTP_QUERY_WARNING = 67 */
2835 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2836 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2837 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2840 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2842 /***********************************************************************
2843 * HTTP_HttpQueryInfoW (internal)
2845 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2846 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2848 LPHTTPHEADERW lphttpHdr = NULL;
2849 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2850 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2851 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2852 INT index = -1;
2854 /* Find requested header structure */
2855 switch (level)
2857 case HTTP_QUERY_CUSTOM:
2858 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2859 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2860 break;
2861 case HTTP_QUERY_RAW_HEADERS_CRLF:
2863 LPWSTR headers;
2864 DWORD len = 0;
2865 DWORD res = ERROR_INVALID_PARAMETER;
2867 if (request_only)
2868 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2869 else
2870 headers = lpwhr->lpszRawHeaders;
2872 if (headers)
2873 len = strlenW(headers) * sizeof(WCHAR);
2875 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2877 len += sizeof(WCHAR);
2878 res = ERROR_INSUFFICIENT_BUFFER;
2880 else if (lpBuffer)
2882 if (headers)
2883 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2884 else
2886 len = strlenW(szCrLf) * sizeof(WCHAR);
2887 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2889 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2890 res = ERROR_SUCCESS;
2892 *lpdwBufferLength = len;
2894 if (request_only)
2895 HeapFree(GetProcessHeap(), 0, headers);
2896 return res;
2898 case HTTP_QUERY_RAW_HEADERS:
2900 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2901 DWORD i, size = 0;
2902 LPWSTR pszString = lpBuffer;
2904 for (i = 0; ppszRawHeaderLines[i]; i++)
2905 size += strlenW(ppszRawHeaderLines[i]) + 1;
2907 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2909 HTTP_FreeTokens(ppszRawHeaderLines);
2910 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2911 return ERROR_INSUFFICIENT_BUFFER;
2913 if (pszString)
2915 for (i = 0; ppszRawHeaderLines[i]; i++)
2917 DWORD len = strlenW(ppszRawHeaderLines[i]);
2918 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2919 pszString += len+1;
2921 *pszString = '\0';
2922 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2924 *lpdwBufferLength = size * sizeof(WCHAR);
2925 HTTP_FreeTokens(ppszRawHeaderLines);
2927 return ERROR_SUCCESS;
2929 case HTTP_QUERY_STATUS_TEXT:
2930 if (lpwhr->lpszStatusText)
2932 DWORD len = strlenW(lpwhr->lpszStatusText);
2933 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2935 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2936 return ERROR_INSUFFICIENT_BUFFER;
2938 if (lpBuffer)
2940 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2941 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2943 *lpdwBufferLength = len * sizeof(WCHAR);
2944 return ERROR_SUCCESS;
2946 break;
2947 case HTTP_QUERY_VERSION:
2948 if (lpwhr->lpszVersion)
2950 DWORD len = strlenW(lpwhr->lpszVersion);
2951 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2953 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2954 return ERROR_INSUFFICIENT_BUFFER;
2956 if (lpBuffer)
2958 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2959 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2961 *lpdwBufferLength = len * sizeof(WCHAR);
2962 return ERROR_SUCCESS;
2964 break;
2965 case HTTP_QUERY_CONTENT_ENCODING:
2966 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2967 requested_index,request_only);
2968 break;
2969 default:
2970 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2972 if (level < LAST_TABLE_HEADER && header_lookup[level])
2973 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2974 requested_index,request_only);
2977 if (index >= 0)
2978 lphttpHdr = &lpwhr->pCustHeaders[index];
2980 /* Ensure header satisfies requested attributes */
2981 if (!lphttpHdr ||
2982 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2983 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2985 return ERROR_HTTP_HEADER_NOT_FOUND;
2988 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2990 /* coalesce value to requested type */
2991 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2993 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2994 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2996 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2998 time_t tmpTime;
2999 struct tm tmpTM;
3000 SYSTEMTIME *STHook;
3002 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3004 tmpTM = *gmtime(&tmpTime);
3005 STHook = (SYSTEMTIME *)lpBuffer;
3006 STHook->wDay = tmpTM.tm_mday;
3007 STHook->wHour = tmpTM.tm_hour;
3008 STHook->wMilliseconds = 0;
3009 STHook->wMinute = tmpTM.tm_min;
3010 STHook->wDayOfWeek = tmpTM.tm_wday;
3011 STHook->wMonth = tmpTM.tm_mon + 1;
3012 STHook->wSecond = tmpTM.tm_sec;
3013 STHook->wYear = tmpTM.tm_year;
3015 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3016 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3017 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3019 else if (lphttpHdr->lpszValue)
3021 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3023 if (len > *lpdwBufferLength)
3025 *lpdwBufferLength = len;
3026 return ERROR_INSUFFICIENT_BUFFER;
3028 if (lpBuffer)
3030 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3031 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3033 *lpdwBufferLength = len - sizeof(WCHAR);
3035 return ERROR_SUCCESS;
3038 /***********************************************************************
3039 * HttpQueryInfoW (WININET.@)
3041 * Queries for information about an HTTP request
3043 * RETURNS
3044 * TRUE on success
3045 * FALSE on failure
3048 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3049 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3051 http_request_t *lpwhr;
3052 DWORD res;
3054 if (TRACE_ON(wininet)) {
3055 #define FE(x) { x, #x }
3056 static const wininet_flag_info query_flags[] = {
3057 FE(HTTP_QUERY_MIME_VERSION),
3058 FE(HTTP_QUERY_CONTENT_TYPE),
3059 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3060 FE(HTTP_QUERY_CONTENT_ID),
3061 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3062 FE(HTTP_QUERY_CONTENT_LENGTH),
3063 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3064 FE(HTTP_QUERY_ALLOW),
3065 FE(HTTP_QUERY_PUBLIC),
3066 FE(HTTP_QUERY_DATE),
3067 FE(HTTP_QUERY_EXPIRES),
3068 FE(HTTP_QUERY_LAST_MODIFIED),
3069 FE(HTTP_QUERY_MESSAGE_ID),
3070 FE(HTTP_QUERY_URI),
3071 FE(HTTP_QUERY_DERIVED_FROM),
3072 FE(HTTP_QUERY_COST),
3073 FE(HTTP_QUERY_LINK),
3074 FE(HTTP_QUERY_PRAGMA),
3075 FE(HTTP_QUERY_VERSION),
3076 FE(HTTP_QUERY_STATUS_CODE),
3077 FE(HTTP_QUERY_STATUS_TEXT),
3078 FE(HTTP_QUERY_RAW_HEADERS),
3079 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3080 FE(HTTP_QUERY_CONNECTION),
3081 FE(HTTP_QUERY_ACCEPT),
3082 FE(HTTP_QUERY_ACCEPT_CHARSET),
3083 FE(HTTP_QUERY_ACCEPT_ENCODING),
3084 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3085 FE(HTTP_QUERY_AUTHORIZATION),
3086 FE(HTTP_QUERY_CONTENT_ENCODING),
3087 FE(HTTP_QUERY_FORWARDED),
3088 FE(HTTP_QUERY_FROM),
3089 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3090 FE(HTTP_QUERY_LOCATION),
3091 FE(HTTP_QUERY_ORIG_URI),
3092 FE(HTTP_QUERY_REFERER),
3093 FE(HTTP_QUERY_RETRY_AFTER),
3094 FE(HTTP_QUERY_SERVER),
3095 FE(HTTP_QUERY_TITLE),
3096 FE(HTTP_QUERY_USER_AGENT),
3097 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3098 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3099 FE(HTTP_QUERY_ACCEPT_RANGES),
3100 FE(HTTP_QUERY_SET_COOKIE),
3101 FE(HTTP_QUERY_COOKIE),
3102 FE(HTTP_QUERY_REQUEST_METHOD),
3103 FE(HTTP_QUERY_REFRESH),
3104 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3105 FE(HTTP_QUERY_AGE),
3106 FE(HTTP_QUERY_CACHE_CONTROL),
3107 FE(HTTP_QUERY_CONTENT_BASE),
3108 FE(HTTP_QUERY_CONTENT_LOCATION),
3109 FE(HTTP_QUERY_CONTENT_MD5),
3110 FE(HTTP_QUERY_CONTENT_RANGE),
3111 FE(HTTP_QUERY_ETAG),
3112 FE(HTTP_QUERY_HOST),
3113 FE(HTTP_QUERY_IF_MATCH),
3114 FE(HTTP_QUERY_IF_NONE_MATCH),
3115 FE(HTTP_QUERY_IF_RANGE),
3116 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3117 FE(HTTP_QUERY_MAX_FORWARDS),
3118 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3119 FE(HTTP_QUERY_RANGE),
3120 FE(HTTP_QUERY_TRANSFER_ENCODING),
3121 FE(HTTP_QUERY_UPGRADE),
3122 FE(HTTP_QUERY_VARY),
3123 FE(HTTP_QUERY_VIA),
3124 FE(HTTP_QUERY_WARNING),
3125 FE(HTTP_QUERY_CUSTOM)
3127 static const wininet_flag_info modifier_flags[] = {
3128 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3129 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3130 FE(HTTP_QUERY_FLAG_NUMBER),
3131 FE(HTTP_QUERY_FLAG_COALESCE)
3133 #undef FE
3134 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3135 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3136 DWORD i;
3138 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3139 TRACE(" Attribute:");
3140 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3141 if (query_flags[i].val == info) {
3142 TRACE(" %s", query_flags[i].name);
3143 break;
3146 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3147 TRACE(" Unknown (%08x)", info);
3150 TRACE(" Modifier:");
3151 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3152 if (modifier_flags[i].val & info_mod) {
3153 TRACE(" %s", modifier_flags[i].name);
3154 info_mod &= ~ modifier_flags[i].val;
3158 if (info_mod) {
3159 TRACE(" Unknown (%08x)", info_mod);
3161 TRACE("\n");
3164 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
3165 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3167 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3168 goto lend;
3171 if (lpBuffer == NULL)
3172 *lpdwBufferLength = 0;
3173 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3174 lpBuffer, lpdwBufferLength, lpdwIndex);
3176 lend:
3177 if( lpwhr )
3178 WININET_Release( &lpwhr->hdr );
3180 TRACE("%u <--\n", res);
3181 if(res != ERROR_SUCCESS)
3182 SetLastError(res);
3183 return res == ERROR_SUCCESS;
3186 /***********************************************************************
3187 * HttpQueryInfoA (WININET.@)
3189 * Queries for information about an HTTP request
3191 * RETURNS
3192 * TRUE on success
3193 * FALSE on failure
3196 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3197 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3199 BOOL result;
3200 DWORD len;
3201 WCHAR* bufferW;
3203 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3204 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3206 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3207 lpdwBufferLength, lpdwIndex );
3210 if (lpBuffer)
3212 DWORD alloclen;
3213 len = (*lpdwBufferLength)*sizeof(WCHAR);
3214 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3216 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3217 if (alloclen < len)
3218 alloclen = len;
3220 else
3221 alloclen = len;
3222 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3223 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3224 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3225 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3226 } else
3228 bufferW = NULL;
3229 len = 0;
3232 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3233 &len, lpdwIndex );
3234 if( result )
3236 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3237 lpBuffer, *lpdwBufferLength, NULL, NULL );
3238 *lpdwBufferLength = len - 1;
3240 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3242 else
3243 /* since the strings being returned from HttpQueryInfoW should be
3244 * only ASCII characters, it is reasonable to assume that all of
3245 * the Unicode characters can be reduced to a single byte */
3246 *lpdwBufferLength = len / sizeof(WCHAR);
3248 HeapFree(GetProcessHeap(), 0, bufferW );
3250 return result;
3253 /***********************************************************************
3254 * HTTP_GetRedirectURL (internal)
3256 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3258 static WCHAR szHttp[] = {'h','t','t','p',0};
3259 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3260 http_session_t *lpwhs = lpwhr->lpHttpSession;
3261 URL_COMPONENTSW urlComponents;
3262 DWORD url_length = 0;
3263 LPWSTR orig_url;
3264 LPWSTR combined_url;
3266 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3267 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3268 urlComponents.dwSchemeLength = 0;
3269 urlComponents.lpszHostName = lpwhs->lpszHostName;
3270 urlComponents.dwHostNameLength = 0;
3271 urlComponents.nPort = lpwhs->nHostPort;
3272 urlComponents.lpszUserName = lpwhs->lpszUserName;
3273 urlComponents.dwUserNameLength = 0;
3274 urlComponents.lpszPassword = NULL;
3275 urlComponents.dwPasswordLength = 0;
3276 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3277 urlComponents.dwUrlPathLength = 0;
3278 urlComponents.lpszExtraInfo = NULL;
3279 urlComponents.dwExtraInfoLength = 0;
3281 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3282 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3283 return NULL;
3285 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3287 /* convert from bytes to characters */
3288 url_length = url_length / sizeof(WCHAR) - 1;
3289 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3291 HeapFree(GetProcessHeap(), 0, orig_url);
3292 return NULL;
3295 url_length = 0;
3296 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3297 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3299 HeapFree(GetProcessHeap(), 0, orig_url);
3300 return NULL;
3302 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3304 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3306 HeapFree(GetProcessHeap(), 0, orig_url);
3307 HeapFree(GetProcessHeap(), 0, combined_url);
3308 return NULL;
3310 HeapFree(GetProcessHeap(), 0, orig_url);
3311 return combined_url;
3315 /***********************************************************************
3316 * HTTP_HandleRedirect (internal)
3318 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3320 http_session_t *lpwhs = lpwhr->lpHttpSession;
3321 appinfo_t *hIC = lpwhs->lpAppInfo;
3322 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3323 WCHAR path[INTERNET_MAX_URL_LENGTH];
3324 int index;
3326 if(lpszUrl[0]=='/')
3328 /* if it's an absolute path, keep the same session info */
3329 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3331 else
3333 URL_COMPONENTSW urlComponents;
3334 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3335 static WCHAR szHttp[] = {'h','t','t','p',0};
3336 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3338 userName[0] = 0;
3339 hostName[0] = 0;
3340 protocol[0] = 0;
3342 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3343 urlComponents.lpszScheme = protocol;
3344 urlComponents.dwSchemeLength = 32;
3345 urlComponents.lpszHostName = hostName;
3346 urlComponents.dwHostNameLength = MAXHOSTNAME;
3347 urlComponents.lpszUserName = userName;
3348 urlComponents.dwUserNameLength = 1024;
3349 urlComponents.lpszPassword = NULL;
3350 urlComponents.dwPasswordLength = 0;
3351 urlComponents.lpszUrlPath = path;
3352 urlComponents.dwUrlPathLength = 2048;
3353 urlComponents.lpszExtraInfo = NULL;
3354 urlComponents.dwExtraInfoLength = 0;
3355 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3356 return INTERNET_GetLastError();
3358 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3359 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3361 TRACE("redirect from secure page to non-secure page\n");
3362 /* FIXME: warn about from secure redirect to non-secure page */
3363 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3365 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3366 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3368 TRACE("redirect from non-secure page to secure page\n");
3369 /* FIXME: notify about redirect to secure page */
3370 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3373 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3375 if (lstrlenW(protocol)>4) /*https*/
3376 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3377 else /*http*/
3378 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3381 #if 0
3383 * This upsets redirects to binary files on sourceforge.net
3384 * and gives an html page instead of the target file
3385 * Examination of the HTTP request sent by native wininet.dll
3386 * reveals that it doesn't send a referrer in that case.
3387 * Maybe there's a flag that enables this, or maybe a referrer
3388 * shouldn't be added in case of a redirect.
3391 /* consider the current host as the referrer */
3392 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3393 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3394 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3395 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3396 #endif
3398 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3399 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3400 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3402 int len;
3403 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3404 len = lstrlenW(hostName);
3405 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3406 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3407 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3409 else
3410 lpwhs->lpszHostName = heap_strdupW(hostName);
3412 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3414 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3415 lpwhs->lpszUserName = NULL;
3416 if (userName[0])
3417 lpwhs->lpszUserName = heap_strdupW(userName);
3419 if (!using_proxy)
3421 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3423 DWORD res;
3425 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3426 lpwhs->lpszServerName = heap_strdupW(hostName);
3427 lpwhs->nServerPort = urlComponents.nPort;
3429 NETCON_close(&lpwhr->netConnection);
3430 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3431 return res;
3433 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3434 if (res != ERROR_SUCCESS)
3435 return res;
3437 lpwhr->read_pos = lpwhr->read_size = 0;
3438 lpwhr->read_chunked = FALSE;
3441 else
3442 TRACE("Redirect through proxy\n");
3445 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3446 lpwhr->lpszPath=NULL;
3447 if (*path)
3449 DWORD needed = 0;
3450 HRESULT rc;
3452 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3453 if (rc != E_POINTER)
3454 needed = strlenW(path)+1;
3455 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3456 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3457 URL_ESCAPE_SPACES_ONLY);
3458 if (rc != S_OK)
3460 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3461 strcpyW(lpwhr->lpszPath,path);
3465 /* Remove custom content-type/length headers on redirects. */
3466 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3467 if (0 <= index)
3468 HTTP_DeleteCustomHeader(lpwhr, index);
3469 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3470 if (0 <= index)
3471 HTTP_DeleteCustomHeader(lpwhr, index);
3473 return ERROR_SUCCESS;
3476 /***********************************************************************
3477 * HTTP_build_req (internal)
3479 * concatenate all the strings in the request together
3481 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3483 LPCWSTR *t;
3484 LPWSTR str;
3486 for( t = list; *t ; t++ )
3487 len += strlenW( *t );
3488 len++;
3490 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3491 *str = 0;
3493 for( t = list; *t ; t++ )
3494 strcatW( str, *t );
3496 return str;
3499 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3501 LPWSTR lpszPath;
3502 LPWSTR requestString;
3503 INT len;
3504 INT cnt;
3505 INT responseLen;
3506 char *ascii_req;
3507 DWORD res;
3508 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3509 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3510 http_session_t *lpwhs = lpwhr->lpHttpSession;
3512 TRACE("\n");
3514 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3515 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3516 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3517 HeapFree( GetProcessHeap(), 0, lpszPath );
3519 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3520 NULL, 0, NULL, NULL );
3521 len--; /* the nul terminator isn't needed */
3522 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3523 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3524 ascii_req, len, NULL, NULL );
3525 HeapFree( GetProcessHeap(), 0, requestString );
3527 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3529 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3530 HeapFree( GetProcessHeap(), 0, ascii_req );
3531 if (res != ERROR_SUCCESS)
3532 return res;
3534 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3535 if (!responseLen)
3536 return ERROR_HTTP_INVALID_HEADER;
3538 return ERROR_SUCCESS;
3541 static void HTTP_InsertCookies(http_request_t *lpwhr)
3543 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3544 LPWSTR lpszCookies, lpszUrl = NULL;
3545 DWORD nCookieSize, size;
3546 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3548 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3549 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3550 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3552 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3554 int cnt = 0;
3555 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3557 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3558 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3560 cnt += sprintfW(lpszCookies, szCookie);
3561 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3562 strcatW(lpszCookies, szCrLf);
3564 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3565 HeapFree(GetProcessHeap(), 0, lpszCookies);
3568 HeapFree(GetProcessHeap(), 0, lpszUrl);
3571 /***********************************************************************
3572 * HTTP_HttpSendRequestW (internal)
3574 * Sends the specified request to the HTTP server
3576 * RETURNS
3577 * TRUE on success
3578 * FALSE on failure
3581 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3582 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3583 DWORD dwContentLength, BOOL bEndRequest)
3585 INT cnt;
3586 BOOL redirected = FALSE;
3587 LPWSTR requestString = NULL;
3588 INT responseLen;
3589 BOOL loop_next;
3590 INTERNET_ASYNC_RESULT iar;
3591 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3592 static const WCHAR szContentLength[] =
3593 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3594 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3595 DWORD res;
3597 TRACE("--> %p\n", lpwhr);
3599 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3601 /* if the verb is NULL default to GET */
3602 if (!lpwhr->lpszVerb)
3603 lpwhr->lpszVerb = heap_strdupW(szGET);
3605 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3607 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3608 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3609 lpwhr->dwBytesToWrite = dwContentLength;
3611 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3613 WCHAR *agent_header;
3614 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3615 int len;
3617 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3618 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3619 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3621 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3622 HeapFree(GetProcessHeap(), 0, agent_header);
3624 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3626 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3627 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3629 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3631 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3632 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3633 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3638 DWORD len;
3639 BOOL reusing_connection;
3640 char *ascii_req;
3642 loop_next = FALSE;
3644 /* like native, just in case the caller forgot to call InternetReadFile
3645 * for all the data */
3646 HTTP_DrainContent(lpwhr);
3647 lpwhr->dwContentRead = 0;
3648 if(redirected) {
3649 lpwhr->dwContentLength = ~0u;
3650 lpwhr->dwBytesToWrite = 0;
3653 if (TRACE_ON(wininet))
3655 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3656 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3659 HTTP_FixURL(lpwhr);
3660 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3662 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3664 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3665 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3667 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3668 HTTP_InsertCookies(lpwhr);
3670 /* add the headers the caller supplied */
3671 if( lpszHeaders && dwHeaderLength )
3673 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3674 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3677 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3679 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3680 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3681 HeapFree(GetProcessHeap(), 0, url);
3683 else
3684 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3687 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3689 /* Send the request and store the results */
3690 if(NETCON_connected(&lpwhr->netConnection))
3691 reusing_connection = TRUE;
3692 else
3693 reusing_connection = FALSE;
3695 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3696 goto lend;
3698 /* send the request as ASCII, tack on the optional data */
3699 if (!lpOptional || redirected)
3700 dwOptionalLength = 0;
3701 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3702 NULL, 0, NULL, NULL );
3703 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3704 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3705 ascii_req, len, NULL, NULL );
3706 if( lpOptional )
3707 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3708 len = (len + dwOptionalLength - 1);
3709 ascii_req[len] = 0;
3710 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3712 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3713 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3715 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3716 HeapFree( GetProcessHeap(), 0, ascii_req );
3718 lpwhr->dwBytesWritten = dwOptionalLength;
3720 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3721 INTERNET_STATUS_REQUEST_SENT,
3722 &len, sizeof(DWORD));
3724 if (bEndRequest)
3726 DWORD dwBufferSize;
3727 DWORD dwStatusCode;
3729 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3730 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3732 if (res != ERROR_SUCCESS)
3733 goto lend;
3735 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3736 /* FIXME: We should know that connection is closed before sending
3737 * headers. Otherwise wrong callbacks are executed */
3738 if(!responseLen && reusing_connection) {
3739 TRACE("Connection closed by server, reconnecting\n");
3740 loop_next = TRUE;
3741 continue;
3744 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3745 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3746 sizeof(DWORD));
3748 HTTP_ProcessCookies(lpwhr);
3750 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3752 dwBufferSize = sizeof(dwStatusCode);
3753 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3754 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3755 dwStatusCode = 0;
3757 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3759 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3760 dwBufferSize=sizeof(szNewLocation);
3761 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3762 dwStatusCode == HTTP_STATUS_MOVED ||
3763 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3764 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3766 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3768 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3769 lpwhr->lpszVerb = heap_strdupW(szGET);
3771 HTTP_DrainContent(lpwhr);
3772 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3774 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3775 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3776 res = HTTP_HandleRedirect(lpwhr, new_url);
3777 if (res == ERROR_SUCCESS)
3779 HeapFree(GetProcessHeap(), 0, requestString);
3780 loop_next = TRUE;
3782 HeapFree( GetProcessHeap(), 0, new_url );
3784 redirected = TRUE;
3787 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3789 WCHAR szAuthValue[2048];
3790 dwBufferSize=2048;
3791 if (dwStatusCode == HTTP_STATUS_DENIED)
3793 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3794 DWORD dwIndex = 0;
3795 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3797 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3798 &lpwhr->pAuthInfo,
3799 lpwhr->lpHttpSession->lpszUserName,
3800 lpwhr->lpHttpSession->lpszPassword,
3801 Host->lpszValue))
3803 HeapFree(GetProcessHeap(), 0, requestString);
3804 loop_next = TRUE;
3805 break;
3809 if(!loop_next) {
3810 TRACE("Cleaning wrong authorization data\n");
3811 destroy_authinfo(lpwhr->pAuthInfo);
3812 lpwhr->pAuthInfo = NULL;
3815 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3817 DWORD dwIndex = 0;
3818 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3820 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3821 &lpwhr->pProxyAuthInfo,
3822 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3823 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3824 NULL))
3826 loop_next = TRUE;
3827 break;
3831 if(!loop_next) {
3832 TRACE("Cleaning wrong proxy authorization data\n");
3833 destroy_authinfo(lpwhr->pProxyAuthInfo);
3834 lpwhr->pProxyAuthInfo = NULL;
3839 else
3840 res = ERROR_SUCCESS;
3842 while (loop_next);
3844 if(res == ERROR_SUCCESS) {
3845 WCHAR url[INTERNET_MAX_URL_LENGTH];
3846 WCHAR cacheFileName[MAX_PATH+1];
3847 BOOL b;
3849 b = HTTP_GetRequestURL(lpwhr, url);
3850 if(!b) {
3851 WARN("Could not get URL\n");
3852 goto lend;
3855 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3856 if(b) {
3857 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3858 CloseHandle(lpwhr->hCacheFile);
3860 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3861 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3862 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3863 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3864 WARN("Could not create file: %u\n", GetLastError());
3865 lpwhr->hCacheFile = NULL;
3867 }else {
3868 WARN("Could not create cache entry: %08x\n", GetLastError());
3872 lend:
3874 HeapFree(GetProcessHeap(), 0, requestString);
3876 /* TODO: send notification for P3P header */
3878 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3880 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3881 HTTP_ReceiveRequestData(lpwhr, TRUE);
3882 else
3884 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3885 iar.dwError = res;
3887 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3888 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3889 sizeof(INTERNET_ASYNC_RESULT));
3893 TRACE("<--\n");
3894 return res;
3897 /***********************************************************************
3899 * Helper functions for the HttpSendRequest(Ex) functions
3902 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3904 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3905 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3907 TRACE("%p\n", lpwhr);
3909 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3910 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3911 req->dwContentLength, req->bEndRequest);
3913 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3917 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3919 INT responseLen;
3920 DWORD dwBufferSize;
3921 INTERNET_ASYNC_RESULT iar;
3922 DWORD res = ERROR_SUCCESS;
3924 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3925 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3927 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3928 if (!responseLen)
3929 res = ERROR_HTTP_HEADER_NOT_FOUND;
3931 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3932 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3934 /* process cookies here. Is this right? */
3935 HTTP_ProcessCookies(lpwhr);
3937 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3939 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3941 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3942 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3943 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3945 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3946 dwBufferSize=sizeof(szNewLocation);
3947 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3949 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3951 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3952 lpwhr->lpszVerb = heap_strdupW(szGET);
3954 HTTP_DrainContent(lpwhr);
3955 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3957 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3958 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3959 res = HTTP_HandleRedirect(lpwhr, new_url);
3960 if (res == ERROR_SUCCESS)
3961 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3962 HeapFree( GetProcessHeap(), 0, new_url );
3968 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3969 iar.dwError = res;
3971 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3972 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3973 sizeof(INTERNET_ASYNC_RESULT));
3974 return res;
3977 /***********************************************************************
3978 * HttpEndRequestA (WININET.@)
3980 * Ends an HTTP request that was started by HttpSendRequestEx
3982 * RETURNS
3983 * TRUE if successful
3984 * FALSE on failure
3987 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3988 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3990 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3992 if (lpBuffersOut)
3994 SetLastError(ERROR_INVALID_PARAMETER);
3995 return FALSE;
3998 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4001 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4003 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4004 http_request_t *lpwhr = (http_request_t*)work->hdr;
4006 TRACE("%p\n", lpwhr);
4008 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4011 /***********************************************************************
4012 * HttpEndRequestW (WININET.@)
4014 * Ends an HTTP request that was started by HttpSendRequestEx
4016 * RETURNS
4017 * TRUE if successful
4018 * FALSE on failure
4021 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4022 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4024 http_request_t *lpwhr;
4025 DWORD res;
4027 TRACE("-->\n");
4029 if (lpBuffersOut)
4031 SetLastError(ERROR_INVALID_PARAMETER);
4032 return FALSE;
4035 lpwhr = (http_request_t*) get_handle_object( hRequest );
4037 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4039 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4040 if (lpwhr)
4041 WININET_Release( &lpwhr->hdr );
4042 return FALSE;
4044 lpwhr->hdr.dwFlags |= dwFlags;
4046 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4048 WORKREQUEST work;
4049 struct WORKREQ_HTTPENDREQUESTW *request;
4051 work.asyncproc = AsyncHttpEndRequestProc;
4052 work.hdr = WININET_AddRef( &lpwhr->hdr );
4054 request = &work.u.HttpEndRequestW;
4055 request->dwFlags = dwFlags;
4056 request->dwContext = dwContext;
4058 INTERNET_AsyncCall(&work);
4059 res = ERROR_IO_PENDING;
4061 else
4062 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4064 WININET_Release( &lpwhr->hdr );
4065 TRACE("%u <--\n", res);
4066 if(res != ERROR_SUCCESS)
4067 SetLastError(res);
4068 return res == ERROR_SUCCESS;
4071 /***********************************************************************
4072 * HttpSendRequestExA (WININET.@)
4074 * Sends the specified request to the HTTP server and allows chunked
4075 * transfers.
4077 * RETURNS
4078 * Success: TRUE
4079 * Failure: FALSE, call GetLastError() for more information.
4081 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4082 LPINTERNET_BUFFERSA lpBuffersIn,
4083 LPINTERNET_BUFFERSA lpBuffersOut,
4084 DWORD dwFlags, DWORD_PTR dwContext)
4086 INTERNET_BUFFERSW BuffersInW;
4087 BOOL rc = FALSE;
4088 DWORD headerlen;
4089 LPWSTR header = NULL;
4091 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4092 lpBuffersOut, dwFlags, dwContext);
4094 if (lpBuffersIn)
4096 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4097 if (lpBuffersIn->lpcszHeader)
4099 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4100 lpBuffersIn->dwHeadersLength,0,0);
4101 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4102 if (!(BuffersInW.lpcszHeader = header))
4104 SetLastError(ERROR_OUTOFMEMORY);
4105 return FALSE;
4107 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4108 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4109 header, headerlen);
4111 else
4112 BuffersInW.lpcszHeader = NULL;
4113 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4114 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4115 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4116 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4117 BuffersInW.Next = NULL;
4120 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4122 HeapFree(GetProcessHeap(),0,header);
4124 return rc;
4127 /***********************************************************************
4128 * HttpSendRequestExW (WININET.@)
4130 * Sends the specified request to the HTTP server and allows chunked
4131 * transfers
4133 * RETURNS
4134 * Success: TRUE
4135 * Failure: FALSE, call GetLastError() for more information.
4137 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4138 LPINTERNET_BUFFERSW lpBuffersIn,
4139 LPINTERNET_BUFFERSW lpBuffersOut,
4140 DWORD dwFlags, DWORD_PTR dwContext)
4142 http_request_t *lpwhr;
4143 http_session_t *lpwhs;
4144 appinfo_t *hIC;
4145 DWORD res;
4147 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4148 lpBuffersOut, dwFlags, dwContext);
4150 lpwhr = (http_request_t*) get_handle_object( hRequest );
4152 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4154 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4155 goto lend;
4158 lpwhs = lpwhr->lpHttpSession;
4159 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4160 hIC = lpwhs->lpAppInfo;
4161 assert(hIC->hdr.htype == WH_HINIT);
4163 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4165 WORKREQUEST workRequest;
4166 struct WORKREQ_HTTPSENDREQUESTW *req;
4168 workRequest.asyncproc = AsyncHttpSendRequestProc;
4169 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4170 req = &workRequest.u.HttpSendRequestW;
4171 if (lpBuffersIn)
4173 DWORD size = 0;
4175 if (lpBuffersIn->lpcszHeader)
4177 if (lpBuffersIn->dwHeadersLength == ~0u)
4178 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4179 else
4180 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4182 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4183 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4185 else req->lpszHeader = NULL;
4187 req->dwHeaderLength = size / sizeof(WCHAR);
4188 req->lpOptional = lpBuffersIn->lpvBuffer;
4189 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4190 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4192 else
4194 req->lpszHeader = NULL;
4195 req->dwHeaderLength = 0;
4196 req->lpOptional = NULL;
4197 req->dwOptionalLength = 0;
4198 req->dwContentLength = 0;
4201 req->bEndRequest = FALSE;
4203 INTERNET_AsyncCall(&workRequest);
4205 * This is from windows.
4207 res = ERROR_IO_PENDING;
4209 else
4211 if (lpBuffersIn)
4212 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4213 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4214 lpBuffersIn->dwBufferTotal, FALSE);
4215 else
4216 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4219 lend:
4220 if ( lpwhr )
4221 WININET_Release( &lpwhr->hdr );
4223 TRACE("<---\n");
4224 SetLastError(res);
4225 return res == ERROR_SUCCESS;
4228 /***********************************************************************
4229 * HttpSendRequestW (WININET.@)
4231 * Sends the specified request to the HTTP server
4233 * RETURNS
4234 * TRUE on success
4235 * FALSE on failure
4238 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4239 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4241 http_request_t *lpwhr;
4242 http_session_t *lpwhs = NULL;
4243 appinfo_t *hIC = NULL;
4244 DWORD res = ERROR_SUCCESS;
4246 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4247 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4249 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
4250 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4252 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4253 goto lend;
4256 lpwhs = lpwhr->lpHttpSession;
4257 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4259 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4260 goto lend;
4263 hIC = lpwhs->lpAppInfo;
4264 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4266 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4267 goto lend;
4270 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4272 WORKREQUEST workRequest;
4273 struct WORKREQ_HTTPSENDREQUESTW *req;
4275 workRequest.asyncproc = AsyncHttpSendRequestProc;
4276 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4277 req = &workRequest.u.HttpSendRequestW;
4278 if (lpszHeaders)
4280 DWORD size;
4282 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4283 else size = dwHeaderLength * sizeof(WCHAR);
4285 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4286 memcpy(req->lpszHeader, lpszHeaders, size);
4288 else
4289 req->lpszHeader = 0;
4290 req->dwHeaderLength = dwHeaderLength;
4291 req->lpOptional = lpOptional;
4292 req->dwOptionalLength = dwOptionalLength;
4293 req->dwContentLength = dwOptionalLength;
4294 req->bEndRequest = TRUE;
4296 INTERNET_AsyncCall(&workRequest);
4298 * This is from windows.
4300 res = ERROR_IO_PENDING;
4302 else
4304 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4305 dwHeaderLength, lpOptional, dwOptionalLength,
4306 dwOptionalLength, TRUE);
4308 lend:
4309 if( lpwhr )
4310 WININET_Release( &lpwhr->hdr );
4312 SetLastError(res);
4313 return res == ERROR_SUCCESS;
4316 /***********************************************************************
4317 * HttpSendRequestA (WININET.@)
4319 * Sends the specified request to the HTTP server
4321 * RETURNS
4322 * TRUE on success
4323 * FALSE on failure
4326 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4327 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4329 BOOL result;
4330 LPWSTR szHeaders=NULL;
4331 DWORD nLen=dwHeaderLength;
4332 if(lpszHeaders!=NULL)
4334 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4335 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4336 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4338 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4339 HeapFree(GetProcessHeap(),0,szHeaders);
4340 return result;
4343 /***********************************************************************
4344 * HTTPSESSION_Destroy (internal)
4346 * Deallocate session handle
4349 static void HTTPSESSION_Destroy(object_header_t *hdr)
4351 http_session_t *lpwhs = (http_session_t*) hdr;
4353 TRACE("%p\n", lpwhs);
4355 WININET_Release(&lpwhs->lpAppInfo->hdr);
4357 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4358 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4359 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4360 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4361 HeapFree(GetProcessHeap(), 0, lpwhs);
4364 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4366 switch(option) {
4367 case INTERNET_OPTION_HANDLE_TYPE:
4368 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4370 if (*size < sizeof(ULONG))
4371 return ERROR_INSUFFICIENT_BUFFER;
4373 *size = sizeof(DWORD);
4374 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4375 return ERROR_SUCCESS;
4378 return INET_QueryOption(hdr, option, buffer, size, unicode);
4381 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4383 http_session_t *ses = (http_session_t*)hdr;
4385 switch(option) {
4386 case INTERNET_OPTION_USERNAME:
4388 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4389 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4390 return ERROR_SUCCESS;
4392 case INTERNET_OPTION_PASSWORD:
4394 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4395 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4396 return ERROR_SUCCESS;
4398 default: break;
4401 return ERROR_INTERNET_INVALID_OPTION;
4404 static const object_vtbl_t HTTPSESSIONVtbl = {
4405 HTTPSESSION_Destroy,
4406 NULL,
4407 HTTPSESSION_QueryOption,
4408 HTTPSESSION_SetOption,
4409 NULL,
4410 NULL,
4411 NULL,
4412 NULL,
4413 NULL
4417 /***********************************************************************
4418 * HTTP_Connect (internal)
4420 * Create http session handle
4422 * RETURNS
4423 * HINTERNET a session handle on success
4424 * NULL on failure
4427 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4428 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4429 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4430 DWORD dwInternalFlags, HINTERNET *ret)
4432 http_session_t *lpwhs = NULL;
4434 TRACE("-->\n");
4436 if (!lpszServerName || !lpszServerName[0])
4437 return ERROR_INVALID_PARAMETER;
4439 assert( hIC->hdr.htype == WH_HINIT );
4441 lpwhs = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
4442 if (!lpwhs)
4443 return ERROR_OUTOFMEMORY;
4446 * According to my tests. The name is not resolved until a request is sent
4449 lpwhs->hdr.htype = WH_HHTTPSESSION;
4450 lpwhs->hdr.dwFlags = dwFlags;
4451 lpwhs->hdr.dwContext = dwContext;
4452 lpwhs->hdr.dwInternalFlags |= dwInternalFlags;
4454 WININET_AddRef( &hIC->hdr );
4455 lpwhs->lpAppInfo = hIC;
4456 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4458 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4459 if(hIC->lpszProxyBypass)
4460 FIXME("Proxy bypass is ignored.\n");
4462 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4463 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4464 if (lpszUserName && lpszUserName[0])
4465 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4466 if (lpszPassword && lpszPassword[0])
4467 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4468 lpwhs->nServerPort = nServerPort;
4469 lpwhs->nHostPort = nServerPort;
4471 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4472 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4474 INTERNET_SendCallback(&hIC->hdr, dwContext,
4475 INTERNET_STATUS_HANDLE_CREATED, &lpwhs->hdr.hInternet,
4476 sizeof(HINTERNET));
4480 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4481 * windows
4484 TRACE("%p --> %p\n", hIC, lpwhs);
4486 *ret = lpwhs->hdr.hInternet;
4487 return ERROR_SUCCESS;
4491 /***********************************************************************
4492 * HTTP_OpenConnection (internal)
4494 * Connect to a web server
4496 * RETURNS
4498 * TRUE on success
4499 * FALSE on failure
4501 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4503 http_session_t *lpwhs;
4504 appinfo_t *hIC = NULL;
4505 char szaddr[INET6_ADDRSTRLEN];
4506 const void *addr;
4507 DWORD res = ERROR_SUCCESS;
4509 TRACE("-->\n");
4512 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4514 res = ERROR_INVALID_PARAMETER;
4515 goto lend;
4518 if (NETCON_connected(&lpwhr->netConnection))
4519 goto lend;
4520 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4522 lpwhs = lpwhr->lpHttpSession;
4524 hIC = lpwhs->lpAppInfo;
4525 switch (lpwhs->socketAddress.ss_family)
4527 case AF_INET:
4528 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4529 break;
4530 case AF_INET6:
4531 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4532 break;
4533 default:
4534 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4535 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4537 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4538 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4539 INTERNET_STATUS_CONNECTING_TO_SERVER,
4540 szaddr,
4541 strlen(szaddr)+1);
4543 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4544 if (res != ERROR_SUCCESS)
4546 WARN("Socket creation failed: %u\n", res);
4547 goto lend;
4550 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4551 lpwhs->sa_len);
4552 if(res != ERROR_SUCCESS)
4553 goto lend;
4555 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4556 INTERNET_STATUS_CONNECTED_TO_SERVER,
4557 szaddr, strlen(szaddr)+1);
4559 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4561 /* Note: we differ from Microsoft's WinINet here. they seem to have
4562 * a bug that causes no status callbacks to be sent when starting
4563 * a tunnel to a proxy server using the CONNECT verb. i believe our
4564 * behaviour to be more correct and to not cause any incompatibilities
4565 * because using a secure connection through a proxy server is a rare
4566 * case that would be hard for anyone to depend on */
4567 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4568 HTTPREQ_CloseConnection(&lpwhr->hdr);
4569 goto lend;
4572 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4573 if(res != ERROR_SUCCESS)
4575 WARN("Couldn't connect securely to host\n");
4577 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4578 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4579 || res == ERROR_INTERNET_INVALID_CA
4580 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4581 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4582 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4583 || res == ERROR_INTERNET_SEC_INVALID_CERT
4584 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4585 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4587 HTTPREQ_CloseConnection(&lpwhr->hdr);
4588 goto lend;
4593 lend:
4594 lpwhr->read_pos = lpwhr->read_size = 0;
4595 lpwhr->read_chunked = FALSE;
4597 TRACE("%d <--\n", res);
4598 return res;
4602 /***********************************************************************
4603 * HTTP_clear_response_headers (internal)
4605 * clear out any old response headers
4607 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4609 DWORD i;
4611 for( i=0; i<lpwhr->nCustHeaders; i++)
4613 if( !lpwhr->pCustHeaders[i].lpszField )
4614 continue;
4615 if( !lpwhr->pCustHeaders[i].lpszValue )
4616 continue;
4617 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4618 continue;
4619 HTTP_DeleteCustomHeader( lpwhr, i );
4620 i--;
4624 /***********************************************************************
4625 * HTTP_GetResponseHeaders (internal)
4627 * Read server response
4629 * RETURNS
4631 * TRUE on success
4632 * FALSE on error
4634 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4636 INT cbreaks = 0;
4637 WCHAR buffer[MAX_REPLY_LEN];
4638 DWORD buflen = MAX_REPLY_LEN;
4639 BOOL bSuccess = FALSE;
4640 INT rc = 0;
4641 char bufferA[MAX_REPLY_LEN];
4642 LPWSTR status_code = NULL, status_text = NULL;
4643 DWORD cchMaxRawHeaders = 1024;
4644 LPWSTR lpszRawHeaders = NULL;
4645 LPWSTR temp;
4646 DWORD cchRawHeaders = 0;
4647 BOOL codeHundred = FALSE;
4649 TRACE("-->\n");
4651 if (!NETCON_connected(&lpwhr->netConnection))
4652 goto lend;
4654 do {
4655 static const WCHAR szHundred[] = {'1','0','0',0};
4657 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4659 buflen = MAX_REPLY_LEN;
4660 if (!read_line(lpwhr, bufferA, &buflen))
4661 goto lend;
4663 /* clear old response headers (eg. from a redirect response) */
4664 if (clear) {
4665 HTTP_clear_response_headers( lpwhr );
4666 clear = FALSE;
4669 rc += buflen;
4670 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4671 /* check is this a status code line? */
4672 if (!strncmpW(buffer, g_szHttp1_0, 4))
4674 /* split the version from the status code */
4675 status_code = strchrW( buffer, ' ' );
4676 if( !status_code )
4677 goto lend;
4678 *status_code++=0;
4680 /* split the status code from the status text */
4681 status_text = strchrW( status_code, ' ' );
4682 if( !status_text )
4683 goto lend;
4684 *status_text++=0;
4686 TRACE("version [%s] status code [%s] status text [%s]\n",
4687 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4689 codeHundred = (!strcmpW(status_code, szHundred));
4691 else if (!codeHundred)
4693 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4695 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4696 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4698 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4699 lpwhr->lpszStatusText = heap_strdupW(szOK);
4701 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4702 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4704 bSuccess = TRUE;
4705 goto lend;
4707 } while (codeHundred);
4709 /* Add status code */
4710 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4711 HTTP_ADDHDR_FLAG_REPLACE);
4713 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4714 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4716 lpwhr->lpszVersion = heap_strdupW(buffer);
4717 lpwhr->lpszStatusText = heap_strdupW(status_text);
4719 /* Restore the spaces */
4720 *(status_code-1) = ' ';
4721 *(status_text-1) = ' ';
4723 /* regenerate raw headers */
4724 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4725 if (!lpszRawHeaders) goto lend;
4727 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4728 cchMaxRawHeaders *= 2;
4729 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4730 if (temp == NULL) goto lend;
4731 lpszRawHeaders = temp;
4732 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4733 cchRawHeaders += (buflen-1);
4734 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4735 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4736 lpszRawHeaders[cchRawHeaders] = '\0';
4738 /* Parse each response line */
4741 buflen = MAX_REPLY_LEN;
4742 if (read_line(lpwhr, bufferA, &buflen))
4744 LPWSTR * pFieldAndValue;
4746 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4748 if (!bufferA[0]) break;
4749 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4751 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4752 if (pFieldAndValue)
4754 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4755 cchMaxRawHeaders *= 2;
4756 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4757 if (temp == NULL) goto lend;
4758 lpszRawHeaders = temp;
4759 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4760 cchRawHeaders += (buflen-1);
4761 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4762 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4763 lpszRawHeaders[cchRawHeaders] = '\0';
4765 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4766 HTTP_ADDREQ_FLAG_ADD );
4768 HTTP_FreeTokens(pFieldAndValue);
4771 else
4773 cbreaks++;
4774 if (cbreaks >= 2)
4775 break;
4777 }while(1);
4779 /* make sure the response header is terminated with an empty line. Some apps really
4780 truly care about that empty line being there for some reason. Just add it to the
4781 header. */
4782 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4784 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4785 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4786 if (temp == NULL) goto lend;
4787 lpszRawHeaders = temp;
4790 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4792 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4793 lpwhr->lpszRawHeaders = lpszRawHeaders;
4794 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4795 bSuccess = TRUE;
4797 lend:
4799 TRACE("<--\n");
4800 if (bSuccess)
4801 return rc;
4802 else
4804 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4805 return 0;
4809 /***********************************************************************
4810 * HTTP_InterpretHttpHeader (internal)
4812 * Parse server response
4814 * RETURNS
4816 * Pointer to array of field, value, NULL on success.
4817 * NULL on error.
4819 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4821 LPWSTR * pTokenPair;
4822 LPWSTR pszColon;
4823 INT len;
4825 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4827 pszColon = strchrW(buffer, ':');
4828 /* must have two tokens */
4829 if (!pszColon)
4831 HTTP_FreeTokens(pTokenPair);
4832 if (buffer[0])
4833 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4834 return NULL;
4837 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4838 if (!pTokenPair[0])
4840 HTTP_FreeTokens(pTokenPair);
4841 return NULL;
4843 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4844 pTokenPair[0][pszColon - buffer] = '\0';
4846 /* skip colon */
4847 pszColon++;
4848 len = strlenW(pszColon);
4849 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4850 if (!pTokenPair[1])
4852 HTTP_FreeTokens(pTokenPair);
4853 return NULL;
4855 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4857 strip_spaces(pTokenPair[0]);
4858 strip_spaces(pTokenPair[1]);
4860 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4861 return pTokenPair;
4864 /***********************************************************************
4865 * HTTP_ProcessHeader (internal)
4867 * Stuff header into header tables according to <dwModifier>
4871 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4873 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4875 LPHTTPHEADERW lphttpHdr = NULL;
4876 INT index = -1;
4877 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4878 DWORD res = ERROR_HTTP_INVALID_HEADER;
4880 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4882 /* REPLACE wins out over ADD */
4883 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4884 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4886 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4887 index = -1;
4888 else
4889 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4891 if (index >= 0)
4893 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4894 return ERROR_HTTP_INVALID_HEADER;
4895 lphttpHdr = &lpwhr->pCustHeaders[index];
4897 else if (value)
4899 HTTPHEADERW hdr;
4901 hdr.lpszField = (LPWSTR)field;
4902 hdr.lpszValue = (LPWSTR)value;
4903 hdr.wFlags = hdr.wCount = 0;
4905 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4906 hdr.wFlags |= HDR_ISREQUEST;
4908 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4910 /* no value to delete */
4911 else return ERROR_SUCCESS;
4913 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4914 lphttpHdr->wFlags |= HDR_ISREQUEST;
4915 else
4916 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4918 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4920 HTTP_DeleteCustomHeader( lpwhr, index );
4922 if (value)
4924 HTTPHEADERW hdr;
4926 hdr.lpszField = (LPWSTR)field;
4927 hdr.lpszValue = (LPWSTR)value;
4928 hdr.wFlags = hdr.wCount = 0;
4930 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4931 hdr.wFlags |= HDR_ISREQUEST;
4933 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4936 return ERROR_SUCCESS;
4938 else if (dwModifier & COALESCEFLAGS)
4940 LPWSTR lpsztmp;
4941 WCHAR ch = 0;
4942 INT len = 0;
4943 INT origlen = strlenW(lphttpHdr->lpszValue);
4944 INT valuelen = strlenW(value);
4946 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4948 ch = ',';
4949 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4951 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4953 ch = ';';
4954 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4957 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4959 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4960 if (lpsztmp)
4962 lphttpHdr->lpszValue = lpsztmp;
4963 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4964 if (ch > 0)
4966 lphttpHdr->lpszValue[origlen] = ch;
4967 origlen++;
4968 lphttpHdr->lpszValue[origlen] = ' ';
4969 origlen++;
4972 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4973 lphttpHdr->lpszValue[len] = '\0';
4974 res = ERROR_SUCCESS;
4976 else
4978 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4979 res = ERROR_OUTOFMEMORY;
4982 TRACE("<-- %d\n", res);
4983 return res;
4987 /***********************************************************************
4988 * HTTP_FinishedReading (internal)
4990 * Called when all content from server has been read by client.
4993 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4995 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4997 TRACE("\n");
5000 if (!keepalive)
5002 HTTPREQ_CloseConnection(&lpwhr->hdr);
5005 /* FIXME: store data in the URL cache here */
5007 return TRUE;
5011 /***********************************************************************
5012 * HTTP_GetCustomHeaderIndex (internal)
5014 * Return index of custom header from header array
5017 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5018 int requested_index, BOOL request_only)
5020 DWORD index;
5022 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5024 for (index = 0; index < lpwhr->nCustHeaders; index++)
5026 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5027 continue;
5029 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5030 continue;
5032 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5033 continue;
5035 if (requested_index == 0)
5036 break;
5037 requested_index --;
5040 if (index >= lpwhr->nCustHeaders)
5041 index = -1;
5043 TRACE("Return: %d\n", index);
5044 return index;
5048 /***********************************************************************
5049 * HTTP_InsertCustomHeader (internal)
5051 * Insert header into array
5054 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5056 INT count;
5057 LPHTTPHEADERW lph = NULL;
5059 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5060 count = lpwhr->nCustHeaders + 1;
5061 if (count > 1)
5062 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5063 else
5064 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5066 if (!lph)
5067 return ERROR_OUTOFMEMORY;
5069 lpwhr->pCustHeaders = lph;
5070 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5071 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5072 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5073 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5074 lpwhr->nCustHeaders++;
5076 return ERROR_SUCCESS;
5080 /***********************************************************************
5081 * HTTP_DeleteCustomHeader (internal)
5083 * Delete header from array
5084 * If this function is called, the indexs may change.
5086 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5088 if( lpwhr->nCustHeaders <= 0 )
5089 return FALSE;
5090 if( index >= lpwhr->nCustHeaders )
5091 return FALSE;
5092 lpwhr->nCustHeaders--;
5094 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5095 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5097 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5098 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5099 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5101 return TRUE;
5105 /***********************************************************************
5106 * HTTP_VerifyValidHeader (internal)
5108 * Verify the given header is not invalid for the given http request
5111 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5113 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5114 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5115 return ERROR_HTTP_INVALID_HEADER;
5117 return ERROR_SUCCESS;
5120 /***********************************************************************
5121 * IsHostInProxyBypassList (@)
5123 * Undocumented
5126 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5128 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5129 return FALSE;
5132 /***********************************************************************
5133 * InternetShowSecurityInfoByURLA (@)
5135 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5137 FIXME("stub: %s %p\n", url, window);
5138 return FALSE;
5141 /***********************************************************************
5142 * InternetShowSecurityInfoByURLW (@)
5144 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5146 FIXME("stub: %s %p\n", debugstr_w(url), window);
5147 return FALSE;