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