[WININET]
[reactos.git] / reactos / dll / win32 / wininet / http.c
1 /*
2 * Wininet - Http Implementation
3 *
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 *
11 * Ulrich Czekalla
12 * David Hammerton
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
35
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
54
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
66
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
71
72 #include "inet_ntop.c"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
75
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR 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 proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1349 static WCHAR szNul[] = { 0 };
1350 URL_COMPONENTSW UrlComponents;
1351 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1352 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1353
1354 memset( &UrlComponents, 0, sizeof UrlComponents );
1355 UrlComponents.dwStructSize = sizeof UrlComponents;
1356 UrlComponents.lpszHostName = buf;
1357 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1358
1359 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1360 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1361 sprintfW(proxy, szFormat, hIC->lpszProxy);
1362 else
1363 strcpyW(proxy, hIC->lpszProxy);
1364 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1365 return FALSE;
1366 if( UrlComponents.dwHostNameLength == 0 )
1367 return FALSE;
1368
1369 if( !lpwhr->lpszPath )
1370 lpwhr->lpszPath = szNul;
1371
1372 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1373 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1374
1375 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1376 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1377 lpwhs->nServerPort = UrlComponents.nPort;
1378
1379 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1380 return TRUE;
1381 }
1382
1383 #ifndef INET6_ADDRSTRLEN
1384 #define INET6_ADDRSTRLEN 46
1385 #endif
1386
1387 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1388 {
1389 char szaddr[INET6_ADDRSTRLEN];
1390 http_session_t *lpwhs = lpwhr->lpHttpSession;
1391 const void *addr;
1392
1393 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1394 INTERNET_STATUS_RESOLVING_NAME,
1395 lpwhs->lpszServerName,
1396 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1397
1398 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1399 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1400 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1401 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1402
1403 switch (lpwhs->socketAddress.ss_family)
1404 {
1405 case AF_INET:
1406 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1407 break;
1408 case AF_INET6:
1409 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1410 break;
1411 default:
1412 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1413 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1414 }
1415 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1416 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1417 INTERNET_STATUS_NAME_RESOLVED,
1418 szaddr, strlen(szaddr)+1);
1419
1420 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1421 return ERROR_SUCCESS;
1422 }
1423
1424
1425 /***********************************************************************
1426 * HTTPREQ_Destroy (internal)
1427 *
1428 * Deallocate request handle
1429 *
1430 */
1431 static void HTTPREQ_Destroy(object_header_t *hdr)
1432 {
1433 http_request_t *lpwhr = (http_request_t*) hdr;
1434 DWORD i;
1435
1436 TRACE("\n");
1437
1438 if(lpwhr->hCacheFile)
1439 CloseHandle(lpwhr->hCacheFile);
1440
1441 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1442
1443 DeleteCriticalSection( &lpwhr->read_section );
1444 WININET_Release(&lpwhr->lpHttpSession->hdr);
1445
1446 destroy_authinfo(lpwhr->pAuthInfo);
1447 destroy_authinfo(lpwhr->pProxyAuthInfo);
1448
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1450 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1451 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1452 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1453 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1454
1455 for (i = 0; i < lpwhr->nCustHeaders; i++)
1456 {
1457 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1458 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1459 }
1460
1461 #ifdef HAVE_ZLIB
1462 if(lpwhr->gzip_stream) {
1463 if(!lpwhr->gzip_stream->end_of_data)
1464 inflateEnd(&lpwhr->gzip_stream->zstream);
1465 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1466 }
1467 #endif
1468
1469 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1470 HeapFree(GetProcessHeap(), 0, lpwhr);
1471 }
1472
1473 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1474 {
1475 http_request_t *lpwhr = (http_request_t*) hdr;
1476
1477 TRACE("%p\n",lpwhr);
1478
1479 if (!NETCON_connected(&lpwhr->netConnection))
1480 return;
1481
1482 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1483 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1484
1485 NETCON_close(&lpwhr->netConnection);
1486
1487 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1488 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1489 }
1490
1491 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1492 {
1493 LPHTTPHEADERW host_header;
1494
1495 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1496
1497 host_header = HTTP_GetHeader(req, hostW);
1498 if(!host_header)
1499 return FALSE;
1500
1501 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1502 return TRUE;
1503 }
1504
1505 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1506 {
1507 WCHAR szVersion[10];
1508 WCHAR szConnectionResponse[20];
1509 DWORD dwBufferSize = sizeof(szVersion);
1510 BOOL keepalive = FALSE;
1511
1512 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1513 * the connection is keep-alive by default */
1514 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1515 && !strcmpiW(szVersion, g_szHttp1_1))
1516 {
1517 keepalive = TRUE;
1518 }
1519
1520 dwBufferSize = sizeof(szConnectionResponse);
1521 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1522 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1523 {
1524 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1525 }
1526
1527 return keepalive;
1528 }
1529
1530 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1531 {
1532 http_request_t *req = (http_request_t*)hdr;
1533
1534 switch(option) {
1535 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1536 {
1537 http_session_t *lpwhs = req->lpHttpSession;
1538 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1539
1540 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1541
1542 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1543 return ERROR_INSUFFICIENT_BUFFER;
1544 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1545 /* FIXME: can't get a SOCKET from our connection since we don't use
1546 * winsock
1547 */
1548 info->Socket = 0;
1549 /* FIXME: get source port from req->netConnection */
1550 info->SourcePort = 0;
1551 info->DestPort = lpwhs->nHostPort;
1552 info->Flags = 0;
1553 if (HTTP_KeepAlive(req))
1554 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1555 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1556 info->Flags |= IDSI_FLAG_PROXY;
1557 if (req->netConnection.useSSL)
1558 info->Flags |= IDSI_FLAG_SECURE;
1559
1560 return ERROR_SUCCESS;
1561 }
1562
1563 case INTERNET_OPTION_SECURITY_FLAGS:
1564 {
1565 http_session_t *lpwhs;
1566 lpwhs = req->lpHttpSession;
1567
1568 if (*size < sizeof(ULONG))
1569 return ERROR_INSUFFICIENT_BUFFER;
1570
1571 *size = sizeof(DWORD);
1572 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1573 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1574 else
1575 *(DWORD*)buffer = 0;
1576 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1577 return ERROR_SUCCESS;
1578 }
1579
1580 case INTERNET_OPTION_HANDLE_TYPE:
1581 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1582
1583 if (*size < sizeof(ULONG))
1584 return ERROR_INSUFFICIENT_BUFFER;
1585
1586 *size = sizeof(DWORD);
1587 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1588 return ERROR_SUCCESS;
1589
1590 case INTERNET_OPTION_URL: {
1591 WCHAR url[INTERNET_MAX_URL_LENGTH];
1592 HTTPHEADERW *host;
1593 DWORD len;
1594 WCHAR *pch;
1595
1596 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1597
1598 TRACE("INTERNET_OPTION_URL\n");
1599
1600 host = HTTP_GetHeader(req, hostW);
1601 strcpyW(url, httpW);
1602 strcatW(url, host->lpszValue);
1603 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1604 *pch = 0;
1605 strcatW(url, req->lpszPath);
1606
1607 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1608
1609 if(unicode) {
1610 len = (strlenW(url)+1) * sizeof(WCHAR);
1611 if(*size < len)
1612 return ERROR_INSUFFICIENT_BUFFER;
1613
1614 *size = len;
1615 strcpyW(buffer, url);
1616 return ERROR_SUCCESS;
1617 }else {
1618 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1619 if(len > *size)
1620 return ERROR_INSUFFICIENT_BUFFER;
1621
1622 *size = len;
1623 return ERROR_SUCCESS;
1624 }
1625 }
1626
1627 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1628 INTERNET_CACHE_ENTRY_INFOW *info;
1629 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1630 WCHAR url[INTERNET_MAX_URL_LENGTH];
1631 DWORD nbytes, error;
1632 BOOL ret;
1633
1634 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1635
1636 if (*size < sizeof(*ts))
1637 {
1638 *size = sizeof(*ts);
1639 return ERROR_INSUFFICIENT_BUFFER;
1640 }
1641 nbytes = 0;
1642 HTTP_GetRequestURL(req, url);
1643 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1644 error = GetLastError();
1645 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1646 {
1647 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1648 return ERROR_OUTOFMEMORY;
1649
1650 GetUrlCacheEntryInfoW(url, info, &nbytes);
1651
1652 ts->ftExpires = info->ExpireTime;
1653 ts->ftLastModified = info->LastModifiedTime;
1654
1655 HeapFree(GetProcessHeap(), 0, info);
1656 *size = sizeof(*ts);
1657 return ERROR_SUCCESS;
1658 }
1659 return error;
1660 }
1661
1662 case INTERNET_OPTION_DATAFILE_NAME: {
1663 DWORD req_size;
1664
1665 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1666
1667 if(!req->lpszCacheFile) {
1668 *size = 0;
1669 return ERROR_INTERNET_ITEM_NOT_FOUND;
1670 }
1671
1672 if(unicode) {
1673 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1674 if(*size < req_size)
1675 return ERROR_INSUFFICIENT_BUFFER;
1676
1677 *size = req_size;
1678 memcpy(buffer, req->lpszCacheFile, *size);
1679 return ERROR_SUCCESS;
1680 }else {
1681 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1682 if (req_size > *size)
1683 return ERROR_INSUFFICIENT_BUFFER;
1684
1685 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1686 -1, buffer, *size, NULL, NULL);
1687 return ERROR_SUCCESS;
1688 }
1689 }
1690
1691 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1692 PCCERT_CONTEXT context;
1693
1694 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1695 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1696 return ERROR_INSUFFICIENT_BUFFER;
1697 }
1698
1699 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1700 if(context) {
1701 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1702 DWORD len;
1703
1704 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1705 info->ftExpiry = context->pCertInfo->NotAfter;
1706 info->ftStart = context->pCertInfo->NotBefore;
1707 if(unicode) {
1708 len = CertNameToStrW(context->dwCertEncodingType,
1709 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1710 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1711 if(info->lpszSubjectInfo)
1712 CertNameToStrW(context->dwCertEncodingType,
1713 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1714 info->lpszSubjectInfo, len);
1715 len = CertNameToStrW(context->dwCertEncodingType,
1716 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1717 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1718 if (info->lpszIssuerInfo)
1719 CertNameToStrW(context->dwCertEncodingType,
1720 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1721 info->lpszIssuerInfo, len);
1722 }else {
1723 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1724
1725 len = CertNameToStrA(context->dwCertEncodingType,
1726 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1727 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1728 if(infoA->lpszSubjectInfo)
1729 CertNameToStrA(context->dwCertEncodingType,
1730 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1731 infoA->lpszSubjectInfo, len);
1732 len = CertNameToStrA(context->dwCertEncodingType,
1733 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1734 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1735 if(infoA->lpszIssuerInfo)
1736 CertNameToStrA(context->dwCertEncodingType,
1737 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1738 infoA->lpszIssuerInfo, len);
1739 }
1740
1741 /*
1742 * Contrary to MSDN, these do not appear to be set.
1743 * lpszProtocolName
1744 * lpszSignatureAlgName
1745 * lpszEncryptionAlgName
1746 * dwKeySize
1747 */
1748 CertFreeCertificateContext(context);
1749 return ERROR_SUCCESS;
1750 }
1751 }
1752 }
1753
1754 return INET_QueryOption(option, buffer, size, unicode);
1755 }
1756
1757 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1758 {
1759 http_request_t *req = (http_request_t*)hdr;
1760
1761 switch(option) {
1762 case INTERNET_OPTION_SEND_TIMEOUT:
1763 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1764 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1765
1766 if (size != sizeof(DWORD))
1767 return ERROR_INVALID_PARAMETER;
1768
1769 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1770 *(DWORD*)buffer);
1771
1772 case INTERNET_OPTION_USERNAME:
1773 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1774 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1775 return ERROR_SUCCESS;
1776
1777 case INTERNET_OPTION_PASSWORD:
1778 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1779 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1780 return ERROR_SUCCESS;
1781 case INTERNET_OPTION_HTTP_DECODING:
1782 if(size != sizeof(BOOL))
1783 return ERROR_INVALID_PARAMETER;
1784 req->decoding = *(BOOL*)buffer;
1785 return ERROR_SUCCESS;
1786 }
1787
1788 return ERROR_INTERNET_INVALID_OPTION;
1789 }
1790
1791 /* read some more data into the read buffer (the read section must be held) */
1792 static DWORD read_more_data( http_request_t *req, int maxlen )
1793 {
1794 DWORD res;
1795 int len;
1796
1797 if (req->read_pos)
1798 {
1799 /* move existing data to the start of the buffer */
1800 if(req->read_size)
1801 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1802 req->read_pos = 0;
1803 }
1804
1805 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1806
1807 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1808 maxlen - req->read_size, 0, &len );
1809 if(res == ERROR_SUCCESS)
1810 req->read_size += len;
1811
1812 return res;
1813 }
1814
1815 /* remove some amount of data from the read buffer (the read section must be held) */
1816 static void remove_data( http_request_t *req, int count )
1817 {
1818 if (!(req->read_size -= count)) req->read_pos = 0;
1819 else req->read_pos += count;
1820 }
1821
1822 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1823 {
1824 int count, bytes_read, pos = 0;
1825 DWORD res;
1826
1827 EnterCriticalSection( &req->read_section );
1828 for (;;)
1829 {
1830 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1831
1832 if (eol)
1833 {
1834 count = eol - (req->read_buf + req->read_pos);
1835 bytes_read = count + 1;
1836 }
1837 else count = bytes_read = req->read_size;
1838
1839 count = min( count, *len - pos );
1840 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1841 pos += count;
1842 remove_data( req, bytes_read );
1843 if (eol) break;
1844
1845 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1846 {
1847 *len = 0;
1848 TRACE( "returning empty string\n" );
1849 LeaveCriticalSection( &req->read_section );
1850 INTERNET_SetLastError(res);
1851 return FALSE;
1852 }
1853 }
1854 LeaveCriticalSection( &req->read_section );
1855
1856 if (pos < *len)
1857 {
1858 if (pos && buffer[pos - 1] == '\r') pos--;
1859 *len = pos + 1;
1860 }
1861 buffer[*len - 1] = 0;
1862 TRACE( "returning %s\n", debugstr_a(buffer));
1863 return TRUE;
1864 }
1865
1866 /* discard data contents until we reach end of line (the read section must be held) */
1867 static DWORD discard_eol( http_request_t *req )
1868 {
1869 DWORD res;
1870
1871 do
1872 {
1873 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1874 if (eol)
1875 {
1876 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1877 break;
1878 }
1879 req->read_pos = req->read_size = 0; /* discard everything */
1880 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1881 } while (req->read_size);
1882 return ERROR_SUCCESS;
1883 }
1884
1885 /* read the size of the next chunk (the read section must be held) */
1886 static DWORD start_next_chunk( http_request_t *req )
1887 {
1888 DWORD chunk_size = 0, res;
1889
1890 if (!req->dwContentLength) return ERROR_SUCCESS;
1891 if (req->dwContentLength == req->dwContentRead)
1892 {
1893 /* read terminator for the previous chunk */
1894 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1895 req->dwContentLength = ~0u;
1896 req->dwContentRead = 0;
1897 }
1898 for (;;)
1899 {
1900 while (req->read_size)
1901 {
1902 char ch = req->read_buf[req->read_pos];
1903 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1904 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1905 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1906 else if (ch == ';' || ch == '\r' || ch == '\n')
1907 {
1908 TRACE( "reading %u byte chunk\n", chunk_size );
1909 req->dwContentLength = chunk_size;
1910 req->dwContentRead = 0;
1911 return discard_eol( req );
1912 }
1913 remove_data( req, 1 );
1914 }
1915 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1916 if (!req->read_size)
1917 {
1918 req->dwContentLength = req->dwContentRead = 0;
1919 return ERROR_SUCCESS;
1920 }
1921 }
1922 }
1923
1924 /* check if we have reached the end of the data to read (the read section must be held) */
1925 static BOOL end_of_read_data( http_request_t *req )
1926 {
1927 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1928 if (req->read_chunked) return (req->dwContentLength == 0);
1929 if (req->dwContentLength == ~0u) return FALSE;
1930 return (req->dwContentLength == req->dwContentRead);
1931 }
1932
1933 /* fetch some more data into the read buffer (the read section must be held) */
1934 static DWORD refill_buffer( http_request_t *req )
1935 {
1936 int len = sizeof(req->read_buf);
1937 DWORD res;
1938
1939 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1940 {
1941 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1942 }
1943
1944 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1945 if (len <= req->read_size) return ERROR_SUCCESS;
1946
1947 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1948 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1949 return ERROR_SUCCESS;
1950 }
1951
1952 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1953 {
1954 DWORD ret = ERROR_SUCCESS;
1955 int read = 0;
1956
1957 #ifdef HAVE_ZLIB
1958 z_stream *zstream = &req->gzip_stream->zstream;
1959 DWORD buf_avail;
1960 int zres;
1961
1962 while(read < size && !req->gzip_stream->end_of_data) {
1963 if(!req->read_size) {
1964 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1965 break;
1966 }
1967
1968 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1969
1970 zstream->next_in = req->read_buf+req->read_pos;
1971 zstream->avail_in = buf_avail;
1972 zstream->next_out = buf+read;
1973 zstream->avail_out = size-read;
1974 zres = inflate(zstream, Z_FULL_FLUSH);
1975 read = size - zstream->avail_out;
1976 req->dwContentRead += buf_avail-zstream->avail_in;
1977 remove_data(req, buf_avail-zstream->avail_in);
1978 if(zres == Z_STREAM_END) {
1979 TRACE("end of data\n");
1980 req->gzip_stream->end_of_data = TRUE;
1981 inflateEnd(&req->gzip_stream->zstream);
1982 }else if(zres != Z_OK) {
1983 WARN("inflate failed %d\n", zres);
1984 if(!read)
1985 ret = ERROR_INTERNET_DECODING_FAILED;
1986 break;
1987 }
1988 }
1989 #endif
1990
1991 *read_ret = read;
1992 return ret;
1993 }
1994
1995 static void refill_gzip_buffer(http_request_t *req)
1996 {
1997 DWORD res;
1998 int len;
1999
2000 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2001 return;
2002
2003 if(req->gzip_stream->buf_pos) {
2004 if(req->gzip_stream->buf_size)
2005 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2006 req->gzip_stream->buf_pos = 0;
2007 }
2008
2009 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2010 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2011 if(res == ERROR_SUCCESS)
2012 req->gzip_stream->buf_size += len;
2013 }
2014
2015 /* return the size of data available to be read immediately (the read section must be held) */
2016 static DWORD get_avail_data( http_request_t *req )
2017 {
2018 if (req->gzip_stream) {
2019 refill_gzip_buffer(req);
2020 return req->gzip_stream->buf_size;
2021 }
2022 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2023 return 0;
2024 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2025 }
2026
2027 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2028 {
2029 INTERNET_ASYNC_RESULT iar;
2030 DWORD res;
2031
2032 TRACE("%p\n", req);
2033
2034 EnterCriticalSection( &req->read_section );
2035 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2036 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2037 iar.dwError = first_notif ? 0 : get_avail_data(req);
2038 }else {
2039 iar.dwResult = 0;
2040 iar.dwError = res;
2041 }
2042 LeaveCriticalSection( &req->read_section );
2043
2044 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2045 sizeof(INTERNET_ASYNC_RESULT));
2046 }
2047
2048 /* read data from the http connection (the read section must be held) */
2049 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2050 {
2051 BOOL finished_reading = FALSE;
2052 int len, bytes_read = 0;
2053 DWORD ret = ERROR_SUCCESS;
2054
2055 EnterCriticalSection( &req->read_section );
2056
2057 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2058 {
2059 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2060 }
2061
2062 if(req->gzip_stream) {
2063 if(req->gzip_stream->buf_size) {
2064 bytes_read = min(req->gzip_stream->buf_size, size);
2065 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2066 req->gzip_stream->buf_pos += bytes_read;
2067 req->gzip_stream->buf_size -= bytes_read;
2068 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2069 refill_buffer(req);
2070 }
2071
2072 if(size > bytes_read) {
2073 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2074 if(ret == ERROR_SUCCESS)
2075 bytes_read += len;
2076 }
2077
2078 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2079 }else {
2080 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2081
2082 if (req->read_size) {
2083 bytes_read = min( req->read_size, size );
2084 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2085 remove_data( req, bytes_read );
2086 }
2087
2088 if (size > bytes_read && (!bytes_read || sync)) {
2089 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2090 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2091 bytes_read += len;
2092 /* always return success, even if the network layer returns an error */
2093 }
2094
2095 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2096 req->dwContentRead += bytes_read;
2097 }
2098 done:
2099 *read = bytes_read;
2100
2101 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2102 LeaveCriticalSection( &req->read_section );
2103
2104 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2105 BOOL res;
2106 DWORD dwBytesWritten;
2107
2108 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2109 if(!res)
2110 WARN("WriteFile failed: %u\n", GetLastError());
2111 }
2112
2113 if(finished_reading)
2114 HTTP_FinishedReading(req);
2115
2116 return ret;
2117 }
2118
2119
2120 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2121 {
2122 http_request_t *req = (http_request_t*)hdr;
2123 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2124 }
2125
2126 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2127 {
2128 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2129 http_request_t *req = (http_request_t*)workRequest->hdr;
2130 INTERNET_ASYNC_RESULT iar;
2131 DWORD res;
2132
2133 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2134
2135 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2136 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2137
2138 iar.dwResult = res == ERROR_SUCCESS;
2139 iar.dwError = res;
2140
2141 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2142 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2143 sizeof(INTERNET_ASYNC_RESULT));
2144 }
2145
2146 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2147 DWORD flags, DWORD_PTR context)
2148 {
2149 http_request_t *req = (http_request_t*)hdr;
2150 DWORD res;
2151
2152 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2153 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2154
2155 if (buffers->dwStructSize != sizeof(*buffers))
2156 return ERROR_INVALID_PARAMETER;
2157
2158 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2159
2160 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2161 {
2162 WORKREQUEST workRequest;
2163
2164 if (TryEnterCriticalSection( &req->read_section ))
2165 {
2166 if (get_avail_data(req))
2167 {
2168 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2169 &buffers->dwBufferLength, FALSE);
2170 LeaveCriticalSection( &req->read_section );
2171 goto done;
2172 }
2173 LeaveCriticalSection( &req->read_section );
2174 }
2175
2176 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2177 workRequest.hdr = WININET_AddRef(&req->hdr);
2178 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2179
2180 INTERNET_AsyncCall(&workRequest);
2181
2182 return ERROR_IO_PENDING;
2183 }
2184
2185 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2186 !(flags & IRF_NO_WAIT));
2187
2188 done:
2189 if (res == ERROR_SUCCESS) {
2190 DWORD size = buffers->dwBufferLength;
2191 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2192 &size, sizeof(size));
2193 }
2194
2195 return res;
2196 }
2197
2198 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2199 {
2200 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2201 http_request_t *req = (http_request_t*)workRequest->hdr;
2202 INTERNET_ASYNC_RESULT iar;
2203 DWORD res;
2204
2205 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2206
2207 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2208 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2209
2210 iar.dwResult = res == ERROR_SUCCESS;
2211 iar.dwError = res;
2212
2213 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2214 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2215 sizeof(INTERNET_ASYNC_RESULT));
2216 }
2217
2218 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2219 DWORD flags, DWORD_PTR context)
2220 {
2221
2222 http_request_t *req = (http_request_t*)hdr;
2223 DWORD res;
2224
2225 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2226 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2227
2228 if (buffers->dwStructSize != sizeof(*buffers))
2229 return ERROR_INVALID_PARAMETER;
2230
2231 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2232
2233 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2234 {
2235 WORKREQUEST workRequest;
2236
2237 if (TryEnterCriticalSection( &req->read_section ))
2238 {
2239 if (get_avail_data(req))
2240 {
2241 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2242 &buffers->dwBufferLength, FALSE);
2243 LeaveCriticalSection( &req->read_section );
2244 goto done;
2245 }
2246 LeaveCriticalSection( &req->read_section );
2247 }
2248
2249 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2250 workRequest.hdr = WININET_AddRef(&req->hdr);
2251 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2252
2253 INTERNET_AsyncCall(&workRequest);
2254
2255 return ERROR_IO_PENDING;
2256 }
2257
2258 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2259 !(flags & IRF_NO_WAIT));
2260
2261 done:
2262 if (res == ERROR_SUCCESS) {
2263 DWORD size = buffers->dwBufferLength;
2264 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2265 &size, sizeof(size));
2266 }
2267
2268 return res;
2269 }
2270
2271 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2272 {
2273 DWORD res;
2274 http_request_t *lpwhr = (http_request_t*)hdr;
2275
2276 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2277
2278 *written = 0;
2279 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2280 if (res == ERROR_SUCCESS)
2281 lpwhr->dwBytesWritten += *written;
2282
2283 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2284 return res;
2285 }
2286
2287 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2288 {
2289 http_request_t *req = (http_request_t*)workRequest->hdr;
2290
2291 HTTP_ReceiveRequestData(req, FALSE);
2292 }
2293
2294 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2295 {
2296 http_request_t *req = (http_request_t*)hdr;
2297
2298 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2299
2300 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2301 {
2302 WORKREQUEST workRequest;
2303
2304 /* never wait, if we can't enter the section we queue an async request right away */
2305 if (TryEnterCriticalSection( &req->read_section ))
2306 {
2307 if ((*available = get_avail_data( req ))) goto done;
2308 if (end_of_read_data( req )) goto done;
2309 LeaveCriticalSection( &req->read_section );
2310 }
2311
2312 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2313 workRequest.hdr = WININET_AddRef( &req->hdr );
2314
2315 INTERNET_AsyncCall(&workRequest);
2316
2317 return ERROR_IO_PENDING;
2318 }
2319
2320 EnterCriticalSection( &req->read_section );
2321
2322 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2323 {
2324 refill_buffer( req );
2325 *available = get_avail_data( req );
2326 }
2327
2328 done:
2329 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2330 {
2331 DWORD extra;
2332 if (NETCON_query_data_available(&req->netConnection, &extra))
2333 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2334 }
2335 LeaveCriticalSection( &req->read_section );
2336
2337 TRACE( "returning %u\n", *available );
2338 return ERROR_SUCCESS;
2339 }
2340
2341 static const object_vtbl_t HTTPREQVtbl = {
2342 HTTPREQ_Destroy,
2343 HTTPREQ_CloseConnection,
2344 HTTPREQ_QueryOption,
2345 HTTPREQ_SetOption,
2346 HTTPREQ_ReadFile,
2347 HTTPREQ_ReadFileExA,
2348 HTTPREQ_ReadFileExW,
2349 HTTPREQ_WriteFile,
2350 HTTPREQ_QueryDataAvailable,
2351 NULL
2352 };
2353
2354 /***********************************************************************
2355 * HTTP_HttpOpenRequestW (internal)
2356 *
2357 * Open a HTTP request handle
2358 *
2359 * RETURNS
2360 * HINTERNET a HTTP request handle on success
2361 * NULL on failure
2362 *
2363 */
2364 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2365 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2366 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2367 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2368 {
2369 appinfo_t *hIC = NULL;
2370 http_request_t *lpwhr;
2371 LPWSTR lpszHostName = NULL;
2372 HINTERNET handle = NULL;
2373 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2374 DWORD len, res;
2375
2376 TRACE("-->\n");
2377
2378 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2379 hIC = lpwhs->lpAppInfo;
2380
2381 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2382 if (NULL == lpwhr)
2383 {
2384 res = ERROR_OUTOFMEMORY;
2385 goto lend;
2386 }
2387 lpwhr->hdr.htype = WH_HHTTPREQ;
2388 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2389 lpwhr->hdr.dwFlags = dwFlags;
2390 lpwhr->hdr.dwContext = dwContext;
2391 lpwhr->hdr.refs = 1;
2392 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2393 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2394 lpwhr->dwContentLength = ~0u;
2395 InitializeCriticalSection( &lpwhr->read_section );
2396
2397 WININET_AddRef( &lpwhs->hdr );
2398 lpwhr->lpHttpSession = lpwhs;
2399 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2400
2401 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2402 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2403 if (NULL == lpszHostName)
2404 {
2405 res = ERROR_OUTOFMEMORY;
2406 goto lend;
2407 }
2408
2409 handle = WININET_AllocHandle( &lpwhr->hdr );
2410 if (NULL == handle)
2411 {
2412 res = ERROR_OUTOFMEMORY;
2413 goto lend;
2414 }
2415
2416 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2417 {
2418 InternetCloseHandle( handle );
2419 handle = NULL;
2420 goto lend;
2421 }
2422
2423 if (lpszObjectName && *lpszObjectName) {
2424 HRESULT rc;
2425
2426 len = 0;
2427 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2428 if (rc != E_POINTER)
2429 len = strlenW(lpszObjectName)+1;
2430 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2431 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2432 URL_ESCAPE_SPACES_ONLY);
2433 if (rc != S_OK)
2434 {
2435 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2436 strcpyW(lpwhr->lpszPath,lpszObjectName);
2437 }
2438 }else {
2439 static const WCHAR slashW[] = {'/',0};
2440
2441 lpwhr->lpszPath = heap_strdupW(slashW);
2442 }
2443
2444 if (lpszReferrer && *lpszReferrer)
2445 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2446
2447 if (lpszAcceptTypes)
2448 {
2449 int i;
2450 for (i = 0; lpszAcceptTypes[i]; i++)
2451 {
2452 if (!*lpszAcceptTypes[i]) continue;
2453 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2454 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2455 HTTP_ADDHDR_FLAG_REQ |
2456 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2457 }
2458 }
2459
2460 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2461 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2462
2463 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2464 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2465 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2466 {
2467 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2468 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2469 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2470 }
2471 else
2472 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2473 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2474
2475 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2476 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2477 INTERNET_DEFAULT_HTTPS_PORT :
2478 INTERNET_DEFAULT_HTTP_PORT);
2479
2480 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2481 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2482 INTERNET_DEFAULT_HTTPS_PORT :
2483 INTERNET_DEFAULT_HTTP_PORT);
2484
2485 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2486 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2487
2488 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2489 INTERNET_STATUS_HANDLE_CREATED, &handle,
2490 sizeof(handle));
2491
2492 lend:
2493 HeapFree(GetProcessHeap(), 0, lpszHostName);
2494 if( lpwhr )
2495 WININET_Release( &lpwhr->hdr );
2496
2497 TRACE("<-- %p (%p)\n", handle, lpwhr);
2498 *ret = handle;
2499 return res;
2500 }
2501
2502 /***********************************************************************
2503 * HttpOpenRequestW (WININET.@)
2504 *
2505 * Open a HTTP request handle
2506 *
2507 * RETURNS
2508 * HINTERNET a HTTP request handle on success
2509 * NULL on failure
2510 *
2511 */
2512 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2513 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2514 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2515 DWORD dwFlags, DWORD_PTR dwContext)
2516 {
2517 http_session_t *lpwhs;
2518 HINTERNET handle = NULL;
2519 DWORD res;
2520
2521 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2522 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2523 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2524 dwFlags, dwContext);
2525 if(lpszAcceptTypes!=NULL)
2526 {
2527 int i;
2528 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2529 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2530 }
2531
2532 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2533 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2534 {
2535 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2536 goto lend;
2537 }
2538
2539 /*
2540 * My tests seem to show that the windows version does not
2541 * become asynchronous until after this point. And anyhow
2542 * if this call was asynchronous then how would you get the
2543 * necessary HINTERNET pointer returned by this function.
2544 *
2545 */
2546 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2547 lpszVersion, lpszReferrer, lpszAcceptTypes,
2548 dwFlags, dwContext, &handle);
2549 lend:
2550 if( lpwhs )
2551 WININET_Release( &lpwhs->hdr );
2552 TRACE("returning %p\n", handle);
2553 if(res != ERROR_SUCCESS)
2554 SetLastError(res);
2555 return handle;
2556 }
2557
2558 /* read any content returned by the server so that the connection can be
2559 * reused */
2560 static void HTTP_DrainContent(http_request_t *req)
2561 {
2562 DWORD bytes_read;
2563
2564 if (!NETCON_connected(&req->netConnection)) return;
2565
2566 if (req->dwContentLength == -1)
2567 {
2568 NETCON_close(&req->netConnection);
2569 return;
2570 }
2571 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2572
2573 do
2574 {
2575 char buffer[2048];
2576 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2577 return;
2578 } while (bytes_read);
2579 }
2580
2581 static const LPCWSTR header_lookup[] = {
2582 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2583 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2584 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2585 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2586 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2587 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2588 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2589 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2590 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2591 szDate, /* HTTP_QUERY_DATE = 9 */
2592 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2593 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2594 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2595 szURI, /* HTTP_QUERY_URI = 13 */
2596 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2597 NULL, /* HTTP_QUERY_COST = 15 */
2598 NULL, /* HTTP_QUERY_LINK = 16 */
2599 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2600 NULL, /* HTTP_QUERY_VERSION = 18 */
2601 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2602 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2603 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2604 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2605 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2606 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2607 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2608 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2609 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2610 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2611 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2612 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2613 NULL, /* HTTP_QUERY_FROM = 31 */
2614 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2615 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2616 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2617 szReferer, /* HTTP_QUERY_REFERER = 35 */
2618 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2619 szServer, /* HTTP_QUERY_SERVER = 37 */
2620 NULL, /* HTTP_TITLE = 38 */
2621 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2622 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2623 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2624 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2625 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2626 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2627 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2628 NULL, /* HTTP_QUERY_REFRESH = 46 */
2629 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2630 szAge, /* HTTP_QUERY_AGE = 48 */
2631 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2632 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2633 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2634 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2635 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2636 szETag, /* HTTP_QUERY_ETAG = 54 */
2637 hostW, /* HTTP_QUERY_HOST = 55 */
2638 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2639 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2640 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2641 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2642 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2643 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2644 szRange, /* HTTP_QUERY_RANGE = 62 */
2645 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2646 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2647 szVary, /* HTTP_QUERY_VARY = 65 */
2648 szVia, /* HTTP_QUERY_VIA = 66 */
2649 szWarning, /* HTTP_QUERY_WARNING = 67 */
2650 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2651 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2652 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2653 };
2654
2655 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2656
2657 /***********************************************************************
2658 * HTTP_HttpQueryInfoW (internal)
2659 */
2660 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2661 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2662 {
2663 LPHTTPHEADERW lphttpHdr = NULL;
2664 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2665 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2666 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2667 INT index = -1;
2668
2669 /* Find requested header structure */
2670 switch (level)
2671 {
2672 case HTTP_QUERY_CUSTOM:
2673 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2674 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2675 break;
2676 case HTTP_QUERY_RAW_HEADERS_CRLF:
2677 {
2678 LPWSTR headers;
2679 DWORD len = 0;
2680 DWORD res = ERROR_INVALID_PARAMETER;
2681
2682 if (request_only)
2683 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2684 else
2685 headers = lpwhr->lpszRawHeaders;
2686
2687 if (headers)
2688 len = strlenW(headers) * sizeof(WCHAR);
2689
2690 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2691 {
2692 len += sizeof(WCHAR);
2693 res = ERROR_INSUFFICIENT_BUFFER;
2694 }
2695 else if (lpBuffer)
2696 {
2697 if (headers)
2698 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2699 else
2700 {
2701 len = strlenW(szCrLf) * sizeof(WCHAR);
2702 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2703 }
2704 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2705 res = ERROR_SUCCESS;
2706 }
2707 *lpdwBufferLength = len;
2708
2709 if (request_only)
2710 HeapFree(GetProcessHeap(), 0, headers);
2711 return res;
2712 }
2713 case HTTP_QUERY_RAW_HEADERS:
2714 {
2715 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2716 DWORD i, size = 0;
2717 LPWSTR pszString = lpBuffer;
2718
2719 for (i = 0; ppszRawHeaderLines[i]; i++)
2720 size += strlenW(ppszRawHeaderLines[i]) + 1;
2721
2722 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2723 {
2724 HTTP_FreeTokens(ppszRawHeaderLines);
2725 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2726 return ERROR_INSUFFICIENT_BUFFER;
2727 }
2728 if (pszString)
2729 {
2730 for (i = 0; ppszRawHeaderLines[i]; i++)
2731 {
2732 DWORD len = strlenW(ppszRawHeaderLines[i]);
2733 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2734 pszString += len+1;
2735 }
2736 *pszString = '\0';
2737 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2738 }
2739 *lpdwBufferLength = size * sizeof(WCHAR);
2740 HTTP_FreeTokens(ppszRawHeaderLines);
2741
2742 return ERROR_SUCCESS;
2743 }
2744 case HTTP_QUERY_STATUS_TEXT:
2745 if (lpwhr->lpszStatusText)
2746 {
2747 DWORD len = strlenW(lpwhr->lpszStatusText);
2748 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2749 {
2750 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2751 return ERROR_INSUFFICIENT_BUFFER;
2752 }
2753 if (lpBuffer)
2754 {
2755 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2756 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2757 }
2758 *lpdwBufferLength = len * sizeof(WCHAR);
2759 return ERROR_SUCCESS;
2760 }
2761 break;
2762 case HTTP_QUERY_VERSION:
2763 if (lpwhr->lpszVersion)
2764 {
2765 DWORD len = strlenW(lpwhr->lpszVersion);
2766 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2767 {
2768 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2769 return ERROR_INSUFFICIENT_BUFFER;
2770 }
2771 if (lpBuffer)
2772 {
2773 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2774 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2775 }
2776 *lpdwBufferLength = len * sizeof(WCHAR);
2777 return ERROR_SUCCESS;
2778 }
2779 break;
2780 case HTTP_QUERY_CONTENT_ENCODING:
2781 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2782 requested_index,request_only);
2783 break;
2784 default:
2785 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2786
2787 if (level < LAST_TABLE_HEADER && header_lookup[level])
2788 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2789 requested_index,request_only);
2790 }
2791
2792 if (index >= 0)
2793 lphttpHdr = &lpwhr->pCustHeaders[index];
2794
2795 /* Ensure header satisfies requested attributes */
2796 if (!lphttpHdr ||
2797 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2798 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2799 {
2800 return ERROR_HTTP_HEADER_NOT_FOUND;
2801 }
2802
2803 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2804
2805 /* coalesce value to requested type */
2806 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2807 {
2808 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2809 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2810 }
2811 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2812 {
2813 time_t tmpTime;
2814 struct tm tmpTM;
2815 SYSTEMTIME *STHook;
2816
2817 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2818
2819 tmpTM = *gmtime(&tmpTime);
2820 STHook = (SYSTEMTIME *)lpBuffer;
2821 STHook->wDay = tmpTM.tm_mday;
2822 STHook->wHour = tmpTM.tm_hour;
2823 STHook->wMilliseconds = 0;