sync with trunk r46493
[reactos.git] / dll / win32 / wininet / http.c
1 /*
2 * Wininet - Http Implementation
3 *
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 *
11 * Ulrich Czekalla
12 * David Hammerton
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
35
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
54
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
66
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
71
72 #include "inet_ntop.c"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
75
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK[] = {'O','K',0};
79 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf[] = {'\r','\n', 0};
88
89 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge[] = { 'A','g','e',0 };
95 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 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 };
107 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 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 };
134 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia[] = { 'V','i','a',0 };
139 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
141
142 #define MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
145
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
149
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
159
160 struct HttpAuthInfo
161 {
162 LPWSTR scheme;
163 CredHandle cred;
164 CtxtHandle ctx;
165 TimeStamp exp;
166 ULONG attr;
167 ULONG max_token;
168 void *auth_data;
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
171 };
172
173
174 struct gzip_stream_t {
175 #ifdef HAVE_ZLIB
176 z_stream zstream;
177 #endif
178 BYTE buf[8192];
179 DWORD buf_size;
180 DWORD buf_pos;
181 BOOL end_of_data;
182 };
183
184 typedef struct _authorizationData
185 {
186 struct list entry;
187
188 LPWSTR lpszwHost;
189 LPWSTR lpszwRealm;
190 LPSTR lpszAuthorization;
191 UINT AuthorizationLen;
192 } authorizationData;
193
194 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
195
196 static CRITICAL_SECTION authcache_cs;
197 static CRITICAL_SECTION_DEBUG critsect_debug =
198 {
199 0, 0, &authcache_cs,
200 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
201 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
202 };
203 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
204
205 static DWORD HTTP_OpenConnection(http_request_t *req);
206 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
207 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
208 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
209 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
210 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
211 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
212 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
213 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
214 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
215 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
216 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
217 static void HTTP_DrainContent(http_request_t *req);
218 static BOOL HTTP_FinishedReading(http_request_t *req);
219
220 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
221 {
222 int HeaderIndex = 0;
223 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
224 if (HeaderIndex == -1)
225 return NULL;
226 else
227 return &req->pCustHeaders[HeaderIndex];
228 }
229
230 #ifdef HAVE_ZLIB
231
232 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
233 {
234 return HeapAlloc(GetProcessHeap(), 0, items*size);
235 }
236
237 static void wininet_zfree(voidpf opaque, voidpf address)
238 {
239 HeapFree(GetProcessHeap(), 0, address);
240 }
241
242 static void init_gzip_stream(http_request_t *req)
243 {
244 gzip_stream_t *gzip_stream;
245 int index, zres;
246
247 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
248 gzip_stream->zstream.zalloc = wininet_zalloc;
249 gzip_stream->zstream.zfree = wininet_zfree;
250 gzip_stream->zstream.opaque = NULL;
251 gzip_stream->zstream.next_in = NULL;
252 gzip_stream->zstream.avail_in = 0;
253 gzip_stream->zstream.next_out = NULL;
254 gzip_stream->zstream.avail_out = 0;
255 gzip_stream->buf_pos = 0;
256 gzip_stream->buf_size = 0;
257 gzip_stream->end_of_data = FALSE;
258
259 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
260 if(zres != Z_OK) {
261 ERR("inflateInit failed: %d\n", zres);
262 HeapFree(GetProcessHeap(), 0, gzip_stream);
263 return;
264 }
265
266 req->gzip_stream = gzip_stream;
267
268 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
269 if(index != -1)
270 HTTP_DeleteCustomHeader(req, index);
271 }
272
273 #else
274
275 static void init_gzip_stream(http_request_t *req)
276 {
277 ERR("gzip stream not supported, missing zlib.\n");
278 }
279
280 #endif
281
282 /* set the request content length based on the headers */
283 static DWORD set_content_length( http_request_t *lpwhr )
284 {
285 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
286 WCHAR encoding[20];
287 DWORD size;
288
289 size = sizeof(lpwhr->dwContentLength);
290 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
291 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
292 lpwhr->dwContentLength = ~0u;
293
294 size = sizeof(encoding);
295 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
296 !strcmpiW(encoding, szChunked))
297 {
298 lpwhr->dwContentLength = ~0u;
299 lpwhr->read_chunked = TRUE;
300 }
301
302 if(lpwhr->decoding) {
303 int encoding_idx;
304
305 static const WCHAR gzipW[] = {'g','z','i','p',0};
306
307 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
308 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
309 init_gzip_stream(lpwhr);
310 }
311
312 return lpwhr->dwContentLength;
313 }
314
315 /***********************************************************************
316 * HTTP_Tokenize (internal)
317 *
318 * Tokenize a string, allocating memory for the tokens.
319 */
320 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
321 {
322 LPWSTR * token_array;
323 int tokens = 0;
324 int i;
325 LPCWSTR next_token;
326
327 if (string)
328 {
329 /* empty string has no tokens */
330 if (*string)
331 tokens++;
332 /* count tokens */
333 for (i = 0; string[i]; i++)
334 {
335 if (!strncmpW(string+i, token_string, strlenW(token_string)))
336 {
337 DWORD j;
338 tokens++;
339 /* we want to skip over separators, but not the null terminator */
340 for (j = 0; j < strlenW(token_string) - 1; j++)
341 if (!string[i+j])
342 break;
343 i += j;
344 }
345 }
346 }
347
348 /* add 1 for terminating NULL */
349 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
350 token_array[tokens] = NULL;
351 if (!tokens)
352 return token_array;
353 for (i = 0; i < tokens; i++)
354 {
355 int len;
356 next_token = strstrW(string, token_string);
357 if (!next_token) next_token = string+strlenW(string);
358 len = next_token - string;
359 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
360 memcpy(token_array[i], string, len*sizeof(WCHAR));
361 token_array[i][len] = '\0';
362 string = next_token+strlenW(token_string);
363 }
364 return token_array;
365 }
366
367 /***********************************************************************
368 * HTTP_FreeTokens (internal)
369 *
370 * Frees memory returned from HTTP_Tokenize.
371 */
372 static void HTTP_FreeTokens(LPWSTR * token_array)
373 {
374 int i;
375 for (i = 0; token_array[i]; i++)
376 HeapFree(GetProcessHeap(), 0, token_array[i]);
377 HeapFree(GetProcessHeap(), 0, token_array);
378 }
379
380 static void HTTP_FixURL(http_request_t *lpwhr)
381 {
382 static const WCHAR szSlash[] = { '/',0 };
383 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
384
385 /* If we don't have a path we set it to root */
386 if (NULL == lpwhr->lpszPath)
387 lpwhr->lpszPath = heap_strdupW(szSlash);
388 else /* remove \r and \n*/
389 {
390 int nLen = strlenW(lpwhr->lpszPath);
391 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
392 {
393 nLen--;
394 lpwhr->lpszPath[nLen]='\0';
395 }
396 /* Replace '\' with '/' */
397 while (nLen>0) {
398 nLen--;
399 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
400 }
401 }
402
403 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
404 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
405 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
406 {
407 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
408 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
409 *fixurl = '/';
410 strcpyW(fixurl + 1, lpwhr->lpszPath);
411 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
412 lpwhr->lpszPath = fixurl;
413 }
414 }
415
416 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
417 {
418 LPWSTR requestString;
419 DWORD len, n;
420 LPCWSTR *req;
421 UINT i;
422 LPWSTR p;
423
424 static const WCHAR szSpace[] = { ' ',0 };
425 static const WCHAR szColon[] = { ':',' ',0 };
426 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
427
428 /* allocate space for an array of all the string pointers to be added */
429 len = (lpwhr->nCustHeaders)*4 + 10;
430 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
431
432 /* add the verb, path and HTTP version string */
433 n = 0;
434 req[n++] = verb;
435 req[n++] = szSpace;
436 req[n++] = path;
437 req[n++] = szSpace;
438 req[n++] = version;
439
440 /* Append custom request headers */
441 for (i = 0; i < lpwhr->nCustHeaders; i++)
442 {
443 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
444 {
445 req[n++] = szCrLf;
446 req[n++] = lpwhr->pCustHeaders[i].lpszField;
447 req[n++] = szColon;
448 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
449
450 TRACE("Adding custom header %s (%s)\n",
451 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
452 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
453 }
454 }
455
456 if( n >= len )
457 ERR("oops. buffer overrun\n");
458
459 req[n] = NULL;
460 requestString = HTTP_build_req( req, 4 );
461 HeapFree( GetProcessHeap(), 0, req );
462
463 /*
464 * Set (header) termination string for request
465 * Make sure there's exactly two new lines at the end of the request
466 */
467 p = &requestString[strlenW(requestString)-1];
468 while ( (*p == '\n') || (*p == '\r') )
469 p--;
470 strcpyW( p+1, sztwocrlf );
471
472 return requestString;
473 }
474
475 static void HTTP_ProcessCookies( http_request_t *lpwhr )
476 {
477 int HeaderIndex;
478 int numCookies = 0;
479 LPHTTPHEADERW setCookieHeader;
480
481 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
482 {
483 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
484
485 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
486 {
487 int len;
488 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
489 LPWSTR buf_url;
490 LPHTTPHEADERW Host;
491
492 Host = HTTP_GetHeader(lpwhr, hostW);
493 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
494 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
495 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
496 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
497
498 HeapFree(GetProcessHeap(), 0, buf_url);
499 }
500 numCookies++;
501 }
502 }
503
504 static void strip_spaces(LPWSTR start)
505 {
506 LPWSTR str = start;
507 LPWSTR end;
508
509 while (*str == ' ' && *str != '\0')
510 str++;
511
512 if (str != start)
513 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
514
515 end = start + strlenW(start) - 1;
516 while (end >= start && *end == ' ')
517 {
518 *end = '\0';
519 end--;
520 }
521 }
522
523 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
524 {
525 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
526 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
527 BOOL is_basic;
528 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
529 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
530 if (is_basic && pszRealm)
531 {
532 LPCWSTR token;
533 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
534 LPCWSTR realm;
535 ptr++;
536 *pszRealm=NULL;
537 token = strchrW(ptr,'=');
538 if (!token)
539 return TRUE;
540 realm = ptr;
541 while (*realm == ' ' && *realm != '\0')
542 realm++;
543 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
544 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
545 {
546 token++;
547 while (*token == ' ' && *token != '\0')
548 token++;
549 if (*token == '\0')
550 return TRUE;
551 *pszRealm = heap_strdupW(token);
552 strip_spaces(*pszRealm);
553 }
554 }
555
556 return is_basic;
557 }
558
559 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
560 {
561 if (!authinfo) return;
562
563 if (SecIsValidHandle(&authinfo->ctx))
564 DeleteSecurityContext(&authinfo->ctx);
565 if (SecIsValidHandle(&authinfo->cred))
566 FreeCredentialsHandle(&authinfo->cred);
567
568 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
569 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
570 HeapFree(GetProcessHeap(), 0, authinfo);
571 }
572
573 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
574 {
575 authorizationData *ad;
576 UINT rc = 0;
577
578 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
579
580 EnterCriticalSection(&authcache_cs);
581 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
582 {
583 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
584 {
585 TRACE("Authorization found in cache\n");
586 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
587 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
588 rc = ad->AuthorizationLen;
589 break;
590 }
591 }
592 LeaveCriticalSection(&authcache_cs);
593 return rc;
594 }
595
596 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
597 {
598 struct list *cursor;
599 authorizationData* ad = NULL;
600
601 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
602
603 EnterCriticalSection(&authcache_cs);
604 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
605 {
606 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
607 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
608 {
609 ad = check;
610 break;
611 }
612 }
613
614 if (ad)
615 {
616 TRACE("Found match in cache, replacing\n");
617 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
618 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
619 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
620 ad->AuthorizationLen = auth_data_len;
621 }
622 else
623 {
624 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
625 ad->lpszwHost = heap_strdupW(host);
626 ad->lpszwRealm = heap_strdupW(realm);
627 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
628 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
629 ad->AuthorizationLen = auth_data_len;
630 list_add_head(&basicAuthorizationCache,&ad->entry);
631 TRACE("authorization cached\n");
632 }
633 LeaveCriticalSection(&authcache_cs);
634 }
635
636 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
637 struct HttpAuthInfo **ppAuthInfo,
638 LPWSTR domain_and_username, LPWSTR password,
639 LPWSTR host )
640 {
641 SECURITY_STATUS sec_status;
642 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
643 BOOL first = FALSE;
644 LPWSTR szRealm = NULL;
645
646 TRACE("%s\n", debugstr_w(pszAuthValue));
647
648 if (!pAuthInfo)
649 {
650 TimeStamp exp;
651
652 first = TRUE;
653 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
654 if (!pAuthInfo)
655 return FALSE;
656
657 SecInvalidateHandle(&pAuthInfo->cred);
658 SecInvalidateHandle(&pAuthInfo->ctx);
659 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
660 pAuthInfo->attr = 0;
661 pAuthInfo->auth_data = NULL;
662 pAuthInfo->auth_data_len = 0;
663 pAuthInfo->finished = FALSE;
664
665 if (is_basic_auth_value(pszAuthValue,NULL))
666 {
667 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
668 pAuthInfo->scheme = heap_strdupW(szBasic);
669 if (!pAuthInfo->scheme)
670 {
671 HeapFree(GetProcessHeap(), 0, pAuthInfo);
672 return FALSE;
673 }
674 }
675 else
676 {
677 PVOID pAuthData;
678 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
679
680 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
681 if (!pAuthInfo->scheme)
682 {
683 HeapFree(GetProcessHeap(), 0, pAuthInfo);
684 return FALSE;
685 }
686
687 if (domain_and_username)
688 {
689 WCHAR *user = strchrW(domain_and_username, '\\');
690 WCHAR *domain = domain_and_username;
691
692 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
693
694 pAuthData = &nt_auth_identity;
695
696 if (user) user++;
697 else
698 {
699 user = domain_and_username;
700 domain = NULL;
701 }
702
703 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
704 nt_auth_identity.User = user;
705 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
706 nt_auth_identity.Domain = domain;
707 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
708 nt_auth_identity.Password = password;
709 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
710 }
711 else
712 /* use default credentials */
713 pAuthData = NULL;
714
715 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
716 SECPKG_CRED_OUTBOUND, NULL,
717 pAuthData, NULL,
718 NULL, &pAuthInfo->cred,
719 &exp);
720 if (sec_status == SEC_E_OK)
721 {
722 PSecPkgInfoW sec_pkg_info;
723 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
724 if (sec_status == SEC_E_OK)
725 {
726 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
727 FreeContextBuffer(sec_pkg_info);
728 }
729 }
730 if (sec_status != SEC_E_OK)
731 {
732 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
733 debugstr_w(pAuthInfo->scheme), sec_status);
734 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
735 HeapFree(GetProcessHeap(), 0, pAuthInfo);
736 return FALSE;
737 }
738 }
739 *ppAuthInfo = pAuthInfo;
740 }
741 else if (pAuthInfo->finished)
742 return FALSE;
743
744 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
745 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
746 {
747 ERR("authentication scheme changed from %s to %s\n",
748 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
749 return FALSE;
750 }
751
752 if (is_basic_auth_value(pszAuthValue,&szRealm))
753 {
754 int userlen;
755 int passlen;
756 char *auth_data = NULL;
757 UINT auth_data_len = 0;
758
759 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
760
761 if (!domain_and_username)
762 {
763 if (host && szRealm)
764 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
765 if (auth_data_len == 0)
766 {
767 HeapFree(GetProcessHeap(),0,szRealm);
768 return FALSE;
769 }
770 }
771 else
772 {
773 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
774 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
775
776 /* length includes a nul terminator, which will be re-used for the ':' */
777 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
778 if (!auth_data)
779 {
780 HeapFree(GetProcessHeap(),0,szRealm);
781 return FALSE;
782 }
783
784 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
785 auth_data[userlen] = ':';
786 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
787 auth_data_len = userlen + 1 + passlen;
788 if (host && szRealm)
789 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
790 }
791
792 pAuthInfo->auth_data = auth_data;
793 pAuthInfo->auth_data_len = auth_data_len;
794 pAuthInfo->finished = TRUE;
795 HeapFree(GetProcessHeap(),0,szRealm);
796
797 return TRUE;
798 }
799 else
800 {
801 LPCWSTR pszAuthData;
802 SecBufferDesc out_desc, in_desc;
803 SecBuffer out, in;
804 unsigned char *buffer;
805 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
806 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
807
808 in.BufferType = SECBUFFER_TOKEN;
809 in.cbBuffer = 0;
810 in.pvBuffer = NULL;
811
812 in_desc.ulVersion = 0;
813 in_desc.cBuffers = 1;
814 in_desc.pBuffers = &in;
815
816 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
817 if (*pszAuthData == ' ')
818 {
819 pszAuthData++;
820 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
821 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
822 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
823 }
824
825 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
826
827 out.BufferType = SECBUFFER_TOKEN;
828 out.cbBuffer = pAuthInfo->max_token;
829 out.pvBuffer = buffer;
830
831 out_desc.ulVersion = 0;
832 out_desc.cBuffers = 1;
833 out_desc.pBuffers = &out;
834
835 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
836 first ? NULL : &pAuthInfo->ctx,
837 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
838 context_req, 0, SECURITY_NETWORK_DREP,
839 in.pvBuffer ? &in_desc : NULL,
840 0, &pAuthInfo->ctx, &out_desc,
841 &pAuthInfo->attr, &pAuthInfo->exp);
842 if (sec_status == SEC_E_OK)
843 {
844 pAuthInfo->finished = TRUE;
845 pAuthInfo->auth_data = out.pvBuffer;
846 pAuthInfo->auth_data_len = out.cbBuffer;
847 TRACE("sending last auth packet\n");
848 }
849 else if (sec_status == SEC_I_CONTINUE_NEEDED)
850 {
851 pAuthInfo->auth_data = out.pvBuffer;
852 pAuthInfo->auth_data_len = out.cbBuffer;
853 TRACE("sending next auth packet\n");
854 }
855 else
856 {
857 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
858 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
859 destroy_authinfo(pAuthInfo);
860 *ppAuthInfo = NULL;
861 return FALSE;
862 }
863 }
864
865 return TRUE;
866 }
867
868 /***********************************************************************
869 * HTTP_HttpAddRequestHeadersW (internal)
870 */
871 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
872 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
873 {
874 LPWSTR lpszStart;
875 LPWSTR lpszEnd;
876 LPWSTR buffer;
877 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
878
879 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
880
881 if( dwHeaderLength == ~0U )
882 len = strlenW(lpszHeader);
883 else
884 len = dwHeaderLength;
885 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
886 lstrcpynW( buffer, lpszHeader, len + 1);
887
888 lpszStart = buffer;
889
890 do
891 {
892 LPWSTR * pFieldAndValue;
893
894 lpszEnd = lpszStart;
895
896 while (*lpszEnd != '\0')
897 {
898 if (*lpszEnd == '\r' || *lpszEnd == '\n')
899 break;
900 lpszEnd++;
901 }
902
903 if (*lpszStart == '\0')
904 break;
905
906 if (*lpszEnd == '\r' || *lpszEnd == '\n')
907 {
908 *lpszEnd = '\0';
909 lpszEnd++; /* Jump over newline */
910 }
911 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
912 if (*lpszStart == '\0')
913 {
914 /* Skip 0-length headers */
915 lpszStart = lpszEnd;
916 res = ERROR_SUCCESS;
917 continue;
918 }
919 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
920 if (pFieldAndValue)
921 {
922 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
923 if (res == ERROR_SUCCESS)
924 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
925 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
926 HTTP_FreeTokens(pFieldAndValue);
927 }
928
929 lpszStart = lpszEnd;
930 } while (res == ERROR_SUCCESS);
931
932 HeapFree(GetProcessHeap(), 0, buffer);
933
934 return res;
935 }
936
937 /***********************************************************************
938 * HttpAddRequestHeadersW (WININET.@)
939 *
940 * Adds one or more HTTP header to the request handler
941 *
942 * NOTE
943 * On Windows if dwHeaderLength includes the trailing '\0', then
944 * HttpAddRequestHeadersW() adds it too. However this results in an
945 * invalid Http header which is rejected by some servers so we probably
946 * don't need to match Windows on that point.
947 *
948 * RETURNS
949 * TRUE on success
950 * FALSE on failure
951 *
952 */
953 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
954 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
955 {
956 http_request_t *lpwhr;
957 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
958
959 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
960
961 if (!lpszHeader)
962 return TRUE;
963
964 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
965 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
966 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
967 if( lpwhr )
968 WININET_Release( &lpwhr->hdr );
969
970 if(res != ERROR_SUCCESS)
971 SetLastError(res);
972 return res == ERROR_SUCCESS;
973 }
974
975 /***********************************************************************
976 * HttpAddRequestHeadersA (WININET.@)
977 *
978 * Adds one or more HTTP header to the request handler
979 *
980 * RETURNS
981 * TRUE on success
982 * FALSE on failure
983 *
984 */
985 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
986 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
987 {
988 DWORD len;
989 LPWSTR hdr;
990 BOOL r;
991
992 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
993
994 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
995 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
996 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
997 if( dwHeaderLength != ~0U )
998 dwHeaderLength = len;
999
1000 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1001
1002 HeapFree( GetProcessHeap(), 0, hdr );
1003
1004 return r;
1005 }
1006
1007 /***********************************************************************
1008 * HttpOpenRequestA (WININET.@)
1009 *
1010 * Open a HTTP request handle
1011 *
1012 * RETURNS
1013 * HINTERNET a HTTP request handle on success
1014 * NULL on failure
1015 *
1016 */
1017 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1018 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1019 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1020 DWORD dwFlags, DWORD_PTR dwContext)
1021 {
1022 LPWSTR szVerb = NULL, szObjectName = NULL;
1023 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1024 INT acceptTypesCount;
1025 HINTERNET rc = FALSE;
1026 LPCSTR *types;
1027
1028 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1029 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1030 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1031 dwFlags, dwContext);
1032
1033 if (lpszVerb)
1034 {
1035 szVerb = heap_strdupAtoW(lpszVerb);
1036 if ( !szVerb )
1037 goto end;
1038 }
1039
1040 if (lpszObjectName)
1041 {
1042 szObjectName = heap_strdupAtoW(lpszObjectName);
1043 if ( !szObjectName )
1044 goto end;
1045 }
1046
1047 if (lpszVersion)
1048 {
1049 szVersion = heap_strdupAtoW(lpszVersion);
1050 if ( !szVersion )
1051 goto end;
1052 }
1053
1054 if (lpszReferrer)
1055 {
1056 szReferrer = heap_strdupAtoW(lpszReferrer);
1057 if ( !szReferrer )
1058 goto end;
1059 }
1060
1061 if (lpszAcceptTypes)
1062 {
1063 acceptTypesCount = 0;
1064 types = lpszAcceptTypes;
1065 while (*types)
1066 {
1067 __TRY
1068 {
1069 /* find out how many there are */
1070 if (*types && **types)
1071 {
1072 TRACE("accept type: %s\n", debugstr_a(*types));
1073 acceptTypesCount++;
1074 }
1075 }
1076 __EXCEPT_PAGE_FAULT
1077 {
1078 WARN("invalid accept type pointer\n");
1079 }
1080 __ENDTRY;
1081 types++;
1082 }
1083 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1084 if (!szAcceptTypes) goto end;
1085
1086 acceptTypesCount = 0;
1087 types = lpszAcceptTypes;
1088 while (*types)
1089 {
1090 __TRY
1091 {
1092 if (*types && **types)
1093 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1094 }
1095 __EXCEPT_PAGE_FAULT
1096 {
1097 /* ignore invalid pointer */
1098 }
1099 __ENDTRY;
1100 types++;
1101 }
1102 szAcceptTypes[acceptTypesCount] = NULL;
1103 }
1104
1105 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1106 szVersion, szReferrer,
1107 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1108
1109 end:
1110 if (szAcceptTypes)
1111 {
1112 acceptTypesCount = 0;
1113 while (szAcceptTypes[acceptTypesCount])
1114 {
1115 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1116 acceptTypesCount++;
1117 }
1118 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1119 }
1120 HeapFree(GetProcessHeap(), 0, szReferrer);
1121 HeapFree(GetProcessHeap(), 0, szVersion);
1122 HeapFree(GetProcessHeap(), 0, szObjectName);
1123 HeapFree(GetProcessHeap(), 0, szVerb);
1124
1125 return rc;
1126 }
1127
1128 /***********************************************************************
1129 * HTTP_EncodeBase64
1130 */
1131 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1132 {
1133 UINT n = 0, x;
1134 static const CHAR HTTP_Base64Enc[] =
1135 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1136
1137 while( len > 0 )
1138 {
1139 /* first 6 bits, all from bin[0] */
1140 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1141 x = (bin[0] & 3) << 4;
1142
1143 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1144 if( len == 1 )
1145 {
1146 base64[n++] = HTTP_Base64Enc[x];
1147 base64[n++] = '=';
1148 base64[n++] = '=';
1149 break;
1150 }
1151 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1152 x = ( bin[1] & 0x0f ) << 2;
1153
1154 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1155 if( len == 2 )
1156 {
1157 base64[n++] = HTTP_Base64Enc[x];
1158 base64[n++] = '=';
1159 break;
1160 }
1161 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1162
1163 /* last 6 bits, all from bin [2] */
1164 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1165 bin += 3;
1166 len -= 3;
1167 }
1168 base64[n] = 0;
1169 return n;
1170 }
1171
1172 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1173 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1174 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1175 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1176 static const signed char HTTP_Base64Dec[256] =
1177 {
1178 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1179 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1180 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1181 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1182 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1183 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1184 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1185 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1186 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1187 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1188 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1189 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1190 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1191 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1192 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1193 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1194 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1195 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1196 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1197 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1198 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1199 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1200 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1201 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1202 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1203 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1204 };
1205 #undef CH
1206
1207 /***********************************************************************
1208 * HTTP_DecodeBase64
1209 */
1210 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1211 {
1212 unsigned int n = 0;
1213
1214 while(*base64)
1215 {
1216 signed char in[4];
1217
1218 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1219 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1220 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1221 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1222 {
1223 WARN("invalid base64: %s\n", debugstr_w(base64));
1224 return 0;
1225 }
1226 if (bin)
1227 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1228 n++;
1229
1230 if ((base64[2] == '=') && (base64[3] == '='))
1231 break;
1232 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1233 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1234 {
1235 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1236 return 0;
1237 }
1238 if (bin)
1239 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1240 n++;
1241
1242 if (base64[3] == '=')
1243 break;
1244 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1245 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1246 {
1247 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1248 return 0;
1249 }
1250 if (bin)
1251 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1252 n++;
1253
1254 base64 += 4;
1255 }
1256
1257 return n;
1258 }
1259
1260 /***********************************************************************
1261 * HTTP_InsertAuthorization
1262 *
1263 * Insert or delete the authorization field in the request header.
1264 */
1265 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1266 {
1267 if (pAuthInfo)
1268 {
1269 static const WCHAR wszSpace[] = {' ',0};
1270 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1271 unsigned int len;
1272 WCHAR *authorization = NULL;
1273
1274 if (pAuthInfo->auth_data_len)
1275 {
1276 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1277 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1278 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1279 if (!authorization)
1280 return FALSE;
1281
1282 strcpyW(authorization, pAuthInfo->scheme);
1283 strcatW(authorization, wszSpace);
1284 HTTP_EncodeBase64(pAuthInfo->auth_data,
1285 pAuthInfo->auth_data_len,
1286 authorization+strlenW(authorization));
1287
1288 /* clear the data as it isn't valid now that it has been sent to the
1289 * server, unless it's Basic authentication which doesn't do
1290 * connection tracking */
1291 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1292 {
1293 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1294 pAuthInfo->auth_data = NULL;
1295 pAuthInfo->auth_data_len = 0;
1296 }
1297 }
1298
1299 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1300
1301 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1302
1303 HeapFree(GetProcessHeap(), 0, authorization);
1304 }
1305 return TRUE;
1306 }
1307
1308 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1309 {
1310 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1311 DWORD size;
1312
1313 size = sizeof(new_location);
1314 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1315 {
1316 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1317 strcpyW( url, new_location );
1318 }
1319 else
1320 {
1321 static const WCHAR slash[] = { '/',0 };
1322 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1323 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1324 http_session_t *session = req->lpHttpSession;
1325
1326 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1327 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1328
1329 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1330
1331 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1332 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1333 else
1334 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1335 if (req->lpszPath[0] != '/') strcatW( url, slash );
1336 strcatW( url, req->lpszPath );
1337 }
1338 TRACE("url=%s\n", debugstr_w(url));
1339 return url;
1340 }
1341
1342 /***********************************************************************
1343 * HTTP_DealWithProxy
1344 */
1345 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1346 {
1347 WCHAR buf[MAXHOSTNAME];
1348 WCHAR protoProxy[MAXHOSTNAME + 15];
1349 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1350 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1351 static WCHAR szNul[] = { 0 };
1352 URL_COMPONENTSW UrlComponents;
1353 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1354 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1355 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1356
1357 memset( &UrlComponents, 0, sizeof UrlComponents );
1358 UrlComponents.dwStructSize = sizeof UrlComponents;
1359 UrlComponents.lpszHostName = buf;
1360 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1361
1362 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1363 return FALSE;
1364 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1365 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1366 sprintfW(proxy, szFormat, protoProxy);
1367 else
1368 strcpyW(proxy, protoProxy);
1369 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1370 return FALSE;
1371 if( UrlComponents.dwHostNameLength == 0 )
1372 return FALSE;
1373
1374 if( !lpwhr->lpszPath )
1375 lpwhr->lpszPath = szNul;
1376
1377 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1378 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1379
1380 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1381 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1382 lpwhs->nServerPort = UrlComponents.nPort;
1383
1384 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1385 return TRUE;
1386 }
1387
1388 #ifndef INET6_ADDRSTRLEN
1389 #define INET6_ADDRSTRLEN 46
1390 #endif
1391
1392 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1393 {
1394 char szaddr[INET6_ADDRSTRLEN];
1395 http_session_t *lpwhs = lpwhr->lpHttpSession;
1396 const void *addr;
1397
1398 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1399 INTERNET_STATUS_RESOLVING_NAME,
1400 lpwhs->lpszServerName,
1401 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1402
1403 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1404 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1405 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1406 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1407
1408 switch (lpwhs->socketAddress.ss_family)
1409 {
1410 case AF_INET:
1411 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1412 break;
1413 case AF_INET6:
1414 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1415 break;
1416 default:
1417 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1418 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1419 }
1420 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1421 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1422 INTERNET_STATUS_NAME_RESOLVED,
1423 szaddr, strlen(szaddr)+1);
1424
1425 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1426 return ERROR_SUCCESS;
1427 }
1428
1429
1430 /***********************************************************************
1431 * HTTPREQ_Destroy (internal)
1432 *
1433 * Deallocate request handle
1434 *
1435 */
1436 static void HTTPREQ_Destroy(object_header_t *hdr)
1437 {
1438 http_request_t *lpwhr = (http_request_t*) hdr;
1439 DWORD i;
1440
1441 TRACE("\n");
1442
1443 if(lpwhr->hCacheFile)
1444 CloseHandle(lpwhr->hCacheFile);
1445
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1447
1448 DeleteCriticalSection( &lpwhr->read_section );
1449 WININET_Release(&lpwhr->lpHttpSession->hdr);
1450
1451 destroy_authinfo(lpwhr->pAuthInfo);
1452 destroy_authinfo(lpwhr->pProxyAuthInfo);
1453
1454 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1455 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1456 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1457 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1458 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1459
1460 for (i = 0; i < lpwhr->nCustHeaders; i++)
1461 {
1462 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1463 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1464 }
1465
1466 #ifdef HAVE_ZLIB
1467 if(lpwhr->gzip_stream) {
1468 if(!lpwhr->gzip_stream->end_of_data)
1469 inflateEnd(&lpwhr->gzip_stream->zstream);
1470 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1471 }
1472 #endif
1473
1474 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1475 HeapFree(GetProcessHeap(), 0, lpwhr);
1476 }
1477
1478 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1479 {
1480 http_request_t *lpwhr = (http_request_t*) hdr;
1481
1482 TRACE("%p\n",lpwhr);
1483
1484 if (!NETCON_connected(&lpwhr->netConnection))
1485 return;
1486
1487 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1488 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1489
1490 NETCON_close(&lpwhr->netConnection);
1491
1492 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1493 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1494 }
1495
1496 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1497 {
1498 LPHTTPHEADERW host_header;
1499
1500 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1501
1502 host_header = HTTP_GetHeader(req, hostW);
1503 if(!host_header)
1504 return FALSE;
1505
1506 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1507 return TRUE;
1508 }
1509
1510 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1511 {
1512 WCHAR szVersion[10];
1513 WCHAR szConnectionResponse[20];
1514 DWORD dwBufferSize = sizeof(szVersion);
1515 BOOL keepalive = FALSE;
1516
1517 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1518 * the connection is keep-alive by default */
1519 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1520 && !strcmpiW(szVersion, g_szHttp1_1))
1521 {
1522 keepalive = TRUE;
1523 }
1524
1525 dwBufferSize = sizeof(szConnectionResponse);
1526 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1527 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1528 {
1529 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1530 }
1531
1532 return keepalive;
1533 }
1534
1535 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1536 {
1537 http_request_t *req = (http_request_t*)hdr;
1538
1539 switch(option) {
1540 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1541 {
1542 http_session_t *lpwhs = req->lpHttpSession;
1543 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1544
1545 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1546
1547 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1548 return ERROR_INSUFFICIENT_BUFFER;
1549 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1550 /* FIXME: can't get a SOCKET from our connection since we don't use
1551 * winsock
1552 */
1553 info->Socket = 0;
1554 /* FIXME: get source port from req->netConnection */
1555 info->SourcePort = 0;
1556 info->DestPort = lpwhs->nHostPort;
1557 info->Flags = 0;
1558 if (HTTP_KeepAlive(req))
1559 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1560 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1561 info->Flags |= IDSI_FLAG_PROXY;
1562 if (req->netConnection.useSSL)
1563 info->Flags |= IDSI_FLAG_SECURE;
1564
1565 return ERROR_SUCCESS;
1566 }
1567
1568 case INTERNET_OPTION_SECURITY_FLAGS:
1569 {
1570 http_session_t *lpwhs;
1571 lpwhs = req->lpHttpSession;
1572
1573 if (*size < sizeof(ULONG))
1574 return ERROR_INSUFFICIENT_BUFFER;
1575
1576 *size = sizeof(DWORD);
1577 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1578 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1579 else
1580 *(DWORD*)buffer = 0;
1581 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1582 return ERROR_SUCCESS;
1583 }
1584
1585 case INTERNET_OPTION_HANDLE_TYPE:
1586 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1587
1588 if (*size < sizeof(ULONG))
1589 return ERROR_INSUFFICIENT_BUFFER;
1590
1591 *size = sizeof(DWORD);
1592 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1593 return ERROR_SUCCESS;
1594
1595 case INTERNET_OPTION_URL: {
1596 WCHAR url[INTERNET_MAX_URL_LENGTH];
1597 HTTPHEADERW *host;
1598 DWORD len;
1599 WCHAR *pch;
1600
1601 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1602
1603 TRACE("INTERNET_OPTION_URL\n");
1604
1605 host = HTTP_GetHeader(req, hostW);
1606 strcpyW(url, httpW);
1607 strcatW(url, host->lpszValue);
1608 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1609 *pch = 0;
1610 strcatW(url, req->lpszPath);
1611
1612 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1613
1614 if(unicode) {
1615 len = (strlenW(url)+1) * sizeof(WCHAR);
1616 if(*size < len)
1617 return ERROR_INSUFFICIENT_BUFFER;
1618
1619 *size = len;
1620 strcpyW(buffer, url);
1621 return ERROR_SUCCESS;
1622 }else {
1623 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1624 if(len > *size)
1625 return ERROR_INSUFFICIENT_BUFFER;
1626
1627 *size = len;
1628 return ERROR_SUCCESS;
1629 }
1630 }
1631
1632 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1633 INTERNET_CACHE_ENTRY_INFOW *info;
1634 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1635 WCHAR url[INTERNET_MAX_URL_LENGTH];
1636 DWORD nbytes, error;
1637 BOOL ret;
1638
1639 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1640
1641 if (*size < sizeof(*ts))
1642 {
1643 *size = sizeof(*ts);
1644 return ERROR_INSUFFICIENT_BUFFER;
1645 }
1646 nbytes = 0;
1647 HTTP_GetRequestURL(req, url);
1648 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1649 error = GetLastError();
1650 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1651 {
1652 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1653 return ERROR_OUTOFMEMORY;
1654
1655 GetUrlCacheEntryInfoW(url, info, &nbytes);
1656
1657 ts->ftExpires = info->ExpireTime;
1658 ts->ftLastModified = info->LastModifiedTime;
1659
1660 HeapFree(GetProcessHeap(), 0, info);
1661 *size = sizeof(*ts);
1662 return ERROR_SUCCESS;
1663 }
1664 return error;
1665 }
1666
1667 case INTERNET_OPTION_DATAFILE_NAME: {
1668 DWORD req_size;
1669
1670 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1671
1672 if(!req->lpszCacheFile) {
1673 *size = 0;
1674 return ERROR_INTERNET_ITEM_NOT_FOUND;
1675 }
1676
1677 if(unicode) {
1678 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1679 if(*size < req_size)
1680 return ERROR_INSUFFICIENT_BUFFER;
1681
1682 *size = req_size;
1683 memcpy(buffer, req->lpszCacheFile, *size);
1684 return ERROR_SUCCESS;
1685 }else {
1686 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1687 if (req_size > *size)
1688 return ERROR_INSUFFICIENT_BUFFER;
1689
1690 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1691 -1, buffer, *size, NULL, NULL);
1692 return ERROR_SUCCESS;
1693 }
1694 }
1695
1696 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1697 PCCERT_CONTEXT context;
1698
1699 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1700 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1701 return ERROR_INSUFFICIENT_BUFFER;
1702 }
1703
1704 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1705 if(context) {
1706 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1707 DWORD len;
1708
1709 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1710 info->ftExpiry = context->pCertInfo->NotAfter;
1711 info->ftStart = context->pCertInfo->NotBefore;
1712 if(unicode) {
1713 len = CertNameToStrW(context->dwCertEncodingType,
1714 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1715 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1716 if(info->lpszSubjectInfo)
1717 CertNameToStrW(context->dwCertEncodingType,
1718 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1719 info->lpszSubjectInfo, len);
1720 len = CertNameToStrW(context->dwCertEncodingType,
1721 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1722 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1723 if (info->lpszIssuerInfo)
1724 CertNameToStrW(context->dwCertEncodingType,
1725 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1726 info->lpszIssuerInfo, len);
1727 }else {
1728 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1729
1730 len = CertNameToStrA(context->dwCertEncodingType,
1731 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1732 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1733 if(infoA->lpszSubjectInfo)
1734 CertNameToStrA(context->dwCertEncodingType,
1735 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1736 infoA->lpszSubjectInfo, len);
1737 len = CertNameToStrA(context->dwCertEncodingType,
1738 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1739 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1740 if(infoA->lpszIssuerInfo)
1741 CertNameToStrA(context->dwCertEncodingType,
1742 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1743 infoA->lpszIssuerInfo, len);
1744 }
1745
1746 /*
1747 * Contrary to MSDN, these do not appear to be set.
1748 * lpszProtocolName
1749 * lpszSignatureAlgName
1750 * lpszEncryptionAlgName
1751 * dwKeySize
1752 */
1753 CertFreeCertificateContext(context);
1754 return ERROR_SUCCESS;
1755 }
1756 }
1757 }
1758
1759 return INET_QueryOption(option, buffer, size, unicode);
1760 }
1761
1762 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1763 {
1764 http_request_t *req = (http_request_t*)hdr;
1765
1766 switch(option) {
1767 case INTERNET_OPTION_SEND_TIMEOUT:
1768 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1769 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1770
1771 if (size != sizeof(DWORD))
1772 return ERROR_INVALID_PARAMETER;
1773
1774 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1775 *(DWORD*)buffer);
1776
1777 case INTERNET_OPTION_USERNAME:
1778 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1779 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1780 return ERROR_SUCCESS;
1781
1782 case INTERNET_OPTION_PASSWORD:
1783 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1784 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1785 return ERROR_SUCCESS;
1786 case INTERNET_OPTION_HTTP_DECODING:
1787 if(size != sizeof(BOOL))
1788 return ERROR_INVALID_PARAMETER;
1789 req->decoding = *(BOOL*)buffer;
1790 return ERROR_SUCCESS;
1791 }
1792
1793 return ERROR_INTERNET_INVALID_OPTION;
1794 }
1795
1796 /* read some more data into the read buffer (the read section must be held) */
1797 static DWORD read_more_data( http_request_t *req, int maxlen )
1798 {
1799 DWORD res;
1800 int len;
1801
1802 if (req->read_pos)
1803 {
1804 /* move existing data to the start of the buffer */
1805 if(req->read_size)
1806 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1807 req->read_pos = 0;
1808 }
1809
1810 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1811
1812 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1813 maxlen - req->read_size, 0, &len );
1814 if(res == ERROR_SUCCESS)
1815 req->read_size += len;
1816
1817 return res;
1818 }
1819
1820 /* remove some amount of data from the read buffer (the read section must be held) */
1821 static void remove_data( http_request_t *req, int count )
1822 {
1823 if (!(req->read_size -= count)) req->read_pos = 0;
1824 else req->read_pos += count;
1825 }
1826
1827 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1828 {
1829 int count, bytes_read, pos = 0;
1830 DWORD res;
1831
1832 EnterCriticalSection( &req->read_section );
1833 for (;;)
1834 {
1835 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1836
1837 if (eol)
1838 {
1839 count = eol - (req->read_buf + req->read_pos);
1840 bytes_read = count + 1;
1841 }
1842 else count = bytes_read = req->read_size;
1843
1844 count = min( count, *len - pos );
1845 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1846 pos += count;
1847 remove_data( req, bytes_read );
1848 if (eol) break;
1849
1850 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1851 {
1852 *len = 0;
1853 TRACE( "returning empty string\n" );
1854 LeaveCriticalSection( &req->read_section );
1855 INTERNET_SetLastError(res);
1856 return FALSE;
1857 }
1858 }
1859 LeaveCriticalSection( &req->read_section );
1860
1861 if (pos < *len)
1862 {
1863 if (pos && buffer[pos - 1] == '\r') pos--;
1864 *len = pos + 1;
1865 }
1866 buffer[*len - 1] = 0;
1867 TRACE( "returning %s\n", debugstr_a(buffer));
1868 return TRUE;
1869 }
1870
1871 /* discard data contents until we reach end of line (the read section must be held) */
1872 static DWORD discard_eol( http_request_t *req )
1873 {
1874 DWORD res;
1875
1876 do
1877 {
1878 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1879 if (eol)
1880 {
1881 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1882 break;
1883 }
1884 req->read_pos = req->read_size = 0; /* discard everything */
1885 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1886 } while (req->read_size);
1887 return ERROR_SUCCESS;
1888 }
1889
1890 /* read the size of the next chunk (the read section must be held) */
1891 static DWORD start_next_chunk( http_request_t *req )
1892 {
1893 DWORD chunk_size = 0, res;
1894
1895 if (!req->dwContentLength) return ERROR_SUCCESS;
1896 if (req->dwContentLength == req->dwContentRead)
1897 {
1898 /* read terminator for the previous chunk */
1899 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1900 req->dwContentLength = ~0u;
1901 req->dwContentRead = 0;
1902 }
1903 for (;;)
1904 {
1905 while (req->read_size)
1906 {
1907 char ch = req->read_buf[req->read_pos];
1908 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1909 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1910 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1911 else if (ch == ';' || ch == '\r' || ch == '\n')
1912 {
1913 TRACE( "reading %u byte chunk\n", chunk_size );
1914 req->dwContentLength = chunk_size;
1915 req->dwContentRead = 0;
1916 return discard_eol( req );
1917 }
1918 remove_data( req, 1 );
1919 }
1920 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1921 if (!req->read_size)
1922 {
1923 req->dwContentLength = req->dwContentRead = 0;
1924 return ERROR_SUCCESS;
1925 }
1926 }
1927 }
1928
1929 /* check if we have reached the end of the data to read (the read section must be held) */
1930 static BOOL end_of_read_data( http_request_t *req )
1931 {
1932 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1933 if (req->read_chunked) return (req->dwContentLength == 0);
1934 if (req->dwContentLength == ~0u) return FALSE;
1935 return (req->dwContentLength == req->dwContentRead);
1936 }
1937
1938 /* fetch some more data into the read buffer (the read section must be held) */
1939 static DWORD refill_buffer( http_request_t *req )
1940 {
1941 int len = sizeof(req->read_buf);
1942 DWORD res;
1943
1944 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1945 {
1946 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1947 }
1948
1949 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1950 if (len <= req->read_size) return ERROR_SUCCESS;
1951
1952 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1953 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1954 return ERROR_SUCCESS;
1955 }
1956
1957 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1958 {
1959 DWORD ret = ERROR_SUCCESS;
1960 int read = 0;
1961
1962 #ifdef HAVE_ZLIB
1963 z_stream *zstream = &req->gzip_stream->zstream;
1964 DWORD buf_avail;
1965 int zres;
1966
1967 while(read < size && !req->gzip_stream->end_of_data) {
1968 if(!req->read_size) {
1969 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1970 break;
1971 }
1972
1973 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1974
1975 zstream->next_in = req->read_buf+req->read_pos;
1976 zstream->avail_in = buf_avail;
1977 zstream->next_out = buf+read;
1978 zstream->avail_out = size-read;
1979 zres = inflate(zstream, Z_FULL_FLUSH);
1980 read = size - zstream->avail_out;
1981 req->dwContentRead += buf_avail-zstream->avail_in;
1982 remove_data(req, buf_avail-zstream->avail_in);
1983 if(zres == Z_STREAM_END) {
1984 TRACE("end of data\n");
1985 req->gzip_stream->end_of_data = TRUE;
1986 inflateEnd(&req->gzip_stream->zstream);
1987 }else if(zres != Z_OK) {
1988 WARN("inflate failed %d\n", zres);
1989 if(!read)
1990 ret = ERROR_INTERNET_DECODING_FAILED;
1991 break;
1992 }
1993 }
1994 #endif
1995
1996 *read_ret = read;
1997 return ret;
1998 }
1999
2000 static void refill_gzip_buffer(http_request_t *req)
2001 {
2002 DWORD res;
2003 int len;
2004
2005 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2006 return;
2007
2008 if(req->gzip_stream->buf_pos) {
2009 if(req->gzip_stream->buf_size)
2010 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2011 req->gzip_stream->buf_pos = 0;
2012 }
2013
2014 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2015 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2016 if(res == ERROR_SUCCESS)
2017 req->gzip_stream->buf_size += len;
2018 }
2019
2020 /* return the size of data available to be read immediately (the read section must be held) */
2021 static DWORD get_avail_data( http_request_t *req )
2022 {
2023 if (req->gzip_stream) {
2024 refill_gzip_buffer(req);
2025 return req->gzip_stream->buf_size;
2026 }
2027 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2028 return 0;
2029 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2030 }
2031
2032 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2033 {
2034 INTERNET_ASYNC_RESULT iar;
2035 DWORD res;
2036
2037 TRACE("%p\n", req);
2038
2039 EnterCriticalSection( &req->read_section );
2040 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2041 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2042 iar.dwError = first_notif ? 0 : get_avail_data(req);
2043 }else {
2044 iar.dwResult = 0;
2045 iar.dwError = res;
2046 }
2047 LeaveCriticalSection( &req->read_section );
2048
2049 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2050 sizeof(INTERNET_ASYNC_RESULT));
2051 }
2052
2053 /* read data from the http connection (the read section must be held) */
2054 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2055 {
2056 BOOL finished_reading = FALSE;
2057 int len, bytes_read = 0;
2058 DWORD ret = ERROR_SUCCESS;
2059
2060 EnterCriticalSection( &req->read_section );
2061
2062 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2063 {
2064 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2065 }
2066
2067 if(req->gzip_stream) {
2068 if(req->gzip_stream->buf_size) {
2069 bytes_read = min(req->gzip_stream->buf_size, size);
2070 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2071 req->gzip_stream->buf_pos += bytes_read;
2072 req->gzip_stream->buf_size -= bytes_read;
2073 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2074 refill_buffer(req);
2075 }
2076
2077 if(size > bytes_read) {
2078 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2079 if(ret == ERROR_SUCCESS)
2080 bytes_read += len;
2081 }
2082
2083 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2084 }else {
2085 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2086
2087 if (req->read_size) {
2088 bytes_read = min( req->read_size, size );
2089 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2090 remove_data( req, bytes_read );
2091 }
2092
2093 if (size > bytes_read && (!bytes_read || sync)) {
2094 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2095 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2096 bytes_read += len;
2097 /* always return success, even if the network layer returns an error */
2098 }
2099
2100 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2101 req->dwContentRead += bytes_read;
2102 }
2103 done:
2104 *read = bytes_read;
2105
2106 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2107 LeaveCriticalSection( &req->read_section );
2108
2109 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2110 BOOL res;
2111 DWORD dwBytesWritten;
2112
2113 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2114 if(!res)
2115 WARN("WriteFile failed: %u\n", GetLastError());
2116 }
2117
2118 if(finished_reading)
2119 HTTP_FinishedReading(req);
2120
2121 return ret;
2122 }
2123
2124
2125 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2126 {
2127 http_request_t *req = (http_request_t*)hdr;
2128 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2129 }
2130
2131 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2132 {
2133 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2134 http_request_t *req = (http_request_t*)workRequest->hdr;
2135 INTERNET_ASYNC_RESULT iar;
2136 DWORD res;
2137
2138 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2139
2140 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2141 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2142
2143 iar.dwResult = res == ERROR_SUCCESS;
2144 iar.dwError = res;
2145
2146 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2147 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2148 sizeof(INTERNET_ASYNC_RESULT));
2149 }
2150
2151 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2152 DWORD flags, DWORD_PTR context)
2153 {
2154 http_request_t *req = (http_request_t*)hdr;
2155 DWORD res;
2156
2157 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2158 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2159
2160 if (buffers->dwStructSize != sizeof(*buffers))
2161 return ERROR_INVALID_PARAMETER;
2162
2163 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2164
2165 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2166 {
2167 WORKREQUEST workRequest;
2168
2169 if (TryEnterCriticalSection( &req->read_section ))
2170 {
2171 if (get_avail_data(req))
2172 {
2173 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2174 &buffers->dwBufferLength, FALSE);
2175 LeaveCriticalSection( &req->read_section );
2176 goto done;
2177 }
2178 LeaveCriticalSection( &req->read_section );
2179 }
2180
2181 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2182 workRequest.hdr = WININET_AddRef(&req->hdr);
2183 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2184
2185 INTERNET_AsyncCall(&workRequest);
2186
2187 return ERROR_IO_PENDING;
2188 }
2189
2190 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2191 !(flags & IRF_NO_WAIT));
2192
2193 done:
2194 if (res == ERROR_SUCCESS) {
2195 DWORD size = buffers->dwBufferLength;
2196 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2197 &size, sizeof(size));
2198 }
2199
2200 return res;
2201 }
2202
2203 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2204 {
2205 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2206 http_request_t *req = (http_request_t*)workRequest->hdr;
2207 INTERNET_ASYNC_RESULT iar;
2208 DWORD res;
2209
2210 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2211
2212 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2213 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2214
2215 iar.dwResult = res == ERROR_SUCCESS;
2216 iar.dwError = res;
2217
2218 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2219 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2220 sizeof(INTERNET_ASYNC_RESULT));
2221 }
2222
2223 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2224 DWORD flags, DWORD_PTR context)
2225 {
2226
2227 http_request_t *req = (http_request_t*)hdr;
2228 DWORD res;
2229
2230 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2231 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2232
2233 if (buffers->dwStructSize != sizeof(*buffers))
2234 return ERROR_INVALID_PARAMETER;
2235
2236 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2237
2238 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2239 {
2240 WORKREQUEST workRequest;
2241
2242 if (TryEnterCriticalSection( &req->read_section ))
2243 {
2244 if (get_avail_data(req))
2245 {
2246 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2247 &buffers->dwBufferLength, FALSE);
2248 LeaveCriticalSection( &req->read_section );
2249 goto done;
2250 }
2251 LeaveCriticalSection( &req->read_section );
2252 }
2253
2254 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2255 workRequest.hdr = WININET_AddRef(&req->hdr);
2256 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2257
2258 INTERNET_AsyncCall(&workRequest);
2259
2260 return ERROR_IO_PENDING;
2261 }
2262
2263 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2264 !(flags & IRF_NO_WAIT));
2265
2266 done:
2267 if (res == ERROR_SUCCESS) {
2268 DWORD size = buffers->dwBufferLength;
2269 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2270 &size, sizeof(size));
2271 }
2272
2273 return res;
2274 }
2275
2276 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2277 {
2278 DWORD res;
2279 http_request_t *lpwhr = (http_request_t*)hdr;
2280
2281 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2282
2283 *written = 0;
2284 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2285 if (res == ERROR_SUCCESS)
2286 lpwhr->dwBytesWritten += *written;
2287
2288 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2289 return res;
2290 }
2291
2292 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2293 {
2294 http_request_t *req = (http_request_t*)workRequest->hdr;
2295
2296 HTTP_ReceiveRequestData(req, FALSE);
2297 }
2298
2299 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2300 {
2301 http_request_t *req = (http_request_t*)hdr;
2302
2303 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2304
2305 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2306 {
2307 WORKREQUEST workRequest;
2308
2309 /* never wait, if we can't enter the section we queue an async request right away */
2310 if (TryEnterCriticalSection( &req->read_section ))
2311 {
2312 if ((*available = get_avail_data( req ))) goto done;
2313 if (end_of_read_data( req )) goto done;
2314 LeaveCriticalSection( &req->read_section );
2315 }
2316
2317 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2318 workRequest.hdr = WININET_AddRef( &req->hdr );
2319
2320 INTERNET_AsyncCall(&workRequest);
2321
2322 return ERROR_IO_PENDING;
2323 }
2324
2325 EnterCriticalSection( &req->read_section );
2326
2327 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2328 {
2329 refill_buffer( req );
2330 *available = get_avail_data( req );
2331 }
2332
2333 done:
2334 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2335 {
2336 DWORD extra;
2337 if (NETCON_query_data_available(&req->netConnection, &extra))
2338 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2339 }
2340 LeaveCriticalSection( &req->read_section );
2341
2342 TRACE( "returning %u\n", *available );
2343 return ERROR_SUCCESS;
2344 }
2345
2346 static const object_vtbl_t HTTPREQVtbl = {
2347 HTTPREQ_Destroy,
2348 HTTPREQ_CloseConnection,
2349 HTTPREQ_QueryOption,
2350 HTTPREQ_SetOption,
2351 HTTPREQ_ReadFile,
2352 HTTPREQ_ReadFileExA,
2353 HTTPREQ_ReadFileExW,
2354 HTTPREQ_WriteFile,
2355 HTTPREQ_QueryDataAvailable,
2356 NULL
2357 };
2358
2359 /***********************************************************************
2360 * HTTP_HttpOpenRequestW (internal)
2361 *
2362 * Open a HTTP request handle
2363 *
2364 * RETURNS
2365 * HINTERNET a HTTP request handle on success
2366 * NULL on failure
2367 *
2368 */
2369 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2370 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2371 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2372 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2373 {
2374 appinfo_t *hIC = NULL;
2375 http_request_t *lpwhr;
2376 LPWSTR lpszHostName = NULL;
2377 HINTERNET handle = NULL;
2378 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2379 DWORD len, res;
2380
2381 TRACE("-->\n");
2382
2383 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2384 hIC = lpwhs->lpAppInfo;
2385
2386 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2387 if (NULL == lpwhr)
2388 {
2389 res = ERROR_OUTOFMEMORY;
2390 goto lend;
2391 }
2392 lpwhr->hdr.htype = WH_HHTTPREQ;
2393 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2394 lpwhr->hdr.dwFlags = dwFlags;
2395 lpwhr->hdr.dwContext = dwContext;
2396 lpwhr->hdr.refs = 1;
2397 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2398 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2399 lpwhr->dwContentLength = ~0u;
2400 InitializeCriticalSection( &lpwhr->read_section );
2401
2402 WININET_AddRef( &lpwhs->hdr );
2403 lpwhr->lpHttpSession = lpwhs;
2404 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2405
2406 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2407 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2408 if (NULL == lpszHostName)
2409 {
2410 res = ERROR_OUTOFMEMORY;
2411 goto lend;
2412 }
2413
2414 handle = WININET_AllocHandle( &lpwhr->hdr );
2415 if (NULL == handle)
2416 {
2417 res = ERROR_OUTOFMEMORY;
2418 goto lend;
2419 }
2420
2421 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2422 {
2423 InternetCloseHandle( handle );
2424 handle = NULL;
2425 goto lend;
2426 }
2427
2428 if (lpszObjectName && *lpszObjectName) {
2429 HRESULT rc;
2430
2431 len = 0;
2432 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2433 if (rc != E_POINTER)
2434 len = strlenW(lpszObjectName)+1;
2435 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2436 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2437 URL_ESCAPE_SPACES_ONLY);
2438 if (rc != S_OK)
2439 {
2440 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2441 strcpyW(lpwhr->lpszPath,lpszObjectName);
2442 }
2443 }else {
2444 static const WCHAR slashW[] = {'/',0};
2445
2446 lpwhr->lpszPath = heap_strdupW(slashW);
2447 }
2448
2449 if (lpszReferrer && *lpszReferrer)
2450 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2451
2452 if (lpszAcceptTypes)
2453 {
2454 int i;
2455 for (i = 0; lpszAcceptTypes[i]; i++)
2456 {
2457 if (!*lpszAcceptTypes[i]) continue;
2458 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2459 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2460 HTTP_ADDHDR_FLAG_REQ |
2461 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2462 }
2463 }
2464
2465 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2466 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2467
2468 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2469 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2470 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2471 {
2472 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2473 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2474 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2475 }
2476 else
2477 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2478 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2479
2480 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2481 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2482 INTERNET_DEFAULT_HTTPS_PORT :
2483 INTERNET_DEFAULT_HTTP_PORT);
2484
2485 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2486 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2487 INTERNET_DEFAULT_HTTPS_PORT :
2488 INTERNET_DEFAULT_HTTP_PORT);
2489
2490 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2491 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2492
2493 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2494 INTERNET_STATUS_HANDLE_CREATED, &handle,
2495 sizeof(handle));
2496
2497 lend:
2498 HeapFree(GetProcessHeap(), 0, lpszHostName);
2499 if( lpwhr )
2500 WININET_Release( &lpwhr->hdr );
2501
2502 TRACE("<-- %p (%p)\n", handle, lpwhr);
2503 *ret = handle;
2504 return res;
2505 }
2506
2507 /***********************************************************************
2508 * HttpOpenRequestW (WININET.@)
2509 *
2510 * Open a HTTP request handle
2511 *
2512 * RETURNS
2513 * HINTERNET a HTTP request handle on success
2514 * NULL on failure
2515 *
2516 */
2517 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2518 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2519 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2520 DWORD dwFlags, DWORD_PTR dwContext)
2521 {
2522 http_session_t *lpwhs;
2523 HINTERNET handle = NULL;
2524 DWORD res;
2525
2526 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2527 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2528 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2529 dwFlags, dwContext);
2530 if(lpszAcceptTypes!=NULL)
2531 {
2532 int i;
2533 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2534 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2535 }
2536
2537 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2538 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2539 {
2540 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2541 goto lend;
2542 }
2543
2544 /*
2545 * My tests seem to show that the windows version does not
2546 * become asynchronous until after this point. And anyhow
2547 * if this call was asynchronous then how would you get the
2548 * necessary HINTERNET pointer returned by this function.
2549 *
2550 */
2551 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2552 lpszVersion, lpszReferrer, lpszAcceptTypes,
2553 dwFlags, dwContext, &handle);
2554 lend:
2555 if( lpwhs )
2556 WININET_Release( &lpwhs->hdr );
2557 TRACE("returning %p\n", handle);
2558 if(res != ERROR_SUCCESS)
2559 SetLastError(res);
2560 return handle;
2561 }
2562
2563 /* read any content returned by the server so that the connection can be
2564 * reused */
2565 static void HTTP_DrainContent(http_request_t *req)
2566 {
2567 DWORD bytes_read;
2568
2569 if (!NETCON_connected(&req->netConnection)) return;
2570
2571 if (req->dwContentLength == -1)
2572 {
2573 NETCON_close(&req->netConnection);
2574 return;
2575 }
2576 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2577
2578 do
2579 {
2580 char buffer[2048];
2581 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2582 return;
2583 } while (bytes_read);
2584 }
2585
2586 static const LPCWSTR header_lookup[] = {
2587 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2588 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2589 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2590 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2591 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2592 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2593 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2594 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2595 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2596 szDate, /* HTTP_QUERY_DATE = 9 */
2597 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2598 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2599 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2600 szURI, /* HTTP_QUERY_URI = 13 */
2601 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2602 NULL, /* HTTP_QUERY_COST = 15 */
2603 NULL, /* HTTP_QUERY_LINK = 16 */
2604 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2605 NULL, /* HTTP_QUERY_VERSION = 18 */
2606 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2607 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2608 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2609 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2610 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2611 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2612 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2613 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2614 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2615 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2616 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2617 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2618 NULL, /* HTTP_QUERY_FROM = 31 */
2619 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2620 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2621 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2622 szReferer, /* HTTP_QUERY_REFERER = 35 */
2623 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2624 szServer, /* HTTP_QUERY_SERVER = 37 */
2625 NULL, /* HTTP_TITLE = 38 */
2626 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2627 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2628 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2629 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2630 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2631 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2632 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2633 NULL, /* HTTP_QUERY_REFRESH = 46 */
2634 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2635 szAge, /* HTTP_QUERY_AGE = 48 */
2636 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2637 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2638 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2639 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2640 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2641 szETag, /* HTTP_QUERY_ETAG = 54 */
2642 hostW, /* HTTP_QUERY_HOST = 55 */
2643 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2644 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2645 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2646 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2647 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2648 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2649 szRange, /* HTTP_QUERY_RANGE = 62 */
2650 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2651 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2652 szVary, /* HTTP_QUERY_VARY = 65 */
2653 szVia, /* HTTP_QUERY_VIA = 66 */
2654 szWarning, /* HTTP_QUERY_WARNING = 67 */
2655 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2656 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2657 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2658 };
2659
2660 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2661
2662 /***********************************************************************
2663 * HTTP_HttpQueryInfoW (internal)
2664 */
2665 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2666 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2667 {
2668 LPHTTPHEADERW lphttpHdr = NULL;
2669 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2670 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2671 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2672 INT index = -1;
2673
2674 /* Find requested header structure */
2675 switch (level)
2676 {
2677 case HTTP_QUERY_CUSTOM:
2678 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2679 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2680 break;
2681 case HTTP_QUERY_RAW_HEADERS_CRLF:
2682 {
2683 LPWSTR headers;
2684 DWORD len = 0;
2685 DWORD res = ERROR_INVALID_PARAMETER;
2686
2687 if (request_only)
2688 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2689 else
2690 headers = lpwhr->lpszRawHeaders;
2691
2692 if (headers)
2693 len = strlenW(headers) * sizeof(WCHAR);
2694
2695 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2696 {
2697 len += sizeof(WCHAR);
2698 res = ERROR_INSUFFICIENT_BUFFER;
2699 }
2700 else if (lpBuffer)
2701 {
2702 if (headers)
2703 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2704 else
2705 {
2706 len = strlenW(szCrLf) * sizeof(WCHAR);
2707 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2708 }
2709 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2710 res = ERROR_SUCCESS;
2711 }
2712 *lpdwBufferLength = len;
2713
2714 if (request_only)
2715 HeapFree(GetProcessHeap(), 0, headers);
2716 return res;
2717 }
2718 case HTTP_QUERY_RAW_HEADERS:
2719 {
2720 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2721 DWORD i, size = 0;
2722 LPWSTR pszString = lpBuffer;
2723
2724 for (i = 0; ppszRawHeaderLines[i]; i++)
2725 size += strlenW(ppszRawHeaderLines[i]) + 1;
2726
2727 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2728 {
2729 HTTP_FreeTokens(ppszRawHeaderLines);
2730 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2731 return ERROR_INSUFFICIENT_BUFFER;
2732 }
2733 if (pszString)
2734 {
2735 for (i = 0; ppszRawHeaderLines[i]; i++)
2736 {
2737 DWORD len = strlenW(ppszRawHeaderLines[i]);
2738 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2739 pszString += len+1;
2740 }
2741 *pszString = '\0';
2742 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2743 }
2744 *lpdwBufferLength = size * sizeof(WCHAR);
2745 HTTP_FreeTokens(ppszRawHeaderLines);
2746
2747 return ERROR_SUCCESS;
2748 }
2749 case HTTP_QUERY_STATUS_TEXT:
2750 if (lpwhr->lpszStatusText)
2751 {
2752 DWORD len = strlenW(lpwhr->lpszStatusText);
2753 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2754 {
2755 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2756 return ERROR_INSUFFICIENT_BUFFER;
2757 }
2758 if (lpBuffer)
2759 {
2760 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2761 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2762 }
2763 *lpdwBufferLength = len * sizeof(WCHAR);
2764 return ERROR_SUCCESS;
2765 }
2766 break;
2767 case HTTP_QUERY_VERSION:
2768 if (lpwhr->lpszVersion)
2769 {
2770 DWORD len = strlenW(lpwhr->lpszVersion);
2771 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2772 {
2773 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2774 return ERROR_INSUFFICIENT_BUFFER;
2775 }
2776 if (lpBuffer)
2777 {
2778 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2779 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2780 }
2781 *lpdwBufferLength = len * sizeof(WCHAR);
2782 return ERROR_SUCCESS;
2783 }
2784 break;
2785 case HTTP_QUERY_CONTENT_ENCODING:
2786 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2787 requested_index,request_only);
2788 break;
2789 default:
2790 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2791
2792 if (level < LAST_TABLE_HEADER && header_lookup[level])
2793 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2794 requested_index,request_only);
2795 }
2796
2797 if (index >= 0)
2798 lphttpHdr = &lpwhr->pCustHeaders[index];
2799
2800 /* Ensure header satisfies requested attributes */
2801 if (!lphttpHdr ||
2802 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2803 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2804 {
2805 return ERROR_HTTP_HEADER_NOT_FOUND;
2806 }
2807
2808 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2809
2810 /* coalesce value to requested type */
2811 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2812 {
2813 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2814 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2815 }
2816 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2817 {
2818 time_t tmpTime;
2819 struct tm tmpTM;
2820 SYSTEMTIME *STHook;
2821
2822 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2823
2824 tmpTM = *gmtime(&tmpTime);
2825 STHook = (SYSTEMTIME *)lpBuffer;
2826 STHook->wDay = tmpTM.tm_mday;
2827 STHook->wHour = tmpTM.tm_hour;
2828 STHook->wMilliseconds = 0;
2829 STHook->wMinute = tmpTM.tm_min;
2830 STHook->wDayOfWeek = tmpTM.tm_wday;
2831 STHook->wMonth = tmpTM.tm_mon + 1;
2832 STHook->wSecond = tmpTM.tm_sec;
2833 STHook->wYear = tmpTM.tm_year;
2834
2835 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2836 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2837 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2838 }
2839 else if (lphttpHdr->lpszValue)
2840 {
2841 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2842
2843 if (len > *lpdwBufferLength)
2844 {
2845 *lpdwBufferLength = len;
2846 return ERROR_INSUFFICIENT_BUFFER;
2847 }
2848 if (lpBuffer)
2849 {
2850 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2851 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2852 }
2853 *lpdwBufferLength = len - sizeof(WCHAR);
2854 }
2855 return ERROR_SUCCESS;
2856 }
2857
2858 /***********************************************************************
2859 * HttpQueryInfoW (WININET.@)
2860 *
2861 * Queries for information about an HTTP request
2862 *
2863 * RETURNS
2864 * TRUE on success
2865 * FALSE on failure
2866 *
2867 */
2868 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2869 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2870 {
2871 http_request_t *lpwhr;
2872 DWORD res;
2873
2874 if (TRACE_ON(wininet)) {
2875 #define FE(x) { x, #x }
2876 static const wininet_flag_info query_flags[] = {
2877 FE(HTTP_QUERY_MIME_VERSION),
2878 FE(HTTP_QUERY_CONTENT_TYPE),
2879 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2880 FE(HTTP_QUERY_CONTENT_ID),
2881 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2882 FE(HTTP_QUERY_CONTENT_LENGTH),
2883 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2884 FE(HTTP_QUERY_ALLOW),
2885 FE(HTTP_QUERY_PUBLIC),
2886 FE(HTTP_QUERY_DATE),
2887 FE(HTTP_QUERY_EXPIRES),
2888 FE(HTTP_QUERY_LAST_MODIFIED),
2889 FE(HTTP_QUERY_MESSAGE_ID),
2890 FE(HTTP_QUERY_URI),
2891 FE(HTTP_QUERY_DERIVED_FROM),
2892