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