localui: Added Hebrew translation.
[wine.git] / dlls / wininet / http.c
blob76cf41c30c83e70e2538363a861901d2cd431c9d
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(), 0, sizeof(gzip_stream_t));
261 gzip_stream->zstream.zalloc = wininet_zalloc;
262 gzip_stream->zstream.zfree = wininet_zfree;
263 gzip_stream->zstream.opaque = NULL;
264 gzip_stream->zstream.next_in = NULL;
265 gzip_stream->zstream.avail_in = 0;
266 gzip_stream->zstream.next_out = NULL;
267 gzip_stream->zstream.avail_out = 0;
268 gzip_stream->buf_pos = 0;
269 gzip_stream->buf_size = 0;
270 gzip_stream->end_of_data = FALSE;
272 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
273 if(zres != Z_OK) {
274 ERR("inflateInit failed: %d\n", zres);
275 HeapFree(GetProcessHeap(), 0, gzip_stream);
276 return;
279 req->gzip_stream = gzip_stream;
281 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
282 if(index != -1)
283 HTTP_DeleteCustomHeader(req, index);
286 #else
288 static void init_gzip_stream(http_request_t *req)
290 ERR("gzip stream not supported, missing zlib.\n");
293 #endif
295 /* set the request content length based on the headers */
296 static DWORD set_content_length( http_request_t *lpwhr )
298 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
299 WCHAR encoding[20];
300 DWORD size;
302 size = sizeof(lpwhr->dwContentLength);
303 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
304 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
305 lpwhr->dwContentLength = ~0u;
307 size = sizeof(encoding);
308 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
309 !strcmpiW(encoding, szChunked))
311 lpwhr->dwContentLength = ~0u;
312 lpwhr->read_chunked = TRUE;
315 if(lpwhr->decoding) {
316 int encoding_idx;
318 static const WCHAR gzipW[] = {'g','z','i','p',0};
320 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
321 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
322 init_gzip_stream(lpwhr);
325 return lpwhr->dwContentLength;
328 /***********************************************************************
329 * HTTP_Tokenize (internal)
331 * Tokenize a string, allocating memory for the tokens.
333 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
335 LPWSTR * token_array;
336 int tokens = 0;
337 int i;
338 LPCWSTR next_token;
340 if (string)
342 /* empty string has no tokens */
343 if (*string)
344 tokens++;
345 /* count tokens */
346 for (i = 0; string[i]; i++)
348 if (!strncmpW(string+i, token_string, strlenW(token_string)))
350 DWORD j;
351 tokens++;
352 /* we want to skip over separators, but not the null terminator */
353 for (j = 0; j < strlenW(token_string) - 1; j++)
354 if (!string[i+j])
355 break;
356 i += j;
361 /* add 1 for terminating NULL */
362 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
363 token_array[tokens] = NULL;
364 if (!tokens)
365 return token_array;
366 for (i = 0; i < tokens; i++)
368 int len;
369 next_token = strstrW(string, token_string);
370 if (!next_token) next_token = string+strlenW(string);
371 len = next_token - string;
372 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
373 memcpy(token_array[i], string, len*sizeof(WCHAR));
374 token_array[i][len] = '\0';
375 string = next_token+strlenW(token_string);
377 return token_array;
380 /***********************************************************************
381 * HTTP_FreeTokens (internal)
383 * Frees memory returned from HTTP_Tokenize.
385 static void HTTP_FreeTokens(LPWSTR * token_array)
387 int i;
388 for (i = 0; token_array[i]; i++)
389 HeapFree(GetProcessHeap(), 0, token_array[i]);
390 HeapFree(GetProcessHeap(), 0, token_array);
393 static void HTTP_FixURL(http_request_t *lpwhr)
395 static const WCHAR szSlash[] = { '/',0 };
396 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
398 /* If we don't have a path we set it to root */
399 if (NULL == lpwhr->lpszPath)
400 lpwhr->lpszPath = heap_strdupW(szSlash);
401 else /* remove \r and \n*/
403 int nLen = strlenW(lpwhr->lpszPath);
404 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
406 nLen--;
407 lpwhr->lpszPath[nLen]='\0';
409 /* Replace '\' with '/' */
410 while (nLen>0) {
411 nLen--;
412 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
416 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
417 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
418 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
420 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
421 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
422 *fixurl = '/';
423 strcpyW(fixurl + 1, lpwhr->lpszPath);
424 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
425 lpwhr->lpszPath = fixurl;
429 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
431 LPWSTR requestString;
432 DWORD len, n;
433 LPCWSTR *req;
434 UINT i;
435 LPWSTR p;
437 static const WCHAR szSpace[] = { ' ',0 };
438 static const WCHAR szColon[] = { ':',' ',0 };
439 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
441 /* allocate space for an array of all the string pointers to be added */
442 len = (lpwhr->nCustHeaders)*4 + 10;
443 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
445 /* add the verb, path and HTTP version string */
446 n = 0;
447 req[n++] = verb;
448 req[n++] = szSpace;
449 req[n++] = path;
450 req[n++] = szSpace;
451 req[n++] = version;
453 /* Append custom request headers */
454 for (i = 0; i < lpwhr->nCustHeaders; i++)
456 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
458 req[n++] = szCrLf;
459 req[n++] = lpwhr->pCustHeaders[i].lpszField;
460 req[n++] = szColon;
461 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
463 TRACE("Adding custom header %s (%s)\n",
464 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
465 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
469 if( n >= len )
470 ERR("oops. buffer overrun\n");
472 req[n] = NULL;
473 requestString = HTTP_build_req( req, 4 );
474 HeapFree( GetProcessHeap(), 0, req );
477 * Set (header) termination string for request
478 * Make sure there's exactly two new lines at the end of the request
480 p = &requestString[strlenW(requestString)-1];
481 while ( (*p == '\n') || (*p == '\r') )
482 p--;
483 strcpyW( p+1, sztwocrlf );
485 return requestString;
488 static void HTTP_ProcessCookies( http_request_t *lpwhr )
490 int HeaderIndex;
491 int numCookies = 0;
492 LPHTTPHEADERW setCookieHeader;
494 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
496 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
498 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
500 int len;
501 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
502 LPWSTR buf_url;
503 LPHTTPHEADERW Host;
505 Host = HTTP_GetHeader(lpwhr, hostW);
506 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
507 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
508 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
509 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
511 HeapFree(GetProcessHeap(), 0, buf_url);
513 numCookies++;
517 static void strip_spaces(LPWSTR start)
519 LPWSTR str = start;
520 LPWSTR end;
522 while (*str == ' ' && *str != '\0')
523 str++;
525 if (str != start)
526 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
528 end = start + strlenW(start) - 1;
529 while (end >= start && *end == ' ')
531 *end = '\0';
532 end--;
536 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
538 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
539 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
540 BOOL is_basic;
541 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
542 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
543 if (is_basic && pszRealm)
545 LPCWSTR token;
546 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
547 LPCWSTR realm;
548 ptr++;
549 *pszRealm=NULL;
550 token = strchrW(ptr,'=');
551 if (!token)
552 return TRUE;
553 realm = ptr;
554 while (*realm == ' ' && *realm != '\0')
555 realm++;
556 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
557 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
559 token++;
560 while (*token == ' ' && *token != '\0')
561 token++;
562 if (*token == '\0')
563 return TRUE;
564 *pszRealm = heap_strdupW(token);
565 strip_spaces(*pszRealm);
569 return is_basic;
572 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
574 if (!authinfo) return;
576 if (SecIsValidHandle(&authinfo->ctx))
577 DeleteSecurityContext(&authinfo->ctx);
578 if (SecIsValidHandle(&authinfo->cred))
579 FreeCredentialsHandle(&authinfo->cred);
581 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
582 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
583 HeapFree(GetProcessHeap(), 0, authinfo);
586 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
588 basicAuthorizationData *ad;
589 UINT rc = 0;
591 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
593 EnterCriticalSection(&authcache_cs);
594 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
596 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
598 TRACE("Authorization found in cache\n");
599 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
600 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
601 rc = ad->AuthorizationLen;
602 break;
605 LeaveCriticalSection(&authcache_cs);
606 return rc;
609 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
611 struct list *cursor;
612 basicAuthorizationData* ad = NULL;
614 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
616 EnterCriticalSection(&authcache_cs);
617 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
619 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
620 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
622 ad = check;
623 break;
627 if (ad)
629 TRACE("Found match in cache, replacing\n");
630 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
631 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
632 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
633 ad->AuthorizationLen = auth_data_len;
635 else
637 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
638 ad->lpszwHost = heap_strdupW(host);
639 ad->lpszwRealm = heap_strdupW(realm);
640 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
641 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
642 ad->AuthorizationLen = auth_data_len;
643 list_add_head(&basicAuthorizationCache,&ad->entry);
644 TRACE("authorization cached\n");
646 LeaveCriticalSection(&authcache_cs);
649 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
650 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
652 authorizationData *ad;
654 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
656 EnterCriticalSection(&authcache_cs);
657 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
658 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
659 TRACE("Authorization found in cache\n");
661 nt_auth_identity->User = heap_strdupW(ad->user);
662 nt_auth_identity->Password = heap_strdupW(ad->password);
663 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
664 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
665 (!nt_auth_identity->Domain && ad->domain_len)) {
666 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
667 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
668 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
669 break;
672 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
673 nt_auth_identity->UserLength = ad->user_len;
674 nt_auth_identity->PasswordLength = ad->password_len;
675 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
676 nt_auth_identity->DomainLength = ad->domain_len;
677 LeaveCriticalSection(&authcache_cs);
678 return TRUE;
681 LeaveCriticalSection(&authcache_cs);
683 return FALSE;
686 static void cache_authorization(LPWSTR host, LPWSTR scheme,
687 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
689 authorizationData *ad;
690 BOOL found = FALSE;
692 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
694 EnterCriticalSection(&authcache_cs);
695 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
696 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
697 found = TRUE;
698 break;
701 if(found) {
702 HeapFree(GetProcessHeap(), 0, ad->user);
703 HeapFree(GetProcessHeap(), 0, ad->password);
704 HeapFree(GetProcessHeap(), 0, ad->domain);
705 } else {
706 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
707 if(!ad) {
708 LeaveCriticalSection(&authcache_cs);
709 return;
712 ad->host = heap_strdupW(host);
713 ad->scheme = heap_strdupW(scheme);
714 list_add_head(&authorizationCache, &ad->entry);
717 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
718 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
719 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
720 ad->user_len = nt_auth_identity->UserLength;
721 ad->password_len = nt_auth_identity->PasswordLength;
722 ad->domain_len = nt_auth_identity->DomainLength;
724 if(!ad->host || !ad->scheme || !ad->user || !ad->password
725 || (nt_auth_identity->Domain && !ad->domain)) {
726 HeapFree(GetProcessHeap(), 0, ad->host);
727 HeapFree(GetProcessHeap(), 0, ad->scheme);
728 HeapFree(GetProcessHeap(), 0, ad->user);
729 HeapFree(GetProcessHeap(), 0, ad->password);
730 HeapFree(GetProcessHeap(), 0, ad->domain);
731 list_remove(&ad->entry);
732 HeapFree(GetProcessHeap(), 0, ad);
735 LeaveCriticalSection(&authcache_cs);
738 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
739 struct HttpAuthInfo **ppAuthInfo,
740 LPWSTR domain_and_username, LPWSTR password,
741 LPWSTR host )
743 SECURITY_STATUS sec_status;
744 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
745 BOOL first = FALSE;
746 LPWSTR szRealm = NULL;
748 TRACE("%s\n", debugstr_w(pszAuthValue));
750 if (!pAuthInfo)
752 TimeStamp exp;
754 first = TRUE;
755 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
756 if (!pAuthInfo)
757 return FALSE;
759 SecInvalidateHandle(&pAuthInfo->cred);
760 SecInvalidateHandle(&pAuthInfo->ctx);
761 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
762 pAuthInfo->attr = 0;
763 pAuthInfo->auth_data = NULL;
764 pAuthInfo->auth_data_len = 0;
765 pAuthInfo->finished = FALSE;
767 if (is_basic_auth_value(pszAuthValue,NULL))
769 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
770 pAuthInfo->scheme = heap_strdupW(szBasic);
771 if (!pAuthInfo->scheme)
773 HeapFree(GetProcessHeap(), 0, pAuthInfo);
774 return FALSE;
777 else
779 PVOID pAuthData;
780 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
782 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
783 if (!pAuthInfo->scheme)
785 HeapFree(GetProcessHeap(), 0, pAuthInfo);
786 return FALSE;
789 if (domain_and_username)
791 WCHAR *user = strchrW(domain_and_username, '\\');
792 WCHAR *domain = domain_and_username;
794 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
796 pAuthData = &nt_auth_identity;
798 if (user) user++;
799 else
801 user = domain_and_username;
802 domain = NULL;
805 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
806 nt_auth_identity.User = user;
807 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
808 nt_auth_identity.Domain = domain;
809 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
810 nt_auth_identity.Password = password;
811 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
813 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
815 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
816 pAuthData = &nt_auth_identity;
817 else
818 /* use default credentials */
819 pAuthData = NULL;
821 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
822 SECPKG_CRED_OUTBOUND, NULL,
823 pAuthData, NULL,
824 NULL, &pAuthInfo->cred,
825 &exp);
827 if(pAuthData && !domain_and_username) {
828 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
829 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
830 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
833 if (sec_status == SEC_E_OK)
835 PSecPkgInfoW sec_pkg_info;
836 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
837 if (sec_status == SEC_E_OK)
839 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
840 FreeContextBuffer(sec_pkg_info);
843 if (sec_status != SEC_E_OK)
845 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
846 debugstr_w(pAuthInfo->scheme), sec_status);
847 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
848 HeapFree(GetProcessHeap(), 0, pAuthInfo);
849 return FALSE;
852 *ppAuthInfo = pAuthInfo;
854 else if (pAuthInfo->finished)
855 return FALSE;
857 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
858 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
860 ERR("authentication scheme changed from %s to %s\n",
861 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
862 return FALSE;
865 if (is_basic_auth_value(pszAuthValue,&szRealm))
867 int userlen;
868 int passlen;
869 char *auth_data = NULL;
870 UINT auth_data_len = 0;
872 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
874 if (!domain_and_username)
876 if (host && szRealm)
877 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
878 if (auth_data_len == 0)
880 HeapFree(GetProcessHeap(),0,szRealm);
881 return FALSE;
884 else
886 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
887 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
889 /* length includes a nul terminator, which will be re-used for the ':' */
890 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
891 if (!auth_data)
893 HeapFree(GetProcessHeap(),0,szRealm);
894 return FALSE;
897 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
898 auth_data[userlen] = ':';
899 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
900 auth_data_len = userlen + 1 + passlen;
901 if (host && szRealm)
902 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
905 pAuthInfo->auth_data = auth_data;
906 pAuthInfo->auth_data_len = auth_data_len;
907 pAuthInfo->finished = TRUE;
908 HeapFree(GetProcessHeap(),0,szRealm);
910 return TRUE;
912 else
914 LPCWSTR pszAuthData;
915 SecBufferDesc out_desc, in_desc;
916 SecBuffer out, in;
917 unsigned char *buffer;
918 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
919 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
921 in.BufferType = SECBUFFER_TOKEN;
922 in.cbBuffer = 0;
923 in.pvBuffer = NULL;
925 in_desc.ulVersion = 0;
926 in_desc.cBuffers = 1;
927 in_desc.pBuffers = &in;
929 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
930 if (*pszAuthData == ' ')
932 pszAuthData++;
933 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
934 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
935 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
938 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
940 out.BufferType = SECBUFFER_TOKEN;
941 out.cbBuffer = pAuthInfo->max_token;
942 out.pvBuffer = buffer;
944 out_desc.ulVersion = 0;
945 out_desc.cBuffers = 1;
946 out_desc.pBuffers = &out;
948 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
949 first ? NULL : &pAuthInfo->ctx,
950 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
951 context_req, 0, SECURITY_NETWORK_DREP,
952 in.pvBuffer ? &in_desc : NULL,
953 0, &pAuthInfo->ctx, &out_desc,
954 &pAuthInfo->attr, &pAuthInfo->exp);
955 if (sec_status == SEC_E_OK)
957 pAuthInfo->finished = TRUE;
958 pAuthInfo->auth_data = out.pvBuffer;
959 pAuthInfo->auth_data_len = out.cbBuffer;
960 TRACE("sending last auth packet\n");
962 else if (sec_status == SEC_I_CONTINUE_NEEDED)
964 pAuthInfo->auth_data = out.pvBuffer;
965 pAuthInfo->auth_data_len = out.cbBuffer;
966 TRACE("sending next auth packet\n");
968 else
970 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
971 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
972 destroy_authinfo(pAuthInfo);
973 *ppAuthInfo = NULL;
974 return FALSE;
978 return TRUE;
981 /***********************************************************************
982 * HTTP_HttpAddRequestHeadersW (internal)
984 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
985 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
987 LPWSTR lpszStart;
988 LPWSTR lpszEnd;
989 LPWSTR buffer;
990 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
992 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
994 if( dwHeaderLength == ~0U )
995 len = strlenW(lpszHeader);
996 else
997 len = dwHeaderLength;
998 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
999 lstrcpynW( buffer, lpszHeader, len + 1);
1001 lpszStart = buffer;
1005 LPWSTR * pFieldAndValue;
1007 lpszEnd = lpszStart;
1009 while (*lpszEnd != '\0')
1011 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1012 break;
1013 lpszEnd++;
1016 if (*lpszStart == '\0')
1017 break;
1019 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1021 *lpszEnd = '\0';
1022 lpszEnd++; /* Jump over newline */
1024 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1025 if (*lpszStart == '\0')
1027 /* Skip 0-length headers */
1028 lpszStart = lpszEnd;
1029 res = ERROR_SUCCESS;
1030 continue;
1032 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1033 if (pFieldAndValue)
1035 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
1036 if (res == ERROR_SUCCESS)
1037 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
1038 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1039 HTTP_FreeTokens(pFieldAndValue);
1042 lpszStart = lpszEnd;
1043 } while (res == ERROR_SUCCESS);
1045 HeapFree(GetProcessHeap(), 0, buffer);
1047 return res;
1050 /***********************************************************************
1051 * HttpAddRequestHeadersW (WININET.@)
1053 * Adds one or more HTTP header to the request handler
1055 * NOTE
1056 * On Windows if dwHeaderLength includes the trailing '\0', then
1057 * HttpAddRequestHeadersW() adds it too. However this results in an
1058 * invalid Http header which is rejected by some servers so we probably
1059 * don't need to match Windows on that point.
1061 * RETURNS
1062 * TRUE on success
1063 * FALSE on failure
1066 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1067 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1069 http_request_t *lpwhr;
1070 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1072 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1074 if (!lpszHeader)
1075 return TRUE;
1077 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
1078 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
1079 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
1080 if( lpwhr )
1081 WININET_Release( &lpwhr->hdr );
1083 if(res != ERROR_SUCCESS)
1084 SetLastError(res);
1085 return res == ERROR_SUCCESS;
1088 /***********************************************************************
1089 * HttpAddRequestHeadersA (WININET.@)
1091 * Adds one or more HTTP header to the request handler
1093 * RETURNS
1094 * TRUE on success
1095 * FALSE on failure
1098 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1099 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1101 DWORD len;
1102 LPWSTR hdr;
1103 BOOL r;
1105 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1107 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1108 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1109 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1110 if( dwHeaderLength != ~0U )
1111 dwHeaderLength = len;
1113 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1115 HeapFree( GetProcessHeap(), 0, hdr );
1117 return r;
1120 /***********************************************************************
1121 * HttpOpenRequestA (WININET.@)
1123 * Open a HTTP request handle
1125 * RETURNS
1126 * HINTERNET a HTTP request handle on success
1127 * NULL on failure
1130 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1131 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1132 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1133 DWORD dwFlags, DWORD_PTR dwContext)
1135 LPWSTR szVerb = NULL, szObjectName = NULL;
1136 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1137 INT acceptTypesCount;
1138 HINTERNET rc = FALSE;
1139 LPCSTR *types;
1141 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1142 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1143 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1144 dwFlags, dwContext);
1146 if (lpszVerb)
1148 szVerb = heap_strdupAtoW(lpszVerb);
1149 if ( !szVerb )
1150 goto end;
1153 if (lpszObjectName)
1155 szObjectName = heap_strdupAtoW(lpszObjectName);
1156 if ( !szObjectName )
1157 goto end;
1160 if (lpszVersion)
1162 szVersion = heap_strdupAtoW(lpszVersion);
1163 if ( !szVersion )
1164 goto end;
1167 if (lpszReferrer)
1169 szReferrer = heap_strdupAtoW(lpszReferrer);
1170 if ( !szReferrer )
1171 goto end;
1174 if (lpszAcceptTypes)
1176 acceptTypesCount = 0;
1177 types = lpszAcceptTypes;
1178 while (*types)
1180 __TRY
1182 /* find out how many there are */
1183 if (*types && **types)
1185 TRACE("accept type: %s\n", debugstr_a(*types));
1186 acceptTypesCount++;
1189 __EXCEPT_PAGE_FAULT
1191 WARN("invalid accept type pointer\n");
1193 __ENDTRY;
1194 types++;
1196 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1197 if (!szAcceptTypes) goto end;
1199 acceptTypesCount = 0;
1200 types = lpszAcceptTypes;
1201 while (*types)
1203 __TRY
1205 if (*types && **types)
1206 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1208 __EXCEPT_PAGE_FAULT
1210 /* ignore invalid pointer */
1212 __ENDTRY;
1213 types++;
1215 szAcceptTypes[acceptTypesCount] = NULL;
1218 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1219 szVersion, szReferrer,
1220 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1222 end:
1223 if (szAcceptTypes)
1225 acceptTypesCount = 0;
1226 while (szAcceptTypes[acceptTypesCount])
1228 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1229 acceptTypesCount++;
1231 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1233 HeapFree(GetProcessHeap(), 0, szReferrer);
1234 HeapFree(GetProcessHeap(), 0, szVersion);
1235 HeapFree(GetProcessHeap(), 0, szObjectName);
1236 HeapFree(GetProcessHeap(), 0, szVerb);
1238 return rc;
1241 /***********************************************************************
1242 * HTTP_EncodeBase64
1244 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1246 UINT n = 0, x;
1247 static const CHAR HTTP_Base64Enc[] =
1248 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1250 while( len > 0 )
1252 /* first 6 bits, all from bin[0] */
1253 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1254 x = (bin[0] & 3) << 4;
1256 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1257 if( len == 1 )
1259 base64[n++] = HTTP_Base64Enc[x];
1260 base64[n++] = '=';
1261 base64[n++] = '=';
1262 break;
1264 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1265 x = ( bin[1] & 0x0f ) << 2;
1267 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1268 if( len == 2 )
1270 base64[n++] = HTTP_Base64Enc[x];
1271 base64[n++] = '=';
1272 break;
1274 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1276 /* last 6 bits, all from bin [2] */
1277 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1278 bin += 3;
1279 len -= 3;
1281 base64[n] = 0;
1282 return n;
1285 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1286 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1287 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1288 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1289 static const signed char HTTP_Base64Dec[256] =
1291 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1292 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1293 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1294 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1295 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1296 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1297 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1298 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1299 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1300 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1301 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1302 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1303 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1304 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1305 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1306 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1307 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1308 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1309 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1310 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1311 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1312 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1313 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1314 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1315 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1316 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1318 #undef CH
1320 /***********************************************************************
1321 * HTTP_DecodeBase64
1323 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1325 unsigned int n = 0;
1327 while(*base64)
1329 signed char in[4];
1331 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1332 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1333 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1334 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1336 WARN("invalid base64: %s\n", debugstr_w(base64));
1337 return 0;
1339 if (bin)
1340 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1341 n++;
1343 if ((base64[2] == '=') && (base64[3] == '='))
1344 break;
1345 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1346 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1348 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1349 return 0;
1351 if (bin)
1352 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1353 n++;
1355 if (base64[3] == '=')
1356 break;
1357 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1358 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1360 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1361 return 0;
1363 if (bin)
1364 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1365 n++;
1367 base64 += 4;
1370 return n;
1373 /***********************************************************************
1374 * HTTP_InsertAuthorization
1376 * Insert or delete the authorization field in the request header.
1378 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1380 if (pAuthInfo)
1382 static const WCHAR wszSpace[] = {' ',0};
1383 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1384 unsigned int len;
1385 WCHAR *authorization = NULL;
1387 if (pAuthInfo->auth_data_len)
1389 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1390 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1391 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1392 if (!authorization)
1393 return FALSE;
1395 strcpyW(authorization, pAuthInfo->scheme);
1396 strcatW(authorization, wszSpace);
1397 HTTP_EncodeBase64(pAuthInfo->auth_data,
1398 pAuthInfo->auth_data_len,
1399 authorization+strlenW(authorization));
1401 /* clear the data as it isn't valid now that it has been sent to the
1402 * server, unless it's Basic authentication which doesn't do
1403 * connection tracking */
1404 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1406 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1407 pAuthInfo->auth_data = NULL;
1408 pAuthInfo->auth_data_len = 0;
1412 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1414 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1416 HeapFree(GetProcessHeap(), 0, authorization);
1418 return TRUE;
1421 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1423 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1424 DWORD size;
1426 size = sizeof(new_location);
1427 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1429 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1430 strcpyW( url, new_location );
1432 else
1434 static const WCHAR slash[] = { '/',0 };
1435 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1436 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1437 http_session_t *session = req->lpHttpSession;
1439 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1440 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1442 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1444 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1445 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1446 else
1447 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1448 if (req->lpszPath[0] != '/') strcatW( url, slash );
1449 strcatW( url, req->lpszPath );
1451 TRACE("url=%s\n", debugstr_w(url));
1452 return url;
1455 /***********************************************************************
1456 * HTTP_DealWithProxy
1458 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1460 WCHAR buf[MAXHOSTNAME];
1461 WCHAR protoProxy[MAXHOSTNAME + 15];
1462 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1463 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1464 static WCHAR szNul[] = { 0 };
1465 URL_COMPONENTSW UrlComponents;
1466 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1467 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1468 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1470 memset( &UrlComponents, 0, sizeof UrlComponents );
1471 UrlComponents.dwStructSize = sizeof UrlComponents;
1472 UrlComponents.lpszHostName = buf;
1473 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1475 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1476 return FALSE;
1477 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1478 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1479 sprintfW(proxy, szFormat, protoProxy);
1480 else
1481 strcpyW(proxy, protoProxy);
1482 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1483 return FALSE;
1484 if( UrlComponents.dwHostNameLength == 0 )
1485 return FALSE;
1487 if( !lpwhr->lpszPath )
1488 lpwhr->lpszPath = szNul;
1490 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1491 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1493 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1494 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1495 lpwhs->nServerPort = UrlComponents.nPort;
1497 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1498 return TRUE;
1501 #ifndef INET6_ADDRSTRLEN
1502 #define INET6_ADDRSTRLEN 46
1503 #endif
1505 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1507 char szaddr[INET6_ADDRSTRLEN];
1508 http_session_t *lpwhs = lpwhr->lpHttpSession;
1509 const void *addr;
1511 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1512 INTERNET_STATUS_RESOLVING_NAME,
1513 lpwhs->lpszServerName,
1514 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1516 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1517 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1518 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1519 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1521 switch (lpwhs->socketAddress.ss_family)
1523 case AF_INET:
1524 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1525 break;
1526 case AF_INET6:
1527 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1528 break;
1529 default:
1530 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1531 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1533 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1534 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1535 INTERNET_STATUS_NAME_RESOLVED,
1536 szaddr, strlen(szaddr)+1);
1538 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1539 return ERROR_SUCCESS;
1542 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1544 LPHTTPHEADERW host_header;
1546 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1548 host_header = HTTP_GetHeader(req, hostW);
1549 if(!host_header)
1550 return FALSE;
1552 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1553 return TRUE;
1557 /***********************************************************************
1558 * HTTPREQ_Destroy (internal)
1560 * Deallocate request handle
1563 static void HTTPREQ_Destroy(object_header_t *hdr)
1565 http_request_t *lpwhr = (http_request_t*) hdr;
1566 DWORD i;
1568 TRACE("\n");
1570 if(lpwhr->hCacheFile) {
1571 WCHAR url[INTERNET_MAX_URL_LENGTH];
1572 FILETIME ft;
1574 CloseHandle(lpwhr->hCacheFile);
1576 memset(&ft, 0, sizeof(FILETIME));
1577 if(HTTP_GetRequestURL(lpwhr, url)) {
1578 CommitUrlCacheEntryW(url, lpwhr->lpszCacheFile, ft, ft,
1579 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1585 DeleteCriticalSection( &lpwhr->read_section );
1586 WININET_Release(&lpwhr->lpHttpSession->hdr);
1588 destroy_authinfo(lpwhr->pAuthInfo);
1589 destroy_authinfo(lpwhr->pProxyAuthInfo);
1591 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1592 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1593 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1594 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1595 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1597 for (i = 0; i < lpwhr->nCustHeaders; i++)
1599 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1600 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1603 #ifdef HAVE_ZLIB
1604 if(lpwhr->gzip_stream) {
1605 if(!lpwhr->gzip_stream->end_of_data)
1606 inflateEnd(&lpwhr->gzip_stream->zstream);
1607 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1609 #endif
1611 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1612 HeapFree(GetProcessHeap(), 0, lpwhr);
1615 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1617 http_request_t *lpwhr = (http_request_t*) hdr;
1619 TRACE("%p\n",lpwhr);
1621 if (!NETCON_connected(&lpwhr->netConnection))
1622 return;
1624 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1625 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1627 NETCON_close(&lpwhr->netConnection);
1629 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1630 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1633 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1635 WCHAR szVersion[10];
1636 WCHAR szConnectionResponse[20];
1637 DWORD dwBufferSize = sizeof(szVersion);
1638 BOOL keepalive = FALSE;
1640 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1641 * the connection is keep-alive by default */
1642 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1643 && !strcmpiW(szVersion, g_szHttp1_1))
1645 keepalive = TRUE;
1648 dwBufferSize = sizeof(szConnectionResponse);
1649 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1650 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1652 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1655 return keepalive;
1658 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1660 http_request_t *req = (http_request_t*)hdr;
1662 switch(option) {
1663 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1665 http_session_t *lpwhs = req->lpHttpSession;
1666 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1668 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1670 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1671 return ERROR_INSUFFICIENT_BUFFER;
1672 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1673 /* FIXME: can't get a SOCKET from our connection since we don't use
1674 * winsock
1676 info->Socket = 0;
1677 /* FIXME: get source port from req->netConnection */
1678 info->SourcePort = 0;
1679 info->DestPort = lpwhs->nHostPort;
1680 info->Flags = 0;
1681 if (HTTP_KeepAlive(req))
1682 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1683 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1684 info->Flags |= IDSI_FLAG_PROXY;
1685 if (req->netConnection.useSSL)
1686 info->Flags |= IDSI_FLAG_SECURE;
1688 return ERROR_SUCCESS;
1691 case INTERNET_OPTION_SECURITY_FLAGS:
1693 DWORD flags;
1694 int bits;
1696 if (*size < sizeof(ULONG))
1697 return ERROR_INSUFFICIENT_BUFFER;
1699 *size = sizeof(DWORD);
1700 flags = 0;
1701 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1702 flags |= SECURITY_FLAG_SECURE;
1703 flags |= req->netConnection.security_flags;
1704 bits = NETCON_GetCipherStrength(&req->netConnection);
1705 if (bits >= 128)
1706 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1707 else if (bits >= 56)
1708 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1709 else
1710 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1711 *(DWORD *)buffer = flags;
1712 return ERROR_SUCCESS;
1715 case INTERNET_OPTION_HANDLE_TYPE:
1716 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1718 if (*size < sizeof(ULONG))
1719 return ERROR_INSUFFICIENT_BUFFER;
1721 *size = sizeof(DWORD);
1722 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1723 return ERROR_SUCCESS;
1725 case INTERNET_OPTION_URL: {
1726 WCHAR url[INTERNET_MAX_URL_LENGTH];
1727 HTTPHEADERW *host;
1728 DWORD len;
1729 WCHAR *pch;
1731 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1733 TRACE("INTERNET_OPTION_URL\n");
1735 host = HTTP_GetHeader(req, hostW);
1736 strcpyW(url, httpW);
1737 strcatW(url, host->lpszValue);
1738 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1739 *pch = 0;
1740 strcatW(url, req->lpszPath);
1742 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1744 if(unicode) {
1745 len = (strlenW(url)+1) * sizeof(WCHAR);
1746 if(*size < len)
1747 return ERROR_INSUFFICIENT_BUFFER;
1749 *size = len;
1750 strcpyW(buffer, url);
1751 return ERROR_SUCCESS;
1752 }else {
1753 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1754 if(len > *size)
1755 return ERROR_INSUFFICIENT_BUFFER;
1757 *size = len;
1758 return ERROR_SUCCESS;
1762 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1763 INTERNET_CACHE_ENTRY_INFOW *info;
1764 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1765 WCHAR url[INTERNET_MAX_URL_LENGTH];
1766 DWORD nbytes, error;
1767 BOOL ret;
1769 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1771 if (*size < sizeof(*ts))
1773 *size = sizeof(*ts);
1774 return ERROR_INSUFFICIENT_BUFFER;
1776 nbytes = 0;
1777 HTTP_GetRequestURL(req, url);
1778 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1779 error = GetLastError();
1780 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1782 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1783 return ERROR_OUTOFMEMORY;
1785 GetUrlCacheEntryInfoW(url, info, &nbytes);
1787 ts->ftExpires = info->ExpireTime;
1788 ts->ftLastModified = info->LastModifiedTime;
1790 HeapFree(GetProcessHeap(), 0, info);
1791 *size = sizeof(*ts);
1792 return ERROR_SUCCESS;
1794 return error;
1797 case INTERNET_OPTION_DATAFILE_NAME: {
1798 DWORD req_size;
1800 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1802 if(!req->lpszCacheFile) {
1803 *size = 0;
1804 return ERROR_INTERNET_ITEM_NOT_FOUND;
1807 if(unicode) {
1808 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1809 if(*size < req_size)
1810 return ERROR_INSUFFICIENT_BUFFER;
1812 *size = req_size;
1813 memcpy(buffer, req->lpszCacheFile, *size);
1814 return ERROR_SUCCESS;
1815 }else {
1816 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1817 if (req_size > *size)
1818 return ERROR_INSUFFICIENT_BUFFER;
1820 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1821 -1, buffer, *size, NULL, NULL);
1822 return ERROR_SUCCESS;
1826 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1827 PCCERT_CONTEXT context;
1829 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1830 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1831 return ERROR_INSUFFICIENT_BUFFER;
1834 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1835 if(context) {
1836 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1837 DWORD len;
1839 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1840 info->ftExpiry = context->pCertInfo->NotAfter;
1841 info->ftStart = context->pCertInfo->NotBefore;
1842 len = CertNameToStrA(context->dwCertEncodingType,
1843 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1844 info->lpszSubjectInfo = LocalAlloc(0, len);
1845 if(info->lpszSubjectInfo)
1846 CertNameToStrA(context->dwCertEncodingType,
1847 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1848 info->lpszSubjectInfo, len);
1849 len = CertNameToStrA(context->dwCertEncodingType,
1850 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1851 info->lpszIssuerInfo = LocalAlloc(0, len);
1852 if(info->lpszIssuerInfo)
1853 CertNameToStrA(context->dwCertEncodingType,
1854 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1855 info->lpszIssuerInfo, len);
1856 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1857 CertFreeCertificateContext(context);
1858 return ERROR_SUCCESS;
1863 return INET_QueryOption(hdr, option, buffer, size, unicode);
1866 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1868 http_request_t *req = (http_request_t*)hdr;
1870 switch(option) {
1871 case INTERNET_OPTION_SECURITY_FLAGS:
1873 DWORD flags;
1875 if (!buffer || size != sizeof(DWORD))
1876 return ERROR_INVALID_PARAMETER;
1877 flags = *(DWORD *)buffer;
1878 TRACE("%08x\n", flags);
1879 req->netConnection.security_flags = flags;
1880 return ERROR_SUCCESS;
1882 case INTERNET_OPTION_SEND_TIMEOUT:
1883 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1884 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1886 if (size != sizeof(DWORD))
1887 return ERROR_INVALID_PARAMETER;
1889 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1890 *(DWORD*)buffer);
1892 case INTERNET_OPTION_USERNAME:
1893 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1894 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1895 return ERROR_SUCCESS;
1897 case INTERNET_OPTION_PASSWORD:
1898 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1899 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1900 return ERROR_SUCCESS;
1901 case INTERNET_OPTION_HTTP_DECODING:
1902 if(size != sizeof(BOOL))
1903 return ERROR_INVALID_PARAMETER;
1904 req->decoding = *(BOOL*)buffer;
1905 return ERROR_SUCCESS;
1908 return ERROR_INTERNET_INVALID_OPTION;
1911 /* read some more data into the read buffer (the read section must be held) */
1912 static DWORD read_more_data( http_request_t *req, int maxlen )
1914 DWORD res;
1915 int len;
1917 if (req->read_pos)
1919 /* move existing data to the start of the buffer */
1920 if(req->read_size)
1921 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1922 req->read_pos = 0;
1925 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1927 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1928 maxlen - req->read_size, 0, &len );
1929 if(res == ERROR_SUCCESS)
1930 req->read_size += len;
1932 return res;
1935 /* remove some amount of data from the read buffer (the read section must be held) */
1936 static void remove_data( http_request_t *req, int count )
1938 if (!(req->read_size -= count)) req->read_pos = 0;
1939 else req->read_pos += count;
1942 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1944 int count, bytes_read, pos = 0;
1945 DWORD res;
1947 EnterCriticalSection( &req->read_section );
1948 for (;;)
1950 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1952 if (eol)
1954 count = eol - (req->read_buf + req->read_pos);
1955 bytes_read = count + 1;
1957 else count = bytes_read = req->read_size;
1959 count = min( count, *len - pos );
1960 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1961 pos += count;
1962 remove_data( req, bytes_read );
1963 if (eol) break;
1965 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1967 *len = 0;
1968 TRACE( "returning empty string\n" );
1969 LeaveCriticalSection( &req->read_section );
1970 INTERNET_SetLastError(res);
1971 return FALSE;
1974 LeaveCriticalSection( &req->read_section );
1976 if (pos < *len)
1978 if (pos && buffer[pos - 1] == '\r') pos--;
1979 *len = pos + 1;
1981 buffer[*len - 1] = 0;
1982 TRACE( "returning %s\n", debugstr_a(buffer));
1983 return TRUE;
1986 /* discard data contents until we reach end of line (the read section must be held) */
1987 static DWORD discard_eol( http_request_t *req )
1989 DWORD res;
1993 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1994 if (eol)
1996 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1997 break;
1999 req->read_pos = req->read_size = 0; /* discard everything */
2000 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2001 } while (req->read_size);
2002 return ERROR_SUCCESS;
2005 /* read the size of the next chunk (the read section must be held) */
2006 static DWORD start_next_chunk( http_request_t *req )
2008 DWORD chunk_size = 0, res;
2010 if (!req->dwContentLength) return ERROR_SUCCESS;
2011 if (req->dwContentLength == req->dwContentRead)
2013 /* read terminator for the previous chunk */
2014 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2015 req->dwContentLength = ~0u;
2016 req->dwContentRead = 0;
2018 for (;;)
2020 while (req->read_size)
2022 char ch = req->read_buf[req->read_pos];
2023 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2024 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2025 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2026 else if (ch == ';' || ch == '\r' || ch == '\n')
2028 TRACE( "reading %u byte chunk\n", chunk_size );
2029 req->dwContentLength = chunk_size;
2030 req->dwContentRead = 0;
2031 return discard_eol( req );
2033 remove_data( req, 1 );
2035 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2036 if (!req->read_size)
2038 req->dwContentLength = req->dwContentRead = 0;
2039 return ERROR_SUCCESS;
2044 /* check if we have reached the end of the data to read (the read section must be held) */
2045 static BOOL end_of_read_data( http_request_t *req )
2047 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2048 if (req->read_chunked) return (req->dwContentLength == 0);
2049 if (req->dwContentLength == ~0u) return FALSE;
2050 return (req->dwContentLength == req->dwContentRead);
2053 /* fetch some more data into the read buffer (the read section must be held) */
2054 static DWORD refill_buffer( http_request_t *req )
2056 int len = sizeof(req->read_buf);
2057 DWORD res;
2059 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2061 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2064 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2065 if (len <= req->read_size) return ERROR_SUCCESS;
2067 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2068 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2069 return ERROR_SUCCESS;
2072 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2074 DWORD ret = ERROR_SUCCESS;
2075 int read = 0;
2077 #ifdef HAVE_ZLIB
2078 z_stream *zstream = &req->gzip_stream->zstream;
2079 DWORD buf_avail;
2080 int zres;
2082 while(read < size && !req->gzip_stream->end_of_data) {
2083 if(!req->read_size) {
2084 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2085 break;
2088 if(req->dwContentRead == req->dwContentLength)
2089 break;
2091 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2093 zstream->next_in = req->read_buf+req->read_pos;
2094 zstream->avail_in = buf_avail;
2095 zstream->next_out = buf+read;
2096 zstream->avail_out = size-read;
2097 zres = inflate(zstream, Z_FULL_FLUSH);
2098 read = size - zstream->avail_out;
2099 req->dwContentRead += buf_avail-zstream->avail_in;
2100 remove_data(req, buf_avail-zstream->avail_in);
2101 if(zres == Z_STREAM_END) {
2102 TRACE("end of data\n");
2103 req->gzip_stream->end_of_data = TRUE;
2104 inflateEnd(&req->gzip_stream->zstream);
2105 }else if(zres != Z_OK) {
2106 WARN("inflate failed %d\n", zres);
2107 if(!read)
2108 ret = ERROR_INTERNET_DECODING_FAILED;
2109 break;
2112 #endif
2114 *read_ret = read;
2115 return ret;
2118 static void refill_gzip_buffer(http_request_t *req)
2120 DWORD res;
2121 int len;
2123 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2124 return;
2126 if(req->gzip_stream->buf_pos) {
2127 if(req->gzip_stream->buf_size)
2128 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2129 req->gzip_stream->buf_pos = 0;
2132 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2133 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2134 if(res == ERROR_SUCCESS)
2135 req->gzip_stream->buf_size += len;
2138 /* return the size of data available to be read immediately (the read section must be held) */
2139 static DWORD get_avail_data( http_request_t *req )
2141 if (req->gzip_stream) {
2142 refill_gzip_buffer(req);
2143 return req->gzip_stream->buf_size;
2145 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2146 return 0;
2147 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2150 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2152 INTERNET_ASYNC_RESULT iar;
2153 DWORD res;
2155 TRACE("%p\n", req);
2157 EnterCriticalSection( &req->read_section );
2158 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2159 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2160 iar.dwError = first_notif ? 0 : get_avail_data(req);
2161 }else {
2162 iar.dwResult = 0;
2163 iar.dwError = res;
2165 LeaveCriticalSection( &req->read_section );
2167 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2168 sizeof(INTERNET_ASYNC_RESULT));
2171 /* read data from the http connection (the read section must be held) */
2172 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2174 BOOL finished_reading = FALSE;
2175 int len, bytes_read = 0;
2176 DWORD ret = ERROR_SUCCESS;
2178 EnterCriticalSection( &req->read_section );
2180 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2182 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2185 if(req->gzip_stream) {
2186 if(req->gzip_stream->buf_size) {
2187 bytes_read = min(req->gzip_stream->buf_size, size);
2188 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2189 req->gzip_stream->buf_pos += bytes_read;
2190 req->gzip_stream->buf_size -= bytes_read;
2191 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2192 refill_buffer(req);
2195 if(size > bytes_read) {
2196 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2197 if(ret == ERROR_SUCCESS)
2198 bytes_read += len;
2201 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2202 }else {
2203 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2205 if (req->read_size) {
2206 bytes_read = min( req->read_size, size );
2207 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2208 remove_data( req, bytes_read );
2211 if (size > bytes_read && (!bytes_read || sync)) {
2212 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2213 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2214 bytes_read += len;
2215 /* always return success, even if the network layer returns an error */
2218 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2219 req->dwContentRead += bytes_read;
2221 done:
2222 *read = bytes_read;
2224 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2225 LeaveCriticalSection( &req->read_section );
2227 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2228 BOOL res;
2229 DWORD dwBytesWritten;
2231 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2232 if(!res)
2233 WARN("WriteFile failed: %u\n", GetLastError());
2236 if(finished_reading)
2237 HTTP_FinishedReading(req);
2239 return ret;
2243 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2245 http_request_t *req = (http_request_t*)hdr;
2246 DWORD res;
2248 EnterCriticalSection( &req->read_section );
2249 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2250 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2252 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2253 if(res == ERROR_SUCCESS)
2254 res = hdr->dwError;
2255 LeaveCriticalSection( &req->read_section );
2257 return res;
2260 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2262 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2263 http_request_t *req = (http_request_t*)workRequest->hdr;
2264 INTERNET_ASYNC_RESULT iar;
2265 DWORD res;
2267 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2269 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2270 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2272 iar.dwResult = res == ERROR_SUCCESS;
2273 iar.dwError = res;
2275 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2276 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2277 sizeof(INTERNET_ASYNC_RESULT));
2280 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2281 DWORD flags, DWORD_PTR context)
2283 http_request_t *req = (http_request_t*)hdr;
2284 DWORD res, size, read, error = ERROR_SUCCESS;
2286 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2287 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2289 if (buffers->dwStructSize != sizeof(*buffers))
2290 return ERROR_INVALID_PARAMETER;
2292 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2294 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2296 WORKREQUEST workRequest;
2298 if (TryEnterCriticalSection( &req->read_section ))
2300 if (get_avail_data(req))
2302 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2303 &buffers->dwBufferLength, FALSE);
2304 size = buffers->dwBufferLength;
2305 LeaveCriticalSection( &req->read_section );
2306 goto done;
2308 LeaveCriticalSection( &req->read_section );
2311 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2312 workRequest.hdr = WININET_AddRef(&req->hdr);
2313 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2315 INTERNET_AsyncCall(&workRequest);
2317 return ERROR_IO_PENDING;
2320 read = 0;
2321 size = buffers->dwBufferLength;
2323 EnterCriticalSection( &req->read_section );
2324 if(hdr->dwError == ERROR_SUCCESS)
2325 hdr->dwError = INTERNET_HANDLE_IN_USE;
2326 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2327 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2329 while(1) {
2330 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2331 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2332 if(res == ERROR_SUCCESS)
2333 read += buffers->dwBufferLength;
2334 else
2335 break;
2337 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2338 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2339 break;
2340 LeaveCriticalSection( &req->read_section );
2342 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2343 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2344 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2345 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2347 EnterCriticalSection( &req->read_section );
2350 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2351 hdr->dwError = ERROR_SUCCESS;
2352 else
2353 error = hdr->dwError;
2355 LeaveCriticalSection( &req->read_section );
2356 size = buffers->dwBufferLength;
2357 buffers->dwBufferLength = read;
2359 done:
2360 if (res == ERROR_SUCCESS) {
2361 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2362 &size, sizeof(size));
2365 return res==ERROR_SUCCESS ? error : res;
2368 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2370 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2371 http_request_t *req = (http_request_t*)workRequest->hdr;
2372 INTERNET_ASYNC_RESULT iar;
2373 DWORD res;
2375 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2377 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2378 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2380 iar.dwResult = res == ERROR_SUCCESS;
2381 iar.dwError = res;
2383 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2384 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2385 sizeof(INTERNET_ASYNC_RESULT));
2388 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2389 DWORD flags, DWORD_PTR context)
2392 http_request_t *req = (http_request_t*)hdr;
2393 DWORD res, size, read, error = ERROR_SUCCESS;
2395 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2396 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2398 if (buffers->dwStructSize != sizeof(*buffers))
2399 return ERROR_INVALID_PARAMETER;
2401 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2403 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2405 WORKREQUEST workRequest;
2407 if (TryEnterCriticalSection( &req->read_section ))
2409 if (get_avail_data(req))
2411 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2412 &buffers->dwBufferLength, FALSE);
2413 size = buffers->dwBufferLength;
2414 LeaveCriticalSection( &req->read_section );
2415 goto done;
2417 LeaveCriticalSection( &req->read_section );
2420 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2421 workRequest.hdr = WININET_AddRef(&req->hdr);
2422 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2424 INTERNET_AsyncCall(&workRequest);
2426 return ERROR_IO_PENDING;
2429 read = 0;
2430 size = buffers->dwBufferLength;
2432 EnterCriticalSection( &req->read_section );
2433 if(hdr->dwError == ERROR_SUCCESS)
2434 hdr->dwError = INTERNET_HANDLE_IN_USE;
2435 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2436 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2438 while(1) {
2439 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2440 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2441 if(res == ERROR_SUCCESS)
2442 read += buffers->dwBufferLength;
2443 else
2444 break;
2446 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2447 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2448 break;
2449 LeaveCriticalSection( &req->read_section );
2451 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2452 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2453 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2454 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2456 EnterCriticalSection( &req->read_section );
2459 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2460 hdr->dwError = ERROR_SUCCESS;
2461 else
2462 error = hdr->dwError;
2464 LeaveCriticalSection( &req->read_section );
2465 size = buffers->dwBufferLength;
2466 buffers->dwBufferLength = read;
2468 done:
2469 if (res == ERROR_SUCCESS) {
2470 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2471 &size, sizeof(size));
2474 return res==ERROR_SUCCESS ? error : res;
2477 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2479 DWORD res;
2480 http_request_t *lpwhr = (http_request_t*)hdr;
2482 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2484 *written = 0;
2485 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2486 if (res == ERROR_SUCCESS)
2487 lpwhr->dwBytesWritten += *written;
2489 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2490 return res;
2493 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2495 http_request_t *req = (http_request_t*)workRequest->hdr;
2497 HTTP_ReceiveRequestData(req, FALSE);
2500 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2502 http_request_t *req = (http_request_t*)hdr;
2504 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2506 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2508 WORKREQUEST workRequest;
2510 /* never wait, if we can't enter the section we queue an async request right away */
2511 if (TryEnterCriticalSection( &req->read_section ))
2513 if ((*available = get_avail_data( req ))) goto done;
2514 if (end_of_read_data( req )) goto done;
2515 LeaveCriticalSection( &req->read_section );
2518 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2519 workRequest.hdr = WININET_AddRef( &req->hdr );
2521 INTERNET_AsyncCall(&workRequest);
2523 return ERROR_IO_PENDING;
2526 EnterCriticalSection( &req->read_section );
2528 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2530 refill_buffer( req );
2531 *available = get_avail_data( req );
2534 done:
2535 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2537 DWORD extra;
2538 if (NETCON_query_data_available(&req->netConnection, &extra))
2539 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2541 LeaveCriticalSection( &req->read_section );
2543 TRACE( "returning %u\n", *available );
2544 return ERROR_SUCCESS;
2547 static const object_vtbl_t HTTPREQVtbl = {
2548 HTTPREQ_Destroy,
2549 HTTPREQ_CloseConnection,
2550 HTTPREQ_QueryOption,
2551 HTTPREQ_SetOption,
2552 HTTPREQ_ReadFile,
2553 HTTPREQ_ReadFileExA,
2554 HTTPREQ_ReadFileExW,
2555 HTTPREQ_WriteFile,
2556 HTTPREQ_QueryDataAvailable,
2557 NULL
2560 /***********************************************************************
2561 * HTTP_HttpOpenRequestW (internal)
2563 * Open a HTTP request handle
2565 * RETURNS
2566 * HINTERNET a HTTP request handle on success
2567 * NULL on failure
2570 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2571 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2572 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2573 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2575 appinfo_t *hIC = NULL;
2576 http_request_t *lpwhr;
2577 LPWSTR lpszHostName = NULL;
2578 HINTERNET handle = NULL;
2579 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2580 DWORD len, res;
2582 TRACE("-->\n");
2584 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2585 hIC = lpwhs->lpAppInfo;
2587 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2588 if (NULL == lpwhr)
2590 res = ERROR_OUTOFMEMORY;
2591 goto lend;
2593 lpwhr->hdr.htype = WH_HHTTPREQ;
2594 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2595 lpwhr->hdr.dwFlags = dwFlags;
2596 lpwhr->hdr.dwContext = dwContext;
2597 lpwhr->hdr.refs = 1;
2598 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2599 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2600 lpwhr->dwContentLength = ~0u;
2601 InitializeCriticalSection( &lpwhr->read_section );
2603 WININET_AddRef( &lpwhs->hdr );
2604 lpwhr->lpHttpSession = lpwhs;
2605 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2607 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2608 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2609 if (NULL == lpszHostName)
2611 res = ERROR_OUTOFMEMORY;
2612 goto lend;
2615 handle = WININET_AllocHandle( &lpwhr->hdr );
2616 if (NULL == handle)
2618 res = ERROR_OUTOFMEMORY;
2619 goto lend;
2622 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2624 InternetCloseHandle( handle );
2625 handle = NULL;
2626 goto lend;
2629 if (lpszObjectName && *lpszObjectName) {
2630 HRESULT rc;
2632 len = 0;
2633 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2634 if (rc != E_POINTER)
2635 len = strlenW(lpszObjectName)+1;
2636 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2637 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2638 URL_ESCAPE_SPACES_ONLY);
2639 if (rc != S_OK)
2641 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2642 strcpyW(lpwhr->lpszPath,lpszObjectName);
2644 }else {
2645 static const WCHAR slashW[] = {'/',0};
2647 lpwhr->lpszPath = heap_strdupW(slashW);
2650 if (lpszReferrer && *lpszReferrer)
2651 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2653 if (lpszAcceptTypes)
2655 int i;
2656 for (i = 0; lpszAcceptTypes[i]; i++)
2658 if (!*lpszAcceptTypes[i]) continue;
2659 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2660 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2661 HTTP_ADDHDR_FLAG_REQ |
2662 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2666 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2667 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2669 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2670 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2671 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2673 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2674 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2675 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2677 else
2678 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2679 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2681 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2682 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2683 INTERNET_DEFAULT_HTTPS_PORT :
2684 INTERNET_DEFAULT_HTTP_PORT);
2686 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2687 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2688 INTERNET_DEFAULT_HTTPS_PORT :
2689 INTERNET_DEFAULT_HTTP_PORT);
2691 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2692 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2694 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2695 INTERNET_STATUS_HANDLE_CREATED, &handle,
2696 sizeof(handle));
2698 lend:
2699 HeapFree(GetProcessHeap(), 0, lpszHostName);
2700 if( lpwhr )
2701 WININET_Release( &lpwhr->hdr );
2703 TRACE("<-- %p (%p)\n", handle, lpwhr);
2704 *ret = handle;
2705 return res;
2708 /***********************************************************************
2709 * HttpOpenRequestW (WININET.@)
2711 * Open a HTTP request handle
2713 * RETURNS
2714 * HINTERNET a HTTP request handle on success
2715 * NULL on failure
2718 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2719 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2720 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2721 DWORD dwFlags, DWORD_PTR dwContext)
2723 http_session_t *lpwhs;
2724 HINTERNET handle = NULL;
2725 DWORD res;
2727 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2728 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2729 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2730 dwFlags, dwContext);
2731 if(lpszAcceptTypes!=NULL)
2733 int i;
2734 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2735 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2738 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2739 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2741 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2742 goto lend;
2746 * My tests seem to show that the windows version does not
2747 * become asynchronous until after this point. And anyhow
2748 * if this call was asynchronous then how would you get the
2749 * necessary HINTERNET pointer returned by this function.
2752 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2753 lpszVersion, lpszReferrer, lpszAcceptTypes,
2754 dwFlags, dwContext, &handle);
2755 lend:
2756 if( lpwhs )
2757 WININET_Release( &lpwhs->hdr );
2758 TRACE("returning %p\n", handle);
2759 if(res != ERROR_SUCCESS)
2760 SetLastError(res);
2761 return handle;
2764 /* read any content returned by the server so that the connection can be
2765 * reused */
2766 static void HTTP_DrainContent(http_request_t *req)
2768 DWORD bytes_read;
2770 if (!NETCON_connected(&req->netConnection)) return;
2772 if (req->dwContentLength == -1)
2774 NETCON_close(&req->netConnection);
2775 return;
2777 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2781 char buffer[2048];
2782 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2783 return;
2784 } while (bytes_read);
2787 static const LPCWSTR header_lookup[] = {
2788 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2789 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2790 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2791 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2792 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2793 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2794 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2795 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2796 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2797 szDate, /* HTTP_QUERY_DATE = 9 */
2798 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2799 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2800 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2801 szURI, /* HTTP_QUERY_URI = 13 */
2802 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2803 NULL, /* HTTP_QUERY_COST = 15 */
2804 NULL, /* HTTP_QUERY_LINK = 16 */
2805 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2806 NULL, /* HTTP_QUERY_VERSION = 18 */
2807 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2808 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2809 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2810 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2811 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2812 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2813 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2814 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2815 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2816 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2817 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2818 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2819 NULL, /* HTTP_QUERY_FROM = 31 */
2820 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2821 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2822 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2823 szReferer, /* HTTP_QUERY_REFERER = 35 */
2824 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2825 szServer, /* HTTP_QUERY_SERVER = 37 */
2826 NULL, /* HTTP_TITLE = 38 */
2827 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2828 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2829 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2830 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2831 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2832 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2833 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2834 NULL, /* HTTP_QUERY_REFRESH = 46 */
2835 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2836 szAge, /* HTTP_QUERY_AGE = 48 */
2837 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2838 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2839 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2840 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2841 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2842 szETag, /* HTTP_QUERY_ETAG = 54 */
2843 hostW, /* HTTP_QUERY_HOST = 55 */
2844 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2845 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2846 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2847 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2848 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2849 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2850 szRange, /* HTTP_QUERY_RANGE = 62 */
2851 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2852 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2853 szVary, /* HTTP_QUERY_VARY = 65 */
2854 szVia, /* HTTP_QUERY_VIA = 66 */
2855 szWarning, /* HTTP_QUERY_WARNING = 67 */
2856 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2857 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2858 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2861 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2863 /***********************************************************************
2864 * HTTP_HttpQueryInfoW (internal)
2866 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2867 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2869 LPHTTPHEADERW lphttpHdr = NULL;
2870 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2871 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2872 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2873 INT index = -1;
2875 /* Find requested header structure */
2876 switch (level)
2878 case HTTP_QUERY_CUSTOM:
2879 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2880 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2881 break;
2882 case HTTP_QUERY_RAW_HEADERS_CRLF:
2884 LPWSTR headers;
2885 DWORD len = 0;
2886 DWORD res = ERROR_INVALID_PARAMETER;
2888 if (request_only)
2889 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2890 else
2891 headers = lpwhr->lpszRawHeaders;
2893 if (headers)
2894 len = strlenW(headers) * sizeof(WCHAR);
2896 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2898 len += sizeof(WCHAR);
2899 res = ERROR_INSUFFICIENT_BUFFER;
2901 else if (lpBuffer)
2903 if (headers)
2904 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2905 else
2907 len = strlenW(szCrLf) * sizeof(WCHAR);
2908 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2910 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2911 res = ERROR_SUCCESS;
2913 *lpdwBufferLength = len;
2915 if (request_only)
2916 HeapFree(GetProcessHeap(), 0, headers);
2917 return res;
2919 case HTTP_QUERY_RAW_HEADERS:
2921 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2922 DWORD i, size = 0;
2923 LPWSTR pszString = lpBuffer;
2925 for (i = 0; ppszRawHeaderLines[i]; i++)
2926 size += strlenW(ppszRawHeaderLines[i]) + 1;
2928 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2930 HTTP_FreeTokens(ppszRawHeaderLines);
2931 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2932 return ERROR_INSUFFICIENT_BUFFER;
2934 if (pszString)
2936 for (i = 0; ppszRawHeaderLines[i]; i++)
2938 DWORD len = strlenW(ppszRawHeaderLines[i]);
2939 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2940 pszString += len+1;
2942 *pszString = '\0';
2943 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2945 *lpdwBufferLength = size * sizeof(WCHAR);
2946 HTTP_FreeTokens(ppszRawHeaderLines);
2948 return ERROR_SUCCESS;
2950 case HTTP_QUERY_STATUS_TEXT:
2951 if (lpwhr->lpszStatusText)
2953 DWORD len = strlenW(lpwhr->lpszStatusText);
2954 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2956 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2957 return ERROR_INSUFFICIENT_BUFFER;
2959 if (lpBuffer)
2961 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2962 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2964 *lpdwBufferLength = len * sizeof(WCHAR);
2965 return ERROR_SUCCESS;
2967 break;
2968 case HTTP_QUERY_VERSION:
2969 if (lpwhr->lpszVersion)
2971 DWORD len = strlenW(lpwhr->lpszVersion);
2972 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2974 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2975 return ERROR_INSUFFICIENT_BUFFER;
2977 if (lpBuffer)
2979 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2980 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2982 *lpdwBufferLength = len * sizeof(WCHAR);
2983 return ERROR_SUCCESS;
2985 break;
2986 case HTTP_QUERY_CONTENT_ENCODING:
2987 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2988 requested_index,request_only);
2989 break;
2990 default:
2991 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2993 if (level < LAST_TABLE_HEADER && header_lookup[level])
2994 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2995 requested_index,request_only);
2998 if (index >= 0)
2999 lphttpHdr = &lpwhr->pCustHeaders[index];
3001 /* Ensure header satisfies requested attributes */
3002 if (!lphttpHdr ||
3003 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3004 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3006 return ERROR_HTTP_HEADER_NOT_FOUND;
3009 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3011 /* coalesce value to requested type */
3012 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3014 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3015 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3017 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3019 time_t tmpTime;
3020 struct tm tmpTM;
3021 SYSTEMTIME *STHook;
3023 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3025 tmpTM = *gmtime(&tmpTime);
3026 STHook = (SYSTEMTIME *)lpBuffer;
3027 STHook->wDay = tmpTM.tm_mday;
3028 STHook->wHour = tmpTM.tm_hour;
3029 STHook->wMilliseconds = 0;
3030 STHook->wMinute = tmpTM.tm_min;
3031 STHook->wDayOfWeek = tmpTM.tm_wday;
3032 STHook->wMonth = tmpTM.tm_mon + 1;
3033 STHook->wSecond = tmpTM.tm_sec;
3034 STHook->wYear = tmpTM.tm_year;
3036 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3037 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3038 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3040 else if (lphttpHdr->lpszValue)
3042 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3044 if (len > *lpdwBufferLength)
3046 *lpdwBufferLength = len;
3047 return ERROR_INSUFFICIENT_BUFFER;
3049 if (lpBuffer)
3051 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3052 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3054 *lpdwBufferLength = len - sizeof(WCHAR);
3056 return ERROR_SUCCESS;
3059 /***********************************************************************
3060 * HttpQueryInfoW (WININET.@)
3062 * Queries for information about an HTTP request
3064 * RETURNS
3065 * TRUE on success
3066 * FALSE on failure
3069 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3070 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3072 http_request_t *lpwhr;
3073 DWORD res;
3075 if (TRACE_ON(wininet)) {
3076 #define FE(x) { x, #x }
3077 static const wininet_flag_info query_flags[] = {
3078 FE(HTTP_QUERY_MIME_VERSION),
3079 FE(HTTP_QUERY_CONTENT_TYPE),
3080 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3081 FE(HTTP_QUERY_CONTENT_ID),
3082 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3083 FE(HTTP_QUERY_CONTENT_LENGTH),
3084 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3085 FE(HTTP_QUERY_ALLOW),
3086 FE(HTTP_QUERY_PUBLIC),
3087 FE(HTTP_QUERY_DATE),
3088 FE(HTTP_QUERY_EXPIRES),
3089 FE(HTTP_QUERY_LAST_MODIFIED),
3090 FE(HTTP_QUERY_MESSAGE_ID),
3091 FE(HTTP_QUERY_URI),
3092 FE(HTTP_QUERY_DERIVED_FROM),
3093 FE(HTTP_QUERY_COST),
3094 FE(HTTP_QUERY_LINK),
3095 FE(HTTP_QUERY_PRAGMA),
3096 FE(HTTP_QUERY_VERSION),
3097 FE(HTTP_QUERY_STATUS_CODE),
3098 FE(HTTP_QUERY_STATUS_TEXT),
3099 FE(HTTP_QUERY_RAW_HEADERS),
3100 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3101 FE(HTTP_QUERY_CONNECTION),
3102 FE(HTTP_QUERY_ACCEPT),
3103 FE(HTTP_QUERY_ACCEPT_CHARSET),
3104 FE(HTTP_QUERY_ACCEPT_ENCODING),
3105 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3106 FE(HTTP_QUERY_AUTHORIZATION),
3107 FE(HTTP_QUERY_CONTENT_ENCODING),
3108 FE(HTTP_QUERY_FORWARDED),
3109 FE(HTTP_QUERY_FROM),
3110 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3111 FE(HTTP_QUERY_LOCATION),
3112 FE(HTTP_QUERY_ORIG_URI),
3113 FE(HTTP_QUERY_REFERER),
3114 FE(HTTP_QUERY_RETRY_AFTER),
3115 FE(HTTP_QUERY_SERVER),
3116 FE(HTTP_QUERY_TITLE),
3117 FE(HTTP_QUERY_USER_AGENT),
3118 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3119 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3120 FE(HTTP_QUERY_ACCEPT_RANGES),
3121 FE(HTTP_QUERY_SET_COOKIE),
3122 FE(HTTP_QUERY_COOKIE),
3123 FE(HTTP_QUERY_REQUEST_METHOD),
3124 FE(HTTP_QUERY_REFRESH),
3125 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3126 FE(HTTP_QUERY_AGE),
3127 FE(HTTP_QUERY_CACHE_CONTROL),
3128 FE(HTTP_QUERY_CONTENT_BASE),
3129 FE(HTTP_QUERY_CONTENT_LOCATION),
3130 FE(HTTP_QUERY_CONTENT_MD5),
3131 FE(HTTP_QUERY_CONTENT_RANGE),
3132 FE(HTTP_QUERY_ETAG),
3133 FE(HTTP_QUERY_HOST),
3134 FE(HTTP_QUERY_IF_MATCH),
3135 FE(HTTP_QUERY_IF_NONE_MATCH),
3136 FE(HTTP_QUERY_IF_RANGE),
3137 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3138 FE(HTTP_QUERY_MAX_FORWARDS),
3139 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3140 FE(HTTP_QUERY_RANGE),
3141 FE(HTTP_QUERY_TRANSFER_ENCODING),
3142 FE(HTTP_QUERY_UPGRADE),
3143 FE(HTTP_QUERY_VARY),
3144 FE(HTTP_QUERY_VIA),
3145 FE(HTTP_QUERY_WARNING),
3146 FE(HTTP_QUERY_CUSTOM)
3148 static const wininet_flag_info modifier_flags[] = {
3149 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3150 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3151 FE(HTTP_QUERY_FLAG_NUMBER),
3152 FE(HTTP_QUERY_FLAG_COALESCE)
3154 #undef FE
3155 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3156 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3157 DWORD i;
3159 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3160 TRACE(" Attribute:");
3161 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3162 if (query_flags[i].val == info) {
3163 TRACE(" %s", query_flags[i].name);
3164 break;
3167 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3168 TRACE(" Unknown (%08x)", info);
3171 TRACE(" Modifier:");
3172 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3173 if (modifier_flags[i].val & info_mod) {
3174 TRACE(" %s", modifier_flags[i].name);
3175 info_mod &= ~ modifier_flags[i].val;
3179 if (info_mod) {
3180 TRACE(" Unknown (%08x)", info_mod);
3182 TRACE("\n");
3185 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3186 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3188 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3189 goto lend;
3192 if (lpBuffer == NULL)
3193 *lpdwBufferLength = 0;
3194 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3195 lpBuffer, lpdwBufferLength, lpdwIndex);
3197 lend:
3198 if( lpwhr )
3199 WININET_Release( &lpwhr->hdr );
3201 TRACE("%u <--\n", res);
3202 if(res != ERROR_SUCCESS)
3203 SetLastError(res);
3204 return res == ERROR_SUCCESS;
3207 /***********************************************************************
3208 * HttpQueryInfoA (WININET.@)
3210 * Queries for information about an HTTP request
3212 * RETURNS
3213 * TRUE on success
3214 * FALSE on failure
3217 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3218 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3220 BOOL result;
3221 DWORD len;
3222 WCHAR* bufferW;
3224 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3225 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3227 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3228 lpdwBufferLength, lpdwIndex );
3231 if (lpBuffer)
3233 DWORD alloclen;
3234 len = (*lpdwBufferLength)*sizeof(WCHAR);
3235 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3237 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3238 if (alloclen < len)
3239 alloclen = len;
3241 else
3242 alloclen = len;
3243 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3244 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3245 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3246 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3247 } else
3249 bufferW = NULL;
3250 len = 0;
3253 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3254 &len, lpdwIndex );
3255 if( result )
3257 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3258 lpBuffer, *lpdwBufferLength, NULL, NULL );
3259 *lpdwBufferLength = len - 1;
3261 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3263 else
3264 /* since the strings being returned from HttpQueryInfoW should be
3265 * only ASCII characters, it is reasonable to assume that all of
3266 * the Unicode characters can be reduced to a single byte */
3267 *lpdwBufferLength = len / sizeof(WCHAR);
3269 HeapFree(GetProcessHeap(), 0, bufferW );
3271 return result;
3274 /***********************************************************************
3275 * HTTP_GetRedirectURL (internal)
3277 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3279 static WCHAR szHttp[] = {'h','t','t','p',0};
3280 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3281 http_session_t *lpwhs = lpwhr->lpHttpSession;
3282 URL_COMPONENTSW urlComponents;
3283 DWORD url_length = 0;
3284 LPWSTR orig_url;
3285 LPWSTR combined_url;
3287 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3288 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3289 urlComponents.dwSchemeLength = 0;
3290 urlComponents.lpszHostName = lpwhs->lpszHostName;
3291 urlComponents.dwHostNameLength = 0;
3292 urlComponents.nPort = lpwhs->nHostPort;
3293 urlComponents.lpszUserName = lpwhs->lpszUserName;
3294 urlComponents.dwUserNameLength = 0;
3295 urlComponents.lpszPassword = NULL;
3296 urlComponents.dwPasswordLength = 0;
3297 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3298 urlComponents.dwUrlPathLength = 0;
3299 urlComponents.lpszExtraInfo = NULL;
3300 urlComponents.dwExtraInfoLength = 0;
3302 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3303 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3304 return NULL;
3306 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3308 /* convert from bytes to characters */
3309 url_length = url_length / sizeof(WCHAR) - 1;
3310 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3312 HeapFree(GetProcessHeap(), 0, orig_url);
3313 return NULL;
3316 url_length = 0;
3317 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3318 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3320 HeapFree(GetProcessHeap(), 0, orig_url);
3321 return NULL;
3323 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3325 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3327 HeapFree(GetProcessHeap(), 0, orig_url);
3328 HeapFree(GetProcessHeap(), 0, combined_url);
3329 return NULL;
3331 HeapFree(GetProcessHeap(), 0, orig_url);
3332 return combined_url;
3336 /***********************************************************************
3337 * HTTP_HandleRedirect (internal)
3339 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3341 http_session_t *lpwhs = lpwhr->lpHttpSession;
3342 appinfo_t *hIC = lpwhs->lpAppInfo;
3343 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3344 WCHAR path[INTERNET_MAX_URL_LENGTH];
3345 int index;
3347 if(lpszUrl[0]=='/')
3349 /* if it's an absolute path, keep the same session info */
3350 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3352 else
3354 URL_COMPONENTSW urlComponents;
3355 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3356 static WCHAR szHttp[] = {'h','t','t','p',0};
3357 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3359 userName[0] = 0;
3360 hostName[0] = 0;
3361 protocol[0] = 0;
3363 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3364 urlComponents.lpszScheme = protocol;
3365 urlComponents.dwSchemeLength = 32;
3366 urlComponents.lpszHostName = hostName;
3367 urlComponents.dwHostNameLength = MAXHOSTNAME;
3368 urlComponents.lpszUserName = userName;
3369 urlComponents.dwUserNameLength = 1024;
3370 urlComponents.lpszPassword = NULL;
3371 urlComponents.dwPasswordLength = 0;
3372 urlComponents.lpszUrlPath = path;
3373 urlComponents.dwUrlPathLength = 2048;
3374 urlComponents.lpszExtraInfo = NULL;
3375 urlComponents.dwExtraInfoLength = 0;
3376 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3377 return INTERNET_GetLastError();
3379 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3380 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3382 TRACE("redirect from secure page to non-secure page\n");
3383 /* FIXME: warn about from secure redirect to non-secure page */
3384 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3386 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3387 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3389 TRACE("redirect from non-secure page to secure page\n");
3390 /* FIXME: notify about redirect to secure page */
3391 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3394 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3396 if (lstrlenW(protocol)>4) /*https*/
3397 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3398 else /*http*/
3399 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3402 #if 0
3404 * This upsets redirects to binary files on sourceforge.net
3405 * and gives an html page instead of the target file
3406 * Examination of the HTTP request sent by native wininet.dll
3407 * reveals that it doesn't send a referrer in that case.
3408 * Maybe there's a flag that enables this, or maybe a referrer
3409 * shouldn't be added in case of a redirect.
3412 /* consider the current host as the referrer */
3413 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3414 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3415 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3416 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3417 #endif
3419 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3420 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3421 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3423 int len;
3424 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3425 len = lstrlenW(hostName);
3426 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3427 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3428 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3430 else
3431 lpwhs->lpszHostName = heap_strdupW(hostName);
3433 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3435 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3436 lpwhs->lpszUserName = NULL;
3437 if (userName[0])
3438 lpwhs->lpszUserName = heap_strdupW(userName);
3440 if (!using_proxy)
3442 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3444 DWORD res;
3446 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3447 lpwhs->lpszServerName = heap_strdupW(hostName);
3448 lpwhs->nServerPort = urlComponents.nPort;
3450 NETCON_close(&lpwhr->netConnection);
3451 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3452 return res;
3454 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3455 if (res != ERROR_SUCCESS)
3456 return res;
3458 lpwhr->read_pos = lpwhr->read_size = 0;
3459 lpwhr->read_chunked = FALSE;
3462 else
3463 TRACE("Redirect through proxy\n");
3466 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3467 lpwhr->lpszPath=NULL;
3468 if (*path)
3470 DWORD needed = 0;
3471 HRESULT rc;
3473 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3474 if (rc != E_POINTER)
3475 needed = strlenW(path)+1;
3476 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3477 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3478 URL_ESCAPE_SPACES_ONLY);
3479 if (rc != S_OK)
3481 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3482 strcpyW(lpwhr->lpszPath,path);
3486 /* Remove custom content-type/length headers on redirects. */
3487 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3488 if (0 <= index)
3489 HTTP_DeleteCustomHeader(lpwhr, index);
3490 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3491 if (0 <= index)
3492 HTTP_DeleteCustomHeader(lpwhr, index);
3494 return ERROR_SUCCESS;
3497 /***********************************************************************
3498 * HTTP_build_req (internal)
3500 * concatenate all the strings in the request together
3502 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3504 LPCWSTR *t;
3505 LPWSTR str;
3507 for( t = list; *t ; t++ )
3508 len += strlenW( *t );
3509 len++;
3511 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3512 *str = 0;
3514 for( t = list; *t ; t++ )
3515 strcatW( str, *t );
3517 return str;
3520 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3522 LPWSTR lpszPath;
3523 LPWSTR requestString;
3524 INT len;
3525 INT cnt;
3526 INT responseLen;
3527 char *ascii_req;
3528 DWORD res;
3529 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3530 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3531 http_session_t *lpwhs = lpwhr->lpHttpSession;
3533 TRACE("\n");
3535 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3536 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3537 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3538 HeapFree( GetProcessHeap(), 0, lpszPath );
3540 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3541 NULL, 0, NULL, NULL );
3542 len--; /* the nul terminator isn't needed */
3543 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3544 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3545 ascii_req, len, NULL, NULL );
3546 HeapFree( GetProcessHeap(), 0, requestString );
3548 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3550 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3551 HeapFree( GetProcessHeap(), 0, ascii_req );
3552 if (res != ERROR_SUCCESS)
3553 return res;
3555 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3556 if (!responseLen)
3557 return ERROR_HTTP_INVALID_HEADER;
3559 return ERROR_SUCCESS;
3562 static void HTTP_InsertCookies(http_request_t *lpwhr)
3564 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3565 LPWSTR lpszCookies, lpszUrl = NULL;
3566 DWORD nCookieSize, size;
3567 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3569 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3570 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3571 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3573 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3575 int cnt = 0;
3576 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3578 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3579 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3581 cnt += sprintfW(lpszCookies, szCookie);
3582 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3583 strcatW(lpszCookies, szCrLf);
3585 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3586 HeapFree(GetProcessHeap(), 0, lpszCookies);
3589 HeapFree(GetProcessHeap(), 0, lpszUrl);
3592 /***********************************************************************
3593 * HTTP_HttpSendRequestW (internal)
3595 * Sends the specified request to the HTTP server
3597 * RETURNS
3598 * TRUE on success
3599 * FALSE on failure
3602 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3603 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3604 DWORD dwContentLength, BOOL bEndRequest)
3606 INT cnt;
3607 BOOL redirected = FALSE;
3608 LPWSTR requestString = NULL;
3609 INT responseLen;
3610 BOOL loop_next;
3611 INTERNET_ASYNC_RESULT iar;
3612 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3613 static const WCHAR szContentLength[] =
3614 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3615 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3616 DWORD res;
3618 TRACE("--> %p\n", lpwhr);
3620 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3622 /* if the verb is NULL default to GET */
3623 if (!lpwhr->lpszVerb)
3624 lpwhr->lpszVerb = heap_strdupW(szGET);
3626 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3628 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3629 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3630 lpwhr->dwBytesToWrite = dwContentLength;
3632 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3634 WCHAR *agent_header;
3635 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3636 int len;
3638 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3639 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3640 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3642 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3643 HeapFree(GetProcessHeap(), 0, agent_header);
3645 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3647 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3648 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3650 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3652 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3653 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3654 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3659 DWORD len;
3660 BOOL reusing_connection;
3661 char *ascii_req;
3663 loop_next = FALSE;
3665 /* like native, just in case the caller forgot to call InternetReadFile
3666 * for all the data */
3667 HTTP_DrainContent(lpwhr);
3668 lpwhr->dwContentRead = 0;
3669 if(redirected) {
3670 lpwhr->dwContentLength = ~0u;
3671 lpwhr->dwBytesToWrite = 0;
3674 if (TRACE_ON(wininet))
3676 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3677 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3680 HTTP_FixURL(lpwhr);
3681 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3683 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3685 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3686 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3688 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3689 HTTP_InsertCookies(lpwhr);
3691 /* add the headers the caller supplied */
3692 if( lpszHeaders && dwHeaderLength )
3694 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3695 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3698 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3700 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3701 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3702 HeapFree(GetProcessHeap(), 0, url);
3704 else
3705 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3708 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3710 /* Send the request and store the results */
3711 if(NETCON_connected(&lpwhr->netConnection))
3712 reusing_connection = TRUE;
3713 else
3714 reusing_connection = FALSE;
3716 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3717 goto lend;
3719 /* send the request as ASCII, tack on the optional data */
3720 if (!lpOptional || redirected)
3721 dwOptionalLength = 0;
3722 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3723 NULL, 0, NULL, NULL );
3724 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3725 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3726 ascii_req, len, NULL, NULL );
3727 if( lpOptional )
3728 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3729 len = (len + dwOptionalLength - 1);
3730 ascii_req[len] = 0;
3731 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3733 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3734 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3736 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3737 HeapFree( GetProcessHeap(), 0, ascii_req );
3739 lpwhr->dwBytesWritten = dwOptionalLength;
3741 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3742 INTERNET_STATUS_REQUEST_SENT,
3743 &len, sizeof(DWORD));
3745 if (bEndRequest)
3747 DWORD dwBufferSize;
3748 DWORD dwStatusCode;
3750 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3751 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3753 if (res != ERROR_SUCCESS)
3754 goto lend;
3756 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3757 /* FIXME: We should know that connection is closed before sending
3758 * headers. Otherwise wrong callbacks are executed */
3759 if(!responseLen && reusing_connection) {
3760 TRACE("Connection closed by server, reconnecting\n");
3761 loop_next = TRUE;
3762 continue;
3765 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3766 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3767 sizeof(DWORD));
3769 HTTP_ProcessCookies(lpwhr);
3771 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3773 dwBufferSize = sizeof(dwStatusCode);
3774 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3775 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3776 dwStatusCode = 0;
3778 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3780 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3781 dwBufferSize=sizeof(szNewLocation);
3782 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3783 dwStatusCode == HTTP_STATUS_MOVED ||
3784 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3785 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3787 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3789 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3790 lpwhr->lpszVerb = heap_strdupW(szGET);
3792 HTTP_DrainContent(lpwhr);
3793 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3795 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3796 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3797 res = HTTP_HandleRedirect(lpwhr, new_url);
3798 if (res == ERROR_SUCCESS)
3800 HeapFree(GetProcessHeap(), 0, requestString);
3801 loop_next = TRUE;
3803 HeapFree( GetProcessHeap(), 0, new_url );
3805 redirected = TRUE;
3808 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3810 WCHAR szAuthValue[2048];
3811 dwBufferSize=2048;
3812 if (dwStatusCode == HTTP_STATUS_DENIED)
3814 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3815 DWORD dwIndex = 0;
3816 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3818 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3819 &lpwhr->pAuthInfo,
3820 lpwhr->lpHttpSession->lpszUserName,
3821 lpwhr->lpHttpSession->lpszPassword,
3822 Host->lpszValue))
3824 HeapFree(GetProcessHeap(), 0, requestString);
3825 loop_next = TRUE;
3826 break;
3830 if(!loop_next) {
3831 TRACE("Cleaning wrong authorization data\n");
3832 destroy_authinfo(lpwhr->pAuthInfo);
3833 lpwhr->pAuthInfo = NULL;
3836 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3838 DWORD dwIndex = 0;
3839 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3841 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3842 &lpwhr->pProxyAuthInfo,
3843 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3844 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3845 NULL))
3847 loop_next = TRUE;
3848 break;
3852 if(!loop_next) {
3853 TRACE("Cleaning wrong proxy authorization data\n");
3854 destroy_authinfo(lpwhr->pProxyAuthInfo);
3855 lpwhr->pProxyAuthInfo = NULL;
3860 else
3861 res = ERROR_SUCCESS;
3863 while (loop_next);
3865 if(res == ERROR_SUCCESS) {
3866 WCHAR url[INTERNET_MAX_URL_LENGTH];
3867 WCHAR cacheFileName[MAX_PATH+1];
3868 BOOL b;
3870 b = HTTP_GetRequestURL(lpwhr, url);
3871 if(!b) {
3872 WARN("Could not get URL\n");
3873 goto lend;
3876 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3877 if(b) {
3878 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3879 CloseHandle(lpwhr->hCacheFile);
3881 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3882 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3883 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3884 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3885 WARN("Could not create file: %u\n", GetLastError());
3886 lpwhr->hCacheFile = NULL;
3888 }else {
3889 WARN("Could not create cache entry: %08x\n", GetLastError());
3893 lend:
3895 HeapFree(GetProcessHeap(), 0, requestString);
3897 /* TODO: send notification for P3P header */
3899 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3901 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3902 HTTP_ReceiveRequestData(lpwhr, TRUE);
3903 else
3905 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3906 iar.dwError = res;
3908 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3909 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3910 sizeof(INTERNET_ASYNC_RESULT));
3914 TRACE("<--\n");
3915 return res;
3918 /***********************************************************************
3920 * Helper functions for the HttpSendRequest(Ex) functions
3923 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3925 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3926 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3928 TRACE("%p\n", lpwhr);
3930 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3931 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3932 req->dwContentLength, req->bEndRequest);
3934 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3938 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3940 INT responseLen;
3941 DWORD dwBufferSize;
3942 INTERNET_ASYNC_RESULT iar;
3943 DWORD res = ERROR_SUCCESS;
3945 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3946 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3948 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3949 if (!responseLen)
3950 res = ERROR_HTTP_HEADER_NOT_FOUND;
3952 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3953 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3955 /* process cookies here. Is this right? */
3956 HTTP_ProcessCookies(lpwhr);
3958 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3960 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3962 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3963 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3964 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3966 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3967 dwBufferSize=sizeof(szNewLocation);
3968 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3970 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3972 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3973 lpwhr->lpszVerb = heap_strdupW(szGET);
3975 HTTP_DrainContent(lpwhr);
3976 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3978 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3979 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3980 res = HTTP_HandleRedirect(lpwhr, new_url);
3981 if (res == ERROR_SUCCESS)
3982 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3983 HeapFree( GetProcessHeap(), 0, new_url );
3989 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3990 iar.dwError = res;
3992 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3993 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3994 sizeof(INTERNET_ASYNC_RESULT));
3995 return res;
3998 /***********************************************************************
3999 * HttpEndRequestA (WININET.@)
4001 * Ends an HTTP request that was started by HttpSendRequestEx
4003 * RETURNS
4004 * TRUE if successful
4005 * FALSE on failure
4008 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4009 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4011 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4013 if (lpBuffersOut)
4015 SetLastError(ERROR_INVALID_PARAMETER);
4016 return FALSE;
4019 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4022 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4024 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4025 http_request_t *lpwhr = (http_request_t*)work->hdr;
4027 TRACE("%p\n", lpwhr);
4029 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4032 /***********************************************************************
4033 * HttpEndRequestW (WININET.@)
4035 * Ends an HTTP request that was started by HttpSendRequestEx
4037 * RETURNS
4038 * TRUE if successful
4039 * FALSE on failure
4042 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4043 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4045 http_request_t *lpwhr;
4046 DWORD res;
4048 TRACE("-->\n");
4050 if (lpBuffersOut)
4052 SetLastError(ERROR_INVALID_PARAMETER);
4053 return FALSE;
4056 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4058 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4060 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4061 if (lpwhr)
4062 WININET_Release( &lpwhr->hdr );
4063 return FALSE;
4065 lpwhr->hdr.dwFlags |= dwFlags;
4067 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4069 WORKREQUEST work;
4070 struct WORKREQ_HTTPENDREQUESTW *request;
4072 work.asyncproc = AsyncHttpEndRequestProc;
4073 work.hdr = WININET_AddRef( &lpwhr->hdr );
4075 request = &work.u.HttpEndRequestW;
4076 request->dwFlags = dwFlags;
4077 request->dwContext = dwContext;
4079 INTERNET_AsyncCall(&work);
4080 res = ERROR_IO_PENDING;
4082 else
4083 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4085 WININET_Release( &lpwhr->hdr );
4086 TRACE("%u <--\n", res);
4087 if(res != ERROR_SUCCESS)
4088 SetLastError(res);
4089 return res == ERROR_SUCCESS;
4092 /***********************************************************************
4093 * HttpSendRequestExA (WININET.@)
4095 * Sends the specified request to the HTTP server and allows chunked
4096 * transfers.
4098 * RETURNS
4099 * Success: TRUE
4100 * Failure: FALSE, call GetLastError() for more information.
4102 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4103 LPINTERNET_BUFFERSA lpBuffersIn,
4104 LPINTERNET_BUFFERSA lpBuffersOut,
4105 DWORD dwFlags, DWORD_PTR dwContext)
4107 INTERNET_BUFFERSW BuffersInW;
4108 BOOL rc = FALSE;
4109 DWORD headerlen;
4110 LPWSTR header = NULL;
4112 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4113 lpBuffersOut, dwFlags, dwContext);
4115 if (lpBuffersIn)
4117 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4118 if (lpBuffersIn->lpcszHeader)
4120 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4121 lpBuffersIn->dwHeadersLength,0,0);
4122 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4123 if (!(BuffersInW.lpcszHeader = header))
4125 SetLastError(ERROR_OUTOFMEMORY);
4126 return FALSE;
4128 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4129 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4130 header, headerlen);
4132 else
4133 BuffersInW.lpcszHeader = NULL;
4134 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4135 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4136 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4137 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4138 BuffersInW.Next = NULL;
4141 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4143 HeapFree(GetProcessHeap(),0,header);
4145 return rc;
4148 /***********************************************************************
4149 * HttpSendRequestExW (WININET.@)
4151 * Sends the specified request to the HTTP server and allows chunked
4152 * transfers
4154 * RETURNS
4155 * Success: TRUE
4156 * Failure: FALSE, call GetLastError() for more information.
4158 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4159 LPINTERNET_BUFFERSW lpBuffersIn,
4160 LPINTERNET_BUFFERSW lpBuffersOut,
4161 DWORD dwFlags, DWORD_PTR dwContext)
4163 http_request_t *lpwhr;
4164 http_session_t *lpwhs;
4165 appinfo_t *hIC;
4166 DWORD res;
4168 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4169 lpBuffersOut, dwFlags, dwContext);
4171 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4173 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4175 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4176 goto lend;
4179 lpwhs = lpwhr->lpHttpSession;
4180 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4181 hIC = lpwhs->lpAppInfo;
4182 assert(hIC->hdr.htype == WH_HINIT);
4184 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4186 WORKREQUEST workRequest;
4187 struct WORKREQ_HTTPSENDREQUESTW *req;
4189 workRequest.asyncproc = AsyncHttpSendRequestProc;
4190 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4191 req = &workRequest.u.HttpSendRequestW;
4192 if (lpBuffersIn)
4194 DWORD size = 0;
4196 if (lpBuffersIn->lpcszHeader)
4198 if (lpBuffersIn->dwHeadersLength == ~0u)
4199 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4200 else
4201 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4203 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4204 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4206 else req->lpszHeader = NULL;
4208 req->dwHeaderLength = size / sizeof(WCHAR);
4209 req->lpOptional = lpBuffersIn->lpvBuffer;
4210 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4211 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4213 else
4215 req->lpszHeader = NULL;
4216 req->dwHeaderLength = 0;
4217 req->lpOptional = NULL;
4218 req->dwOptionalLength = 0;
4219 req->dwContentLength = 0;
4222 req->bEndRequest = FALSE;
4224 INTERNET_AsyncCall(&workRequest);
4226 * This is from windows.
4228 res = ERROR_IO_PENDING;
4230 else
4232 if (lpBuffersIn)
4233 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4234 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4235 lpBuffersIn->dwBufferTotal, FALSE);
4236 else
4237 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4240 lend:
4241 if ( lpwhr )
4242 WININET_Release( &lpwhr->hdr );
4244 TRACE("<---\n");
4245 SetLastError(res);
4246 return res == ERROR_SUCCESS;
4249 /***********************************************************************
4250 * HttpSendRequestW (WININET.@)
4252 * Sends the specified request to the HTTP server
4254 * RETURNS
4255 * TRUE on success
4256 * FALSE on failure
4259 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4260 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4262 http_request_t *lpwhr;
4263 http_session_t *lpwhs = NULL;
4264 appinfo_t *hIC = NULL;
4265 DWORD res = ERROR_SUCCESS;
4267 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4268 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4270 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4271 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4273 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4274 goto lend;
4277 lpwhs = lpwhr->lpHttpSession;
4278 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4280 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4281 goto lend;
4284 hIC = lpwhs->lpAppInfo;
4285 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4287 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4288 goto lend;
4291 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4293 WORKREQUEST workRequest;
4294 struct WORKREQ_HTTPSENDREQUESTW *req;
4296 workRequest.asyncproc = AsyncHttpSendRequestProc;
4297 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4298 req = &workRequest.u.HttpSendRequestW;
4299 if (lpszHeaders)
4301 DWORD size;
4303 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4304 else size = dwHeaderLength * sizeof(WCHAR);
4306 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4307 memcpy(req->lpszHeader, lpszHeaders, size);
4309 else
4310 req->lpszHeader = 0;
4311 req->dwHeaderLength = dwHeaderLength;
4312 req->lpOptional = lpOptional;
4313 req->dwOptionalLength = dwOptionalLength;
4314 req->dwContentLength = dwOptionalLength;
4315 req->bEndRequest = TRUE;
4317 INTERNET_AsyncCall(&workRequest);
4319 * This is from windows.
4321 res = ERROR_IO_PENDING;
4323 else
4325 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4326 dwHeaderLength, lpOptional, dwOptionalLength,
4327 dwOptionalLength, TRUE);
4329 lend:
4330 if( lpwhr )
4331 WININET_Release( &lpwhr->hdr );
4333 SetLastError(res);
4334 return res == ERROR_SUCCESS;
4337 /***********************************************************************
4338 * HttpSendRequestA (WININET.@)
4340 * Sends the specified request to the HTTP server
4342 * RETURNS
4343 * TRUE on success
4344 * FALSE on failure
4347 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4348 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4350 BOOL result;
4351 LPWSTR szHeaders=NULL;
4352 DWORD nLen=dwHeaderLength;
4353 if(lpszHeaders!=NULL)
4355 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4356 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4357 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4359 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4360 HeapFree(GetProcessHeap(),0,szHeaders);
4361 return result;
4364 /***********************************************************************
4365 * HTTPSESSION_Destroy (internal)
4367 * Deallocate session handle
4370 static void HTTPSESSION_Destroy(object_header_t *hdr)
4372 http_session_t *lpwhs = (http_session_t*) hdr;
4374 TRACE("%p\n", lpwhs);
4376 WININET_Release(&lpwhs->lpAppInfo->hdr);
4378 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4379 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4380 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4381 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4382 HeapFree(GetProcessHeap(), 0, lpwhs);
4385 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4387 switch(option) {
4388 case INTERNET_OPTION_HANDLE_TYPE:
4389 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4391 if (*size < sizeof(ULONG))
4392 return ERROR_INSUFFICIENT_BUFFER;
4394 *size = sizeof(DWORD);
4395 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4396 return ERROR_SUCCESS;
4399 return INET_QueryOption(hdr, option, buffer, size, unicode);
4402 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4404 http_session_t *ses = (http_session_t*)hdr;
4406 switch(option) {
4407 case INTERNET_OPTION_USERNAME:
4409 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4410 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4411 return ERROR_SUCCESS;
4413 case INTERNET_OPTION_PASSWORD:
4415 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4416 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4417 return ERROR_SUCCESS;
4419 default: break;
4422 return ERROR_INTERNET_INVALID_OPTION;
4425 static const object_vtbl_t HTTPSESSIONVtbl = {
4426 HTTPSESSION_Destroy,
4427 NULL,
4428 HTTPSESSION_QueryOption,
4429 HTTPSESSION_SetOption,
4430 NULL,
4431 NULL,
4432 NULL,
4433 NULL,
4434 NULL
4438 /***********************************************************************
4439 * HTTP_Connect (internal)
4441 * Create http session handle
4443 * RETURNS
4444 * HINTERNET a session handle on success
4445 * NULL on failure
4448 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4449 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4450 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4451 DWORD dwInternalFlags, HINTERNET *ret)
4453 http_session_t *lpwhs = NULL;
4454 HINTERNET handle = NULL;
4455 DWORD res = ERROR_SUCCESS;
4457 TRACE("-->\n");
4459 if (!lpszServerName || !lpszServerName[0])
4460 return ERROR_INVALID_PARAMETER;
4462 assert( hIC->hdr.htype == WH_HINIT );
4464 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4465 if (!lpwhs)
4466 return ERROR_OUTOFMEMORY;
4469 * According to my tests. The name is not resolved until a request is sent
4472 lpwhs->hdr.htype = WH_HHTTPSESSION;
4473 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4474 lpwhs->hdr.dwFlags = dwFlags;
4475 lpwhs->hdr.dwContext = dwContext;
4476 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4477 lpwhs->hdr.refs = 1;
4478 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4480 WININET_AddRef( &hIC->hdr );
4481 lpwhs->lpAppInfo = hIC;
4482 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4484 handle = WININET_AllocHandle( &lpwhs->hdr );
4485 if (NULL == handle)
4487 ERR("Failed to alloc handle\n");
4488 res = ERROR_OUTOFMEMORY;
4489 goto lerror;
4492 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4493 if(hIC->lpszProxyBypass)
4494 FIXME("Proxy bypass is ignored.\n");
4496 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4497 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4498 if (lpszUserName && lpszUserName[0])
4499 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4500 if (lpszPassword && lpszPassword[0])
4501 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4502 lpwhs->nServerPort = nServerPort;
4503 lpwhs->nHostPort = nServerPort;
4505 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4506 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4508 INTERNET_SendCallback(&hIC->hdr, dwContext,
4509 INTERNET_STATUS_HANDLE_CREATED, &handle,
4510 sizeof(handle));
4513 lerror:
4514 if( lpwhs )
4515 WININET_Release( &lpwhs->hdr );
4518 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4519 * windows
4522 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4524 if(res == ERROR_SUCCESS)
4525 *ret = handle;
4526 return res;
4530 /***********************************************************************
4531 * HTTP_OpenConnection (internal)
4533 * Connect to a web server
4535 * RETURNS
4537 * TRUE on success
4538 * FALSE on failure
4540 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4542 http_session_t *lpwhs;
4543 appinfo_t *hIC = NULL;
4544 char szaddr[INET6_ADDRSTRLEN];
4545 const void *addr;
4546 DWORD res = ERROR_SUCCESS;
4548 TRACE("-->\n");
4551 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4553 res = ERROR_INVALID_PARAMETER;
4554 goto lend;
4557 if (NETCON_connected(&lpwhr->netConnection))
4558 goto lend;
4559 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4561 lpwhs = lpwhr->lpHttpSession;
4563 hIC = lpwhs->lpAppInfo;
4564 switch (lpwhs->socketAddress.ss_family)
4566 case AF_INET:
4567 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4568 break;
4569 case AF_INET6:
4570 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4571 break;
4572 default:
4573 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4574 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4576 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4577 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4578 INTERNET_STATUS_CONNECTING_TO_SERVER,
4579 szaddr,
4580 strlen(szaddr)+1);
4582 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4583 if (res != ERROR_SUCCESS)
4585 WARN("Socket creation failed: %u\n", res);
4586 goto lend;
4589 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4590 lpwhs->sa_len);
4591 if(res != ERROR_SUCCESS)
4592 goto lend;
4594 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4595 INTERNET_STATUS_CONNECTED_TO_SERVER,
4596 szaddr, strlen(szaddr)+1);
4598 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4600 /* Note: we differ from Microsoft's WinINet here. they seem to have
4601 * a bug that causes no status callbacks to be sent when starting
4602 * a tunnel to a proxy server using the CONNECT verb. i believe our
4603 * behaviour to be more correct and to not cause any incompatibilities
4604 * because using a secure connection through a proxy server is a rare
4605 * case that would be hard for anyone to depend on */
4606 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4607 HTTPREQ_CloseConnection(&lpwhr->hdr);
4608 goto lend;
4611 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4612 if(res != ERROR_SUCCESS)
4614 WARN("Couldn't connect securely to host\n");
4616 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4617 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4618 || res == ERROR_INTERNET_INVALID_CA
4619 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4620 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4621 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4622 || res == ERROR_INTERNET_SEC_INVALID_CERT
4623 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4624 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4626 HTTPREQ_CloseConnection(&lpwhr->hdr);
4627 goto lend;
4632 lend:
4633 lpwhr->read_pos = lpwhr->read_size = 0;
4634 lpwhr->read_chunked = FALSE;
4636 TRACE("%d <--\n", res);
4637 return res;
4641 /***********************************************************************
4642 * HTTP_clear_response_headers (internal)
4644 * clear out any old response headers
4646 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4648 DWORD i;
4650 for( i=0; i<lpwhr->nCustHeaders; i++)
4652 if( !lpwhr->pCustHeaders[i].lpszField )
4653 continue;
4654 if( !lpwhr->pCustHeaders[i].lpszValue )
4655 continue;
4656 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4657 continue;
4658 HTTP_DeleteCustomHeader( lpwhr, i );
4659 i--;
4663 /***********************************************************************
4664 * HTTP_GetResponseHeaders (internal)
4666 * Read server response
4668 * RETURNS
4670 * TRUE on success
4671 * FALSE on error
4673 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4675 INT cbreaks = 0;
4676 WCHAR buffer[MAX_REPLY_LEN];
4677 DWORD buflen = MAX_REPLY_LEN;
4678 BOOL bSuccess = FALSE;
4679 INT rc = 0;
4680 char bufferA[MAX_REPLY_LEN];
4681 LPWSTR status_code = NULL, status_text = NULL;
4682 DWORD cchMaxRawHeaders = 1024;
4683 LPWSTR lpszRawHeaders = NULL;
4684 LPWSTR temp;
4685 DWORD cchRawHeaders = 0;
4686 BOOL codeHundred = FALSE;
4688 TRACE("-->\n");
4690 if (!NETCON_connected(&lpwhr->netConnection))
4691 goto lend;
4693 do {
4694 static const WCHAR szHundred[] = {'1','0','0',0};
4696 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4698 buflen = MAX_REPLY_LEN;
4699 if (!read_line(lpwhr, bufferA, &buflen))
4700 goto lend;
4702 /* clear old response headers (eg. from a redirect response) */
4703 if (clear) {
4704 HTTP_clear_response_headers( lpwhr );
4705 clear = FALSE;
4708 rc += buflen;
4709 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4710 /* check is this a status code line? */
4711 if (!strncmpW(buffer, g_szHttp1_0, 4))
4713 /* split the version from the status code */
4714 status_code = strchrW( buffer, ' ' );
4715 if( !status_code )
4716 goto lend;
4717 *status_code++=0;
4719 /* split the status code from the status text */
4720 status_text = strchrW( status_code, ' ' );
4721 if( !status_text )
4722 goto lend;
4723 *status_text++=0;
4725 TRACE("version [%s] status code [%s] status text [%s]\n",
4726 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4728 codeHundred = (!strcmpW(status_code, szHundred));
4730 else if (!codeHundred)
4732 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4734 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4735 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4737 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4738 lpwhr->lpszStatusText = heap_strdupW(szOK);
4740 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4741 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4743 bSuccess = TRUE;
4744 goto lend;
4746 } while (codeHundred);
4748 /* Add status code */
4749 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4750 HTTP_ADDHDR_FLAG_REPLACE);
4752 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4753 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4755 lpwhr->lpszVersion = heap_strdupW(buffer);
4756 lpwhr->lpszStatusText = heap_strdupW(status_text);
4758 /* Restore the spaces */
4759 *(status_code-1) = ' ';
4760 *(status_text-1) = ' ';
4762 /* regenerate raw headers */
4763 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4764 if (!lpszRawHeaders) goto lend;
4766 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4767 cchMaxRawHeaders *= 2;
4768 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4769 if (temp == NULL) goto lend;
4770 lpszRawHeaders = temp;
4771 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4772 cchRawHeaders += (buflen-1);
4773 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4774 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4775 lpszRawHeaders[cchRawHeaders] = '\0';
4777 /* Parse each response line */
4780 buflen = MAX_REPLY_LEN;
4781 if (read_line(lpwhr, bufferA, &buflen))
4783 LPWSTR * pFieldAndValue;
4785 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4787 if (!bufferA[0]) break;
4788 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4790 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4791 if (pFieldAndValue)
4793 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4794 cchMaxRawHeaders *= 2;
4795 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4796 if (temp == NULL) goto lend;
4797 lpszRawHeaders = temp;
4798 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4799 cchRawHeaders += (buflen-1);
4800 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4801 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4802 lpszRawHeaders[cchRawHeaders] = '\0';
4804 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4805 HTTP_ADDREQ_FLAG_ADD );
4807 HTTP_FreeTokens(pFieldAndValue);
4810 else
4812 cbreaks++;
4813 if (cbreaks >= 2)
4814 break;
4816 }while(1);
4818 /* make sure the response header is terminated with an empty line. Some apps really
4819 truly care about that empty line being there for some reason. Just add it to the
4820 header. */
4821 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4823 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4824 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4825 if (temp == NULL) goto lend;
4826 lpszRawHeaders = temp;
4829 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4831 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4832 lpwhr->lpszRawHeaders = lpszRawHeaders;
4833 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4834 bSuccess = TRUE;
4836 lend:
4838 TRACE("<--\n");
4839 if (bSuccess)
4840 return rc;
4841 else
4843 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4844 return 0;
4848 /***********************************************************************
4849 * HTTP_InterpretHttpHeader (internal)
4851 * Parse server response
4853 * RETURNS
4855 * Pointer to array of field, value, NULL on success.
4856 * NULL on error.
4858 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4860 LPWSTR * pTokenPair;
4861 LPWSTR pszColon;
4862 INT len;
4864 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4866 pszColon = strchrW(buffer, ':');
4867 /* must have two tokens */
4868 if (!pszColon)
4870 HTTP_FreeTokens(pTokenPair);
4871 if (buffer[0])
4872 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4873 return NULL;
4876 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4877 if (!pTokenPair[0])
4879 HTTP_FreeTokens(pTokenPair);
4880 return NULL;
4882 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4883 pTokenPair[0][pszColon - buffer] = '\0';
4885 /* skip colon */
4886 pszColon++;
4887 len = strlenW(pszColon);
4888 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4889 if (!pTokenPair[1])
4891 HTTP_FreeTokens(pTokenPair);
4892 return NULL;
4894 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4896 strip_spaces(pTokenPair[0]);
4897 strip_spaces(pTokenPair[1]);
4899 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4900 return pTokenPair;
4903 /***********************************************************************
4904 * HTTP_ProcessHeader (internal)
4906 * Stuff header into header tables according to <dwModifier>
4910 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4912 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4914 LPHTTPHEADERW lphttpHdr = NULL;
4915 INT index = -1;
4916 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4917 DWORD res = ERROR_HTTP_INVALID_HEADER;
4919 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4921 /* REPLACE wins out over ADD */
4922 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4923 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4925 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4926 index = -1;
4927 else
4928 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4930 if (index >= 0)
4932 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4933 return ERROR_HTTP_INVALID_HEADER;
4934 lphttpHdr = &lpwhr->pCustHeaders[index];
4936 else if (value)
4938 HTTPHEADERW hdr;
4940 hdr.lpszField = (LPWSTR)field;
4941 hdr.lpszValue = (LPWSTR)value;
4942 hdr.wFlags = hdr.wCount = 0;
4944 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4945 hdr.wFlags |= HDR_ISREQUEST;
4947 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4949 /* no value to delete */
4950 else return ERROR_SUCCESS;
4952 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4953 lphttpHdr->wFlags |= HDR_ISREQUEST;
4954 else
4955 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4957 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4959 HTTP_DeleteCustomHeader( lpwhr, index );
4961 if (value)
4963 HTTPHEADERW hdr;
4965 hdr.lpszField = (LPWSTR)field;
4966 hdr.lpszValue = (LPWSTR)value;
4967 hdr.wFlags = hdr.wCount = 0;
4969 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4970 hdr.wFlags |= HDR_ISREQUEST;
4972 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4975 return ERROR_SUCCESS;
4977 else if (dwModifier & COALESCEFLAGS)
4979 LPWSTR lpsztmp;
4980 WCHAR ch = 0;
4981 INT len = 0;
4982 INT origlen = strlenW(lphttpHdr->lpszValue);
4983 INT valuelen = strlenW(value);
4985 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4987 ch = ',';
4988 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4990 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4992 ch = ';';
4993 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4996 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4998 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4999 if (lpsztmp)
5001 lphttpHdr->lpszValue = lpsztmp;
5002 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5003 if (ch > 0)
5005 lphttpHdr->lpszValue[origlen] = ch;
5006 origlen++;
5007 lphttpHdr->lpszValue[origlen] = ' ';
5008 origlen++;
5011 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5012 lphttpHdr->lpszValue[len] = '\0';
5013 res = ERROR_SUCCESS;
5015 else
5017 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5018 res = ERROR_OUTOFMEMORY;
5021 TRACE("<-- %d\n", res);
5022 return res;
5026 /***********************************************************************
5027 * HTTP_FinishedReading (internal)
5029 * Called when all content from server has been read by client.
5032 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5034 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5036 TRACE("\n");
5039 if (!keepalive)
5041 HTTPREQ_CloseConnection(&lpwhr->hdr);
5044 /* FIXME: store data in the URL cache here */
5046 return TRUE;
5050 /***********************************************************************
5051 * HTTP_GetCustomHeaderIndex (internal)
5053 * Return index of custom header from header array
5056 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5057 int requested_index, BOOL request_only)
5059 DWORD index;
5061 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5063 for (index = 0; index < lpwhr->nCustHeaders; index++)
5065 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5066 continue;
5068 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5069 continue;
5071 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5072 continue;
5074 if (requested_index == 0)
5075 break;
5076 requested_index --;
5079 if (index >= lpwhr->nCustHeaders)
5080 index = -1;
5082 TRACE("Return: %d\n", index);
5083 return index;
5087 /***********************************************************************
5088 * HTTP_InsertCustomHeader (internal)
5090 * Insert header into array
5093 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5095 INT count;
5096 LPHTTPHEADERW lph = NULL;
5098 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5099 count = lpwhr->nCustHeaders + 1;
5100 if (count > 1)
5101 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5102 else
5103 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5105 if (!lph)
5106 return ERROR_OUTOFMEMORY;
5108 lpwhr->pCustHeaders = lph;
5109 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5110 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5111 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5112 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5113 lpwhr->nCustHeaders++;
5115 return ERROR_SUCCESS;
5119 /***********************************************************************
5120 * HTTP_DeleteCustomHeader (internal)
5122 * Delete header from array
5123 * If this function is called, the indexs may change.
5125 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5127 if( lpwhr->nCustHeaders <= 0 )
5128 return FALSE;
5129 if( index >= lpwhr->nCustHeaders )
5130 return FALSE;
5131 lpwhr->nCustHeaders--;
5133 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5134 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5136 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5137 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5138 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5140 return TRUE;
5144 /***********************************************************************
5145 * HTTP_VerifyValidHeader (internal)
5147 * Verify the given header is not invalid for the given http request
5150 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5152 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5153 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5154 return ERROR_HTTP_INVALID_HEADER;
5156 return ERROR_SUCCESS;
5159 /***********************************************************************
5160 * IsHostInProxyBypassList (@)
5162 * Undocumented
5165 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5167 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5168 return FALSE;