fix warning about no newline at end of file
[reactos.git] / reactos / dll / win32 / wininet / http.c
1 /*
2 * Wininet - Http Implementation
3 *
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
10 *
11 * Ulrich Czekalla
12 * David Hammerton
13 *
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.
18 *
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.
23 *
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
27 */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
35
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
54
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"
66
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
71
72 #include "inet_ntop.c"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
75
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',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};
86
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 };
139
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
143
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
147
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
155
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
157
158 struct HttpAuthInfo
159 {
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 */
169 };
170
171
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;
180 };
181
182 typedef struct _authorizationData
183 {
184 struct list entry;
185
186 LPWSTR lpszwHost;
187 LPWSTR lpszwRealm;
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
190 } authorizationData;
191
192 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
193
194 static CRITICAL_SECTION authcache_cs;
195 static CRITICAL_SECTION_DEBUG critsect_debug =
196 {
197 0, 0, &authcache_cs,
198 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
199 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
200 };
201 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
202
203 static BOOL HTTP_OpenConnection(http_request_t *req);
204 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
205 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
206 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
207 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
208 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
209 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
210 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
211 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
212 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
213 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
214 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
215 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
216 static void HTTP_DrainContent(http_request_t *req);
217 static BOOL HTTP_FinishedReading(http_request_t *req);
218
219 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
220 {
221 int HeaderIndex = 0;
222 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
223 if (HeaderIndex == -1)
224 return NULL;
225 else
226 return &req->pCustHeaders[HeaderIndex];
227 }
228
229 #ifdef HAVE_ZLIB
230
231 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
232 {
233 return HeapAlloc(GetProcessHeap(), 0, items*size);
234 }
235
236 static void wininet_zfree(voidpf opaque, voidpf address)
237 {
238 HeapFree(GetProcessHeap(), 0, address);
239 }
240
241 static void init_gzip_stream(http_request_t *req)
242 {
243 gzip_stream_t *gzip_stream;
244 int index, zres;
245
246 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
247 gzip_stream->zstream.zalloc = wininet_zalloc;
248 gzip_stream->zstream.zfree = wininet_zfree;
249 gzip_stream->zstream.opaque = NULL;
250 gzip_stream->zstream.next_in = NULL;
251 gzip_stream->zstream.avail_in = 0;
252 gzip_stream->zstream.next_out = NULL;
253 gzip_stream->zstream.avail_out = 0;
254 gzip_stream->buf_pos = 0;
255 gzip_stream->buf_size = 0;
256 gzip_stream->end_of_data = FALSE;
257
258 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
259 if(zres != Z_OK) {
260 ERR("inflateInit failed: %d\n", zres);
261 HeapFree(GetProcessHeap(), 0, gzip_stream);
262 return;
263 }
264
265 req->gzip_stream = gzip_stream;
266
267 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
268 if(index != -1)
269 HTTP_DeleteCustomHeader(req, index);
270 }
271
272 #else
273
274 static void init_gzip_stream(http_request_t *req)
275 {
276 ERR("gzip stream not supported, missing zlib.\n");
277 }
278
279 #endif
280
281 /* set the request content length based on the headers */
282 static DWORD set_content_length( http_request_t *lpwhr )
283 {
284 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
285 WCHAR encoding[20];
286 DWORD size;
287
288 size = sizeof(lpwhr->dwContentLength);
289 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
290 &lpwhr->dwContentLength, &size, NULL))
291 lpwhr->dwContentLength = ~0u;
292
293 size = sizeof(encoding);
294 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
295 !strcmpiW(encoding, szChunked))
296 {
297 lpwhr->dwContentLength = ~0u;
298 lpwhr->read_chunked = TRUE;
299 }
300
301 if(lpwhr->decoding) {
302 int encoding_idx;
303
304 static const WCHAR gzipW[] = {'g','z','i','p',0};
305
306 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
307 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
308 init_gzip_stream(lpwhr);
309 }
310
311 return lpwhr->dwContentLength;
312 }
313
314 /***********************************************************************
315 * HTTP_Tokenize (internal)
316 *
317 * Tokenize a string, allocating memory for the tokens.
318 */
319 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
320 {
321 LPWSTR * token_array;
322 int tokens = 0;
323 int i;
324 LPCWSTR next_token;
325
326 if (string)
327 {
328 /* empty string has no tokens */
329 if (*string)
330 tokens++;
331 /* count tokens */
332 for (i = 0; string[i]; i++)
333 {
334 if (!strncmpW(string+i, token_string, strlenW(token_string)))
335 {
336 DWORD j;
337 tokens++;
338 /* we want to skip over separators, but not the null terminator */
339 for (j = 0; j < strlenW(token_string) - 1; j++)
340 if (!string[i+j])
341 break;
342 i += j;
343 }
344 }
345 }
346
347 /* add 1 for terminating NULL */
348 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
349 token_array[tokens] = NULL;
350 if (!tokens)
351 return token_array;
352 for (i = 0; i < tokens; i++)
353 {
354 int len;
355 next_token = strstrW(string, token_string);
356 if (!next_token) next_token = string+strlenW(string);
357 len = next_token - string;
358 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
359 memcpy(token_array[i], string, len*sizeof(WCHAR));
360 token_array[i][len] = '\0';
361 string = next_token+strlenW(token_string);
362 }
363 return token_array;
364 }
365
366 /***********************************************************************
367 * HTTP_FreeTokens (internal)
368 *
369 * Frees memory returned from HTTP_Tokenize.
370 */
371 static void HTTP_FreeTokens(LPWSTR * token_array)
372 {
373 int i;
374 for (i = 0; token_array[i]; i++)
375 HeapFree(GetProcessHeap(), 0, token_array[i]);
376 HeapFree(GetProcessHeap(), 0, token_array);
377 }
378
379 /* **********************************************************************
380 *
381 * Helper functions for the HttpSendRequest(Ex) functions
382 *
383 */
384 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
385 {
386 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
387 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
388
389 TRACE("%p\n", lpwhr);
390
391 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
392 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
393 req->dwContentLength, req->bEndRequest);
394
395 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
396 }
397
398 static void HTTP_FixURL(http_request_t *lpwhr)
399 {
400 static const WCHAR szSlash[] = { '/',0 };
401 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
402
403 /* If we don't have a path we set it to root */
404 if (NULL == lpwhr->lpszPath)
405 lpwhr->lpszPath = heap_strdupW(szSlash);
406 else /* remove \r and \n*/
407 {
408 int nLen = strlenW(lpwhr->lpszPath);
409 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
410 {
411 nLen--;
412 lpwhr->lpszPath[nLen]='\0';
413 }
414 /* Replace '\' with '/' */
415 while (nLen>0) {
416 nLen--;
417 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
418 }
419 }
420
421 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
422 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
423 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
424 {
425 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
426 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
427 *fixurl = '/';
428 strcpyW(fixurl + 1, lpwhr->lpszPath);
429 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
430 lpwhr->lpszPath = fixurl;
431 }
432 }
433
434 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
435 {
436 LPWSTR requestString;
437 DWORD len, n;
438 LPCWSTR *req;
439 UINT i;
440 LPWSTR p;
441
442 static const WCHAR szSpace[] = { ' ',0 };
443 static const WCHAR szColon[] = { ':',' ',0 };
444 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
445
446 /* allocate space for an array of all the string pointers to be added */
447 len = (lpwhr->nCustHeaders)*4 + 10;
448 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
449
450 /* add the verb, path and HTTP version string */
451 n = 0;
452 req[n++] = verb;
453 req[n++] = szSpace;
454 req[n++] = path;
455 req[n++] = szSpace;
456 req[n++] = version;
457
458 /* Append custom request headers */
459 for (i = 0; i < lpwhr->nCustHeaders; i++)
460 {
461 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
462 {
463 req[n++] = szCrLf;
464 req[n++] = lpwhr->pCustHeaders[i].lpszField;
465 req[n++] = szColon;
466 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
467
468 TRACE("Adding custom header %s (%s)\n",
469 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
470 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
471 }
472 }
473
474 if( n >= len )
475 ERR("oops. buffer overrun\n");
476
477 req[n] = NULL;
478 requestString = HTTP_build_req( req, 4 );
479 HeapFree( GetProcessHeap(), 0, req );
480
481 /*
482 * Set (header) termination string for request
483 * Make sure there's exactly two new lines at the end of the request
484 */
485 p = &requestString[strlenW(requestString)-1];
486 while ( (*p == '\n') || (*p == '\r') )
487 p--;
488 strcpyW( p+1, sztwocrlf );
489
490 return requestString;
491 }
492
493 static void HTTP_ProcessCookies( http_request_t *lpwhr )
494 {
495 int HeaderIndex;
496 int numCookies = 0;
497 LPHTTPHEADERW setCookieHeader;
498
499 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
500 {
501 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
502
503 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
504 {
505 int len;
506 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
507 LPWSTR buf_url;
508 LPHTTPHEADERW Host;
509
510 Host = HTTP_GetHeader(lpwhr, hostW);
511 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
512 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
513 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
514 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
515
516 HeapFree(GetProcessHeap(), 0, buf_url);
517 }
518 numCookies++;
519 }
520 }
521
522 static void strip_spaces(LPWSTR start)
523 {
524 LPWSTR str = start;
525 LPWSTR end;
526
527 while (*str == ' ' && *str != '\0')
528 str++;
529
530 if (str != start)
531 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
532
533 end = start + strlenW(start) - 1;
534 while (end >= start && *end == ' ')
535 {
536 *end = '\0';
537 end--;
538 }
539 }
540
541 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
542 {
543 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
544 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
545 BOOL is_basic;
546 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
547 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
548 if (is_basic && pszRealm)
549 {
550 LPCWSTR token;
551 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
552 LPCWSTR realm;
553 ptr++;
554 *pszRealm=NULL;
555 token = strchrW(ptr,'=');
556 if (!token)
557 return TRUE;
558 realm = ptr;
559 while (*realm == ' ' && *realm != '\0')
560 realm++;
561 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
562 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
563 {
564 token++;
565 while (*token == ' ' && *token != '\0')
566 token++;
567 if (*token == '\0')
568 return TRUE;
569 *pszRealm = heap_strdupW(token);
570 strip_spaces(*pszRealm);
571 }
572 }
573
574 return is_basic;
575 }
576
577 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
578 {
579 if (!authinfo) return;
580
581 if (SecIsValidHandle(&authinfo->ctx))
582 DeleteSecurityContext(&authinfo->ctx);
583 if (SecIsValidHandle(&authinfo->cred))
584 FreeCredentialsHandle(&authinfo->cred);
585
586 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
587 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
588 HeapFree(GetProcessHeap(), 0, authinfo);
589 }
590
591 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
592 {
593 authorizationData *ad;
594 UINT rc = 0;
595
596 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
597
598 EnterCriticalSection(&authcache_cs);
599 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
600 {
601 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
602 {
603 TRACE("Authorization found in cache\n");
604 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
605 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
606 rc = ad->AuthorizationLen;
607 break;
608 }
609 }
610 LeaveCriticalSection(&authcache_cs);
611 return rc;
612 }
613
614 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
615 {
616 struct list *cursor;
617 authorizationData* ad = NULL;
618
619 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
620
621 EnterCriticalSection(&authcache_cs);
622 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
623 {
624 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
625 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
626 {
627 ad = check;
628 break;
629 }
630 }
631
632 if (ad)
633 {
634 TRACE("Found match in cache, replacing\n");
635 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
636 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
637 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
638 ad->AuthorizationLen = auth_data_len;
639 }
640 else
641 {
642 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
643 ad->lpszwHost = heap_strdupW(host);
644 ad->lpszwRealm = heap_strdupW(realm);
645 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
646 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
647 ad->AuthorizationLen = auth_data_len;
648 list_add_head(&basicAuthorizationCache,&ad->entry);
649 TRACE("authorization cached\n");
650 }
651 LeaveCriticalSection(&authcache_cs);
652 }
653
654 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
655 struct HttpAuthInfo **ppAuthInfo,
656 LPWSTR domain_and_username, LPWSTR password,
657 LPWSTR host )
658 {
659 SECURITY_STATUS sec_status;
660 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
661 BOOL first = FALSE;
662 LPWSTR szRealm = NULL;
663
664 TRACE("%s\n", debugstr_w(pszAuthValue));
665
666 if (!pAuthInfo)
667 {
668 TimeStamp exp;
669
670 first = TRUE;
671 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
672 if (!pAuthInfo)
673 return FALSE;
674
675 SecInvalidateHandle(&pAuthInfo->cred);
676 SecInvalidateHandle(&pAuthInfo->ctx);
677 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
678 pAuthInfo->attr = 0;
679 pAuthInfo->auth_data = NULL;
680 pAuthInfo->auth_data_len = 0;
681 pAuthInfo->finished = FALSE;
682
683 if (is_basic_auth_value(pszAuthValue,NULL))
684 {
685 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
686 pAuthInfo->scheme = heap_strdupW(szBasic);
687 if (!pAuthInfo->scheme)
688 {
689 HeapFree(GetProcessHeap(), 0, pAuthInfo);
690 return FALSE;
691 }
692 }
693 else
694 {
695 PVOID pAuthData;
696 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
697
698 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
699 if (!pAuthInfo->scheme)
700 {
701 HeapFree(GetProcessHeap(), 0, pAuthInfo);
702 return FALSE;
703 }
704
705 if (domain_and_username)
706 {
707 WCHAR *user = strchrW(domain_and_username, '\\');
708 WCHAR *domain = domain_and_username;
709
710 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
711
712 pAuthData = &nt_auth_identity;
713
714 if (user) user++;
715 else
716 {
717 user = domain_and_username;
718 domain = NULL;
719 }
720
721 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
722 nt_auth_identity.User = user;
723 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
724 nt_auth_identity.Domain = domain;
725 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
726 nt_auth_identity.Password = password;
727 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
728 }
729 else
730 /* use default credentials */
731 pAuthData = NULL;
732
733 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
734 SECPKG_CRED_OUTBOUND, NULL,
735 pAuthData, NULL,
736 NULL, &pAuthInfo->cred,
737 &exp);
738 if (sec_status == SEC_E_OK)
739 {
740 PSecPkgInfoW sec_pkg_info;
741 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
742 if (sec_status == SEC_E_OK)
743 {
744 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
745 FreeContextBuffer(sec_pkg_info);
746 }
747 }
748 if (sec_status != SEC_E_OK)
749 {
750 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
751 debugstr_w(pAuthInfo->scheme), sec_status);
752 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
753 HeapFree(GetProcessHeap(), 0, pAuthInfo);
754 return FALSE;
755 }
756 }
757 *ppAuthInfo = pAuthInfo;
758 }
759 else if (pAuthInfo->finished)
760 return FALSE;
761
762 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
763 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
764 {
765 ERR("authentication scheme changed from %s to %s\n",
766 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
767 return FALSE;
768 }
769
770 if (is_basic_auth_value(pszAuthValue,&szRealm))
771 {
772 int userlen;
773 int passlen;
774 char *auth_data = NULL;
775 UINT auth_data_len = 0;
776
777 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
778
779 if (!domain_and_username)
780 {
781 if (host && szRealm)
782 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
783 if (auth_data_len == 0)
784 {
785 HeapFree(GetProcessHeap(),0,szRealm);
786 return FALSE;
787 }
788 }
789 else
790 {
791 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
792 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
793
794 /* length includes a nul terminator, which will be re-used for the ':' */
795 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
796 if (!auth_data)
797 {
798 HeapFree(GetProcessHeap(),0,szRealm);
799 return FALSE;
800 }
801
802 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
803 auth_data[userlen] = ':';
804 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
805 auth_data_len = userlen + 1 + passlen;
806 if (host && szRealm)
807 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
808 }
809
810 pAuthInfo->auth_data = auth_data;
811 pAuthInfo->auth_data_len = auth_data_len;
812 pAuthInfo->finished = TRUE;
813 HeapFree(GetProcessHeap(),0,szRealm);
814
815 return TRUE;
816 }
817 else
818 {
819 LPCWSTR pszAuthData;
820 SecBufferDesc out_desc, in_desc;
821 SecBuffer out, in;
822 unsigned char *buffer;
823 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
824 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
825
826 in.BufferType = SECBUFFER_TOKEN;
827 in.cbBuffer = 0;
828 in.pvBuffer = NULL;
829
830 in_desc.ulVersion = 0;
831 in_desc.cBuffers = 1;
832 in_desc.pBuffers = &in;
833
834 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
835 if (*pszAuthData == ' ')
836 {
837 pszAuthData++;
838 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
839 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
840 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
841 }
842
843 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
844
845 out.BufferType = SECBUFFER_TOKEN;
846 out.cbBuffer = pAuthInfo->max_token;
847 out.pvBuffer = buffer;
848
849 out_desc.ulVersion = 0;
850 out_desc.cBuffers = 1;
851 out_desc.pBuffers = &out;
852
853 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
854 first ? NULL : &pAuthInfo->ctx,
855 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
856 context_req, 0, SECURITY_NETWORK_DREP,
857 in.pvBuffer ? &in_desc : NULL,
858 0, &pAuthInfo->ctx, &out_desc,
859 &pAuthInfo->attr, &pAuthInfo->exp);
860 if (sec_status == SEC_E_OK)
861 {
862 pAuthInfo->finished = TRUE;
863 pAuthInfo->auth_data = out.pvBuffer;
864 pAuthInfo->auth_data_len = out.cbBuffer;
865 TRACE("sending last auth packet\n");
866 }
867 else if (sec_status == SEC_I_CONTINUE_NEEDED)
868 {
869 pAuthInfo->auth_data = out.pvBuffer;
870 pAuthInfo->auth_data_len = out.cbBuffer;
871 TRACE("sending next auth packet\n");
872 }
873 else
874 {
875 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
876 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
877 destroy_authinfo(pAuthInfo);
878 *ppAuthInfo = NULL;
879 return FALSE;
880 }
881 }
882
883 return TRUE;
884 }
885
886 /***********************************************************************
887 * HTTP_HttpAddRequestHeadersW (internal)
888 */
889 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
890 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
891 {
892 LPWSTR lpszStart;
893 LPWSTR lpszEnd;
894 LPWSTR buffer;
895 BOOL bSuccess = FALSE;
896 DWORD len;
897
898 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
899
900 if( dwHeaderLength == ~0U )
901 len = strlenW(lpszHeader);
902 else
903 len = dwHeaderLength;
904 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
905 lstrcpynW( buffer, lpszHeader, len + 1);
906
907 lpszStart = buffer;
908
909 do
910 {
911 LPWSTR * pFieldAndValue;
912
913 lpszEnd = lpszStart;
914
915 while (*lpszEnd != '\0')
916 {
917 if (*lpszEnd == '\r' || *lpszEnd == '\n')
918 break;
919 lpszEnd++;
920 }
921
922 if (*lpszStart == '\0')
923 break;
924
925 if (*lpszEnd == '\r' || *lpszEnd == '\n')
926 {
927 *lpszEnd = '\0';
928 lpszEnd++; /* Jump over newline */
929 }
930 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
931 if (*lpszStart == '\0')
932 {
933 /* Skip 0-length headers */
934 lpszStart = lpszEnd;
935 bSuccess = TRUE;
936 continue;
937 }
938 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
939 if (pFieldAndValue)
940 {
941 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
942 if (bSuccess)
943 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
944 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
945 HTTP_FreeTokens(pFieldAndValue);
946 }
947
948 lpszStart = lpszEnd;
949 } while (bSuccess);
950
951 HeapFree(GetProcessHeap(), 0, buffer);
952
953 return bSuccess;
954 }
955
956 /***********************************************************************
957 * HttpAddRequestHeadersW (WININET.@)
958 *
959 * Adds one or more HTTP header to the request handler
960 *
961 * NOTE
962 * On Windows if dwHeaderLength includes the trailing '\0', then
963 * HttpAddRequestHeadersW() adds it too. However this results in an
964 * invalid Http header which is rejected by some servers so we probably
965 * don't need to match Windows on that point.
966 *
967 * RETURNS
968 * TRUE on success
969 * FALSE on failure
970 *
971 */
972 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
973 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
974 {
975 BOOL bSuccess = FALSE;
976 http_request_t *lpwhr;
977
978 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
979
980 if (!lpszHeader)
981 return TRUE;
982
983 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
984 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
985 {
986 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
987 goto lend;
988 }
989 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
990 lend:
991 if( lpwhr )
992 WININET_Release( &lpwhr->hdr );
993
994 return bSuccess;
995 }
996
997 /***********************************************************************
998 * HttpAddRequestHeadersA (WININET.@)
999 *
1000 * Adds one or more HTTP header to the request handler
1001 *
1002 * RETURNS
1003 * TRUE on success
1004 * FALSE on failure
1005 *
1006 */
1007 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1008 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1009 {
1010 DWORD len;
1011 LPWSTR hdr;
1012 BOOL r;
1013
1014 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1015
1016 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1017 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1018 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1019 if( dwHeaderLength != ~0U )
1020 dwHeaderLength = len;
1021
1022 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1023
1024 HeapFree( GetProcessHeap(), 0, hdr );
1025
1026 return r;
1027 }
1028
1029 /***********************************************************************
1030 * HttpEndRequestA (WININET.@)
1031 *
1032 * Ends an HTTP request that was started by HttpSendRequestEx
1033 *
1034 * RETURNS
1035 * TRUE if successful
1036 * FALSE on failure
1037 *
1038 */
1039 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
1040 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
1041 {
1042 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
1043
1044 if (lpBuffersOut)
1045 {
1046 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1047 return FALSE;
1048 }
1049
1050 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
1051 }
1052
1053 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
1054 {
1055 BOOL rc = FALSE;
1056 INT responseLen;
1057 DWORD dwBufferSize;
1058 INTERNET_ASYNC_RESULT iar;
1059
1060 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1061 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1062
1063 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
1064 if (responseLen)
1065 rc = TRUE;
1066
1067 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1068 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
1069
1070 /* process cookies here. Is this right? */
1071 HTTP_ProcessCookies(lpwhr);
1072
1073 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
1074
1075 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
1076 {
1077 DWORD dwCode,dwCodeLength = sizeof(DWORD);
1078 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
1079 (dwCode == 302 || dwCode == 301 || dwCode == 303))
1080 {
1081 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
1082 dwBufferSize=sizeof(szNewLocation);
1083 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
1084 {
1085 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
1086 {
1087 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1088 lpwhr->lpszVerb = heap_strdupW(szGET);
1089 }
1090 HTTP_DrainContent(lpwhr);
1091 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
1092 {
1093 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
1094 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
1095 rc = HTTP_HandleRedirect(lpwhr, new_url);
1096 if (rc)
1097 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
1098 HeapFree( GetProcessHeap(), 0, new_url );
1099 }
1100 }
1101 }
1102 }
1103
1104 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
1105 iar.dwError = rc ? 0 : INTERNET_GetLastError();
1106
1107 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1108 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1109 sizeof(INTERNET_ASYNC_RESULT));
1110 return rc;
1111 }
1112
1113 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
1114 {
1115 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
1116 http_request_t *lpwhr = (http_request_t*)work->hdr;
1117
1118 TRACE("%p\n", lpwhr);
1119
1120 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
1121 }
1122
1123 /***********************************************************************
1124 * HttpEndRequestW (WININET.@)
1125 *
1126 * Ends an HTTP request that was started by HttpSendRequestEx
1127 *
1128 * RETURNS
1129 * TRUE if successful
1130 * FALSE on failure
1131 *
1132 */
1133 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
1134 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
1135 {
1136 BOOL rc = FALSE;
1137 http_request_t *lpwhr;
1138
1139 TRACE("-->\n");
1140
1141 if (lpBuffersOut)
1142 {
1143 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1144 return FALSE;
1145 }
1146
1147 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
1148
1149 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
1150 {
1151 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1152 if (lpwhr)
1153 WININET_Release( &lpwhr->hdr );
1154 return FALSE;
1155 }
1156 lpwhr->hdr.dwFlags |= dwFlags;
1157
1158 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1159 {
1160 WORKREQUEST work;
1161 struct WORKREQ_HTTPENDREQUESTW *request;
1162
1163 work.asyncproc = AsyncHttpEndRequestProc;
1164 work.hdr = WININET_AddRef( &lpwhr->hdr );
1165
1166 request = &work.u.HttpEndRequestW;
1167 request->dwFlags = dwFlags;
1168 request->dwContext = dwContext;
1169
1170 INTERNET_AsyncCall(&work);
1171 INTERNET_SetLastError(ERROR_IO_PENDING);
1172 }
1173 else
1174 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1175
1176 WININET_Release( &lpwhr->hdr );
1177 TRACE("%i <--\n",rc);
1178 return rc;
1179 }
1180
1181 /***********************************************************************
1182 * HttpOpenRequestW (WININET.@)
1183 *
1184 * Open a HTTP request handle
1185 *
1186 * RETURNS
1187 * HINTERNET a HTTP request handle on success
1188 * NULL on failure
1189 *
1190 */
1191 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1192 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1193 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1194 DWORD dwFlags, DWORD_PTR dwContext)
1195 {
1196 http_session_t *lpwhs;
1197 HINTERNET handle = NULL;
1198
1199 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1200 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1201 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1202 dwFlags, dwContext);
1203 if(lpszAcceptTypes!=NULL)
1204 {
1205 int i;
1206 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1207 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1208 }
1209
1210 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1211 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1212 {
1213 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1214 goto lend;
1215 }
1216
1217 /*
1218 * My tests seem to show that the windows version does not
1219 * become asynchronous until after this point. And anyhow
1220 * if this call was asynchronous then how would you get the
1221 * necessary HINTERNET pointer returned by this function.
1222 *
1223 */
1224 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1225 lpszVersion, lpszReferrer, lpszAcceptTypes,
1226 dwFlags, dwContext);
1227 lend:
1228 if( lpwhs )
1229 WININET_Release( &lpwhs->hdr );
1230 TRACE("returning %p\n", handle);
1231 return handle;
1232 }
1233
1234
1235 /***********************************************************************
1236 * HttpOpenRequestA (WININET.@)
1237 *
1238 * Open a HTTP request handle
1239 *
1240 * RETURNS
1241 * HINTERNET a HTTP request handle on success
1242 * NULL on failure
1243 *
1244 */
1245 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1246 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1247 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1248 DWORD dwFlags, DWORD_PTR dwContext)
1249 {
1250 LPWSTR szVerb = NULL, szObjectName = NULL;
1251 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1252 INT acceptTypesCount;
1253 HINTERNET rc = FALSE;
1254 LPCSTR *types;
1255
1256 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1257 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1258 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1259 dwFlags, dwContext);
1260
1261 if (lpszVerb)
1262 {
1263 szVerb = heap_strdupAtoW(lpszVerb);
1264 if ( !szVerb )
1265 goto end;
1266 }
1267
1268 if (lpszObjectName)
1269 {
1270 szObjectName = heap_strdupAtoW(lpszObjectName);
1271 if ( !szObjectName )
1272 goto end;
1273 }
1274
1275 if (lpszVersion)
1276 {
1277 szVersion = heap_strdupAtoW(lpszVersion);
1278 if ( !szVersion )
1279 goto end;
1280 }
1281
1282 if (lpszReferrer)
1283 {
1284 szReferrer = heap_strdupAtoW(lpszReferrer);
1285 if ( !szReferrer )
1286 goto end;
1287 }
1288
1289 if (lpszAcceptTypes)
1290 {
1291 acceptTypesCount = 0;
1292 types = lpszAcceptTypes;
1293 while (*types)
1294 {
1295 __TRY
1296 {
1297 /* find out how many there are */
1298 if (*types && **types)
1299 {
1300 TRACE("accept type: %s\n", debugstr_a(*types));
1301 acceptTypesCount++;
1302 }
1303 }
1304 __EXCEPT_PAGE_FAULT
1305 {
1306 WARN("invalid accept type pointer\n");
1307 }
1308 __ENDTRY;
1309 types++;
1310 }
1311 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1312 if (!szAcceptTypes) goto end;
1313
1314 acceptTypesCount = 0;
1315 types = lpszAcceptTypes;
1316 while (*types)
1317 {
1318 __TRY
1319 {
1320 if (*types && **types)
1321 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1322 }
1323 __EXCEPT_PAGE_FAULT
1324 {
1325 /* ignore invalid pointer */
1326 }
1327 __ENDTRY;
1328 types++;
1329 }
1330 szAcceptTypes[acceptTypesCount] = NULL;
1331 }
1332
1333 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1334 szVersion, szReferrer,
1335 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1336
1337 end:
1338 if (szAcceptTypes)
1339 {
1340 acceptTypesCount = 0;
1341 while (szAcceptTypes[acceptTypesCount])
1342 {
1343 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1344 acceptTypesCount++;
1345 }
1346 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1347 }
1348 HeapFree(GetProcessHeap(), 0, szReferrer);
1349 HeapFree(GetProcessHeap(), 0, szVersion);
1350 HeapFree(GetProcessHeap(), 0, szObjectName);
1351 HeapFree(GetProcessHeap(), 0, szVerb);
1352
1353 return rc;
1354 }
1355
1356 /***********************************************************************
1357 * HTTP_EncodeBase64
1358 */
1359 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1360 {
1361 UINT n = 0, x;
1362 static const CHAR HTTP_Base64Enc[] =
1363 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1364
1365 while( len > 0 )
1366 {
1367 /* first 6 bits, all from bin[0] */
1368 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1369 x = (bin[0] & 3) << 4;
1370
1371 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1372 if( len == 1 )
1373 {
1374 base64[n++] = HTTP_Base64Enc[x];
1375 base64[n++] = '=';
1376 base64[n++] = '=';
1377 break;
1378 }
1379 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1380 x = ( bin[1] & 0x0f ) << 2;
1381
1382 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1383 if( len == 2 )
1384 {
1385 base64[n++] = HTTP_Base64Enc[x];
1386 base64[n++] = '=';
1387 break;
1388 }
1389 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1390
1391 /* last 6 bits, all from bin [2] */
1392 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1393 bin += 3;
1394 len -= 3;
1395 }
1396 base64[n] = 0;
1397 return n;
1398 }
1399
1400 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1401 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1402 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1403 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1404 static const signed char HTTP_Base64Dec[256] =
1405 {
1406 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1407 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1408 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1409 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1410 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1411 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1412 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1413 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1414 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1415 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1416 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1417 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1418 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1419 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1420 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1421 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1422 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1423 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1424 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1425 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1426 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1427 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1428 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1429 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1430 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1431 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1432 };
1433 #undef CH
1434
1435 /***********************************************************************
1436 * HTTP_DecodeBase64
1437 */
1438 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1439 {
1440 unsigned int n = 0;
1441
1442 while(*base64)
1443 {
1444 signed char in[4];
1445
1446 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1447 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1448 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1449 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1450 {
1451 WARN("invalid base64: %s\n", debugstr_w(base64));
1452 return 0;
1453 }
1454 if (bin)
1455 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1456 n++;
1457
1458 if ((base64[2] == '=') && (base64[3] == '='))
1459 break;
1460 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1461 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1462 {
1463 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1464 return 0;
1465 }
1466 if (bin)
1467 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1468 n++;
1469
1470 if (base64[3] == '=')
1471 break;
1472 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1473 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1474 {
1475 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1476 return 0;
1477 }
1478 if (bin)
1479 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1480 n++;
1481
1482 base64 += 4;
1483 }
1484
1485 return n;
1486 }
1487
1488 /***********************************************************************
1489 * HTTP_InsertAuthorization
1490 *
1491 * Insert or delete the authorization field in the request header.
1492 */
1493 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1494 {
1495 if (pAuthInfo)
1496 {
1497 static const WCHAR wszSpace[] = {' ',0};
1498 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1499 unsigned int len;
1500 WCHAR *authorization = NULL;
1501
1502 if (pAuthInfo->auth_data_len)
1503 {
1504 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1505 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1506 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1507 if (!authorization)
1508 return FALSE;
1509
1510 strcpyW(authorization, pAuthInfo->scheme);
1511 strcatW(authorization, wszSpace);
1512 HTTP_EncodeBase64(pAuthInfo->auth_data,
1513 pAuthInfo->auth_data_len,
1514 authorization+strlenW(authorization));
1515
1516 /* clear the data as it isn't valid now that it has been sent to the
1517 * server, unless it's Basic authentication which doesn't do
1518 * connection tracking */
1519 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1520 {
1521 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1522 pAuthInfo->auth_data = NULL;
1523 pAuthInfo->auth_data_len = 0;
1524 }
1525 }
1526
1527 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1528
1529 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1530
1531 HeapFree(GetProcessHeap(), 0, authorization);
1532 }
1533 return TRUE;
1534 }
1535
1536 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1537 {
1538 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1539 DWORD size;
1540
1541 size = sizeof(new_location);
1542 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1543 {
1544 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1545 strcpyW( url, new_location );
1546 }
1547 else
1548 {
1549 static const WCHAR slash[] = { '/',0 };
1550 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1551 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1552 http_session_t *session = req->lpHttpSession;
1553
1554 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1555 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1556
1557 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1558
1559 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1560 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1561 else
1562 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1563 if (req->lpszPath[0] != '/') strcatW( url, slash );
1564 strcatW( url, req->lpszPath );
1565 }
1566 TRACE("url=%s\n", debugstr_w(url));
1567 return url;
1568 }
1569
1570 /***********************************************************************
1571 * HTTP_DealWithProxy
1572 */
1573 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1574 {
1575 WCHAR buf[MAXHOSTNAME];
1576 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1577 static WCHAR szNul[] = { 0 };
1578 URL_COMPONENTSW UrlComponents;
1579 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1580 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1581
1582 memset( &UrlComponents, 0, sizeof UrlComponents );
1583 UrlComponents.dwStructSize = sizeof UrlComponents;
1584 UrlComponents.lpszHostName = buf;
1585 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1586
1587 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1588 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1589 sprintfW(proxy, szFormat, hIC->lpszProxy);
1590 else
1591 strcpyW(proxy, hIC->lpszProxy);
1592 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1593 return FALSE;
1594 if( UrlComponents.dwHostNameLength == 0 )
1595 return FALSE;
1596
1597 if( !lpwhr->lpszPath )
1598 lpwhr->lpszPath = szNul;
1599
1600 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1601 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1602
1603 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1604 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1605 lpwhs->nServerPort = UrlComponents.nPort;
1606
1607 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1608 return TRUE;
1609 }
1610
1611 #ifndef INET6_ADDRSTRLEN
1612 #define INET6_ADDRSTRLEN 46
1613 #endif
1614
1615 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1616 {
1617 char szaddr[INET6_ADDRSTRLEN];
1618 http_session_t *lpwhs = lpwhr->lpHttpSession;
1619 const void *addr;
1620
1621 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1622 INTERNET_STATUS_RESOLVING_NAME,
1623 lpwhs->lpszServerName,
1624 strlenW(lpwhs->lpszServerName)+1);
1625
1626 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1627 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1628 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1629 {
1630 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1631 return FALSE;
1632 }
1633
1634 switch (lpwhs->socketAddress.ss_family)
1635 {
1636 case AF_INET:
1637 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1638 break;
1639 case AF_INET6:
1640 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1641 break;
1642 default:
1643 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1644 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1645 return FALSE;
1646 }
1647 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1648 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1649 INTERNET_STATUS_NAME_RESOLVED,
1650 szaddr, strlen(szaddr)+1);
1651
1652 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1653 return TRUE;
1654 }
1655
1656
1657 /***********************************************************************
1658 * HTTPREQ_Destroy (internal)
1659 *
1660 * Deallocate request handle
1661 *
1662 */
1663 static void HTTPREQ_Destroy(object_header_t *hdr)
1664 {
1665 http_request_t *lpwhr = (http_request_t*) hdr;
1666 DWORD i;
1667
1668 TRACE("\n");
1669
1670 if(lpwhr->hCacheFile)
1671 CloseHandle(lpwhr->hCacheFile);
1672
1673 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1674
1675 DeleteCriticalSection( &lpwhr->read_section );
1676 WININET_Release(&lpwhr->lpHttpSession->hdr);
1677
1678 destroy_authinfo(lpwhr->pAuthInfo);
1679 destroy_authinfo(lpwhr->pProxyAuthInfo);
1680
1681 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1682 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1683 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1684 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1685 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1686
1687 for (i = 0; i < lpwhr->nCustHeaders; i++)
1688 {
1689 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1690 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1691 }
1692
1693 #ifdef HAVE_ZLIB
1694 if(lpwhr->gzip_stream) {
1695 if(!lpwhr->gzip_stream->end_of_data)
1696 inflateEnd(&lpwhr->gzip_stream->zstream);
1697 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1698 }
1699 #endif
1700
1701 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1702 HeapFree(GetProcessHeap(), 0, lpwhr);
1703 }
1704
1705 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1706 {
1707 http_request_t *lpwhr = (http_request_t*) hdr;
1708
1709 TRACE("%p\n",lpwhr);
1710
1711 if (!NETCON_connected(&lpwhr->netConnection))
1712 return;
1713
1714 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1715 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1716
1717 NETCON_close(&lpwhr->netConnection);
1718
1719 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1720 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1721 }
1722
1723 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1724 {
1725 LPHTTPHEADERW host_header;
1726
1727 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1728
1729 host_header = HTTP_GetHeader(req, hostW);
1730 if(!host_header)
1731 return FALSE;
1732
1733 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1734 return TRUE;
1735 }
1736
1737 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1738 {
1739 WCHAR szVersion[10];
1740 WCHAR szConnectionResponse[20];
1741 DWORD dwBufferSize = sizeof(szVersion);
1742 BOOL keepalive = FALSE;
1743
1744 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1745 * the connection is keep-alive by default */
1746 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1747 &dwBufferSize, NULL) &&
1748 !strcmpiW(szVersion, g_szHttp1_1))
1749 {
1750 keepalive = TRUE;
1751 }
1752
1753 dwBufferSize = sizeof(szConnectionResponse);
1754 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1755 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1756 {
1757 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1758 }
1759
1760 return keepalive;
1761 }
1762
1763 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1764 {
1765 http_request_t *req = (http_request_t*)hdr;
1766
1767 switch(option) {
1768 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1769 {
1770 http_session_t *lpwhs = req->lpHttpSession;
1771 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1772
1773 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1774
1775 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1776 return ERROR_INSUFFICIENT_BUFFER;
1777 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1778 /* FIXME: can't get a SOCKET from our connection since we don't use
1779 * winsock
1780 */
1781 info->Socket = 0;
1782 /* FIXME: get source port from req->netConnection */
1783 info->SourcePort = 0;
1784 info->DestPort = lpwhs->nHostPort;
1785 info->Flags = 0;
1786 if (HTTP_KeepAlive(req))
1787 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1788 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1789 info->Flags |= IDSI_FLAG_PROXY;
1790 if (req->netConnection.useSSL)
1791 info->Flags |= IDSI_FLAG_SECURE;
1792
1793 return ERROR_SUCCESS;
1794 }
1795
1796 case INTERNET_OPTION_SECURITY_FLAGS:
1797 {
1798 http_session_t *lpwhs;
1799 lpwhs = req->lpHttpSession;
1800
1801 if (*size < sizeof(ULONG))
1802 return ERROR_INSUFFICIENT_BUFFER;
1803
1804 *size = sizeof(DWORD);
1805 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1806 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1807 else
1808 *(DWORD*)buffer = 0;
1809 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1810 return ERROR_SUCCESS;
1811 }
1812
1813 case INTERNET_OPTION_HANDLE_TYPE:
1814 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1815
1816 if (*size < sizeof(ULONG))
1817 return ERROR_INSUFFICIENT_BUFFER;
1818
1819 *size = sizeof(DWORD);
1820 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1821 return ERROR_SUCCESS;
1822
1823 case INTERNET_OPTION_URL: {
1824 WCHAR url[INTERNET_MAX_URL_LENGTH];
1825 HTTPHEADERW *host;
1826 DWORD len;
1827 WCHAR *pch;
1828
1829 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1830
1831 TRACE("INTERNET_OPTION_URL\n");
1832
1833 host = HTTP_GetHeader(req, hostW);
1834 strcpyW(url, httpW);
1835 strcatW(url, host->lpszValue);
1836 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1837 *pch = 0;
1838 strcatW(url, req->lpszPath);
1839
1840 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1841
1842 if(unicode) {
1843 len = (strlenW(url)+1) * sizeof(WCHAR);
1844 if(*size < len)
1845 return ERROR_INSUFFICIENT_BUFFER;
1846
1847 *size = len;
1848 strcpyW(buffer, url);
1849 return ERROR_SUCCESS;
1850 }else {
1851 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1852 if(len > *size)
1853 return ERROR_INSUFFICIENT_BUFFER;
1854
1855 *size = len;
1856 return ERROR_SUCCESS;
1857 }
1858 }
1859
1860 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1861 INTERNET_CACHE_ENTRY_INFOW *info;
1862 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1863 WCHAR url[INTERNET_MAX_URL_LENGTH];
1864 DWORD nbytes, error;
1865 BOOL ret;
1866
1867 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1868
1869 if (*size < sizeof(*ts))
1870 {
1871 *size = sizeof(*ts);
1872 return ERROR_INSUFFICIENT_BUFFER;
1873 }
1874 nbytes = 0;
1875 HTTP_GetRequestURL(req, url);
1876 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1877 error = GetLastError();
1878 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1879 {
1880 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1881 return ERROR_OUTOFMEMORY;
1882
1883 GetUrlCacheEntryInfoW(url, info, &nbytes);
1884
1885 ts->ftExpires = info->ExpireTime;
1886 ts->ftLastModified = info->LastModifiedTime;
1887
1888 HeapFree(GetProcessHeap(), 0, info);
1889 *size = sizeof(*ts);
1890 return ERROR_SUCCESS;
1891 }
1892 return error;
1893 }
1894
1895 case INTERNET_OPTION_DATAFILE_NAME: {
1896 DWORD req_size;
1897
1898 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1899
1900 if(!req->lpszCacheFile) {
1901 *size = 0;
1902 return ERROR_INTERNET_ITEM_NOT_FOUND;
1903 }
1904
1905 if(unicode) {
1906 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1907 if(*size < req_size)
1908 return ERROR_INSUFFICIENT_BUFFER;
1909
1910 *size = req_size;
1911 memcpy(buffer, req->lpszCacheFile, *size);
1912 return ERROR_SUCCESS;
1913 }else {
1914 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1915 if (req_size > *size)
1916 return ERROR_INSUFFICIENT_BUFFER;
1917
1918 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1919 -1, buffer, *size, NULL, NULL);
1920 return ERROR_SUCCESS;
1921 }
1922 }
1923
1924 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1925 PCCERT_CONTEXT context;
1926
1927 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1928 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1929 return ERROR_INSUFFICIENT_BUFFER;
1930 }
1931
1932 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1933 if(context) {
1934 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1935 DWORD len;
1936
1937 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1938 info->ftExpiry = context->pCertInfo->NotAfter;
1939 info->ftStart = context->pCertInfo->NotBefore;
1940 if(unicode) {
1941 len = CertNameToStrW(context->dwCertEncodingType,
1942 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1943 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1944 if(info->lpszSubjectInfo)
1945 CertNameToStrW(context->dwCertEncodingType,
1946 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1947 info->lpszSubjectInfo, len);
1948 len = CertNameToStrW(context->dwCertEncodingType,
1949 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1950 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1951 if (info->lpszIssuerInfo)
1952 CertNameToStrW(context->dwCertEncodingType,
1953 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1954 info->lpszIssuerInfo, len);
1955 }else {
1956 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1957
1958 len = CertNameToStrA(context->dwCertEncodingType,
1959 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1960 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1961 if(infoA->lpszSubjectInfo)
1962 CertNameToStrA(context->dwCertEncodingType,
1963 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1964 infoA->lpszSubjectInfo, len);
1965 len = CertNameToStrA(context->dwCertEncodingType,
1966 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1967 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1968 if(infoA->lpszIssuerInfo)
1969 CertNameToStrA(context->dwCertEncodingType,
1970 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1971 infoA->lpszIssuerInfo, len);
1972 }
1973
1974 /*
1975 * Contrary to MSDN, these do not appear to be set.
1976 * lpszProtocolName
1977 * lpszSignatureAlgName
1978 * lpszEncryptionAlgName
1979 * dwKeySize
1980 */
1981 CertFreeCertificateContext(context);
1982 return ERROR_SUCCESS;
1983 }
1984 }
1985 }
1986
1987 return INET_QueryOption(option, buffer, size, unicode);
1988 }
1989
1990 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1991 {
1992 http_request_t *req = (http_request_t*)hdr;
1993
1994 switch(option) {
1995 case INTERNET_OPTION_SEND_TIMEOUT:
1996 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1997 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1998
1999 if (size != sizeof(DWORD))
2000 return ERROR_INVALID_PARAMETER;
2001
2002 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
2003 *(DWORD*)buffer);
2004
2005 case INTERNET_OPTION_USERNAME:
2006 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
2007 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2008 return ERROR_SUCCESS;
2009
2010 case INTERNET_OPTION_PASSWORD:
2011 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
2012 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2013 return ERROR_SUCCESS;
2014 case INTERNET_OPTION_HTTP_DECODING:
2015 if(size != sizeof(BOOL))
2016 return ERROR_INVALID_PARAMETER;
2017 req->decoding = *(BOOL*)buffer;
2018 return ERROR_SUCCESS;
2019 }
2020
2021 return ERROR_INTERNET_INVALID_OPTION;
2022 }
2023
2024 /* read some more data into the read buffer (the read section must be held) */
2025 static BOOL read_more_data( http_request_t *req, int maxlen )
2026 {
2027 int len;
2028
2029 if (req->read_pos)
2030 {
2031 /* move existing data to the start of the buffer */
2032 if(req->read_size)
2033 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2034 req->read_pos = 0;
2035 }
2036
2037 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2038
2039 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2040 maxlen - req->read_size, 0, &len ))
2041 return FALSE;
2042
2043 req->read_size += len;
2044 return TRUE;
2045 }
2046
2047 /* remove some amount of data from the read buffer (the read section must be held) */
2048 static void remove_data( http_request_t *req, int count )
2049 {
2050 if (!(req->read_size -= count)) req->read_pos = 0;
2051 else req->read_pos += count;
2052 }
2053
2054 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2055 {
2056 int count, bytes_read, pos = 0;
2057
2058 EnterCriticalSection( &req->read_section );
2059 for (;;)
2060 {
2061 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2062
2063 if (eol)
2064 {
2065 count = eol - (req->read_buf + req->read_pos);
2066 bytes_read = count + 1;
2067 }
2068 else count = bytes_read = req->read_size;
2069
2070 count = min( count, *len - pos );
2071 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2072 pos += count;
2073 remove_data( req, bytes_read );
2074 if (eol) break;
2075
2076 if (!read_more_data( req, -1 ) || !req->read_size)
2077 {
2078 *len = 0;
2079 TRACE( "returning empty string\n" );
2080 LeaveCriticalSection( &req->read_section );
2081 return FALSE;
2082 }
2083 }
2084 LeaveCriticalSection( &req->read_section );
2085
2086 if (pos < *len)
2087 {
2088 if (pos && buffer[pos - 1] == '\r') pos--;
2089 *len = pos + 1;
2090 }
2091 buffer[*len - 1] = 0;
2092 TRACE( "returning %s\n", debugstr_a(buffer));
2093 return TRUE;
2094 }
2095
2096 /* discard data contents until we reach end of line (the read section must be held) */
2097 static BOOL discard_eol( http_request_t *req )
2098 {
2099 do
2100 {
2101 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2102 if (eol)
2103 {
2104 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2105 break;
2106 }
2107 req->read_pos = req->read_size = 0; /* discard everything */
2108 if (!read_more_data( req, -1 )) return FALSE;
2109 } while (req->read_size);
2110 return TRUE;
2111 }
2112
2113 /* read the size of the next chunk (the read section must be held) */
2114 static BOOL start_next_chunk( http_request_t *req )
2115 {
2116 DWORD chunk_size = 0;
2117
2118 if (!req->dwContentLength) return TRUE;
2119 if (req->dwContentLength == req->dwContentRead)
2120 {
2121 /* read terminator for the previous chunk */
2122 if (!discard_eol( req )) return FALSE;
2123 req->dwContentLength = ~0u;
2124 req->dwContentRead = 0;
2125 }
2126 for (;;)
2127 {
2128 while (req->read_size)
2129 {
2130 char ch = req->read_buf[req->read_pos];
2131 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2132 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2133 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2134 else if (ch == ';' || ch == '\r' || ch == '\n')
2135 {
2136 TRACE( "reading %u byte chunk\n", chunk_size );
2137 req->dwContentLength = chunk_size;
2138 req->dwContentRead = 0;
2139 if (!discard_eol( req )) return FALSE;
2140 return TRUE;
2141 }
2142 remove_data( req, 1 );
2143 }
2144 if (!read_more_data( req, -1 )) return FALSE;
2145 if (!req->read_size)
2146 {
2147 req->dwContentLength = req->dwContentRead = 0;
2148 return TRUE;
2149 }
2150 }
2151 }
2152
2153 /* check if we have reached the end of the data to read (the read section must be held) */
2154 static BOOL end_of_read_data( http_request_t *req )
2155 {
2156 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2157 if (req->read_chunked) return (req->dwContentLength == 0);
2158 if (req->dwContentLength == ~0u) return FALSE;
2159 return (req->dwContentLength == req->dwContentRead);
2160 }
2161
2162 /* fetch some more data into the read buffer (the read section must be held) */
2163 static BOOL refill_buffer( http_request_t *req )
2164 {
2165 int len = sizeof(req->read_buf);
2166
2167 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2168 {
2169 if (!start_next_chunk( req )) return FALSE;
2170 }
2171
2172 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2173 if (len <= req->read_size) return TRUE;
2174
2175 if (!read_more_data( req, len )) return FALSE;
2176 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2177 return TRUE;
2178 }
2179
2180 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2181 {
2182 DWORD ret = ERROR_SUCCESS;
2183 int read = 0;
2184
2185 #ifdef HAVE_ZLIB
2186 z_stream *zstream = &req->gzip_stream->zstream;
2187 int zres;
2188
2189 while(read < size && !req->gzip_stream->end_of_data) {
2190 if(!req->read_size) {
2191 if(!sync || !refill_buffer(req))
2192 break;
2193 }
2194
2195 zstream->next_in = req->read_buf+req->read_pos;
2196 zstream->avail_in = req->read_size;
2197 zstream->next_out = buf+read;
2198 zstream->avail_out = size-read;
2199 zres = inflate(zstream, Z_FULL_FLUSH);
2200 read = size - zstream->avail_out;
2201 remove_data(req, req->read_size-zstream->avail_in);
2202 if(zres == Z_STREAM_END) {
2203 TRACE("end of data\n");
2204 req->gzip_stream->end_of_data = TRUE;
2205 inflateEnd(&req->gzip_stream->zstream);
2206 }else if(zres != Z_OK) {
2207 WARN("inflate failed %d\n", zres);
2208 if(!read)
2209 ret = ERROR_INTERNET_DECODING_FAILED;
2210 break;
2211 }
2212 }
2213 #endif
2214
2215 *read_ret = read;
2216 return ret;
2217 }
2218
2219 static void refill_gzip_buffer(http_request_t *req)
2220 {
2221 DWORD res;
2222 int len;
2223
2224 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2225 return;
2226
2227 if(req->gzip_stream->buf_pos) {
2228 if(req->gzip_stream->buf_size)
2229 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2230 req->gzip_stream->buf_pos = 0;
2231 }
2232
2233 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2234 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2235 if(res == ERROR_SUCCESS)
2236 req->gzip_stream->buf_size += len;
2237 }
2238
2239 /* return the size of data available to be read immediately (the read section must be held) */
2240 static DWORD get_avail_data( http_request_t *req )
2241 {
2242 if (req->gzip_stream) {
2243 refill_gzip_buffer(req);
2244 return req->gzip_stream->buf_size;
2245 }
2246 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2247 return 0;
2248 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2249 }
2250
2251 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2252 {
2253 INTERNET_ASYNC_RESULT iar;
2254
2255 TRACE("%p\n", req);
2256
2257 EnterCriticalSection( &req->read_section );
2258 if (refill_buffer( req )) {
2259 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2260 iar.dwError = first_notif ? 0 : get_avail_data(req);
2261 }else {
2262 iar.dwResult = 0;
2263 iar.dwError = INTERNET_GetLastError();
2264 }
2265 LeaveCriticalSection( &req->read_section );
2266
2267 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2268 sizeof(INTERNET_ASYNC_RESULT));
2269 }
2270
2271 /* read data from the http connection (the read section must be held) */
2272 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2273 {
2274 BOOL finished_reading = FALSE;
2275 int len, bytes_read = 0;
2276 DWORD ret = ERROR_SUCCESS;
2277
2278 EnterCriticalSection( &req->read_section );
2279
2280 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2281 {
2282 if (!start_next_chunk( req )) goto done;
2283 }
2284
2285 if(req->gzip_stream) {
2286 if(req->gzip_stream->buf_size) {
2287 bytes_read = min(req->gzip_stream->buf_size, size);
2288 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2289 req->gzip_stream->buf_pos += bytes_read;
2290 req->gzip_stream->buf_size -= bytes_read;
2291 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2292 refill_buffer(req);
2293 }
2294
2295 if(size > bytes_read) {
2296 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2297 if(ret == ERROR_SUCCESS)
2298 bytes_read += len;
2299 }
2300
2301 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2302 }else {
2303 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2304
2305 if (req->read_size) {
2306 bytes_read = min( req->read_size, size );
2307 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2308 remove_data( req, bytes_read );
2309 }
2310
2311 if (size > bytes_read && (!bytes_read || sync)) {
2312 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2313 sync ? MSG_WAITALL : 0, &len))
2314 bytes_read += len;
2315 /* always return success, even if the network layer returns an error */
2316 }
2317
2318 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2319 }
2320 done:
2321 req->dwContentRead += bytes_read;
2322 *read = bytes_read;
2323
2324 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2325 LeaveCriticalSection( &req->read_section );
2326
2327 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2328 BOOL res;
2329 DWORD dwBytesWritten;
2330
2331 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2332 if(!res)
2333 WARN("WriteFile failed: %u\n", GetLastError());
2334 }
2335
2336 if(finished_reading)
2337 HTTP_FinishedReading(req);
2338
2339 return ret;
2340 }
2341
2342
2343 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2344 {
2345 http_request_t *req = (http_request_t*)hdr;
2346 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2347 }
2348
2349 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2350 {
2351 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2352 http_request_t *req = (http_request_t*)workRequest->hdr;
2353 INTERNET_ASYNC_RESULT iar;
2354 DWORD res;
2355
2356 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2357
2358 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2359 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2360
2361 iar.dwResult = res == ERROR_SUCCESS;
2362 iar.dwError = res;
2363
2364 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2365 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2366 sizeof(INTERNET_ASYNC_RESULT));
2367 }
2368
2369 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2370 DWORD flags, DWORD_PTR context)
2371 {
2372 http_request_t *req = (http_request_t*)hdr;
2373 DWORD res;
2374
2375 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2376 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2377
2378 if (buffers->dwStructSize != sizeof(*buffers))
2379 return ERROR_INVALID_PARAMETER;
2380
2381 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2382
2383 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2384 {
2385 WORKREQUEST workRequest;
2386
2387 if (TryEnterCriticalSection( &req->read_section ))
2388 {
2389 if (get_avail_data(req))
2390 {
2391 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2392 &buffers->dwBufferLength, FALSE);
2393 LeaveCriticalSection( &req->read_section );
2394 goto done;
2395 }
2396 LeaveCriticalSection( &req->read_section );
2397 }
2398
2399 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2400 workRequest.hdr = WININET_AddRef(&req->hdr);
2401 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2402
2403 INTERNET_AsyncCall(&workRequest);
2404
2405 return ERROR_IO_PENDING;
2406 }
2407
2408 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2409 !(flags & IRF_NO_WAIT));
2410
2411 done:
2412 if (res == ERROR_SUCCESS) {
2413 DWORD size = buffers->dwBufferLength;
2414 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2415 &size, sizeof(size));
2416 }
2417
2418 return res;
2419 }
2420
2421 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2422 {
2423 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2424 http_request_t *req = (http_request_t*)workRequest->hdr;
2425 INTERNET_ASYNC_RESULT iar;
2426 DWORD res;
2427
2428 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2429
2430 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2431 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2432
2433 iar.dwResult = res == ERROR_SUCCESS;
2434 iar.dwError = res;
2435
2436 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2437 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2438 sizeof(INTERNET_ASYNC_RESULT));
2439 }
2440
2441 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2442 DWORD flags, DWORD_PTR context)
2443 {
2444
2445 http_request_t *req = (http_request_t*)hdr;
2446 DWORD res;
2447
2448 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2449 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2450
2451 if (buffers->dwStructSize != sizeof(*buffers))
2452 return ERROR_INVALID_PARAMETER;
2453
2454 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2455
2456 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2457 {
2458 WORKREQUEST workRequest;
2459
2460 if (TryEnterCriticalSection( &req->read_section ))
2461 {
2462 if (get_avail_data(req))
2463 {
2464 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2465 &buffers->dwBufferLength, FALSE);
2466 LeaveCriticalSection( &req->read_section );
2467 goto done;
2468 }
2469 LeaveCriticalSection( &req->read_section );
2470 }
2471
2472 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2473 workRequest.hdr = WININET_AddRef(&req->hdr);
2474 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2475
2476 INTERNET_AsyncCall(&workRequest);
2477
2478 return ERROR_IO_PENDING;
2479 }
2480
2481 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2482 !(flags & IRF_NO_WAIT));
2483
2484 done:
2485 if (res == ERROR_SUCCESS) {
2486 DWORD size = buffers->dwBufferLength;
2487 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2488 &size, sizeof(size));
2489 }
2490
2491 return res;
2492 }
2493
2494 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2495 {
2496 BOOL ret;
2497 http_request_t *lpwhr = (http_request_t*)hdr;
2498
2499 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2500
2501 *written = 0;
2502 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2503 lpwhr->dwBytesWritten += *written;
2504
2505 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2506 return ret;
2507 }
2508
2509 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2510 {
2511 http_request_t *req = (http_request_t*)workRequest->hdr;
2512
2513 HTTP_ReceiveRequestData(req, FALSE);
2514 }
2515
2516 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2517 {
2518 http_request_t *req = (http_request_t*)hdr;
2519
2520 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2521
2522 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2523 {
2524 WORKREQUEST workRequest;
2525
2526 /* never wait, if we can't enter the section we queue an async request right away */
2527 if (TryEnterCriticalSection( &req->read_section ))
2528 {
2529 if ((*available = get_avail_data( req ))) goto done;
2530 if (end_of_read_data( req )) goto done;
2531 LeaveCriticalSection( &req->read_section );
2532 }
2533
2534 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2535 workRequest.hdr = WININET_AddRef( &req->hdr );
2536
2537 INTERNET_AsyncCall(&workRequest);
2538
2539 return ERROR_IO_PENDING;
2540 }
2541
2542 EnterCriticalSection( &req->read_section );
2543
2544 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2545 {
2546 refill_buffer( req );
2547 *available = get_avail_data( req );
2548 }
2549
2550 done:
2551 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2552 {
2553 DWORD extra;
2554 if (NETCON_query_data_available(&req->netConnection, &extra))
2555 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2556 }
2557 LeaveCriticalSection( &req->read_section );
2558
2559 TRACE( "returning %u\n", *available );
2560 return ERROR_SUCCESS;
2561 }
2562
2563 static const object_vtbl_t HTTPREQVtbl = {
2564 HTTPREQ_Destroy,
2565 HTTPREQ_CloseConnection,
2566 HTTPREQ_QueryOption,
2567 HTTPREQ_SetOption,
2568 HTTPREQ_ReadFile,
2569 HTTPREQ_ReadFileExA,
2570 HTTPREQ_ReadFileExW,
2571 HTTPREQ_WriteFile,
2572 HTTPREQ_QueryDataAvailable,
2573 NULL
2574 };
2575
2576 /***********************************************************************
2577 * HTTP_HttpOpenRequestW (internal)
2578 *
2579 * Open a HTTP request handle
2580 *
2581 * RETURNS
2582 * HINTERNET a HTTP request handle on success
2583 * NULL on failure
2584 *
2585 */
2586 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2587 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2588 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2589 DWORD dwFlags, DWORD_PTR dwContext)
2590 {
2591 appinfo_t *hIC = NULL;
2592 http_request_t *lpwhr;
2593 LPWSTR lpszHostName = NULL;
2594 HINTERNET handle = NULL;
2595 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2596 DWORD len;
2597
2598 TRACE("-->\n");
2599
2600 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2601 hIC = lpwhs->lpAppInfo;
2602
2603 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2604 if (NULL == lpwhr)
2605 {
2606 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2607 goto lend;
2608 }
2609 lpwhr->hdr.htype = WH_HHTTPREQ;
2610 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2611 lpwhr->hdr.dwFlags = dwFlags;
2612 lpwhr->hdr.dwContext = dwContext;
2613 lpwhr->hdr.refs = 1;
2614 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2615 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2616 lpwhr->dwContentLength = ~0u;
2617 InitializeCriticalSection( &lpwhr->read_section );
2618
2619 WININET_AddRef( &lpwhs->hdr );
2620 lpwhr->lpHttpSession = lpwhs;
2621 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2622
2623 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2624 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2625 if (NULL == lpszHostName)
2626 {
2627 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2628 goto lend;
2629 }
2630
2631 handle = WININET_AllocHandle( &lpwhr->hdr );
2632 if (NULL == handle)
2633 {
2634 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2635 goto lend;
2636 }
2637
2638 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2639 {
2640 InternetCloseHandle( handle );
2641 handle = NULL;
2642 goto lend;
2643 }
2644
2645 if (lpszObjectName && *lpszObjectName) {
2646 HRESULT rc;
2647
2648 len = 0;
2649 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2650 if (rc != E_POINTER)
2651 len = strlenW(lpszObjectName)+1;
2652 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2653 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2654 URL_ESCAPE_SPACES_ONLY);
2655 if (rc != S_OK)
2656 {
2657 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2658 strcpyW(lpwhr->lpszPath,lpszObjectName);
2659 }
2660 }else {
2661 static const WCHAR slashW[] = {'/',0};
2662
2663 lpwhr->lpszPath = heap_strdupW(slashW);
2664 }
2665
2666 if (lpszReferrer && *lpszReferrer)
2667 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2668
2669 if (lpszAcceptTypes)
2670 {
2671 int i;
2672 for (i = 0; lpszAcceptTypes[i]; i++)
2673 {
2674 if (!*lpszAcceptTypes[i]) continue;
2675 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2676 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2677 HTTP_ADDHDR_FLAG_REQ |
2678 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2679 }
2680 }
2681
2682 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2683 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2684
2685 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2686 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2687 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2688 {
2689 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2690 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2691 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2692 }
2693 else
2694 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2695 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2696
2697 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2698 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2699 INTERNET_DEFAULT_HTTPS_PORT :
2700 INTERNET_DEFAULT_HTTP_PORT);
2701
2702 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2703 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2704 INTERNET_DEFAULT_HTTPS_PORT :
2705 INTERNET_DEFAULT_HTTP_PORT);
2706
2707 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2708 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2709
2710 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2711 INTERNET_STATUS_HANDLE_CREATED, &handle,
2712 sizeof(handle));
2713
2714 lend:
2715 HeapFree(GetProcessHeap(), 0, lpszHostName);
2716 if( lpwhr )
2717 WININET_Release( &lpwhr->hdr );
2718
2719 TRACE("<-- %p (%p)\n", handle, lpwhr);
2720 return handle;
2721 }
2722
2723 /* read any content returned by the server so that the connection can be
2724 * reused */
2725 static void HTTP_DrainContent(http_request_t *req)
2726 {
2727 DWORD bytes_read;
2728
2729 if (!NETCON_connected(&req->netConnection)) return;
2730
2731 if (req->dwContentLength == -1)
2732 {
2733 NETCON_close(&req->netConnection);
2734 return;
2735 }
2736 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2737
2738 do
2739 {
2740 char buffer[2048];
2741 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2742 return;
2743 } while (bytes_read);
2744 }
2745
2746 static const LPCWSTR header_lookup[] = {
2747 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2748 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2749 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2750 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2751 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2752 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2753 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2754 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2755 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2756 szDate, /* HTTP_QUERY_DATE = 9 */
2757 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2758 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2759 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2760 szURI, /* HTTP_QUERY_URI = 13 */
2761 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2762 NULL, /* HTTP_QUERY_COST = 15 */
2763 NULL, /* HTTP_QUERY_LINK = 16 */
2764 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2765 NULL, /* HTTP_QUERY_VERSION = 18 */
2766 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2767 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2768 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2769 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2770 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2771 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2772 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2773 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2774 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2775 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2776 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2777 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2778 NULL, /* HTTP_QUERY_FROM = 31 */
2779 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2780 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2781 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2782 szReferer, /* HTTP_QUERY_REFERER = 35 */
2783 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2784 szServer, /* HTTP_QUERY_SERVER = 37 */
2785 NULL, /* HTTP_TITLE = 38 */
2786 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2787 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2788 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2789 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2790 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2791 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2792 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2793 NULL, /* HTTP_QUERY_REFRESH = 46 */
2794 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2795 szAge, /* HTTP_QUERY_AGE = 48 */
2796 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2797 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2798 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2799 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2800 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2801 szETag, /* HTTP_QUERY_ETAG = 54 */
2802 hostW, /* HTTP_QUERY_HOST = 55 */
2803 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2804 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2805 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2806 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2807 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2808 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2809 szRange, /* HTTP_QUERY_RANGE = 62 */
2810 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2811 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2812 szVary, /* HTTP_QUERY_VARY = 65 */
2813 szVia, /* HTTP_QUERY_VIA = 66 */
2814 szWarning, /* HTTP_QUERY_WARNING = 67 */
2815 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2816 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2817 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2818 };
2819
2820 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2821
2822 /***********************************************************************
2823 * HTTP_HttpQueryInfoW (internal)
2824 */
2825 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2826 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2827 {
2828 LPHTTPHEADERW lphttpHdr = NULL;
2829 BOOL bSuccess = FALSE;
2830 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2831 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2832 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2833 INT index = -1;
2834
2835 /* Find requested header structure */
2836 switch (level)
2837 {
2838 case HTTP_QUERY_CUSTOM:
2839 if (!lpBuffer) return FALSE;
2840 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2841 break;
2842 case HTTP_QUERY_RAW_HEADERS_CRLF:
2843 {
2844 LPWSTR headers;
2845 DWORD len = 0;
2846 BOOL ret = FALSE;
2847
2848 if (request_only)
2849 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2850 else
2851 headers = lpwhr->lpszRawHeaders;
2852
2853 if (headers)
2854 len = strlenW(headers) * sizeof(WCHAR);
2855
2856 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2857 {
2858 len += sizeof(WCHAR);
2859 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2860 ret = FALSE;
2861 }
2862 else if (lpBuffer)
2863 {
2864 if (headers)
2865 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2866 else
2867 {
2868 len = strlenW(szCrLf) * sizeof(WCHAR);
2869 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2870 }
2871 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2872 ret = TRUE;
2873 }
2874 *lpdwBufferLength = len;
2875
2876 if (request_only)
2877 HeapFree(GetProcessHeap(), 0, headers);
2878 return ret;
2879 }
2880 case HTTP_QUERY_RAW_HEADERS:
2881 {
2882 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2883 DWORD i, size = 0;
2884 LPWSTR pszString = lpBuffer;
2885
2886 for (i = 0; ppszRawHeaderLines[i]; i++)
2887 size += strlenW(ppszRawHeaderLines[i]) + 1;
2888
2889 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2890 {
2891 HTTP_FreeTokens(ppszRawHeaderLines);
2892 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2893 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2894 return FALSE;
2895 }
2896 if (pszString)
2897 {
2898 for (i = 0; ppszRawHeaderLines[i]; i++)
2899 {
2900 DWORD len = strlenW(ppszRawHeaderLines[i]);
2901 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2902 pszString += len+1;
2903 }
2904 *pszString = '\0';
2905 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2906 }
2907 *lpdwBufferLength = size * sizeof(WCHAR);
2908 HTTP_FreeTokens(ppszRawHeaderLines);
2909
2910 return TRUE;
2911 }
2912 case HTTP_QUERY_STATUS_TEXT:
2913 if (lpwhr->lpszStatusText)
2914 {
2915 DWORD len = strlenW(lpwhr->lpszStatusText);
2916 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2917 {
2918 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2919 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2920 return FALSE;
2921 }
2922 if (lpBuffer)
2923 {
2924 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2925 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2926 }
2927 *lpdwBufferLength = len * sizeof(WCHAR);
2928 return TRUE;
2929 }
2930 break;
2931 case HTTP_QUERY_VERSION:
2932 if (lpwhr->lpszVersion)
2933 {
2934 DWORD len = strlenW(lpwhr->lpszVersion);
2935 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2936 {
2937 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2938 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2939 return FALSE;
2940 }
2941 if (lpBuffer)
2942 {
2943 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2944 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2945 }
2946 *lpdwBufferLength = len * sizeof(WCHAR);
2947 return TRUE;
2948 }
2949 break;
2950 case HTTP_QUERY_CONTENT_ENCODING:
2951 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2952 requested_index,request_only);
2953 break;
2954 default:
2955 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2956
2957 if (level < LAST_TABLE_HEADER && header_lookup[level])
2958 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2959 requested_index,request_only);
2960 }
2961
2962 if (index >= 0)
2963 lphttpHdr = &lpwhr->pCustHeaders[index];
2964
2965 /* Ensure header satisfies requested attributes */
2966 if (!lphttpHdr ||
2967 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2968 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2969 {
2970 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2971 return bSuccess;
2972 }
2973
2974 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2975
2976 /* coalesce value to requested type */
2977 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2978 {
2979 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2980 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2981 bSuccess = TRUE;
2982 }
2983 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2984 {
2985 time_t tmpTime;
2986 struct tm tmpTM;
2987 SYSTEMTIME *STHook;
2988
2989 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2990
2991 tmpTM = *gmtime(&tmpTime);
2992 STHook = (SYSTEMTIME *)lpBuffer;
2993 STHook->wDay = tmpTM.tm_mday;
2994 STHook->wHour = tmpTM.tm_hour;
2995 STHook->wMilliseconds = 0;
2996 STHook->wMinute = tmpTM.tm_min;
2997 STHook->wDayOfWeek = tmpTM.tm_wday;
2998 STHook->wMonth = tmpTM.tm_mon + 1;
2999 STHook->wSecond = tmpTM.tm_sec;
3000 STHook->wYear = tmpTM.tm_year;
3001 bSuccess = TRUE;
3002
3003 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3004 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3005 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3006 }
3007 else if (lphttpHdr->lpszValue)
3008 {
3009 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3010
3011 if (len > *lpdwBufferLength)
3012 {
3013 *lpdwBufferLength = len;
3014 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
3015 return bSuccess;
3016 }
3017 if (lpBuffer)
3018 {
3019 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3020 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
3021 }
3022 *lpdwBufferLength = len - sizeof(WCHAR);
3023 bSuccess = TRUE;
3024 }
3025 return bSuccess;
3026 }
3027
3028 /***********************************************************************
3029 * HttpQueryInfoW (WININET.@)
3030 *
3031 * Queries for information about an HTTP request
3032 *
3033 * RETURNS
3034 * TRUE on success
3035 * FALSE on failure
3036 *
3037 */
3038 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3039 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3040 {
3041 BOOL bSuccess = FALSE;
3042 http_request_t *lpwhr;
3043
3044 if (TRACE_ON(wininet)) {
3045 #define FE(x) { x, #x }
3046 static const wininet_flag_info query_flags[] = {
3047 FE(HTTP_QUERY_MIME_VERSION),
3048 FE(HTTP_QUERY_CONTENT_TYPE),
3049 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3050 FE(HTTP_QUERY_CONTENT_ID),
3051 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3052 FE(HTTP_QUERY_CONTENT_LENGTH),
3053 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3054 FE(HTTP_QUERY_ALLOW),
3055 FE(HTTP_QUERY_PUBLIC),
3056 FE(HTTP_QUERY_DATE),
3057 FE(HTTP_QUERY_EXPIRES),
3058 FE(HTTP_QUERY_LAST_MODIFIED),
3059 FE(HTTP_QUERY_MESSAGE_ID),
3060 FE(HTTP_QUERY_URI),
3061 FE(HTTP_QUERY_DERIVED_FROM),
3062 FE(HTTP_QUERY_COST),
3063 FE(HTTP_QUERY_LINK),
3064 FE(HTTP_QUERY_PRAGMA),
3065 FE(HTTP_QUERY_VERSION),
3066 FE(HTTP_QUERY_STATUS_CODE),
3067 FE(HTTP_QUERY_STATUS_TEXT),
3068 FE(HTTP_QUERY_RAW_HEADERS),
3069 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3070 FE(HTTP_QUERY_CONNECTION),
3071 FE(HTTP_QUERY_ACCEPT),
3072 FE(HTTP_QUERY_ACCEPT_CHARSET),
3073 FE(HTTP_QUERY_ACCEPT_ENCODING),
3074 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3075 FE(HTTP_QUERY_AUTHORIZATION),
3076 FE(HTTP_QUERY_CONTENT_ENCODING),
3077 FE(HTTP_QUERY_FORWARDED),
3078 FE(HTTP_QUERY_FROM),
3079 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3080 FE(HTTP_QUERY_LOCATION),
3081 FE(HTTP_QUERY_ORIG_URI),
3082 FE(HTTP_QUERY_REFERER),
3083 FE(HTTP_QUERY_RETRY_AFTER),
3084 FE(HTTP_QUERY_SERVER),
3085 FE(HTTP_QUERY_TITLE),
3086 FE(HTTP_QUERY_USER_AGENT),
3087 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3088 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3089 FE(HTTP_QUERY_ACCEPT_RANGES),
3090 FE(HTTP_QUERY_SET_COOKIE),
3091 FE(HTTP_QUERY_COOKIE),
3092 FE(HTTP_QUERY_REQUEST_METHOD),
3093 FE(HTTP_QUERY_REFRESH),
3094 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3095 FE(HTTP_QUERY_AGE),
3096 FE(HTTP_QUERY_CACHE_CONTROL),
3097 FE(HTTP_QUERY_CONTENT_BASE),
3098 FE(HTTP_QUERY_CONTENT_LOCATION),
3099 FE(HTTP_QUERY_CONTENT_MD5),
3100 FE(HTTP_QUERY_CONTENT_RANGE),
3101 FE(HTTP_QUERY_ETAG),
3102 FE(HTTP_QUERY_HOST),
3103 FE(HTTP_QUERY_IF_MATCH),
3104 FE(HTTP_QUERY_IF_NONE_MATCH),
3105 FE(HTTP_QUERY_IF_RANGE),
3106 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3107 FE(HTTP_QUERY_MAX_FORWARDS),
3108 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3109 FE(HTTP_QUERY_RANGE),
3110 FE(HTTP_QUERY_TRANSFER_ENCODING),
3111 FE(HTTP_QUERY_UPGRADE),
3112 FE(HTTP_QUERY_VARY),
3113 FE(HTTP_QUERY_VIA),
3114 FE(HTTP_QUERY_WARNING),
3115 FE(HTTP_QUERY_CUSTOM)
3116 };
3117 static const wininet_flag_info modifier_flags[] = {
3118 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3119 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3120 FE(HTTP_QUERY_FLAG_NUMBER),
3121 FE(HTTP_QUERY_FLAG_COALESCE)
3122 };
3123 #undef FE
3124 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3125 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3126 DWORD i;
3127
3128 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
3129 TRACE(" Attribute:");
3130 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3131 if (query_flags[i].val == info) {
3132 TRACE(" %s", query_flags[i].name);
3133 break;
3134 }
3135 }
3136 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3137 TRACE(" Unknown (%08x)", info);
3138 }
3139
3140 TRACE(" Modifier:");
3141 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3142 if (modifier_flags[i].val & info_mod) {
3143 TRACE(" %s", modifier_flags[i].name);
3144 info_mod &= ~ modifier_flags[i].val;
3145 }
3146 }
3147
3148 if (info_mod) {
3149 TRACE(" Unknown (%08x)", info_mod);
3150 }
3151 TRACE("\n");
3152 }
3153
3154 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3155 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3156 {
3157 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3158 goto lend;
3159 }
3160
3161 if (lpBuffer == NULL)
3162 *lpdwBufferLength = 0;
3163 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3164 lpBuffer, lpdwBufferLength, lpdwIndex);
3165
3166 lend:
3167 if( lpwhr )
3168 WININET_Release( &lpwhr->hdr );
3169
3170 TRACE("%d <--\n", bSuccess);
3171 return bSuccess;
3172 }
3173
3174 /***********************************************************************
3175 * HttpQueryInfoA (WININET.@)
3176 *
3177 * Queries for information about an HTTP request
3178 *
3179 * RETURNS
3180 * TRUE on success
3181 * FALSE on failure
3182 *
3183 */
3184 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3185 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3186 {
3187 BOOL result;
3188 DWORD len;
3189 WCHAR* bufferW;
3190
3191 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3192 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3193 {
3194 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3195 lpdwBufferLength, lpdwIndex );
3196 }
3197
3198 if (lpBuffer)
3199 {
3200 DWORD alloclen;
3201 len = (*lpdwBufferLength)*sizeof(WCHAR);
3202 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3203 {
3204 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3205 if (alloclen < len)
3206 alloclen = len;
3207 }
3208 else
3209 alloclen = len;
3210 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3211 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3212 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3213 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3214 } else
3215 {
3216 bufferW = NULL;
3217 len = 0;
3218 }
3219
3220 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3221 &len, lpdwIndex );
3222 if( result )
3223 {
3224 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3225 lpBuffer, *lpdwBufferLength, NULL, NULL );
3226 *lpdwBufferLength = len - 1;
3227
3228 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3229 }
3230 else
3231 /* since the strings being returned from HttpQueryInfoW should be
3232 * only ASCII characters, it is reasonable to assume that all of
3233 * the Unicode characters can be reduced to a single byte */
3234 *lpdwBufferLength = len / sizeof(WCHAR);
3235
3236 HeapFree(GetProcessHeap(), 0, bufferW );
3237
3238 return result;
3239 }
3240
3241 /***********************************************************************
3242 * HttpSendRequestExA (WININET.@)
3243 *
3244 * Sends the specified request to the HTTP server and allows chunked
3245 * transfers.
3246 *
3247 * RETURNS
3248 * Success: TRUE
3249 * Failure: FALSE, call GetLastError() for more information.
3250 */
3251 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3252 LPINTERNET_BUFFERSA lpBuffersIn,
3253 LPINTERNET_BUFFERSA lpBuffersOut,
3254 DWORD dwFlags, DWORD_PTR dwContext)
3255 {
3256 INTERNET_BUFFERSW BuffersInW;
3257 BOOL rc = FALSE;
3258 DWORD headerlen;
3259 LPWSTR header = NULL;
3260
3261 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3262 lpBuffersOut, dwFlags, dwContext);
3263
3264 if (lpBuffersIn)
3265 {
3266 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3267 if (lpBuffersIn->lpcszHeader)
3268 {
3269 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3270 lpBuffersIn->dwHeadersLength,0,0);
3271 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3272 if (!(BuffersInW.lpcszHeader = header))
3273 {
3274 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3275 return FALSE;
3276 }
3277 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3278 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3279 header, headerlen);
3280 }
3281 else
3282 BuffersInW.lpcszHeader = NULL;
3283 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3284 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3285 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3286 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3287 BuffersInW.Next = NULL;
3288 }
3289
3290 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3291
3292 HeapFree(GetProcessHeap(),0,header);
3293
3294 return rc;
3295 }
3296
3297 /***********************************************************************
3298 * HttpSendRequestExW (WININET.@)
3299 *
3300 * Sends the specified request to the HTTP server and allows chunked
3301 * transfers
3302 *
3303 * RETURNS
3304 * Success: TRUE
3305 * Failure: FALSE, call GetLastError() for more information.
3306 */
3307 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3308 LPINTERNET_BUFFERSW lpBuffersIn,
3309 LPINTERNET_BUFFERSW lpBuffersOut,
3310 DWORD dwFlags, DWORD_PTR dwContext)
3311 {
3312 BOOL ret = FALSE;
3313 http_request_t *lpwhr;
3314 http_session_t *lpwhs;
3315 appinfo_t *hIC;
3316
3317 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3318 lpBuffersOut, dwFlags, dwContext);
3319
3320 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3321
3322 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3323 {
3324 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3325 goto lend;
3326 }
3327
3328 lpwhs = lpwhr->lpHttpSession;
3329 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3330 hIC = lpwhs->lpAppInfo;
3331 assert(hIC->hdr.htype == WH_HINIT);
3332
3333 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3334 {
3335 WORKREQUEST workRequest;
3336 struct WORKREQ_HTTPSENDREQUESTW *req;
3337
3338 workRequest.asyncproc = AsyncHttpSendRequestProc;
3339 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3340 req = &workRequest.u.HttpSendRequestW;
3341 if (lpBuffersIn)
3342 {
3343 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3344 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3345 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3346 req->lpOptional = lpBuffersIn->lpvBuffer;
3347 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3348 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3349 }
3350 else
3351 {
3352 req->lpszHeader = NULL;
3353 req->dwHeaderLength = 0;
3354 req->lpOptional = NULL;
3355 req->dwOptionalLength = 0;
3356 req->dwContentLength = 0;
3357 }
3358
3359 req->bEndRequest = FALSE;
3360
3361 INTERNET_AsyncCall(&workRequest);
3362 /*
3363 * This is from windows.
3364 */
3365 INTERNET_SetLastError(ERROR_IO_PENDING);
3366 }
3367 else
3368 {
3369 if (lpBuffersIn)
3370 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3371 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3372 lpBuffersIn->dwBufferTotal, FALSE);
3373 else
3374 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3375 }
3376
3377 lend:
3378 if ( lpwhr )
3379 WININET_Release( &lpwhr->hdr );
3380
3381 TRACE("<---\n");
3382 return ret;
3383 }
3384
3385 /***********************************************************************
3386 * HttpSendRequestW (WININET.@)
3387 *
3388 * Sends the specified request to the HTTP server
3389 *
3390 * RETURNS
3391 * TRUE on success
3392 * FALSE on failure
3393 *
3394 */
3395 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3396 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3397 {
3398 http_request_t *lpwhr;
3399 http_session_t *lpwhs = NULL;
3400 appinfo_t *hIC = NULL;
3401 BOOL r;
3402
3403 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3404 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3405
3406 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3407 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3408 {
3409 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3410 r = FALSE;
3411 goto lend;
3412 }
3413
3414 lpwhs = lpwhr->lpHttpSession;
3415 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3416 {
3417 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3418 r = FALSE;
3419 goto lend;
3420 }
3421
3422 hIC = lpwhs->lpAppInfo;
3423 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3424 {
3425 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3426 r = FALSE;
3427 goto lend;
3428 }
3429
3430 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3431 {
3432 WORKREQUEST workRequest;
3433 struct WORKREQ_HTTPSENDREQUESTW *req;
3434
3435 workRequest.asyncproc = AsyncHttpSendRequestProc;
3436 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3437 req = &workRequest.u.HttpSendRequestW;
3438 if (lpszHeaders)
3439 {
3440 DWORD size;
3441
3442 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3443 else size = dwHeaderLength * sizeof(WCHAR);
3444
3445 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3446 memcpy(req->lpszHeader, lpszHeaders, size);
3447 }
3448 else
3449 req->lpszHeader = 0;
3450 req->dwHeaderLength = dwHeaderLength;
3451 req->lpOptional = lpOptional;
3452 req->dwOptionalLength = dwOptionalLength;
3453 req->dwContentLength = dwOptionalLength;
3454 req->bEndRequest = TRUE;
3455
3456 INTERNET_AsyncCall(&workRequest);
3457 /*
3458 * This is from windows.
3459 */
3460 INTERNET_SetLastError(ERROR_IO_PENDING);
3461 r = FALSE;
3462 }
3463 else
3464 {
3465 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3466 dwHeaderLength, lpOptional, dwOptionalLength,
3467 dwOptionalLength, TRUE);
3468 }
3469 lend:
3470 if( lpwhr )
3471 WININET_Release( &lpwhr->hdr );
3472 return r;
3473 }
3474
3475 /***********************************************************************
3476 * HttpSendRequestA (WININET.@)
3477 *
3478 * Sends the specified request to the HTTP server
3479 *
3480 * RETURNS
3481 * TRUE on success
3482 * FALSE on failure
3483 *
3484 */
3485 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3486 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3487 {
3488 BOOL result;
3489 LPWSTR szHeaders=NULL;
3490 DWORD nLen=dwHeaderLength;
3491 if(lpszHeaders!=NULL)
3492 {
3493 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3494 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3495 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3496 }
3497 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3498 HeapFree(GetProcessHeap(),0,szHeaders);
3499 return result;
3500 }
3501
3502 /***********************************************************************
3503 * HTTP_GetRedirectURL (internal)
3504 */
3505 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3506 {
3507 static WCHAR szHttp[] = {'h','t','t','p',0};
3508 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3509 http_session_t *lpwhs = lpwhr->lpHttpSession;
3510 URL_COMPONENTSW urlComponents;
3511 DWORD url_length = 0;
3512 LPWSTR orig_url;
3513 LPWSTR combined_url;
3514
3515 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3516 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3517 urlComponents.dwSchemeLength = 0;
3518 urlComponents.lpszHostName = lpwhs->lpszHostName;
3519 urlComponents.dwHostNameLength = 0;
3520 urlComponents.nPort = lpwhs->nHostPort;
3521 urlComponents.lpszUserName = lpwhs->lpszUserName;
3522 urlComponents.dwUserNameLength = 0;
3523 urlComponents.lpszPassword = NULL;
3524 urlComponents.dwPasswordLength = 0;
3525 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3526 urlComponents.dwUrlPathLength = 0;
3527 urlComponents.lpszExtraInfo = NULL;
3528 urlComponents.dwExtraInfoLength = 0;
3529
3530 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3531 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3532 return NULL;
3533
3534 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3535
3536 /* convert from bytes to characters */
3537 url_length = url_length / sizeof(WCHAR) - 1;
3538 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3539 {
3540 HeapFree(GetProcessHeap(), 0, orig_url);
3541 return NULL;
3542 }
3543
3544 url_length = 0;
3545 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3546 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3547 {
3548 HeapFree(GetProcessHeap(), 0, orig_url);
3549 return NULL;
3550 }
3551 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3552
3553 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3554 {
3555 HeapFree(GetProcessHeap(), 0, orig_url);
3556 HeapFree(GetProcessHeap(), 0, combined_url);
3557 return NULL;
3558 }
3559 HeapFree(GetProcessHeap(), 0, orig_url);
3560 return combined_url;
3561 }
3562
3563
3564 /***********************************************************************
3565 * HTTP_HandleRedirect (internal)
3566 */
3567 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3568 {
3569 http_session_t *lpwhs = lpwhr->lpHttpSession;
3570 appinfo_t *hIC = lpwhs->lpAppInfo;
3571 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3572 WCHAR path[INTERNET_MAX_URL_LENGTH];
3573 int index;
3574
3575 if(lpszUrl[0]=='/')
3576 {
3577 /* if it's an absolute path, keep the same session info */
3578 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3579 }
3580 else
3581 {
3582 URL_COMPONENTSW urlComponents;
3583 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3584 static WCHAR szHttp[] = {'h','t','t','p',0};
3585 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3586
3587 userName[0] = 0;
3588 hostName[0] = 0;
3589 protocol[0] = 0;
3590
3591 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3592 urlComponents.lpszScheme = protocol;
3593 urlComponents.dwSchemeLength = 32;
3594 urlComponents.lpszHostName = hostName;
3595 urlComponents.dwHostNameLength = MAXHOSTNAME;
3596 urlComponents.lpszUserName = userName;
3597 urlComponents.dwUserNameLength = 1024;
3598 urlComponents.lpszPassword = NULL;
3599 urlComponents.dwPasswordLength = 0;
3600 urlComponents.lpszUrlPath = path;
3601 urlComponents.dwUrlPathLength = 2048;
3602 urlComponents.lpszExtraInfo = NULL;
3603 urlComponents.dwExtraInfoLength = 0;
3604 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3605 return FALSE;
3606
3607 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3608 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3609 {
3610 TRACE("redirect from secure page to non-secure page\n");
3611 /* FIXME: warn about from secure redirect to non-secure page */
3612 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3613 }
3614 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3615 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3616 {
3617 TRACE("redirect from non-secure page to secure page\n");
3618 /* FIXME: notify about redirect to secure page */
3619 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3620 }
3621
3622 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3623 {
3624 if (lstrlenW(protocol)>4) /*https*/
3625 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3626 else /*http*/
3627 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3628 }
3629
3630 #if 0
3631 /*
3632 * This upsets redirects to binary files on sourceforge.net
3633 * and gives an html page instead of the target file
3634 * Examination of the HTTP request sent by native wininet.dll
3635 * reveals that it doesn't send a referrer in that case.
3636 * Maybe there's a flag that enables this, or maybe a referrer
3637 * shouldn't be added in case of a redirect.
3638 */
3639
3640 /* consider the current host as the referrer */
3641 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3642 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3643 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3644 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3645 #endif
3646
3647 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3648 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3649 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3650 {
3651 int len;
3652 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3653 len = lstrlenW(hostName);
3654 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3655 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3656 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3657 }
3658 else
3659 lpwhs->lpszHostName = heap_strdupW(hostName);
3660
3661 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3662
3663 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3664 lpwhs->lpszUserName = NULL;
3665 if (userName[0])
3666 lpwhs->lpszUserName = heap_strdupW(userName);
3667
3668 if (!using_proxy)
3669 {
3670 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3671 {
3672 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3673 lpwhs->lpszServerName = heap_strdupW(hostName);
3674 lpwhs->nServerPort = urlComponents.nPort;
3675
3676 NETCON_close(&lpwhr->netConnection);
3677 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3678 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3679 lpwhr->read_pos = lpwhr->read_size = 0;
3680 lpwhr->read_chunked = FALSE;
3681 }
3682 }
3683 else
3684 TRACE("Redirect through proxy\n");
3685 }
3686
3687 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3688 lpwhr->lpszPath=NULL;
3689 if (*path)
3690 {
3691 DWORD needed = 0;
3692 HRESULT rc;
3693
3694 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3695 if (rc != E_POINTER)
3696 needed = strlenW(path)+1;
3697 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3698 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3699 URL_ESCAPE_SPACES_ONLY);
3700 if (rc != S_OK)
3701 {
3702 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3703 strcpyW(lpwhr->lpszPath,path);
3704 }
3705 }
3706
3707 /* Remove custom content-type/length headers on redirects. */
3708 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3709 if (0 <= index)
3710 HTTP_DeleteCustomHeader(lpwhr, index);
3711 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3712 if (0 <= index)
3713 HTTP_DeleteCustomHeader(lpwhr, index);
3714
3715 return TRUE;
3716 }
3717
3718 /***********************************************************************
3719 * HTTP_build_req (internal)
3720 *
3721 * concatenate all the strings in the request together
3722 */
3723 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3724 {
3725 LPCWSTR *t;
3726 LPWSTR str;
3727
3728 for( t = list; *t ; t++ )
3729 len += strlenW( *t );
3730 len++;
3731
3732 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3733 *str = 0;
3734
3735 for( t = list; *t ; t++ )
3736 strcatW( str, *t );
3737
3738 return str;
3739 }
3740
3741 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3742 {
3743 LPWSTR lpszPath;
3744 LPWSTR requestString;
3745 INT len;
3746 INT cnt;
3747 INT responseLen;
3748 char *ascii_req;
3749 BOOL ret;
3750 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3751 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3752 http_session_t *lpwhs = lpwhr->lpHttpSession;
3753
3754 TRACE("\n");
3755
3756 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3757 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3758 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3759 HeapFree( GetProcessHeap(), 0, lpszPath );
3760
3761 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3762 NULL, 0, NULL, NULL );
3763 len--; /* the nul terminator isn't needed */
3764 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3765 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3766 ascii_req, len, NULL, NULL );
3767 HeapFree( GetProcessHeap(), 0, requestString );
3768
3769 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3770
3771 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3772 HeapFree( GetProcessHeap(), 0, ascii_req );
3773 if (!ret || cnt < 0)
3774 return FALSE;
3775
3776 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3777 if (!responseLen)
3778 return FALSE;
3779
3780 return TRUE;
3781 }
3782
3783 static void HTTP_InsertCookies(http_request_t *lpwhr)
3784 {
3785 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3786 LPWSTR lpszCookies, lpszUrl = NULL;
3787 DWORD nCookieSize, size;
3788 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3789
3790 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3791 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3792 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3793
3794 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3795 {
3796 int cnt = 0;
3797 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3798
3799 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3800 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3801 {
3802 cnt += sprintfW(lpszCookies, szCookie);
3803 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3804 strcatW(lpszCookies, szCrLf);
3805
3806 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3807 HeapFree(GetProcessHeap(), 0, lpszCookies);
3808 }
3809 }
3810 HeapFree(GetProcessHeap(), 0, lpszUrl);
3811 }
3812
3813 /***********************************************************************
3814 * HTTP_HttpSendRequestW (internal)
3815 *
3816 * Sends the specified request to the HTTP server
3817 *
3818 * RETURNS
3819 * TRUE on success
3820 * FALSE on failure
3821 *
3822 */
3823 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3824 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3825 DWORD dwContentLength, BOOL bEndRequest)
3826 {
3827 INT cnt;
3828 BOOL bSuccess = FALSE, redirected = FALSE;
3829 LPWSTR requestString = NULL;
3830 INT responseLen;
3831 BOOL loop_next;
3832 INTERNET_ASYNC_RESULT iar;
3833 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3834 static const WCHAR szContentLength[] =
3835 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3836 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3837
3838 TRACE("--> %p\n", lpwhr);
3839
3840 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3841
3842 /* if the verb is NULL default to GET */
3843 if (!lpwhr->lpszVerb)
3844 lpwhr->lpszVerb = heap_strdupW(szGET);
3845
3846 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3847 {
3848 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3849 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3850 lpwhr->dwBytesToWrite = dwContentLength;
3851 }
3852 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3853 {
3854 WCHAR *agent_header;
3855 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3856 int len;
3857
3858 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3859 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3860 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3861
3862 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3863 HeapFree(GetProcessHeap(), 0, agent_header);
3864 }
3865 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3866 {
3867 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3868 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3869 }
3870 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3871 {
3872 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3873 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3874 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3875 }
3876
3877 do
3878 {
3879 DWORD len;
3880 char *ascii_req;
3881
3882 loop_next = FALSE;
3883
3884 /* like native, just in case the caller forgot to call InternetReadFile
3885 * for all the data */
3886 HTTP_DrainContent(lpwhr);
3887 lpwhr->dwContentRead = 0;
3888
3889 if (TRACE_ON(wininet))
3890 {
3891 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3892 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3893 }
3894
3895 HTTP_FixURL(lpwhr);
3896 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3897 {
3898 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3899 }
3900 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3901 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3902
3903 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3904 HTTP_InsertCookies(lpwhr);
3905
3906 /* add the headers the caller supplied */
3907 if( lpszHeaders && dwHeaderLength )
3908 {
3909 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3910 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3911 }
3912
3913 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3914 {
3915 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3916 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3917 HeapFree(GetProcessHeap(), 0, url);
3918 }
3919 else
3920 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3921
3922
3923 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3924
3925 /* Send the request and store the results */
3926 if (!HTTP_OpenConnection(lpwhr))
3927 goto lend;
3928
3929 /* send the request as ASCII, tack on the optional data */
3930 if (!lpOptional || redirected)
3931 dwOptionalLength = 0;
3932 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3933 NULL, 0, NULL, NULL );
3934 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3935 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3936 ascii_req, len, NULL, NULL );
3937 if( lpOptional )
3938 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3939 len = (len + dwOptionalLength - 1);
3940 ascii_req[len] = 0;
3941 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3942
3943 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3944 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3945
3946 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3947 HeapFree( GetProcessHeap(), 0, ascii_req );
3948
3949 lpwhr->dwBytesWritten = dwOptionalLength;
3950
3951 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3952 INTERNET_STATUS_REQUEST_SENT,
3953 &len, sizeof(DWORD));
3954
3955 if (bEndRequest)
3956 {
3957 DWORD dwBufferSize;
3958 DWORD dwStatusCode;
3959
3960 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3961 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3962
3963 if (cnt < 0)
3964 goto lend;
3965
3966 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3967 if (responseLen)
3968 bSuccess = TRUE;
3969
3970 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3971 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3972 sizeof(DWORD));
3973
3974 HTTP_ProcessCookies(lpwhr);
3975
3976 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3977
3978 dwBufferSize = sizeof(dwStatusCode);
3979 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3980 &dwStatusCode,&dwBufferSize,NULL))
3981 dwStatusCode = 0;
3982
3983 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3984 {
3985 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3986 dwBufferSize=sizeof(szNewLocation);
3987 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3988 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3989 {
3990 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3991 {
3992 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3993 lpwhr->lpszVerb = heap_strdupW(szGET);
3994 }
3995 HTTP_DrainContent(lpwhr);
3996 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3997 {
3998 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3999 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4000 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
4001 if (bSuccess)
4002 {
4003 HeapFree(GetProcessHeap(), 0, requestString);
4004 loop_next = TRUE;
4005 }
4006 HeapFree( GetProcessHeap(), 0, new_url );
4007 }
4008 redirected = TRUE;
4009 }
4010 }
4011 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
4012 {
4013 WCHAR szAuthValue[2048];
4014 dwBufferSize=2048;
4015 if (dwStatusCode == HTTP_STATUS_DENIED)
4016 {
4017 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
4018 DWORD dwIndex = 0;
4019 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4020 {
4021 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4022 &lpwhr->pAuthInfo,
4023 lpwhr->lpHttpSession->lpszUserName,
4024 lpwhr->lpHttpSession->lpszPassword,
4025 Host->lpszValue))
4026 {
4027 loop_next = TRUE;
4028 break;
4029 }
4030 }
4031 }
4032 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4033 {
4034 DWORD dwIndex = 0;
4035 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
4036 {
4037 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
4038 &lpwhr->pProxyAuthInfo,
4039 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
4040 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
4041 NULL))
4042 {
4043 loop_next = TRUE;
4044 break;
4045 }
4046 }
4047 }
4048 }
4049 }
4050 else
4051 bSuccess = TRUE;
4052 }
4053 while (loop_next);
4054
4055 if(bSuccess) {
4056 WCHAR url[INTERNET_MAX_URL_LENGTH];
4057 WCHAR cacheFileName[MAX_PATH+1];
4058 BOOL b;
4059
4060 b = HTTP_GetRequestURL(lpwhr, url);
4061 if(!b) {
4062 WARN("Could not get URL\n");
4063 goto lend;
4064 }
4065
4066 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
4067 if(b) {
4068 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
4069 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4070 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4071 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
4072 WARN("Could not create file: %u\n", GetLastError());
4073 lpwhr->hCacheFile = NULL;
4074 }
4075 }else {
4076 WARN("Could not create cache entry: %08x\n", GetLastError());
4077 }
4078 }
4079
4080 lend:
4081
4082 HeapFree(GetProcessHeap(), 0, requestString);
4083
4084 /* TODO: send notification for P3P header */
4085
4086 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4087 {
4088 if (bSuccess)
4089 {
4090 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
4091 else
4092 {
4093 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4094 iar.dwError = 0;
4095
4096 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4097 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4098 sizeof(INTERNET_ASYNC_RESULT));
4099 }
4100 }
4101 else
4102 {
4103 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
4104 iar.dwError = INTERNET_GetLastError();
4105
4106 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4107 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4108 sizeof(INTERNET_ASYNC_RESULT));
4109 }
4110 }
4111
4112 TRACE("<--\n");
4113 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
4114 return bSuccess;
4115 }
4116
4117 /***********************************************************************
4118 * HTTPSESSION_Destroy (internal)
4119 *
4120 * Deallocate session handle
4121 *
4122 */
4123 static void HTTPSESSION_Destroy(object_header_t *hdr)
4124 {
4125 http_session_t *lpwhs = (http_session_t*) hdr;
4126
4127 TRACE("%p\n", lpwhs);
4128
4129 WININET_Release(&lpwhs->lpAppInfo->hdr);
4130
4131 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4132 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4133 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4134 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4135 HeapFree(GetProcessHeap(), 0, lpwhs);
4136 }
4137
4138 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4139 {
4140 switch(option) {
4141 case INTERNET_OPTION_HANDLE_TYPE:
4142 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4143
4144 if (*size < sizeof(ULONG))
4145 return ERROR_INSUFFICIENT_BUFFER;
4146
4147 *size = sizeof(DWORD);
4148 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4149 return ERROR_SUCCESS;
4150 }
4151
4152 return INET_QueryOption(option, buffer, size, unicode);
4153 }
4154
4155 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4156 {
4157 http_session_t *ses = (http_session_t*)hdr;
4158
4159 switch(option) {
4160 case INTERNET_OPTION_USERNAME:
4161 {
4162 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4163 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4164 return ERROR_SUCCESS;
4165 }
4166 case INTERNET_OPTION_PASSWORD:
4167 {
4168 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4169 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4170 return ERROR_SUCCESS;
4171 }
4172 default: break;
4173 }
4174
4175 return ERROR_INTERNET_INVALID_OPTION;
4176 }
4177
4178 static const object_vtbl_t HTTPSESSIONVtbl = {
4179 HTTPSESSION_Destroy,
4180 NULL,
4181 HTTPSESSION_QueryOption,
4182 HTTPSESSION_SetOption,
4183 NULL,
4184 NULL,
4185 NULL,
4186 NULL,
4187 NULL
4188 };
4189
4190
4191 /***********************************************************************
4192 * HTTP_Connect (internal)
4193 *
4194 * Create http session handle
4195 *
4196 * RETURNS
4197 * HINTERNET a session handle on success
4198 * NULL on failure
4199 *
4200 */
4201 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4202 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4203 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4204 DWORD dwInternalFlags)
4205 {
4206 http_session_t *lpwhs = NULL;
4207 HINTERNET handle = NULL;
4208
4209 TRACE("-->\n");
4210
4211 if (!lpszServerName || !lpszServerName[0])
4212 {
4213 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4214 goto lerror;
4215 }
4216
4217 assert( hIC->hdr.htype == WH_HINIT );
4218
4219 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4220 if (NULL == lpwhs)
4221 {
4222 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4223 goto lerror;
4224 }
4225
4226 /*
4227 * According to my tests. The name is not resolved until a request is sent
4228 */
4229
4230 lpwhs->hdr.htype = WH_HHTTPSESSION;
4231 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4232 lpwhs->hdr.dwFlags = dwFlags;
4233 lpwhs->hdr.dwContext = dwContext;
4234 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4235 lpwhs->hdr.refs = 1;
4236 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4237
4238 WININET_AddRef( &hIC->hdr );
4239 lpwhs->lpAppInfo = hIC;
4240 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4241
4242 handle = WININET_AllocHandle( &lpwhs->hdr );
4243 if (NULL == handle)
4244 {
4245 ERR("Failed to alloc handle\n");
4246 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4247 goto lerror;
4248 }
4249
4250 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4251 if(strchrW(hIC->lpszProxy, ' '))
4252 FIXME("Several proxies not implemented.\n");
4253 if(hIC->lpszProxyBypass)
4254 FIXME("Proxy bypass is ignored.\n");
4255 }
4256 if (lpszServerName && lpszServerName[0])
4257 {
4258 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4259 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4260 }
4261 if (lpszUserName && lpszUserName[0])
4262 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4263 if (lpszPassword && lpszPassword[0])
4264 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4265 lpwhs->nServerPort = nServerPort;
4266 lpwhs->nHostPort = nServerPort;
4267
4268 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4269 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4270 {
4271 INTERNET_SendCallback(&hIC->hdr, dwContext,
4272 INTERNET_STATUS_HANDLE_CREATED, &handle,
4273 sizeof(handle));
4274 }
4275
4276 lerror:
4277 if( lpwhs )
4278 WININET_Release( &lpwhs->hdr );
4279
4280 /*
4281 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4282 * windows
4283 */
4284
4285 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4286 return handle;
4287 }
4288
4289
4290 /***********************************************************************
4291 * HTTP_OpenConnection (internal)
4292 *
4293 * Connect to a web server
4294 *
4295 * RETURNS
4296 *
4297 * TRUE on success
4298 * FALSE on failure
4299 */
4300 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4301 {
4302 BOOL bSuccess = FALSE;
4303 http_session_t *lpwhs;
4304 appinfo_t *hIC = NULL;
4305 char szaddr[INET6_ADDRSTRLEN];
4306 const void *addr;
4307
4308 TRACE("-->\n");
4309
4310
4311 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4312 {
4313 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4314 goto lend;
4315 }
4316
4317 if (NETCON_connected(&lpwhr->netConnection))
4318 {
4319 bSuccess = TRUE;
4320 goto lend;
4321 }
4322 if (!HTTP_ResolveName(lpwhr)) goto lend;
4323
4324 lpwhs = lpwhr->lpHttpSession;
4325
4326 hIC = lpwhs->lpAppInfo;
4327 switch (lpwhs->socketAddress.ss_family)
4328 {
4329 case AF_INET:
4330 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4331 break;
4332 case AF_INET6:
4333 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4334 break;
4335 default:
4336 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4337 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4338 return FALSE;
4339 }
4340 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4341 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4342 INTERNET_STATUS_CONNECTING_TO_SERVER,
4343 szaddr,
4344 strlen(szaddr)+1);
4345
4346 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4347 SOCK_STREAM, 0))
4348 {
4349 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4350 goto lend;
4351 }
4352
4353 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4354 lpwhs->sa_len))
4355 goto lend;
4356
4357 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4358 {
4359 /* Note: we differ from Microsoft's WinINet here. they seem to have
4360 * a bug that causes no status callbacks to be sent when starting
4361 * a tunnel to a proxy server using the CONNECT verb. i believe our
4362 * behaviour to be more correct and to not cause any incompatibilities
4363 * because using a secure connection through a proxy server is a rare
4364 * case that would be hard for anyone to depend on */
4365 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4366 goto lend;
4367
4368 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4369 {
4370 WARN("Couldn't connect securely to host\n");
4371 goto lend;
4372 }
4373 }
4374
4375 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4376 INTERNET_STATUS_CONNECTED_TO_SERVER,
4377 szaddr, strlen(szaddr)+1);
4378
4379 bSuccess = TRUE;
4380
4381 lend:
4382 lpwhr->read_pos = lpwhr->read_size = 0;
4383 lpwhr->read_chunked = FALSE;
4384
4385 TRACE("%d <--\n", bSuccess);
4386 return bSuccess;
4387 }
4388
4389
4390 /***********************************************************************
4391 * HTTP_clear_response_headers (internal)
4392 *
4393 * clear out any old response headers
4394 */
4395 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4396 {
4397 DWORD i;
4398
4399 for( i=0; i<lpwhr->nCustHeaders; i++)
4400 {
4401 if( !lpwhr->pCustHeaders[i].lpszField )
4402 continue;
4403 if( !lpwhr->pCustHeaders[i].lpszValue )
4404 continue;
4405 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4406 continue;
4407 HTTP_DeleteCustomHeader( lpwhr, i );
4408 i--;
4409 }
4410 }
4411
4412 /***********************************************************************
4413 * HTTP_GetResponseHeaders (internal)
4414 *
4415 * Read server response
4416 *
4417 * RETURNS
4418 *
4419 * TRUE on success
4420 * FALSE on error
4421 */
4422 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4423 {
4424 INT cbreaks = 0;
4425 WCHAR buffer[MAX_REPLY_LEN];
4426 DWORD buflen = MAX_REPLY_LEN;
4427 BOOL bSuccess = FALSE;
4428 INT rc = 0;
4429 char bufferA[MAX_REPLY_LEN];
4430 LPWSTR status_code = NULL, status_text = NULL;
4431 DWORD cchMaxRawHeaders = 1024;
4432 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4433 LPWSTR temp;
4434 DWORD cchRawHeaders = 0;
4435 BOOL codeHundred = FALSE;
4436
4437 TRACE("-->\n");
4438
4439 /* clear old response headers (eg. from a redirect response) */
4440 if (clear) HTTP_clear_response_headers( lpwhr );
4441
4442 if (!NETCON_connected(&lpwhr->netConnection))
4443 goto lend;
4444
4445 do {
4446 static const WCHAR szHundred[] = {'1','0','0',0};
4447 /*
4448 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4449 */
4450 buflen = MAX_REPLY_LEN;
4451 if (!read_line(lpwhr, bufferA, &buflen))
4452 goto lend;
4453 rc += buflen;
4454 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4455 /* check is this a status code line? */
4456 if (!strncmpW(buffer, g_szHttp1_0, 4))
4457 {
4458 /* split the version from the status code */
4459 status_code = strchrW( buffer, ' ' );
4460 if( !status_code )
4461 goto lend;
4462 *status_code++=0;
4463
4464 /* split the status code from the status text */
4465 status_text = strchrW( status_code, ' ' );
4466 if( !status_text )
4467 goto lend;
4468 *status_text++=0;
4469
4470 TRACE("version [%s] status code [%s] status text [%s]\n",
4471 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4472
4473 codeHundred = (!strcmpW(status_code, szHundred));
4474 }
4475 else if (!codeHundred)
4476 {
4477 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4478 goto lend;
4479 }
4480 } while (codeHundred);
4481
4482 /* Add status code */
4483 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4484 HTTP_ADDHDR_FLAG_REPLACE);
4485
4486 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4487 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4488
4489 lpwhr->lpszVersion = heap_strdupW(buffer);
4490 lpwhr->lpszStatusText = heap_strdupW(status_text);
4491
4492 /* Restore the spaces */
4493 *(status_code-1) = ' ';
4494 *(status_text-1) = ' ';
4495
4496 /* regenerate raw headers */
4497 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4498 cchMaxRawHeaders *= 2;
4499 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4500 if (temp == NULL) goto lend;
4501 lpszRawHeaders = temp;
4502 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4503 cchRawHeaders += (buflen-1);
4504 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4505 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4506 lpszRawHeaders[cchRawHeaders] = '\0';
4507
4508 /* Parse each response line */
4509 do
4510 {
4511 buflen = MAX_REPLY_LEN;
4512 if (read_line(lpwhr, bufferA, &buflen))
4513 {
4514 LPWSTR * pFieldAndValue;
4515
4516 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4517
4518 if (!bufferA[0]) break;
4519 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4520
4521 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4522 if (pFieldAndValue)
4523 {
4524 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4525 cchMaxRawHeaders *= 2;
4526 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4527 if (temp == NULL) goto lend;
4528 lpszRawHeaders = temp;
4529 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4530 cchRawHeaders += (buflen-1);
4531 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4532 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4533 lpszRawHeaders[cchRawHeaders] = '\0';
4534
4535 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4536 HTTP_ADDREQ_FLAG_ADD );
4537
4538 HTTP_FreeTokens(pFieldAndValue);
4539 }
4540 }
4541 else
4542 {
4543 cbreaks++;
4544 if (cbreaks >= 2)
4545 break;
4546 }
4547 }while(1);
4548
4549 /* make sure the response header is terminated with an empty line. Some apps really
4550 truly care about that empty line being there for some reason. Just add it to the
4551 header. */
4552 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4553 {
4554 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4555 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4556 if (temp == NULL) goto lend;
4557 lpszRawHeaders = temp;
4558 }
4559
4560 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4561
4562 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4563 lpwhr->lpszRawHeaders = lpszRawHeaders;
4564 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4565 bSuccess = TRUE;
4566
4567 lend:
4568
4569 TRACE("<--\n");
4570 if (bSuccess)
4571 return rc;
4572 else
4573 {
4574 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4575 return 0;
4576 }
4577 }
4578
4579 /***********************************************************************
4580 * HTTP_InterpretHttpHeader (internal)
4581 *
4582 * Parse server response
4583 *
4584 * RETURNS
4585 *
4586 * Pointer to array of field, value, NULL on success.
4587 * NULL on error.
4588 */
4589 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4590 {
4591 LPWSTR * pTokenPair;
4592 LPWSTR pszColon;
4593 INT len;
4594
4595 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4596
4597 pszColon = strchrW(buffer, ':');
4598 /* must have two tokens */
4599 if (!pszColon)
4600 {
4601 HTTP_FreeTokens(pTokenPair);
4602 if (buffer[0])
4603 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4604 return NULL;
4605 }
4606
4607 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4608 if (!pTokenPair[0])
4609 {
4610 HTTP_FreeTokens(pTokenPair);
4611 return NULL;
4612 }
4613 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4614 pTokenPair[0][pszColon - buffer] = '\0';
4615
4616 /* skip colon */
4617 pszColon++;
4618 len = strlenW(pszColon);
4619 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4620 if (!pTokenPair[1])
4621 {
4622 HTTP_FreeTokens(pTokenPair);
4623 return NULL;
4624 }
4625 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4626
4627 strip_spaces(pTokenPair[0]);
4628 strip_spaces(pTokenPair[1]);
4629
4630 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4631 return pTokenPair;
4632 }
4633
4634 /***********************************************************************
4635 * HTTP_ProcessHeader (internal)
4636 *
4637 * Stuff header into header tables according to <dwModifier>
4638 *
4639 */
4640
4641 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4642
4643 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4644 {
4645 LPHTTPHEADERW lphttpHdr = NULL;
4646 BOOL bSuccess = FALSE;
4647 INT index = -1;
4648 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4649
4650 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4651
4652 /* REPLACE wins out over ADD */
4653 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4654 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4655
4656 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4657 index = -1;
4658 else
4659 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4660
4661 if (index >= 0)
4662 {
4663 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4664 {
4665 return FALSE;
4666 }
4667 lphttpHdr = &lpwhr->pCustHeaders[index];
4668 }
4669 else if (value)
4670 {
4671 HTTPHEADERW hdr;
4672
4673 hdr.lpszField = (LPWSTR)field;
4674 hdr.lpszValue = (LPWSTR)value;
4675 hdr.wFlags = hdr.wCount = 0;
4676
4677 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4678 hdr.wFlags |= HDR_ISREQUEST;
4679
4680 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4681 }
4682 /* no value to delete */
4683 else return TRUE;
4684
4685 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4686 lphttpHdr->wFlags |= HDR_ISREQUEST;
4687 else
4688 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4689
4690 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4691 {
4692 HTTP_DeleteCustomHeader( lpwhr, index );
4693
4694 if (value)
4695 {
4696 HTTPHEADERW hdr;
4697
4698 hdr.lpszField = (LPWSTR)field;
4699 hdr.lpszValue = (LPWSTR)value;
4700 hdr.wFlags = hdr.wCount = 0;
4701
4702 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4703 hdr.wFlags |= HDR_ISREQUEST;
4704
4705 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4706 }
4707
4708 return TRUE;
4709 }
4710 else if (dwModifier & COALESCEFLAGS)
4711 {
4712 LPWSTR lpsztmp;
4713 WCHAR ch = 0;
4714 INT len = 0;
4715 INT origlen = strlenW(lphttpHdr->lpszValue);
4716 INT valuelen = strlenW(value);
4717
4718 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4719 {
4720 ch = ',';
4721 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4722 }
4723 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4724 {
4725 ch = ';';
4726 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4727 }
4728
4729 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4730
4731 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4732 if (lpsztmp)
4733 {
4734 lphttpHdr->lpszValue = lpsztmp;
4735 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4736 if (ch > 0)
4737 {
4738 lphttpHdr->lpszValue[origlen] = ch;
4739 origlen++;
4740 lphttpHdr->lpszValue[origlen] = ' ';
4741 origlen++;
4742 }
4743
4744 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4745 lphttpHdr->lpszValue[len] = '\0';
4746 bSuccess = TRUE;
4747 }
4748 else
4749 {
4750 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4751 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4752 }
4753 }
4754 TRACE("<-- %d\n",bSuccess);
4755 return bSuccess;
4756 }
4757
4758
4759 /***********************************************************************
4760 * HTTP_FinishedReading (internal)
4761 *
4762 * Called when all content from server has been read by client.
4763 *
4764 */
4765 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4766 {
4767 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4768
4769 TRACE("\n");
4770
4771
4772 if (!keepalive)
4773 {
4774 HTTPREQ_CloseConnection(&lpwhr->hdr);
4775 }
4776
4777 /* FIXME: store data in the URL cache here */
4778
4779 return TRUE;
4780 }
4781
4782
4783 /***********************************************************************
4784 * HTTP_GetCustomHeaderIndex (internal)
4785 *
4786 * Return index of custom header from header array
4787 *
4788 */
4789 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4790 int requested_index, BOOL request_only)
4791 {
4792 DWORD index;
4793
4794 TRACE("%s\n", debugstr_w(lpszField));
4795
4796 for (index = 0; index < lpwhr->nCustHeaders; index++)
4797 {
4798 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4799 continue;
4800
4801 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4802 continue;
4803
4804 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4805 continue;
4806
4807 if (requested_index == 0)
4808 break;
4809 requested_index --;
4810 }
4811
4812 if (index >= lpwhr->nCustHeaders)
4813 index = -1;
4814
4815 TRACE("Return: %d\n", index);
4816 return index;
4817 }
4818
4819
4820 /***********************************************************************
4821 * HTTP_InsertCustomHeader (internal)
4822 *
4823 * Insert header into array
4824 *
4825 */
4826 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4827 {
4828 INT count;
4829 LPHTTPHEADERW lph = NULL;
4830 BOOL r = FALSE;
4831
4832 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4833 count = lpwhr->nCustHeaders + 1;
4834 if (count > 1)
4835 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4836 else
4837 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4838
4839 if (NULL != lph)
4840 {
4841 lpwhr->pCustHeaders = lph;
4842 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4843 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4844 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4845 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4846 lpwhr->nCustHeaders++;
4847 r = TRUE;
4848 }
4849 else
4850 {
4851 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4852 }
4853
4854 return r;
4855 }
4856
4857
4858 /***********************************************************************
4859 * HTTP_DeleteCustomHeader (internal)
4860 *
4861 * Delete header from array
4862 * If this function is called, the indexs may change.
4863 */
4864 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4865 {
4866 if( lpwhr->nCustHeaders <= 0 )
4867 return FALSE;
4868 if( index >= lpwhr->nCustHeaders )
4869 return FALSE;
4870 lpwhr->nCustHeaders--;
4871
4872 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4873 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4874
4875 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4876 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4877 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4878
4879 return TRUE;
4880 }
4881
4882
4883 /***********************************************************************
4884 * HTTP_VerifyValidHeader (internal)
4885 *
4886 * Verify the given header is not invalid for the given http request
4887 *
4888 */
4889 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4890 {
4891 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4892 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4893 return FALSE;
4894
4895 return TRUE;
4896 }
4897
4898 /***********************************************************************
4899 * IsHostInProxyBypassList (@)
4900 *
4901 * Undocumented
4902 *
4903 */
4904 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4905 {
4906 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4907 return FALSE;
4908 }