1 #ifdef __REACTOS__
2 #include "precomp.h"
3 #include "zlib.h"
4 #else/*
5 * Wininet - HTTP Implementation
6 *
7 * Copyright 1999 Corel Corporation
8 * Copyright 2002 CodeWeavers Inc.
9 * Copyright 2002 TransGaming Technologies Inc.
10 * Copyright 2004 Mike McCormack for CodeWeavers
11 * Copyright 2005 Aric Stewart for CodeWeavers
12 * Copyright 2006 Robert Shearman for CodeWeavers
13 * Copyright 2011 Jacek Caban for CodeWeavers
14 *
15 * Ulrich Czekalla
16 * David Hammerton
17 *
18 * This library is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU Lesser General Public
20 * License as published by the Free Software Foundation; either
21 * version 2.1 of the License, or (at your option) any later version.
22 *
23 * This library is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * Lesser General Public License for more details.
27 *
28 * You should have received a copy of the GNU Lesser General Public
29 * License along with this library; if not, write to the Free Software
30 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 */
32
33 #include <stdlib.h>
34
35 #include "winsock2.h"
36 #include "ws2ipdef.h"
37
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <time.h>
41 #include <assert.h>
42 #include <errno.h>
43 #include <limits.h>
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wininet.h"
48 #include "winerror.h"
49 #include "winternl.h"
50 #define NO_SHLWAPI_STREAM
51 #define NO_SHLWAPI_REG
52 #define NO_SHLWAPI_GDI
53 #include "shlwapi.h"
54 #include "sspi.h"
55 #include "wincrypt.h"
56 #include "winuser.h"
57
58 #include "internet.h"
59 #include "zlib.h"
60 #include "resource.h"
61 #include "wine/debug.h"
62 #include "wine/exception.h"
63 #endif /* defined(__REACTOS__) */
64
65 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
66
67 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
68 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
69 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
70 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
71 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
72 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
73
74 #define COLLECT_TIME 60000
75
76 struct HttpAuthInfo
77 {
78 LPWSTR scheme;
79 CredHandle cred;
80 CtxtHandle ctx;
81 TimeStamp exp;
82 ULONG attr;
83 ULONG max_token;
84 void *auth_data;
85 unsigned int auth_data_len;
86 BOOL finished; /* finished authenticating */
87 };
88
89
90 typedef struct _basicAuthorizationData
91 {
92 struct list entry;
93
94 LPWSTR host;
95 LPWSTR realm;
96 LPSTR authorization;
97 UINT authorizationLen;
98 } basicAuthorizationData;
99
100 typedef struct _authorizationData
101 {
102 struct list entry;
103
104 LPWSTR host;
105 LPWSTR scheme;
106 LPWSTR domain;
107 UINT domain_len;
108 LPWSTR user;
109 UINT user_len;
110 LPWSTR password;
111 UINT password_len;
112 } authorizationData;
113
114 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
115 static struct list authorizationCache = LIST_INIT(authorizationCache);
116
117 static CRITICAL_SECTION authcache_cs;
118 static CRITICAL_SECTION_DEBUG critsect_debug =
119 {
120 0, 0, &authcache_cs,
121 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
122 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
123 };
124 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
125
126 static DWORD HTTP_GetResponseHeaders(http_request_t *req, INT *len);
127 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
128 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
129 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
130 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
131 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
132 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
133 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
134 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
135 static DWORD drain_content(http_request_t*,BOOL);
136
137 static CRITICAL_SECTION connection_pool_cs;
138 static CRITICAL_SECTION_DEBUG connection_pool_debug =
139 {
140 0, 0, &connection_pool_cs,
141 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
142 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
143 };
144 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
145
146 static struct list connection_pool = LIST_INIT(connection_pool);
147 static BOOL collector_running;
148
server_addref(server_t * server)149 void server_addref(server_t *server)
150 {
151 InterlockedIncrement(&server->ref);
152 }
153
server_release(server_t * server)154 void server_release(server_t *server)
155 {
156 if(InterlockedDecrement(&server->ref))
157 return;
158
159 #ifdef __REACTOS__
160 EnterCriticalSection(&connection_pool_cs);
161 #endif
162 list_remove(&server->entry);
163 #ifdef __REACTOS__
164 LeaveCriticalSection(&connection_pool_cs);
165 #endif
166
167 if(server->cert_chain)
168 CertFreeCertificateChain(server->cert_chain);
169 heap_free(server->name);
170 heap_free(server->scheme_host_port);
171 heap_free(server);
172 }
173
process_host_port(server_t * server)174 static BOOL process_host_port(server_t *server)
175 {
176 BOOL default_port;
177 size_t name_len, len;
178 WCHAR *buf;
179
180 name_len = lstrlenW(server->name);
181 len = name_len + 10 /* strlen("://:<port>") */ + ARRAY_SIZE(L"https");
182 buf = heap_alloc( len * sizeof(WCHAR) );
183 if(!buf)
184 return FALSE;
185
186 swprintf(buf, len, L"%s://%s:%u", server->is_https ? L"https" : L"http", server->name, server->port);
187 server->scheme_host_port = buf;
188
189 server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
190 if(server->is_https)
191 server->host_port++;
192
193 default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
194 server->canon_host_port = default_port ? server->name : server->host_port;
195 return TRUE;
196 }
197
get_server(substr_t name,INTERNET_PORT port,BOOL is_https,BOOL do_create)198 server_t *get_server(substr_t name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
199 {
200 server_t *iter, *server = NULL;
201
202 EnterCriticalSection(&connection_pool_cs);
203
204 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
205 if(iter->port == port && name.len == lstrlenW(iter->name) && !wcsnicmp(iter->name, name.str, name.len)
206 && iter->is_https == is_https) {
207 server = iter;
208 server_addref(server);
209 break;
210 }
211 }
212
213 if(!server && do_create) {
214 server = heap_alloc_zero(sizeof(*server));
215 if(server) {
216 server->ref = 2; /* list reference and return */
217 server->port = port;
218 server->is_https = is_https;
219 list_init(&server->conn_pool);
220 server->name = heap_strndupW(name.str, name.len);
221 if(server->name && process_host_port(server)) {
222 list_add_head(&connection_pool, &server->entry);
223 }else {
224 heap_free(server);
225 server = NULL;
226 }
227 }
228 }
229
230 LeaveCriticalSection(&connection_pool_cs);
231
232 return server;
233 }
234
collect_connections(collect_type_t collect_type)235 BOOL collect_connections(collect_type_t collect_type)
236 {
237 netconn_t *netconn, *netconn_safe;
238 server_t *server, *server_safe;
239 BOOL remaining = FALSE;
240 DWORD64 now;
241
242 #ifdef __REACTOS__
243 now = GetTickCount();
244 #else
245 now = GetTickCount64();
246 #endif
247
248 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
249 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
250 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
251 TRACE("freeing %p\n", netconn);
252 list_remove(&netconn->pool_entry);
253 free_netconn(netconn);
254 }else {
255 remaining = TRUE;
256 }
257 }
258
259 if(collect_type == COLLECT_CLEANUP) {
260 list_remove(&server->entry);
261 list_init(&server->entry);
262 server_release(server);
263 }
264 }
265
266 return remaining;
267 }
268
collect_connections_proc(void * arg)269 static DWORD WINAPI collect_connections_proc(void *arg)
270 {
271 BOOL remaining_conns;
272
273 do {
274 /* FIXME: Use more sophisticated method */
275 Sleep(5000);
276
277 EnterCriticalSection(&connection_pool_cs);
278
279 remaining_conns = collect_connections(COLLECT_TIMEOUT);
280 if(!remaining_conns)
281 collector_running = FALSE;
282
283 LeaveCriticalSection(&connection_pool_cs);
284 }while(remaining_conns);
285
286 FreeLibraryAndExitThread(WININET_hModule, 0);
287 }
288
289 /***********************************************************************
290 * HTTP_GetHeader (internal)
291 *
292 * Headers section must be held
293 */
HTTP_GetHeader(http_request_t * req,LPCWSTR head)294 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
295 {
296 int HeaderIndex = 0;
297 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
298 if (HeaderIndex == -1)
299 return NULL;
300 else
301 return &req->custHeaders[HeaderIndex];
302 }
303
get_host_header(http_request_t * req)304 static WCHAR *get_host_header( http_request_t *req )
305 {
306 HTTPHEADERW *header;
307 WCHAR *ret = NULL;
308
309 EnterCriticalSection( &req->headers_section );
310 if ((header = HTTP_GetHeader( req, L"Host" ))) ret = heap_strdupW( header->lpszValue );
311 else ret = heap_strdupW( req->server->canon_host_port );
312 LeaveCriticalSection( &req->headers_section );
313 return ret;
314 }
315
316 struct data_stream_vtbl_t {
317 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
318 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,BOOL);
319 DWORD (*drain_content)(data_stream_t*,http_request_t*,BOOL);
320 void (*destroy)(data_stream_t*);
321 };
322
323 typedef struct {
324 data_stream_t data_stream;
325
326 BYTE buf[READ_BUFFER_SIZE];
327 DWORD buf_size;
328 DWORD buf_pos;
329 DWORD chunk_size;
330
331 enum {
332 CHUNKED_STREAM_STATE_READING_CHUNK_SIZE,
333 CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE,
334 CHUNKED_STREAM_STATE_READING_CHUNK,
335 CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA,
336 CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END,
337 CHUNKED_STREAM_STATE_END_OF_STREAM,
338 CHUNKED_STREAM_STATE_ERROR
339 } state;
340 } chunked_stream_t;
341
destroy_data_stream(data_stream_t * stream)342 static inline void destroy_data_stream(data_stream_t *stream)
343 {
344 stream->vtbl->destroy(stream);
345 }
346
reset_data_stream(http_request_t * req)347 static void reset_data_stream(http_request_t *req)
348 {
349 destroy_data_stream(req->data_stream);
350 req->data_stream = &req->netconn_stream.data_stream;
351 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
352 req->read_gzip = FALSE;
353 }
354
remove_header(http_request_t * request,const WCHAR * str,BOOL from_request)355 static void remove_header( http_request_t *request, const WCHAR *str, BOOL from_request )
356 {
357 int index;
358 EnterCriticalSection( &request->headers_section );
359 index = HTTP_GetCustomHeaderIndex( request, str, 0, from_request );
360 if (index != -1) HTTP_DeleteCustomHeader( request, index );
361 LeaveCriticalSection( &request->headers_section );
362 }
363
364 typedef struct {
365 data_stream_t stream;
366 data_stream_t *parent_stream;
367 z_stream zstream;
368 BYTE buf[READ_BUFFER_SIZE];
369 DWORD buf_size;
370 DWORD buf_pos;
371 BOOL end_of_data;
372 } gzip_stream_t;
373
gzip_end_of_data(data_stream_t * stream,http_request_t * req)374 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
375 {
376 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
377 return gzip_stream->end_of_data
378 || (!gzip_stream->buf_size && gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req));
379 }
380
gzip_read(data_stream_t * stream,http_request_t * req,BYTE * buf,DWORD size,DWORD * read,BOOL allow_blocking)381 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
382 DWORD *read, BOOL allow_blocking)
383 {
384 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
385 z_stream *zstream = &gzip_stream->zstream;
386 DWORD current_read, ret_read = 0;
387 int zres;
388 DWORD res = ERROR_SUCCESS;
389
390 TRACE("(%d %x)\n", size, allow_blocking);
391
392 while(size && !gzip_stream->end_of_data) {
393 if(!gzip_stream->buf_size) {
394 if(gzip_stream->buf_pos) {
395 if(gzip_stream->buf_size)
396 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
397 gzip_stream->buf_pos = 0;
398 }
399 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
400 sizeof(gzip_stream->buf)-gzip_stream->buf_size, ¤t_read, allow_blocking);
401 if(res != ERROR_SUCCESS)
402 break;
403
404 gzip_stream->buf_size += current_read;
405 if(!current_read) {
406 WARN("unexpected end of data\n");
407 gzip_stream->end_of_data = TRUE;
408 break;
409 }
410 }
411
412 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
413 zstream->avail_in = gzip_stream->buf_size;
414 zstream->next_out = buf+ret_read;
415 zstream->avail_out = size;
416 zres = inflate(&gzip_stream->zstream, 0);
417 current_read = size - zstream->avail_out;
418 size -= current_read;
419 ret_read += current_read;
420 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
421 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
422 if(zres == Z_STREAM_END) {
423 TRACE("end of data\n");
424 gzip_stream->end_of_data = TRUE;
425 inflateEnd(zstream);
426 }else if(zres != Z_OK) {
427 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
428 if(!ret_read)
429 res = ERROR_INTERNET_DECODING_FAILED;
430 break;
431 }
432
433 if(ret_read)
434 allow_blocking = FALSE;
435 }
436
437 TRACE("read %u bytes\n", ret_read);
438 if(ret_read)
439 res = ERROR_SUCCESS;
440 *read = ret_read;
441 return res;
442 }
443
gzip_drain_content(data_stream_t * stream,http_request_t * req,BOOL allow_blocking)444 static DWORD gzip_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
445 {
446 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
447 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req, allow_blocking);
448 }
449
gzip_destroy(data_stream_t * stream)450 static void gzip_destroy(data_stream_t *stream)
451 {
452 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
453
454 destroy_data_stream(gzip_stream->parent_stream);
455
456 if(!gzip_stream->end_of_data)
457 inflateEnd(&gzip_stream->zstream);
458 heap_free(gzip_stream);
459 }
460
461 static const data_stream_vtbl_t gzip_stream_vtbl = {
462 gzip_end_of_data,
463 gzip_read,
464 gzip_drain_content,
465 gzip_destroy
466 };
467
wininet_zalloc(voidpf opaque,uInt items,uInt size)468 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
469 {
470 return heap_alloc(items*size);
471 }
472
wininet_zfree(voidpf opaque,voidpf address)473 static void wininet_zfree(voidpf opaque, voidpf address)
474 {
475 heap_free(address);
476 }
477
init_gzip_stream(http_request_t * req,BOOL is_gzip)478 static DWORD init_gzip_stream(http_request_t *req, BOOL is_gzip)
479 {
480 gzip_stream_t *gzip_stream;
481 int zres;
482
483 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
484 if(!gzip_stream)
485 return ERROR_OUTOFMEMORY;
486
487 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
488 gzip_stream->zstream.zalloc = wininet_zalloc;
489 gzip_stream->zstream.zfree = wininet_zfree;
490
491 zres = inflateInit2(&gzip_stream->zstream, is_gzip ? 0x1f : -15);
492 if(zres != Z_OK) {
493 ERR("inflateInit failed: %d\n", zres);
494 heap_free(gzip_stream);
495 return ERROR_OUTOFMEMORY;
496 }
497
498 remove_header(req, L"Content-Length", FALSE);
499
500 if(req->read_size) {
501 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
502 gzip_stream->buf_size = req->read_size;
503 req->read_pos = req->read_size = 0;
504 }
505
506 req->read_gzip = TRUE;
507 gzip_stream->parent_stream = req->data_stream;
508 req->data_stream = &gzip_stream->stream;
509 return ERROR_SUCCESS;
510 }
511
512 /***********************************************************************
513 * HTTP_FreeTokens (internal)
514 *
515 * Frees table of pointers.
516 */
HTTP_FreeTokens(LPWSTR * token_array)517 static void HTTP_FreeTokens(LPWSTR * token_array)
518 {
519 int i;
520 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
521 heap_free(token_array);
522 }
523
HTTP_FixURL(http_request_t * request)524 static void HTTP_FixURL(http_request_t *request)
525 {
526 /* If we don't have a path we set it to root */
527 if (NULL == request->path)
528 request->path = heap_strdupW(L"/");
529 else /* remove \r and \n*/
530 {
531 int nLen = lstrlenW(request->path);
532 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
533 {
534 nLen--;
535 request->path[nLen]='\0';
536 }
537 /* Replace '\' with '/' */
538 while (nLen>0) {
539 nLen--;
540 if (request->path[nLen] == '\\') request->path[nLen]='/';
541 }
542 }
543
544 if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
545 request->path, lstrlenW(request->path), L"http://", lstrlenW(L"http://") )
546 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
547 {
548 WCHAR *fixurl = heap_alloc((lstrlenW(request->path) + 2)*sizeof(WCHAR));
549 *fixurl = '/';
550 lstrcpyW(fixurl + 1, request->path);
551 heap_free( request->path );
552 request->path = fixurl;
553 }
554 }
555
build_request_header(http_request_t * request,const WCHAR * verb,const WCHAR * path,const WCHAR * version,BOOL use_cr)556 static WCHAR* build_request_header(http_request_t *request, const WCHAR *verb,
557 const WCHAR *path, const WCHAR *version, BOOL use_cr)
558 {
559 LPWSTR requestString;
560 DWORD len, n;
561 LPCWSTR *req;
562 UINT i;
563
564 EnterCriticalSection( &request->headers_section );
565
566 /* allocate space for an array of all the string pointers to be added */
567 len = request->nCustHeaders * 5 + 10;
568 if (!(req = heap_alloc( len * sizeof(const WCHAR *) )))
569 {
570 LeaveCriticalSection( &request->headers_section );
571 return NULL;
572 }
573
574 /* add the verb, path and HTTP version string */
575 n = 0;
576 req[n++] = verb;
577 req[n++] = L" ";
578 req[n++] = path;
579 req[n++] = L" ";
580 req[n++] = version;
581 if (use_cr)
582 req[n++] = L"\r";
583 req[n++] = L"\n";
584
585 /* Append custom request headers */
586 for (i = 0; i < request->nCustHeaders; i++)
587 {
588 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
589 {
590 req[n++] = request->custHeaders[i].lpszField;
591 req[n++] = L": ";
592 req[n++] = request->custHeaders[i].lpszValue;
593 if (use_cr)
594 req[n++] = L"\r";
595 req[n++] = L"\n";
596
597 TRACE("Adding custom header %s (%s)\n",
598 debugstr_w(request->custHeaders[i].lpszField),
599 debugstr_w(request->custHeaders[i].lpszValue));
600 }
601 }
602 if (use_cr)
603 req[n++] = L"\r";
604 req[n++] = L"\n";
605 req[n] = NULL;
606
607 requestString = HTTP_build_req( req, 4 );
608 heap_free( req );
609 LeaveCriticalSection( &request->headers_section );
610 return requestString;
611 }
612
build_response_header(http_request_t * request,BOOL use_cr)613 static WCHAR* build_response_header(http_request_t *request, BOOL use_cr)
614 {
615 const WCHAR **req;
616 WCHAR *ret, buf[14];
617 DWORD i, n = 0;
618
619 EnterCriticalSection( &request->headers_section );
620
621 if (!(req = heap_alloc( (request->nCustHeaders * 5 + 8) * sizeof(WCHAR *) )))
622 {
623 LeaveCriticalSection( &request->headers_section );
624 return NULL;
625 }
626
627 if (request->status_code)
628 {
629 req[n++] = request->version;
630 swprintf(buf, ARRAY_SIZE(buf), L" %u ", request->status_code);
631 req[n++] = buf;
632 req[n++] = request->statusText;
633 if (use_cr)
634 req[n++] = L"\r";
635 req[n++] = L"\n";
636 }
637
638 for(i = 0; i < request->nCustHeaders; i++)
639 {
640 if(!(request->custHeaders[i].wFlags & HDR_ISREQUEST)
641 && wcscmp(request->custHeaders[i].lpszField, L"Status"))
642 {
643 req[n++] = request->custHeaders[i].lpszField;
644 req[n++] = L": ";
645 req[n++] = request->custHeaders[i].lpszValue;
646 if(use_cr)
647 req[n++] = L"\r";
648 req[n++] = L"\n";
649
650 TRACE("Adding custom header %s (%s)\n",
651 debugstr_w(request->custHeaders[i].lpszField),
652 debugstr_w(request->custHeaders[i].lpszValue));
653 }
654 }
655 if(use_cr)
656 req[n++] = L"\r";
657 req[n++] = L"\n";
658 req[n] = NULL;
659
660 ret = HTTP_build_req(req, 0);
661 heap_free(req);
662 LeaveCriticalSection( &request->headers_section );
663 return ret;
664 }
665
HTTP_ProcessCookies(http_request_t * request)666 static void HTTP_ProcessCookies( http_request_t *request )
667 {
668 int HeaderIndex;
669 int numCookies = 0;
670 LPHTTPHEADERW setCookieHeader;
671 WCHAR *path, *tmp;
672
673 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
674 return;
675
676 path = heap_strdupW(request->path);
677 if (!path)
678 return;
679
680 tmp = wcsrchr(path, '/');
681 if (tmp && tmp[1]) tmp[1] = 0;
682
683 EnterCriticalSection( &request->headers_section );
684
685 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, L"Set-Cookie", numCookies++, FALSE)) != -1)
686 {
687 const WCHAR *data;
688 substr_t name;
689
690 setCookieHeader = &request->custHeaders[HeaderIndex];
691
692 if (!setCookieHeader->lpszValue)
693 continue;
694
695 data = wcschr(setCookieHeader->lpszValue, '=');
696 if(!data)
697 continue;
698
699 name = substr(setCookieHeader->lpszValue, data - setCookieHeader->lpszValue);
700 data++;
701 set_cookie(substrz(request->server->name), substrz(path), name, substrz(data), INTERNET_COOKIE_HTTPONLY);
702 }
703
704 LeaveCriticalSection( &request->headers_section );
705 heap_free(path);
706 }
707
strip_spaces(LPWSTR start)708 static void strip_spaces(LPWSTR start)
709 {
710 LPWSTR str = start;
711 LPWSTR end;
712
713 while (*str == ' ')
714 str++;
715
716 if (str != start)
717 memmove(start, str, sizeof(WCHAR) * (lstrlenW(str) + 1));
718
719 end = start + lstrlenW(start) - 1;
720 while (end >= start && *end == ' ')
721 {
722 *end = '\0';
723 end--;
724 }
725 }
726
is_basic_auth_value(LPCWSTR pszAuthValue,LPWSTR * pszRealm)727 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
728 {
729 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
730 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
731 BOOL is_basic;
732 is_basic = !wcsnicmp(pszAuthValue, szBasic, ARRAY_SIZE(szBasic)) &&
733 ((pszAuthValue[ARRAY_SIZE(szBasic)] == ' ') || !pszAuthValue[ARRAY_SIZE(szBasic)]);
734 if (is_basic && pszRealm)
735 {
736 LPCWSTR token;
737 LPCWSTR ptr = &pszAuthValue[ARRAY_SIZE(szBasic)];
738 LPCWSTR realm;
739 ptr++;
740 *pszRealm=NULL;
741 token = wcschr(ptr,'=');
742 if (!token)
743 return TRUE;
744 realm = ptr;
745 while (*realm == ' ')
746 realm++;
747 if(!wcsnicmp(realm, szRealm, ARRAY_SIZE(szRealm)) &&
748 (realm[ARRAY_SIZE(szRealm)] == ' ' || realm[ARRAY_SIZE(szRealm)] == '='))
749 {
750 token++;
751 while (*token == ' ')
752 token++;
753 if (*token == '\0')
754 return TRUE;
755 *pszRealm = heap_strdupW(token);
756 strip_spaces(*pszRealm);
757 }
758 }
759
760 return is_basic;
761 }
762
destroy_authinfo(struct HttpAuthInfo * authinfo)763 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
764 {
765 if (!authinfo) return;
766
767 if (SecIsValidHandle(&authinfo->ctx))
768 DeleteSecurityContext(&authinfo->ctx);
769 if (SecIsValidHandle(&authinfo->cred))
770 FreeCredentialsHandle(&authinfo->cred);
771
772 heap_free(authinfo->auth_data);
773 heap_free(authinfo->scheme);
774 heap_free(authinfo);
775 }
776
retrieve_cached_basic_authorization(http_request_t * req,const WCHAR * host,const WCHAR * realm,char ** auth_data)777 static UINT retrieve_cached_basic_authorization(http_request_t *req, const WCHAR *host, const WCHAR *realm, char **auth_data)
778 {
779 basicAuthorizationData *ad;
780 UINT rc = 0;
781
782 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
783
784 EnterCriticalSection(&authcache_cs);
785 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
786 {
787 if (!wcsicmp(host, ad->host) && (!realm || !wcscmp(realm, ad->realm)))
788 {
789 char *colon;
790 DWORD length;
791
792 TRACE("Authorization found in cache\n");
793 *auth_data = heap_alloc(ad->authorizationLen);
794 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
795 rc = ad->authorizationLen;
796
797 /* update session username and password to reflect current credentials */
798 colon = strchr(ad->authorization, ':');
799 length = colon - ad->authorization;
800
801 heap_free(req->session->userName);
802 heap_free(req->session->password);
803
804 req->session->userName = heap_strndupAtoW(ad->authorization, length, &length);
805 length++;
806 req->session->password = heap_strndupAtoW(&ad->authorization[length], ad->authorizationLen - length, &length);
807 break;
808 }
809 }
810 LeaveCriticalSection(&authcache_cs);
811 return rc;
812 }
813
cache_basic_authorization(LPWSTR host,LPWSTR realm,LPSTR auth_data,UINT auth_data_len)814 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
815 {
816 struct list *cursor;
817 basicAuthorizationData* ad = NULL;
818
819 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
820
821 EnterCriticalSection(&authcache_cs);
822 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
823 {
824 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
825 if (!wcsicmp(host,check->host) && !wcscmp(realm,check->realm))
826 {
827 ad = check;
828 break;
829 }
830 }
831
832 if (ad)
833 {
834 TRACE("Found match in cache, replacing\n");
835 heap_free(ad->authorization);
836 ad->authorization = heap_alloc(auth_data_len);
837 memcpy(ad->authorization, auth_data, auth_data_len);
838 ad->authorizationLen = auth_data_len;
839 }
840 else
841 {
842 ad = heap_alloc(sizeof(basicAuthorizationData));
843 ad->host = heap_strdupW(host);
844 ad->realm = heap_strdupW(realm);
845 ad->authorization = heap_alloc(auth_data_len);
846 memcpy(ad->authorization, auth_data, auth_data_len);
847 ad->authorizationLen = auth_data_len;
848 list_add_head(&basicAuthorizationCache,&ad->entry);
849 TRACE("authorization cached\n");
850 }
851 LeaveCriticalSection(&authcache_cs);
852 }
853
retrieve_cached_authorization(LPWSTR host,LPWSTR scheme,SEC_WINNT_AUTH_IDENTITY_W * nt_auth_identity)854 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
855 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
856 {
857 authorizationData *ad;
858
859 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
860
861 EnterCriticalSection(&authcache_cs);
862 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
863 if(!wcsicmp(host, ad->host) && !wcsicmp(scheme, ad->scheme)) {
864 TRACE("Authorization found in cache\n");
865
866 nt_auth_identity->User = heap_strdupW(ad->user);
867 nt_auth_identity->Password = heap_strdupW(ad->password);
868 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
869 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
870 (!nt_auth_identity->Domain && ad->domain_len)) {
871 heap_free(nt_auth_identity->User);
872 heap_free(nt_auth_identity->Password);
873 heap_free(nt_auth_identity->Domain);
874 break;
875 }
876
877 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
878 nt_auth_identity->UserLength = ad->user_len;
879 nt_auth_identity->PasswordLength = ad->password_len;
880 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
881 nt_auth_identity->DomainLength = ad->domain_len;
882 LeaveCriticalSection(&authcache_cs);
883 return TRUE;
884 }
885 }
886 LeaveCriticalSection(&authcache_cs);
887
888 return FALSE;
889 }
890
cache_authorization(LPWSTR host,LPWSTR scheme,SEC_WINNT_AUTH_IDENTITY_W * nt_auth_identity)891 static void cache_authorization(LPWSTR host, LPWSTR scheme,
892 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
893 {
894 authorizationData *ad;
895 BOOL found = FALSE;
896
897 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
898
899 EnterCriticalSection(&authcache_cs);
900 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
901 if(!wcsicmp(host, ad->host) && !wcsicmp(scheme, ad->scheme)) {
902 found = TRUE;
903 break;
904 }
905
906 if(found) {
907 heap_free(ad->user);
908 heap_free(ad->password);
909 heap_free(ad->domain);
910 } else {
911 ad = heap_alloc(sizeof(authorizationData));
912 if(!ad) {
913 LeaveCriticalSection(&authcache_cs);
914 return;
915 }
916
917 ad->host = heap_strdupW(host);
918 ad->scheme = heap_strdupW(scheme);
919 list_add_head(&authorizationCache, &ad->entry);
920 }
921
922 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
923 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
924 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
925 ad->user_len = nt_auth_identity->UserLength;
926 ad->password_len = nt_auth_identity->PasswordLength;
927 ad->domain_len = nt_auth_identity->DomainLength;
928
929 if(!ad->host || !ad->scheme || !ad->user || !ad->password
930 || (nt_auth_identity->Domain && !ad->domain)) {
931 heap_free(ad->host);
932 heap_free(ad->scheme);
933 heap_free(ad->user);
934 heap_free(ad->password);
935 heap_free(ad->domain);
936 list_remove(&ad->entry);
937 heap_free(ad);
938 }
939
940 LeaveCriticalSection(&authcache_cs);
941 }
942
free_authorization_cache(void)943 void free_authorization_cache(void)
944 {
945 authorizationData *ad, *sa_safe;
946 basicAuthorizationData *basic, *basic_safe;
947
948 EnterCriticalSection(&authcache_cs);
949
950 LIST_FOR_EACH_ENTRY_SAFE(basic, basic_safe, &basicAuthorizationCache, basicAuthorizationData, entry)
951 {
952 heap_free(basic->host);
953 heap_free(basic->realm);
954 heap_free(basic->authorization);
955
956 list_remove(&basic->entry);
957 heap_free(basic);
958 }
959
960 LIST_FOR_EACH_ENTRY_SAFE(ad, sa_safe, &authorizationCache, authorizationData, entry)
961 {
962 heap_free(ad->host);
963 heap_free(ad->scheme);
964 heap_free(ad->user);
965 heap_free(ad->password);
966 heap_free(ad->domain);
967 list_remove(&ad->entry);
968 heap_free(ad);
969 }
970
971 LeaveCriticalSection(&authcache_cs);
972 }
973
HTTP_DoAuthorization(http_request_t * request,LPCWSTR pszAuthValue,struct HttpAuthInfo ** ppAuthInfo,LPWSTR domain_and_username,LPWSTR password,LPWSTR host)974 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
975 struct HttpAuthInfo **ppAuthInfo,
976 LPWSTR domain_and_username, LPWSTR password,
977 LPWSTR host )
978 {
979 SECURITY_STATUS sec_status;
980 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
981 BOOL first = FALSE;
982 LPWSTR szRealm = NULL;
983
984 TRACE("%s\n", debugstr_w(pszAuthValue));
985
986 if (!pAuthInfo)
987 {
988 TimeStamp exp;
989
990 first = TRUE;
991 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
992 if (!pAuthInfo)
993 return FALSE;
994
995 SecInvalidateHandle(&pAuthInfo->cred);
996 SecInvalidateHandle(&pAuthInfo->ctx);
997 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
998 pAuthInfo->attr = 0;
999 pAuthInfo->auth_data = NULL;
1000 pAuthInfo->auth_data_len = 0;
1001 pAuthInfo->finished = FALSE;
1002
1003 if (is_basic_auth_value(pszAuthValue,NULL))
1004 {
1005 pAuthInfo->scheme = heap_strdupW(L"Basic");
1006 if (!pAuthInfo->scheme)
1007 {
1008 heap_free(pAuthInfo);
1009 return FALSE;
1010 }
1011 }
1012 else
1013 {
1014 PVOID pAuthData;
1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1016
1017 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1018 if (!pAuthInfo->scheme)
1019 {
1020 heap_free(pAuthInfo);
1021 return FALSE;
1022 }
1023
1024 if (domain_and_username)
1025 {
1026 WCHAR *user = wcschr(domain_and_username, '\\');
1027 WCHAR *domain = domain_and_username;
1028
1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1030
1031 pAuthData = &nt_auth_identity;
1032
1033 if (user) user++;
1034 else
1035 {
1036 user = domain_and_username;
1037 domain = NULL;
1038 }
1039
1040 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1041 nt_auth_identity.User = user;
1042 nt_auth_identity.UserLength = lstrlenW(nt_auth_identity.User);
1043 nt_auth_identity.Domain = domain;
1044 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1045 nt_auth_identity.Password = password;
1046 nt_auth_identity.PasswordLength = lstrlenW(nt_auth_identity.Password);
1047
1048 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1049 }
1050 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1051 pAuthData = &nt_auth_identity;
1052 else
1053 /* use default credentials */
1054 pAuthData = NULL;
1055
1056 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1057 SECPKG_CRED_OUTBOUND, NULL,
1058 pAuthData, NULL,
1059 NULL, &pAuthInfo->cred,
1060 &exp);
1061
1062 if(pAuthData && !domain_and_username) {
1063 heap_free(nt_auth_identity.User);
1064 heap_free(nt_auth_identity.Domain);
1065 heap_free(nt_auth_identity.Password);
1066 }
1067
1068 if (sec_status == SEC_E_OK)
1069 {
1070 PSecPkgInfoW sec_pkg_info;
1071 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1072 if (sec_status == SEC_E_OK)
1073 {
1074 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1075 FreeContextBuffer(sec_pkg_info);
1076 }
1077 }
1078 if (sec_status != SEC_E_OK)
1079 {
1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1081 debugstr_w(pAuthInfo->scheme), sec_status);
1082 heap_free(pAuthInfo->scheme);
1083 heap_free(pAuthInfo);
1084 return FALSE;
1085 }
1086 }
1087 *ppAuthInfo = pAuthInfo;
1088 }
1089 else if (pAuthInfo->finished)
1090 return FALSE;
1091
1092 if ((lstrlenW(pszAuthValue) < lstrlenW(pAuthInfo->scheme)) ||
1093 wcsnicmp(pszAuthValue, pAuthInfo->scheme, lstrlenW(pAuthInfo->scheme)))
1094 {
1095 ERR("authentication scheme changed from %s to %s\n",
1096 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1097 return FALSE;
1098 }
1099
1100 if (is_basic_auth_value(pszAuthValue,&szRealm))
1101 {
1102 int userlen;
1103 int passlen;
1104 char *auth_data = NULL;
1105 UINT auth_data_len = 0;
1106
1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1108
1109 if (!domain_and_username)
1110 {
1111 if (host && szRealm)
1112 auth_data_len = retrieve_cached_basic_authorization(request, host, szRealm,&auth_data);
1113 if (auth_data_len == 0)
1114 {
1115 heap_free(szRealm);
1116 return FALSE;
1117 }
1118 }
1119 else
1120 {
1121 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1122 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1123
1124 /* length includes a nul terminator, which will be re-used for the ':' */
1125 auth_data = heap_alloc(userlen + 1 + passlen);
1126 if (!auth_data)
1127 {
1128 heap_free(szRealm);
1129 return FALSE;
1130 }
1131
1132 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1133 auth_data[userlen] = ':';
1134 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1135 auth_data_len = userlen + 1 + passlen;
1136 if (host && szRealm)
1137 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1138 }
1139
1140 pAuthInfo->auth_data = auth_data;
1141 pAuthInfo->auth_data_len = auth_data_len;
1142 pAuthInfo->finished = TRUE;
1143 heap_free(szRealm);
1144 return TRUE;
1145 }
1146 else
1147 {
1148 LPCWSTR pszAuthData;
1149 SecBufferDesc out_desc, in_desc;
1150 SecBuffer out, in;
1151 unsigned char *buffer;
1152 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1153 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1154
1155 in.BufferType = SECBUFFER_TOKEN;
1156 in.cbBuffer = 0;
1157 in.pvBuffer = NULL;
1158
1159 in_desc.ulVersion = 0;
1160 in_desc.cBuffers = 1;
1161 in_desc.pBuffers = ∈
1162
1163 pszAuthData = pszAuthValue + lstrlenW(pAuthInfo->scheme);
1164 if (*pszAuthData == ' ')
1165 {
1166 pszAuthData++;
1167 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1168 in.pvBuffer = heap_alloc(in.cbBuffer);
1169 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1170 }
1171
1172 buffer = heap_alloc(pAuthInfo->max_token);
1173
1174 out.BufferType = SECBUFFER_TOKEN;
1175 out.cbBuffer = pAuthInfo->max_token;
1176 out.pvBuffer = buffer;
1177
1178 out_desc.ulVersion = 0;
1179 out_desc.cBuffers = 1;
1180 out_desc.pBuffers = &out;
1181
1182 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1183 first ? NULL : &pAuthInfo->ctx,
1184 first ? request->server->name : NULL,
1185 context_req, 0, SECURITY_NETWORK_DREP,
1186 in.pvBuffer ? &in_desc : NULL,
1187 0, &pAuthInfo->ctx, &out_desc,
1188 &pAuthInfo->attr, &pAuthInfo->exp);
1189 if (sec_status == SEC_E_OK)
1190 {
1191 pAuthInfo->finished = TRUE;
1192 pAuthInfo->auth_data = out.pvBuffer;
1193 pAuthInfo->auth_data_len = out.cbBuffer;
1194 TRACE("sending last auth packet\n");
1195 }
1196 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1197 {
1198 pAuthInfo->auth_data = out.pvBuffer;
1199 pAuthInfo->auth_data_len = out.cbBuffer;
1200 TRACE("sending next auth packet\n");
1201 }
1202 else
1203 {
1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1205 heap_free(out.pvBuffer);
1206 destroy_authinfo(pAuthInfo);
1207 *ppAuthInfo = NULL;
1208 return FALSE;
1209 }
1210 }
1211
1212 return TRUE;
1213 }
1214
1215 /***********************************************************************
1216 * HTTP_HttpAddRequestHeadersW (internal)
1217 */
HTTP_HttpAddRequestHeadersW(http_request_t * request,LPCWSTR lpszHeader,DWORD dwHeaderLength,DWORD dwModifier)1218 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1219 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1220 {
1221 LPWSTR lpszStart;
1222 LPWSTR lpszEnd;
1223 LPWSTR buffer;
1224 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1225
1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1227
1228 if( dwHeaderLength == ~0U )
1229 len = lstrlenW(lpszHeader);
1230 else
1231 len = dwHeaderLength;
1232 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1233 lstrcpynW( buffer, lpszHeader, len + 1);
1234
1235 lpszStart = buffer;
1236
1237 do
1238 {
1239 LPWSTR * pFieldAndValue;
1240
1241 lpszEnd = lpszStart;
1242
1243 while (*lpszEnd != '\0')
1244 {
1245 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1246 break;
1247 lpszEnd++;
1248 }
1249
1250 if (*lpszStart == '\0')
1251 break;
1252
1253 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1254 {
1255 *lpszEnd = '\0';
1256 lpszEnd++; /* Jump over newline */
1257 }
1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1259 if (*lpszStart == '\0')
1260 {
1261 /* Skip 0-length headers */
1262 lpszStart = lpszEnd;
1263 res = ERROR_SUCCESS;
1264 continue;
1265 }
1266 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1267 if (pFieldAndValue)
1268 {
1269 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1270 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1271 HTTP_FreeTokens(pFieldAndValue);
1272 }
1273
1274 lpszStart = lpszEnd;
1275 } while (res == ERROR_SUCCESS);
1276
1277 heap_free(buffer);
1278 return res;
1279 }
1280
1281 /***********************************************************************
1282 * HttpAddRequestHeadersW (WININET.@)
1283 *
1284 * Adds one or more HTTP header to the request handler
1285 *
1286 * NOTE
1287 * On Windows if dwHeaderLength includes the trailing '\0', then
1288 * HttpAddRequestHeadersW() adds it too. However this results in an
1289 * invalid HTTP header which is rejected by some servers so we probably
1290 * don't need to match Windows on that point.
1291 *
1292 * RETURNS
1293 * TRUE on success
1294 * FALSE on failure
1295 *
1296 */
HttpAddRequestHeadersW(HINTERNET hHttpRequest,LPCWSTR lpszHeader,DWORD dwHeaderLength,DWORD dwModifier)1297 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1298 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1299 {
1300 http_request_t *request;
1301 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1302
1303 TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1304
1305 if (!lpszHeader)
1306 return TRUE;
1307
1308 request = (http_request_t*) get_handle_object( hHttpRequest );
1309 if (request && request->hdr.htype == WH_HHTTPREQ)
1310 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1311 if( request )
1312 WININET_Release( &request->hdr );
1313
1314 if(res != ERROR_SUCCESS)
1315 SetLastError(res);
1316 return res == ERROR_SUCCESS;
1317 }
1318
1319 /***********************************************************************
1320 * HttpAddRequestHeadersA (WININET.@)
1321 *
1322 * Adds one or more HTTP header to the request handler
1323 *
1324 * RETURNS
1325 * TRUE on success
1326 * FALSE on failure
1327 *
1328 */
HttpAddRequestHeadersA(HINTERNET hHttpRequest,LPCSTR lpszHeader,DWORD dwHeaderLength,DWORD dwModifier)1329 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1330 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1331 {
1332 WCHAR *headers = NULL;
1333 BOOL r;
1334
1335 TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1336
1337 if(lpszHeader)
1338 headers = heap_strndupAtoW(lpszHeader, dwHeaderLength, &dwHeaderLength);
1339
1340 r = HttpAddRequestHeadersW(hHttpRequest, headers, dwHeaderLength, dwModifier);
1341
1342 heap_free(headers);
1343 return r;
1344 }
1345
free_accept_types(WCHAR ** accept_types)1346 static void free_accept_types( WCHAR **accept_types )
1347 {
1348 WCHAR *ptr, **types = accept_types;
1349
1350 if (!types) return;
1351 while ((ptr = *types))
1352 {
1353 heap_free( ptr );
1354 types++;
1355 }
1356 heap_free( accept_types );
1357 }
1358
convert_accept_types(const char ** accept_types)1359 static WCHAR **convert_accept_types( const char **accept_types )
1360 {
1361 unsigned int count;
1362 const char **types = accept_types;
1363 WCHAR **typesW;
1364 BOOL invalid_pointer = FALSE;
1365
1366 if (!types) return NULL;
1367 count = 0;
1368 while (*types)
1369 {
1370 __TRY
1371 {
1372 /* find out how many there are */
1373 if (*types && **types)
1374 {
1375 TRACE("accept type: %s\n", debugstr_a(*types));
1376 count++;
1377 }
1378 }
1379 __EXCEPT_PAGE_FAULT
1380 {
1381 WARN("invalid accept type pointer\n");
1382 invalid_pointer = TRUE;
1383 }
1384 __ENDTRY;
1385 types++;
1386 }
1387 if (invalid_pointer) return NULL;
1388 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1389 count = 0;
1390 types = accept_types;
1391 while (*types)
1392 {
1393 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1394 types++;
1395 }
1396 typesW[count] = NULL;
1397 return typesW;
1398 }
1399
1400 /***********************************************************************
1401 * HttpOpenRequestA (WININET.@)
1402 *
1403 * Open a HTTP request handle
1404 *
1405 * RETURNS
1406 * HINTERNET a HTTP request handle on success
1407 * NULL on failure
1408 *
1409 */
HttpOpenRequestA(HINTERNET hHttpSession,LPCSTR lpszVerb,LPCSTR lpszObjectName,LPCSTR lpszVersion,LPCSTR lpszReferrer,LPCSTR * lpszAcceptTypes,DWORD dwFlags,DWORD_PTR dwContext)1410 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1411 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1412 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1413 DWORD dwFlags, DWORD_PTR dwContext)
1414 {
1415 LPWSTR szVerb = NULL, szObjectName = NULL;
1416 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1417 HINTERNET rc = NULL;
1418
1419 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1420 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1421 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1422 dwFlags, dwContext);
1423
1424 if (lpszVerb)
1425 {
1426 szVerb = heap_strdupAtoW(lpszVerb);
1427 if ( !szVerb )
1428 goto end;
1429 }
1430
1431 if (lpszObjectName)
1432 {
1433 szObjectName = heap_strdupAtoW(lpszObjectName);
1434 if ( !szObjectName )
1435 goto end;
1436 }
1437
1438 if (lpszVersion)
1439 {
1440 szVersion = heap_strdupAtoW(lpszVersion);
1441 if ( !szVersion )
1442 goto end;
1443 }
1444
1445 if (lpszReferrer)
1446 {
1447 szReferrer = heap_strdupAtoW(lpszReferrer);
1448 if ( !szReferrer )
1449 goto end;
1450 }
1451
1452 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1453 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1454 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1455
1456 end:
1457 free_accept_types(szAcceptTypes);
1458 heap_free(szReferrer);
1459 heap_free(szVersion);
1460 heap_free(szObjectName);
1461 heap_free(szVerb);
1462 return rc;
1463 }
1464
1465 /***********************************************************************
1466 * HTTP_EncodeBase64
1467 */
HTTP_EncodeBase64(LPCSTR bin,unsigned int len,LPWSTR base64)1468 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1469 {
1470 UINT n = 0, x;
1471 static const CHAR HTTP_Base64Enc[] =
1472 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1473
1474 while( len > 0 )
1475 {
1476 /* first 6 bits, all from bin[0] */
1477 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1478 x = (bin[0] & 3) << 4;
1479
1480 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1481 if( len == 1 )
1482 {
1483 base64[n++] = HTTP_Base64Enc[x];
1484 base64[n++] = '=';
1485 base64[n++] = '=';
1486 break;
1487 }
1488 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1489 x = ( bin[1] & 0x0f ) << 2;
1490
1491 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1492 if( len == 2 )
1493 {
1494 base64[n++] = HTTP_Base64Enc[x];
1495 base64[n++] = '=';
1496 break;
1497 }
1498 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1499
1500 /* last 6 bits, all from bin [2] */
1501 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1502 bin += 3;
1503 len -= 3;
1504 }
1505 base64[n] = 0;
1506 return n;
1507 }
1508
1509 static const signed char HTTP_Base64Dec[] =
1510 {
1511 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
1512 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
1513 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
1514 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
1515 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
1516 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
1517 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
1518 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70 */
1519 };
1520
1521 /***********************************************************************
1522 * HTTP_DecodeBase64
1523 */
HTTP_DecodeBase64(LPCWSTR base64,LPSTR bin)1524 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1525 {
1526 unsigned int n = 0;
1527
1528 while(*base64)
1529 {
1530 signed char in[4];
1531
1532 if (base64[0] >= ARRAY_SIZE(HTTP_Base64Dec) ||
1533 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1534 base64[1] >= ARRAY_SIZE(HTTP_Base64Dec) ||
1535 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1536 {
1537 WARN("invalid base64: %s\n", debugstr_w(base64));
1538 return 0;
1539 }
1540 if (bin)
1541 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1542 n++;
1543
1544 if ((base64[2] == '=') && (base64[3] == '='))
1545 break;
1546 if (base64[2] > ARRAY_SIZE(HTTP_Base64Dec) ||
1547 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1548 {
1549 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1550 return 0;
1551 }
1552 if (bin)
1553 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1554 n++;
1555
1556 if (base64[3] == '=')
1557 break;
1558 if (base64[3] > ARRAY_SIZE(HTTP_Base64Dec) ||
1559 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1560 {
1561 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1562 return 0;
1563 }
1564 if (bin)
1565 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1566 n++;
1567
1568 base64 += 4;
1569 }
1570
1571 return n;
1572 }
1573
encode_auth_data(const WCHAR * scheme,const char * data,UINT data_len)1574 static WCHAR *encode_auth_data( const WCHAR *scheme, const char *data, UINT data_len )
1575 {
1576 WCHAR *ret;
1577 UINT len, scheme_len = lstrlenW( scheme );
1578
1579 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1580 len = scheme_len + 1 + ((data_len + 2) * 4) / 3;
1581 if (!(ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
1582 memcpy( ret, scheme, scheme_len * sizeof(WCHAR) );
1583 ret[scheme_len] = ' ';
1584 HTTP_EncodeBase64( data, data_len, ret + scheme_len + 1 );
1585 return ret;
1586 }
1587
1588
1589 /***********************************************************************
1590 * HTTP_InsertAuthorization
1591 *
1592 * Insert or delete the authorization field in the request header.
1593 */
HTTP_InsertAuthorization(http_request_t * request,struct HttpAuthInfo * pAuthInfo,LPCWSTR header)1594 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1595 {
1596 WCHAR *host, *authorization = NULL;
1597
1598 if (pAuthInfo)
1599 {
1600 if (pAuthInfo->auth_data_len)
1601 {
1602 if (!(authorization = encode_auth_data(pAuthInfo->scheme, pAuthInfo->auth_data, pAuthInfo->auth_data_len)))
1603 return FALSE;
1604
1605 /* clear the data as it isn't valid now that it has been sent to the
1606 * server, unless it's Basic authentication which doesn't do
1607 * connection tracking */
1608 if (wcsicmp(pAuthInfo->scheme, L"Basic"))
1609 {
1610 heap_free(pAuthInfo->auth_data);
1611 pAuthInfo->auth_data = NULL;
1612 pAuthInfo->auth_data_len = 0;
1613 }
1614 }
1615
1616 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1617
1618 HTTP_ProcessHeader(request, header, authorization,
1619 HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD);
1620 heap_free(authorization);
1621 }
1622 else
1623 {
1624 UINT data_len;
1625 char *data;
1626
1627 /* Don't use cached credentials when a username or Authorization was specified */
1628 if ((request->session->userName && request->session->userName[0]) || wcscmp(header, L"Authorization"))
1629 return TRUE;
1630
1631 if (!(host = get_host_header(request)))
1632 return TRUE;
1633
1634 if ((data_len = retrieve_cached_basic_authorization(request, host, NULL, &data)))
1635 {
1636 TRACE("Found cached basic authorization for %s\n", debugstr_w(host));
1637
1638 if (!(authorization = encode_auth_data(L"Basic", data, data_len)))
1639 {
1640 heap_free(data);
1641 heap_free(host);
1642 return FALSE;
1643 }
1644
1645 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1646
1647 HTTP_ProcessHeader(request, header, authorization,
1648 HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD);
1649 heap_free(data);
1650 heap_free(authorization);
1651 }
1652 heap_free(host);
1653 }
1654 return TRUE;
1655 }
1656
build_proxy_path_url(http_request_t * req)1657 static WCHAR *build_proxy_path_url(http_request_t *req)
1658 {
1659 DWORD size, len;
1660 WCHAR *url;
1661
1662 len = lstrlenW(req->server->scheme_host_port);
1663 size = len + lstrlenW(req->path) + 1;
1664 if(*req->path != '/')
1665 size++;
1666 url = heap_alloc(size * sizeof(WCHAR));
1667 if(!url)
1668 return NULL;
1669
1670 memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
1671 if(*req->path != '/')
1672 url[len++] = '/';
1673
1674 lstrcpyW(url+len, req->path);
1675
1676 TRACE("url=%s\n", debugstr_w(url));
1677 return url;
1678 }
1679
HTTP_DomainMatches(LPCWSTR server,substr_t domain)1680 static BOOL HTTP_DomainMatches(LPCWSTR server, substr_t domain)
1681 {
1682 const WCHAR *dot, *ptr;
1683 int len;
1684
1685 if(domain.len == ARRAY_SIZE(L"<local>")-1 && !wcsnicmp(domain.str, L"<local>", domain.len) && !wcschr(server, '.' ))
1686 return TRUE;
1687
1688 if(domain.len && *domain.str != '*')
1689 return domain.len == lstrlenW(server) && !wcsnicmp(server, domain.str, domain.len);
1690
1691 if(domain.len < 2 || domain.str[1] != '.')
1692 return FALSE;
1693
1694 /* For a hostname to match a wildcard, the last domain must match
1695 * the wildcard exactly. E.g. if the wildcard is *.a.b, and the
1696 * hostname is www.foo.a.b, it matches, but a.b does not.
1697 */
1698 dot = wcschr(server, '.');
1699 if(!dot)
1700 return FALSE;
1701
1702 len = lstrlenW(dot + 1);
1703 if(len < domain.len - 2)
1704 return FALSE;
1705
1706 /* The server's domain is longer than the wildcard, so it
1707 * could be a subdomain. Compare the last portion of the
1708 * server's domain.
1709 */
1710 ptr = dot + 1 + len - domain.len + 2;
1711 if(!wcsnicmp(ptr, domain.str+2, domain.len-2))
1712 /* This is only a match if the preceding character is
1713 * a '.', i.e. that it is a matching domain. E.g.
1714 * if domain is '*.b.c' and server is 'www.ab.c' they
1715 * do not match.
1716 */
1717 return *(ptr - 1) == '.';
1718
1719 return len == domain.len-2 && !wcsnicmp(dot + 1, domain.str + 2, len);
1720 }
1721
HTTP_ShouldBypassProxy(appinfo_t * lpwai,LPCWSTR server)1722 static BOOL HTTP_ShouldBypassProxy(appinfo_t *lpwai, LPCWSTR server)
1723 {
1724 LPCWSTR ptr;
1725 BOOL ret = FALSE;
1726
1727 if (!lpwai->proxyBypass) return FALSE;
1728 ptr = lpwai->proxyBypass;
1729 while(1) {
1730 LPCWSTR tmp = ptr;
1731
1732 ptr = wcschr( ptr, ';' );
1733 if (!ptr)
1734 ptr = wcschr( tmp, ' ' );
1735 if (!ptr)
1736 ptr = tmp + lstrlenW(tmp);
1737 ret = HTTP_DomainMatches( server, substr(tmp, ptr-tmp) );
1738 if (ret || !*ptr)
1739 break;
1740 ptr++;
1741 }
1742 return ret;
1743 }
1744
1745 /***********************************************************************
1746 * HTTP_DealWithProxy
1747 */
HTTP_DealWithProxy(appinfo_t * hIC,http_session_t * session,http_request_t * request)1748 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1749 {
1750 static WCHAR szNul[] = L"";
1751 URL_COMPONENTSW UrlComponents = { sizeof(UrlComponents) };
1752 server_t *new_server = NULL;
1753 WCHAR *proxy;
1754
1755 proxy = INTERNET_FindProxyForProtocol(hIC->proxy, L"http");
1756 if(!proxy)
1757 return FALSE;
1758 if(CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1759 proxy, lstrlenW(L"http://"), L"http://", lstrlenW(L"http://"))) {
1760 WCHAR *proxy_url = heap_alloc(lstrlenW(proxy)*sizeof(WCHAR) + sizeof(L"http://"));
1761 if(!proxy_url) {
1762 heap_free(proxy);
1763 return FALSE;
1764 }
1765 lstrcpyW(proxy_url, L"http://");
1766 lstrcatW(proxy_url, proxy);
1767 heap_free(proxy);
1768 proxy = proxy_url;
1769 }
1770
1771 UrlComponents.dwHostNameLength = 1;
1772 if(InternetCrackUrlW(proxy, 0, 0, &UrlComponents) && UrlComponents.dwHostNameLength) {
1773 if( !request->path )
1774 request->path = szNul;
1775
1776 new_server = get_server(substr(UrlComponents.lpszHostName, UrlComponents.dwHostNameLength),
1777 UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
1778 }
1779 heap_free(proxy);
1780 if(!new_server)
1781 return FALSE;
1782
1783 request->proxy = new_server;
1784
1785 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1786 return TRUE;
1787 }
1788
HTTP_ResolveName(http_request_t * request)1789 static DWORD HTTP_ResolveName(http_request_t *request)
1790 {
1791 server_t *server = request->proxy ? request->proxy : request->server;
1792 int addr_len;
1793
1794 if(server->addr_len)
1795 return ERROR_SUCCESS;
1796
1797 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1798 INTERNET_STATUS_RESOLVING_NAME,
1799 server->name,
1800 (lstrlenW(server->name)+1) * sizeof(WCHAR));
1801
1802 addr_len = sizeof(server->addr);
1803 if (!GetAddress(server->name, server->port, (SOCKADDR*)&server->addr, &addr_len, server->addr_str))
1804 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1805
1806 server->addr_len = addr_len;
1807 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1808 INTERNET_STATUS_NAME_RESOLVED,
1809 server->addr_str, strlen(server->addr_str)+1);
1810
1811 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1812 return ERROR_SUCCESS;
1813 }
1814
compose_request_url(http_request_t * req)1815 static WCHAR *compose_request_url(http_request_t *req)
1816 {
1817 const WCHAR *host, *scheme;
1818 WCHAR *buf, *ptr;
1819 size_t len;
1820
1821 host = req->server->canon_host_port;
1822
1823 if (req->server->is_https)
1824 scheme = L"https://";
1825 else
1826 scheme = L"http://";
1827
1828 len = lstrlenW(scheme) + lstrlenW(host) + (req->path[0] != '/' ? 1 : 0) + lstrlenW(req->path);
1829 ptr = buf = heap_alloc((len+1) * sizeof(WCHAR));
1830 if(buf) {
1831 lstrcpyW(ptr, scheme);
1832 ptr += lstrlenW(ptr);
1833
1834 lstrcpyW(ptr, host);
1835 ptr += lstrlenW(ptr);
1836
1837 if(req->path[0] != '/')
1838 *ptr++ = '/';
1839
1840 lstrcpyW(ptr, req->path);
1841 ptr += lstrlenW(ptr);
1842 *ptr = 0;
1843 }
1844
1845 return buf;
1846 }
1847
1848
1849 /***********************************************************************
1850 * HTTPREQ_Destroy (internal)
1851 *
1852 * Deallocate request handle
1853 *
1854 */
HTTPREQ_Destroy(object_header_t * hdr)1855 static void HTTPREQ_Destroy(object_header_t *hdr)
1856 {
1857 http_request_t *request = (http_request_t*) hdr;
1858 DWORD i;
1859
1860 TRACE("\n");
1861
1862 if(request->hCacheFile)
1863 CloseHandle(request->hCacheFile);
1864 if(request->req_file)
1865 req_file_release(request->req_file);
1866
1867 request->headers_section.DebugInfo->Spare[0] = 0;
1868 DeleteCriticalSection( &request->headers_section );
1869 request->read_section.DebugInfo->Spare[0] = 0;
1870 DeleteCriticalSection( &request->read_section );
1871 WININET_Release(&request->session->hdr);
1872
1873 destroy_authinfo(request->authInfo);
1874 destroy_authinfo(request->proxyAuthInfo);
1875
1876 if(request->server)
1877 server_release(request->server);
1878 if(request->proxy)
1879 server_release(request->proxy);
1880
1881 heap_free(request->path);
1882 heap_free(request->verb);
1883 heap_free(request->version);
1884 heap_free(request->statusText);
1885
1886 for (i = 0; i < request->nCustHeaders; i++)
1887 {
1888 heap_free(request->custHeaders[i].lpszField);
1889 heap_free(request->custHeaders[i].lpszValue);
1890 }
1891 destroy_data_stream(request->data_stream);
1892 heap_free(request->custHeaders);
1893 }
1894
http_release_netconn(http_request_t * req,BOOL reuse)1895 static void http_release_netconn(http_request_t *req, BOOL reuse)
1896 {
1897 TRACE("%p %p %x\n",req, req->netconn, reuse);
1898
1899 if(!is_valid_netconn(req->netconn))
1900 return;
1901
1902 #ifndef __REACTOS__
1903 if(reuse && req->netconn->keep_alive) {
1904 BOOL run_collector;
1905
1906 EnterCriticalSection(&connection_pool_cs);
1907
1908 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1909 req->netconn->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME;
1910 req->netconn = NULL;
1911
1912 run_collector = !collector_running;
1913 collector_running = TRUE;
1914
1915 LeaveCriticalSection(&connection_pool_cs);
1916
1917 if(run_collector) {
1918 HANDLE thread = NULL;
1919 HMODULE module;
1920
1921 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1922 if(module)
1923 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1924 if(!thread) {
1925 EnterCriticalSection(&connection_pool_cs);
1926 collector_running = FALSE;
1927 LeaveCriticalSection(&connection_pool_cs);
1928
1929 if(module)
1930 FreeLibrary(module);
1931 }
1932 else
1933 CloseHandle(thread);
1934 }
1935 return;
1936 }
1937 #else
1938 /* Silence unused function warning */
1939 (void)collect_connections_proc;
1940 #endif
1941
1942 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1943 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1944
1945 close_netconn(req->netconn);
1946
1947 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1948 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1949 }
1950
HTTP_KeepAlive(http_request_t * request)1951 static BOOL HTTP_KeepAlive(http_request_t *request)
1952 {
1953 WCHAR szVersion[10];
1954 WCHAR szConnectionResponse[20];
1955 DWORD dwBufferSize = sizeof(szVersion);
1956 BOOL keepalive = FALSE;
1957
1958 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1959 * the connection is keep-alive by default */
1960 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1961 && !wcsicmp(szVersion, L"HTTP/1.1"))
1962 {
1963 keepalive = TRUE;
1964 }
1965
1966 dwBufferSize = sizeof(szConnectionResponse);
1967 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1968 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1969 {
1970 keepalive = !wcsicmp(szConnectionResponse, L"Keep-Alive");
1971 }
1972
1973 return keepalive;
1974 }
1975
HTTPREQ_CloseConnection(object_header_t * hdr)1976 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1977 {
1978 http_request_t *req = (http_request_t*)hdr;
1979
1980 http_release_netconn(req, drain_content(req, FALSE) == ERROR_SUCCESS);
1981 }
1982
str_to_buffer(const WCHAR * str,void * buffer,DWORD * size,BOOL unicode)1983 static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL unicode)
1984 {
1985 int len;
1986 if (unicode)
1987 {
1988 WCHAR *buf = buffer;
1989
1990 if (str) len = lstrlenW(str);
1991 else len = 0;
1992 if (*size < (len + 1) * sizeof(WCHAR))
1993 {
1994 *size = (len + 1) * sizeof(WCHAR);
1995 return ERROR_INSUFFICIENT_BUFFER;
1996 }
1997 if (str) lstrcpyW(buf, str);
1998 else buf[0] = 0;
1999
2000 *size = len;
2001 return ERROR_SUCCESS;
2002 }
2003 else
2004 {
2005 char *buf = buffer;
2006
2007 if (str) len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
2008 else len = 1;
2009 if (*size < len)
2010 {
2011 *size = len;
2012 return ERROR_INSUFFICIENT_BUFFER;
2013 }
2014 if (str) WideCharToMultiByte(CP_ACP, 0, str, -1, buf, *size, NULL, NULL);
2015 else buf[0] = 0;
2016
2017 *size = len - 1;
2018 return ERROR_SUCCESS;
2019 }
2020 }
2021
get_security_cert_struct(http_request_t * req,INTERNET_CERTIFICATE_INFOA * info)2022 static DWORD get_security_cert_struct(http_request_t *req, INTERNET_CERTIFICATE_INFOA *info)
2023 {
2024 PCCERT_CONTEXT context;
2025 DWORD len;
2026
2027 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2028 if(!context)
2029 return ERROR_NOT_SUPPORTED;
2030
2031 memset(info, 0, sizeof(*info));
2032 info->ftExpiry = context->pCertInfo->NotAfter;
2033 info->ftStart = context->pCertInfo->NotBefore;
2034 len = CertNameToStrA(context->dwCertEncodingType,
2035 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2036 info->lpszSubjectInfo = LocalAlloc(0, len);
2037 if(info->lpszSubjectInfo)
2038 CertNameToStrA(context->dwCertEncodingType,
2039 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2040 info->lpszSubjectInfo, len);
2041 len = CertNameToStrA(context->dwCertEncodingType,
2042 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2043 info->lpszIssuerInfo = LocalAlloc(0, len);
2044 if(info->lpszIssuerInfo)
2045 CertNameToStrA(context->dwCertEncodingType,
2046 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2047 info->lpszIssuerInfo, len);
2048 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2049
2050 CertFreeCertificateContext(context);
2051 return ERROR_SUCCESS;
2052 }
2053
HTTPREQ_QueryOption(object_header_t * hdr,DWORD option,void * buffer,DWORD * size,BOOL unicode)2054 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2055 {
2056 http_request_t *req = (http_request_t*)hdr;
2057
2058 switch(option) {
2059 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
2060 {
2061 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
2062
2063 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
2064
2065 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
2066 return ERROR_INSUFFICIENT_BUFFER;
2067 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
2068 /* FIXME: can't get a SOCKET from our connection since we don't use
2069 * winsock
2070 */
2071 info->Socket = 0;
2072 /* FIXME: get source port from req->netConnection */
2073 info->SourcePort = 0;
2074 info->DestPort = req->server->port;
2075 info->Flags = 0;
2076 if (HTTP_KeepAlive(req))
2077 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
2078 if (req->proxy)
2079 info->Flags |= IDSI_FLAG_PROXY;
2080 if (is_valid_netconn(req->netconn) && req->netconn->secure)
2081 info->Flags |= IDSI_FLAG_SECURE;
2082
2083 return ERROR_SUCCESS;
2084 }
2085
2086 case 98:
2087 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
2088 /* fall through */
2089 case INTERNET_OPTION_SECURITY_FLAGS:
2090 {
2091 DWORD flags;
2092
2093 if (*size < sizeof(ULONG))
2094 return ERROR_INSUFFICIENT_BUFFER;
2095
2096 *size = sizeof(DWORD);
2097 flags = is_valid_netconn(req->netconn) ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2098 *(DWORD *)buffer = flags;
2099
2100 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2101 return ERROR_SUCCESS;
2102 }
2103
2104 case INTERNET_OPTION_HANDLE_TYPE:
2105 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2106
2107 if (*size < sizeof(ULONG))
2108 return ERROR_INSUFFICIENT_BUFFER;
2109
2110 *size = sizeof(DWORD);
2111 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2112 return ERROR_SUCCESS;
2113
2114 case INTERNET_OPTION_URL: {
2115 WCHAR *url;
2116 DWORD res;
2117
2118 TRACE("INTERNET_OPTION_URL\n");
2119
2120 url = compose_request_url(req);
2121 if(!url)
2122 return ERROR_OUTOFMEMORY;
2123
2124 res = str_to_buffer(url, buffer, size, unicode);
2125 heap_free(url);
2126 return res;
2127 }
2128 case INTERNET_OPTION_USER_AGENT:
2129 return str_to_buffer(req->session->appInfo->agent, buffer, size, unicode);
2130 case INTERNET_OPTION_USERNAME:
2131 return str_to_buffer(req->session->userName, buffer, size, unicode);
2132 case INTERNET_OPTION_PASSWORD:
2133 return str_to_buffer(req->session->password, buffer, size, unicode);
2134 case INTERNET_OPTION_PROXY_USERNAME:
2135 return str_to_buffer(req->session->appInfo->proxyUsername, buffer, size, unicode);
2136 case INTERNET_OPTION_PROXY_PASSWORD:
2137 return str_to_buffer(req->session->appInfo->proxyPassword, buffer, size, unicode);
2138
2139 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2140 INTERNET_CACHE_ENTRY_INFOW *info;
2141 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2142 DWORD nbytes, error;
2143 BOOL ret;
2144
2145 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2146
2147 if(!req->req_file)
2148 return ERROR_FILE_NOT_FOUND;
2149
2150 if (*size < sizeof(*ts))
2151 {
2152 *size = sizeof(*ts);
2153 return ERROR_INSUFFICIENT_BUFFER;
2154 }
2155
2156 nbytes = 0;
2157 ret = GetUrlCacheEntryInfoW(req->req_file->url, NULL, &nbytes);
2158 error = GetLastError();
2159 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2160 {
2161 if (!(info = heap_alloc(nbytes)))
2162 return ERROR_OUTOFMEMORY;
2163
2164 GetUrlCacheEntryInfoW(req->req_file->url, info, &nbytes);
2165
2166 ts->ftExpires = info->ExpireTime;
2167 ts->ftLastModified = info->LastModifiedTime;
2168
2169 heap_free(info);
2170 *size = sizeof(*ts);
2171 return ERROR_SUCCESS;
2172 }
2173 return error;
2174 }
2175
2176 case INTERNET_OPTION_DATAFILE_NAME: {
2177 DWORD req_size;
2178
2179 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2180
2181 if(!req->req_file) {
2182 *size = 0;
2183 return ERROR_INTERNET_ITEM_NOT_FOUND;
2184 }
2185
2186 if(unicode) {
2187 req_size = (lstrlenW(req->req_file->file_name)+1) * sizeof(WCHAR);
2188 if(*size < req_size)
2189 return ERROR_INSUFFICIENT_BUFFER;
2190
2191 *size = req_size;
2192 memcpy(buffer, req->req_file->file_name, *size);
2193 return ERROR_SUCCESS;
2194 }else {
2195 req_size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name, -1, NULL, 0, NULL, NULL);
2196 if (req_size > *size)
2197 return ERROR_INSUFFICIENT_BUFFER;
2198
2199 *size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name,
2200 -1, buffer, *size, NULL, NULL);
2201 return ERROR_SUCCESS;
2202 }
2203 }
2204
2205 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2206 if(!req->netconn)
2207 return ERROR_INTERNET_INVALID_OPERATION;
2208
2209 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2210 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2211 return ERROR_INSUFFICIENT_BUFFER;
2212 }
2213
2214 return get_security_cert_struct(req, (INTERNET_CERTIFICATE_INFOA*)buffer);
2215 }
2216 case INTERNET_OPTION_SECURITY_CERTIFICATE: {
2217 DWORD err;
2218 int needed;
2219 char subject[64];
2220 char issuer[64];
2221 char effective[64];
2222 char expiration[64];
2223 char protocol[64];
2224 char signature[64];
2225 char encryption[64];
2226 char privacy[64];
2227 char bits[16];
2228 char strength[16];
2229 char start_date[32];
2230 char start_time[32];
2231 char expiry_date[32];
2232 char expiry_time[32];
2233 SYSTEMTIME start, expiry;
2234 INTERNET_CERTIFICATE_INFOA info;
2235
2236 if(!size)
2237 return ERROR_INVALID_PARAMETER;
2238
2239 if(!req->netconn) {
2240 *size = 0;
2241 return ERROR_INTERNET_INVALID_OPERATION;
2242 }
2243
2244 if(!buffer) {
2245 *size = 1;
2246 return ERROR_INSUFFICIENT_BUFFER;
2247 }
2248
2249 if((err = get_security_cert_struct(req, &info)))
2250 return err;
2251
2252 LoadStringA(WININET_hModule, IDS_CERT_SUBJECT, subject, sizeof(subject));
2253 LoadStringA(WININET_hModule, IDS_CERT_ISSUER, issuer, sizeof(issuer));
2254 LoadStringA(WININET_hModule, IDS_CERT_EFFECTIVE, effective, sizeof(effective));
2255 LoadStringA(WININET_hModule, IDS_CERT_EXPIRATION, expiration, sizeof(expiration));
2256 LoadStringA(WININET_hModule, IDS_CERT_PROTOCOL, protocol, sizeof(protocol));
2257 LoadStringA(WININET_hModule, IDS_CERT_SIGNATURE, signature, sizeof(signature));
2258 LoadStringA(WININET_hModule, IDS_CERT_ENCRYPTION, encryption, sizeof(encryption));
2259 LoadStringA(WININET_hModule, IDS_CERT_PRIVACY, privacy, sizeof(privacy));
2260 LoadStringA(WININET_hModule, info.dwKeySize >= 128 ? IDS_CERT_HIGH : IDS_CERT_LOW,
2261 strength, sizeof(strength));
2262 LoadStringA(WININET_hModule, IDS_CERT_BITS, bits, sizeof(bits));
2263
2264 FileTimeToSystemTime(&info.ftStart, &start);
2265 FileTimeToSystemTime(&info.ftExpiry, &expiry);
2266 GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_date, sizeof(start_date));
2267 GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_time, sizeof(start_time));
2268 GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_date, sizeof(expiry_date));
2269 GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_time, sizeof(expiry_time));
2270
2271 needed = _scprintf("%s:\r\n%s\r\n"
2272 "%s:\r\n%s\r\n"
2273 "%s:\t%s %s\r\n"
2274 "%s:\t%s %s\r\n"
2275 "%s:\t(null)\r\n"
2276 "%s:\t(null)\r\n"
2277 "%s:\t(null)\r\n"
2278 "%s:\t%s (%u %s)",
2279 subject, info.lpszSubjectInfo,
2280 issuer, info.lpszIssuerInfo,
2281 effective, start_date, start_time,
2282 expiration, expiry_date, expiry_time,
2283 protocol, signature, encryption,
2284 privacy, strength, info.dwKeySize, bits);
2285
2286 if(needed < *size) {
2287 err = ERROR_SUCCESS;
2288 *size = snprintf(buffer, *size,
2289 "%s:\r\n%s\r\n"
2290 "%s:\r\n%s\r\n"
2291 "%s:\t%s %s\r\n"
2292 "%s:\t%s %s\r\n"
2293 "%s:\t(null)\r\n"
2294 "%s:\t(null)\r\n"
2295 "%s:\t(null)\r\n"
2296 "%s:\t%s (%u %s)",
2297 subject, info.lpszSubjectInfo,
2298 issuer, info.lpszIssuerInfo,
2299 effective, start_date, start_time,
2300 expiration, expiry_date, expiry_time,
2301 protocol, signature, encryption,
2302 privacy, strength, info.dwKeySize, bits);
2303 }else {
2304 err = ERROR_INSUFFICIENT_BUFFER;
2305 *size = 1;
2306 }
2307
2308 LocalFree(info.lpszSubjectInfo);
2309 LocalFree(info.lpszIssuerInfo);
2310 LocalFree(info.lpszProtocolName);
2311 LocalFree(info.lpszSignatureAlgName);
2312 LocalFree(info.lpszEncryptionAlgName);
2313 return err;
2314 }
2315 case INTERNET_OPTION_CONNECT_TIMEOUT:
2316 if (*size < sizeof(DWORD))
2317 return ERROR_INSUFFICIENT_BUFFER;
2318
2319 *size = sizeof(DWORD);
2320 *(DWORD *)buffer = req->connect_timeout;
2321 return ERROR_SUCCESS;
2322 case INTERNET_OPTION_REQUEST_FLAGS: {
2323 DWORD flags = 0;
2324
2325 if (*size < sizeof(DWORD))
2326 return ERROR_INSUFFICIENT_BUFFER;
2327
2328 /* FIXME: Add support for:
2329 * INTERNET_REQFLAG_FROM_CACHE
2330 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2331 */
2332
2333 if(req->proxy)
2334 flags |= INTERNET_REQFLAG_VIA_PROXY;
2335 if(!req->status_code)
2336 flags |= INTERNET_REQFLAG_NO_HEADERS;
2337
2338 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2339
2340 *size = sizeof(DWORD);
2341 *(DWORD*)buffer = flags;
2342 return ERROR_SUCCESS;
2343 }
2344 case INTERNET_OPTION_ERROR_MASK:
2345 TRACE("INTERNET_OPTION_ERROR_MASK\n");
2346
2347 if (*size < sizeof(ULONG))
2348 return ERROR_INSUFFICIENT_BUFFER;
2349
2350 *(ULONG*)buffer = hdr->ErrorMask;
2351 *size = sizeof(ULONG);
2352 return ERROR_SUCCESS;
2353 }
2354
2355 return INET_QueryOption(hdr, option, buffer, size, unicode);
2356 }
2357
HTTPREQ_SetOption(object_header_t * hdr,DWORD option,void * buffer,DWORD size)2358 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2359 {
2360 http_request_t *req = (http_request_t*)hdr;
2361
2362 switch(option) {
2363 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2364 TRACE("Undocumented option 99\n");
2365
2366 if (!buffer || size != sizeof(DWORD))
2367 return ERROR_INVALID_PARAMETER;
2368 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2369 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2370
2371 /* fall through */
2372 case INTERNET_OPTION_SECURITY_FLAGS:
2373 {
2374 DWORD flags;
2375
2376 if (!buffer || size != sizeof(DWORD))
2377 return ERROR_INVALID_PARAMETER;
2378 flags = *(DWORD *)buffer;
2379 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2380 flags &= SECURITY_SET_MASK;
2381 req->security_flags |= flags;
2382 if(is_valid_netconn(req->netconn))
2383 req->netconn->security_flags |= flags;
2384 return ERROR_SUCCESS;
2385 }
2386 case INTERNET_OPTION_CONNECT_TIMEOUT:
2387 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2388 req->connect_timeout = *(DWORD *)buffer;
2389 return ERROR_SUCCESS;
2390
2391 case INTERNET_OPTION_SEND_TIMEOUT:
2392 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2393 req->send_timeout = *(DWORD *)buffer;
2394 return ERROR_SUCCESS;
2395
2396 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2397 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2398 req->receive_timeout = *(DWORD *)buffer;
2399 return ERROR_SUCCESS;
2400
2401 case INTERNET_OPTION_USERNAME:
2402 heap_free(req->session->userName);
2403 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2404 return ERROR_SUCCESS;
2405
2406 case INTERNET_OPTION_PASSWORD:
2407 heap_free(req->session->password);
2408 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2409 return ERROR_SUCCESS;
2410
2411 case INTERNET_OPTION_PROXY_USERNAME:
2412 heap_free(req->session->appInfo->proxyUsername);
2413 if (!(req->session->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2414 return ERROR_SUCCESS;
2415
2416 case INTERNET_OPTION_PROXY_PASSWORD:
2417 heap_free(req->session->appInfo->proxyPassword);
2418 if (!(req->session->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2419 return ERROR_SUCCESS;
2420
2421 }
2422
2423 return INET_SetOption(hdr, option, buffer, size);
2424 }
2425
commit_cache_entry(http_request_t * req)2426 static void commit_cache_entry(http_request_t *req)
2427 {
2428 WCHAR *header;
2429 DWORD header_len;
2430 BOOL res;
2431
2432 TRACE("%p\n", req);
2433
2434 CloseHandle(req->hCacheFile);
2435 req->hCacheFile = NULL;
2436
2437 header = build_response_header(req, TRUE);
2438 header_len = (header ? lstrlenW(header) : 0);
2439 res = CommitUrlCacheEntryW(req->req_file->url, req->req_file->file_name, req->expires,
2440 req->last_modified, NORMAL_CACHE_ENTRY,
2441 header, header_len, NULL, 0);
2442 if(res)
2443 req->req_file->is_committed = TRUE;
2444 else
2445 WARN("CommitUrlCacheEntry failed: %u\n", GetLastError());
2446 heap_free(header);
2447 }
2448
create_cache_entry(http_request_t * req)2449 static void create_cache_entry(http_request_t *req)
2450 {
2451 WCHAR file_name[MAX_PATH+1];
2452 WCHAR *url;
2453 BOOL b = TRUE;
2454
2455 /* FIXME: We should free previous cache file earlier */
2456 if(req->req_file) {
2457 req_file_release(req->req_file);
2458 req->req_file = NULL;
2459 }
2460 if(req->hCacheFile) {
2461 CloseHandle(req->hCacheFile);
2462 req->hCacheFile = NULL;
2463 }
2464
2465 if(req->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)
2466 b = FALSE;
2467
2468 if(b) {
2469 int header_idx;
2470
2471 EnterCriticalSection( &req->headers_section );
2472
2473 header_idx = HTTP_GetCustomHeaderIndex(req, L"Cache-Control", 0, FALSE);
2474 if(header_idx != -1) {
2475 WCHAR *ptr;
2476
2477 for(ptr=req->custHeaders[header_idx].lpszValue; *ptr; ) {
2478 WCHAR *end;
2479
2480 while(*ptr==' ' || *ptr=='\t')
2481 ptr++;
2482
2483 end = wcschr(ptr, ',');
2484 if(!end)
2485 end = ptr + lstrlenW(ptr);
2486
2487 if(!wcsnicmp(ptr, L"no-cache", ARRAY_SIZE(L"no-cache")-1)
2488 || !wcsnicmp(ptr, L"no-store", ARRAY_SIZE(L"no-store")-1)) {
2489 b = FALSE;
2490 break;
2491 }
2492
2493 ptr = end;
2494 if(*ptr == ',')
2495 ptr++;
2496 }
2497 }
2498
2499 LeaveCriticalSection( &req->headers_section );
2500 }
2501
2502 if(!b) {
2503 if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2504 return;
2505
2506 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2507 }
2508
2509 url = compose_request_url(req);
2510 if(!url) {
2511 WARN("Could not get URL\n");
2512 return;
2513 }
2514
2515 b = CreateUrlCacheEntryW(url, req->contentLength == ~0 ? 0 : req->contentLength, NULL, file_name, 0);
2516 if(!b) {
2517 WARN("Could not create cache entry: %08x\n", GetLastError());
2518 return;
2519 }
2520
2521 create_req_file(file_name, &req->req_file);
2522 req->req_file->url = url;
2523
2524 req->hCacheFile = CreateFileW(file_name, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2525 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2526 if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2527 WARN("Could not create file: %u\n", GetLastError());
2528 req->hCacheFile = NULL;
2529 return;
2530 }
2531
2532 if(req->read_size) {
2533 DWORD written;
2534
2535 b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2536 if(!b)
2537 FIXME("WriteFile failed: %u\n", GetLastError());
2538
2539 if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2540 commit_cache_entry(req);
2541 }
2542 }
2543
2544 /* read some more data into the read buffer (the read section must be held) */
read_more_data(http_request_t * req,int maxlen)2545 static DWORD read_more_data( http_request_t *req, int maxlen )
2546 {
2547 DWORD res;
2548 int len;
2549
2550 if (req->read_pos)
2551 {
2552 /* move existing data to the start of the buffer */
2553 if(req->read_size)
2554 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2555 req->read_pos = 0;
2556 }
2557
2558 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2559
2560 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2561 maxlen - req->read_size, TRUE, &len );
2562 if(res == ERROR_SUCCESS)
2563 req->read_size += len;
2564
2565 return res;
2566 }
2567
2568 /* remove some amount of data from the read buffer (the read section must be held) */
remove_data(http_request_t * req,int count)2569 static void remove_data( http_request_t *req, int count )
2570 {
2571 if (!(req->read_size -= count)) req->read_pos = 0;
2572 else req->read_pos += count;
2573 }
2574
read_line(http_request_t * req,LPSTR buffer,DWORD * len)2575 static DWORD read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2576 {
2577 int count, bytes_read, pos = 0;
2578 DWORD res;
2579
2580 EnterCriticalSection( &req->read_section );
2581 for (;;)
2582 {
2583 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2584
2585 if (eol)
2586 {
2587 count = eol - (req->read_buf + req->read_pos);
2588 bytes_read = count + 1;
2589 }
2590 else count = bytes_read = req->read_size;
2591
2592 count = min( count, *len - pos );
2593 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2594 pos += count;
2595 remove_data( req, bytes_read );
2596 if (eol) break;
2597
2598 if ((res = read_more_data( req, -1 )))
2599 {
2600 WARN( "read failed %u\n", res );
2601 LeaveCriticalSection( &req->read_section );
2602 return res;
2603 }
2604 if (!req->read_size)
2605 {
2606 *len = 0;
2607 TRACE( "returning empty string\n" );
2608 LeaveCriticalSection( &req->read_section );
2609 return ERROR_SUCCESS;
2610 }
2611 }
2612 LeaveCriticalSection( &req->read_section );
2613
2614 if (pos < *len)
2615 {
2616 if (pos && buffer[pos - 1] == '\r') pos--;
2617 *len = pos + 1;
2618 }
2619 buffer[*len - 1] = 0;
2620 TRACE( "returning %s\n", debugstr_a(buffer));
2621 return ERROR_SUCCESS;
2622 }
2623
2624 /* check if we have reached the end of the data to read (the read section must be held) */
end_of_read_data(http_request_t * req)2625 static BOOL end_of_read_data( http_request_t *req )
2626 {
2627 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2628 }
2629
read_http_stream(http_request_t * req,BYTE * buf,DWORD size,DWORD * read,BOOL allow_blocking)2630 static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, BOOL allow_blocking)
2631 {
2632 DWORD res;
2633
2634 res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, allow_blocking);
2635 if(res != ERROR_SUCCESS)
2636 *read = 0;
2637 assert(*read <= size);
2638
2639 if(req->hCacheFile) {
2640 if(*read) {
2641 BOOL bres;
2642 DWORD written;
2643
2644 bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2645 if(!bres)
2646 FIXME("WriteFile failed: %u\n", GetLastError());
2647 }
2648
2649 if((res == ERROR_SUCCESS && !*read) || req->data_stream->vtbl->end_of_data(req->data_stream, req))
2650 commit_cache_entry(req);
2651 }
2652
2653 return res;
2654 }
2655
2656 /* fetch some more data into the read buffer (the read section must be held) */
refill_read_buffer(http_request_t * req,BOOL allow_blocking,DWORD * read_bytes)2657 static DWORD refill_read_buffer(http_request_t *req, BOOL allow_blocking, DWORD *read_bytes)
2658 {
2659 DWORD res, read=0;
2660
2661 if(req->read_size == sizeof(req->read_buf))
2662 return ERROR_SUCCESS;
2663
2664 if(req->read_pos) {
2665 if(req->read_size)
2666 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2667 req->read_pos = 0;
2668 }
2669
2670 res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2671 &read, allow_blocking);
2672 if(res != ERROR_SUCCESS)
2673 return res;
2674
2675 req->read_size += read;
2676
2677 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2678 if(read_bytes)
2679 *read_bytes = read;
2680 return res;
2681 }
2682
netconn_end_of_data(data_stream_t * stream,http_request_t * req)2683 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2684 {
2685 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2686 return netconn_stream->content_read == netconn_stream->content_length || !is_valid_netconn(req->netconn);
2687 }
2688
netconn_read(data_stream_t * stream,http_request_t * req,BYTE * buf,DWORD size,DWORD * read,BOOL allow_blocking)2689 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2690 DWORD *read, BOOL allow_blocking)
2691 {
2692 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2693 DWORD res = ERROR_SUCCESS;
2694 int ret = 0;
2695
2696 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2697
2698 if(size && is_valid_netconn(req->netconn)) {
2699 res = NETCON_recv(req->netconn, buf, size, allow_blocking, &ret);
2700 if(res == ERROR_SUCCESS) {
2701 if(!ret)
2702 netconn_stream->content_length = netconn_stream->content_read;
2703 netconn_stream->content_read += ret;
2704 }
2705 }
2706
2707 TRACE("res %u read %u bytes\n", res, ret);
2708 *read = ret;
2709 return res;
2710 }
2711
netconn_drain_content(data_stream_t * stream,http_request_t * req,BOOL allow_blocking)2712 static DWORD netconn_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
2713 {
2714 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2715 BYTE buf[1024];
2716 int len, res;
2717 size_t size;
2718
2719 if(netconn_stream->content_length == ~0)
2720 return WSAEISCONN;
2721
2722 while(netconn_stream->content_read < netconn_stream->content_length) {
2723 size = min(sizeof(buf), netconn_stream->content_length-netconn_stream->content_read);
2724 res = NETCON_recv(req->netconn, buf, size, allow_blocking, &len);
2725 if(res)
2726 return res;
2727 if(!len)
2728 return WSAECONNABORTED;
2729
2730 netconn_stream->content_read += len;
2731 }
2732
2733 return ERROR_SUCCESS;
2734 }
2735
netconn_destroy(data_stream_t * stream)2736 static void netconn_destroy(data_stream_t *stream)
2737 {
2738 }
2739
2740 static const data_stream_vtbl_t netconn_stream_vtbl = {
2741 netconn_end_of_data,
2742 netconn_read,
2743 netconn_drain_content,
2744 netconn_destroy
2745 };
2746
next_chunked_data_char(chunked_stream_t * stream)2747 static char next_chunked_data_char(chunked_stream_t *stream)
2748 {
2749 assert(stream->buf_size);
2750
2751 stream->buf_size--;
2752 return stream->buf[stream->buf_pos++];
2753 }
2754
chunked_end_of_data(data_stream_t * stream,http_request_t * req)2755 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2756 {
2757 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2758 switch(chunked_stream->state) {
2759 case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2760 case CHUNKED_STREAM_STATE_END_OF_STREAM:
2761 case CHUNKED_STREAM_STATE_ERROR:
2762 return TRUE;
2763 default:
2764 return FALSE;
2765 }
2766 }
2767
chunked_read(data_stream_t * stream,http_request_t * req,BYTE * buf,DWORD size,DWORD * read,BOOL allow_blocking)2768 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2769 DWORD *read, BOOL allow_blocking)
2770 {
2771 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2772 DWORD ret_read = 0, res = ERROR_SUCCESS;
2773 BOOL continue_read = TRUE;
2774 int read_bytes;
2775 char ch;
2776
2777 do {
2778 TRACE("state %d\n", chunked_stream->state);
2779
2780 /* Ensure that we have data in the buffer for states that need it. */
2781 if(!chunked_stream->buf_size) {
2782 BOOL blocking_read = allow_blocking;
2783
2784 switch(chunked_stream->state) {
2785 case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2786 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE:
2787 /* never allow blocking after 0 chunk size */
2788 if(!chunked_stream->chunk_size)
2789 blocking_read = FALSE;
2790 /* fall through */
2791 case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE:
2792 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA:
2793 chunked_stream->buf_pos = 0;
2794 res = NETCON_recv(req->netconn, chunked_stream->buf, sizeof(chunked_stream->buf), blocking_read, &read_bytes);
2795 if(res == ERROR_SUCCESS && read_bytes) {
2796 chunked_stream->buf_size += read_bytes;
2797 }else if(res == WSAEWOULDBLOCK) {
2798 if(ret_read || allow_blocking)
2799 res = ERROR_SUCCESS;
2800 continue_read = FALSE;
2801 continue;
2802 }else {
2803 chunked_stream->state = CHUNKED_STREAM_STATE_ERROR;
2804 }
2805 break;
2806 default:
2807 break;
2808 }
2809 }
2810
2811 switch(chunked_stream->state) {
2812 case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE:
2813 ch = next_chunked_data_char(chunked_stream);
2814
2815 if(ch >= '0' && ch <= '9') {
2816 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - '0';
2817 }else if(ch >= 'a' && ch <= 'f') {
2818 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'a' + 10;
2819 }else if (ch >= 'A' && ch <= 'F') {
2820 chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'A' + 10;
2821 }else if (ch == ';' || ch == '\r' || ch == '\n') {
2822 TRACE("reading %u byte chunk\n", chunked_stream->chunk_size);
2823 chunked_stream->buf_size++;
2824 chunked_stream->buf_pos--;
2825 if(req->contentLength == ~0) req->contentLength = chunked_stream->chunk_size;
2826 else req->contentLength += chunked_stream->chunk_size;
2827 chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE;
2828 }
2829 break;
2830
2831 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE:
2832 ch = next_chunked_data_char(chunked_stream);
2833 if(ch == '\n')
2834 chunked_stream->state = chunked_stream->chunk_size
2835 ? CHUNKED_STREAM_STATE_READING_CHUNK
2836 : CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END;
2837 else if(ch != '\r')
2838 WARN("unexpected char '%c'\n", ch);
2839 break;
2840
2841 case CHUNKED_STREAM_STATE_READING_CHUNK:
2842 assert(chunked_stream->chunk_size);
2843 if(!size) {
2844 continue_read = FALSE;
2845 break;
2846 }
2847 read_bytes = min(size, chunked_stream->chunk_size);
2848
2849 if(chunked_stream->buf_size) {
2850 if(read_bytes > chunked_stream->buf_size)
2851 read_bytes = chunked_stream->buf_size;
2852
2853 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2854 chunked_stream->buf_pos += read_bytes;
2855 chunked_stream->buf_size -= read_bytes;
2856 }else {
2857 res = NETCON_recv(req->netconn, (char*)buf+ret_read, read_bytes,
2858 allow_blocking, (int*)&read_bytes);
2859 if(res != ERROR_SUCCESS) {
2860 continue_read = FALSE;
2861 break;
2862 }
2863
2864 if(!read_bytes) {
2865 chunked_stream->state = CHUNKED_STREAM_STATE_ERROR;
2866 continue;
2867 }
2868 }
2869
2870 chunked_stream->chunk_size -= read_bytes;
2871 size -= read_bytes;
2872 ret_read += read_bytes;
2873 if(!chunked_stream->chunk_size)
2874 chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA;
2875 allow_blocking = FALSE;
2876 break;
2877
2878 case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA:
2879 ch = next_chunked_data_char(chunked_stream);
2880 if(ch == '\n')
2881 chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE;
2882 else if(ch != '\r')
2883 WARN("unexpected char '%c'\n", ch);
2884 break;
2885
2886 case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2887 ch = next_chunked_data_char(chunked_stream);
2888 if(ch == '\n')
2889 chunked_stream->state = CHUNKED_STREAM_STATE_END_OF_STREAM;
2890 else if(ch != '\r')
2891 WARN("unexpected char '%c'\n", ch);
2892 break;
2893
2894 case CHUNKED_STREAM_STATE_END_OF_STREAM:
2895 case CHUNKED_STREAM_STATE_ERROR:
2896 continue_read = FALSE;
2897 break;
2898 }
2899 } while(continue_read);
2900
2901 if(ret_read)
2902 res = ERROR_SUCCESS;
2903 if(res != ERROR_SUCCESS)
2904 return res;
2905
2906 TRACE("read %d bytes\n", ret_read);
2907 *read = ret_read;
2908 return ERROR_SUCCESS;
2909 }
2910
chunked_drain_content(data_stream_t * stream,http_request_t * req,BOOL allow_blocking)2911 static DWORD chunked_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
2912 {
2913 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2914 BYTE buf[1024];
2915 DWORD size, res;
2916
2917 while(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM
2918 && chunked_stream->state != CHUNKED_STREAM_STATE_ERROR) {
2919 res = chunked_read(stream, req, buf, sizeof(buf), &size, allow_blocking);
2920 if(res != ERROR_SUCCESS)
2921 return res;
2922 }
2923
2924 if(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM)
2925 return ERROR_NO_DATA;
2926 return ERROR_SUCCESS;
2927 }
2928
chunked_destroy(data_stream_t * stream)2929 static void chunked_destroy(data_stream_t *stream)
2930 {
2931 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2932 heap_free(chunked_stream);
2933 }
2934
2935 static const data_stream_vtbl_t chunked_stream_vtbl = {
2936 chunked_end_of_data,
2937 chunked_read,
2938 chunked_drain_content,
2939 chunked_destroy
2940 };
2941
2942 /* set the request content length based on the headers */
set_content_length(http_request_t * request)2943 static DWORD set_content_length(http_request_t *request)
2944 {
2945 WCHAR contentLength[32];
2946 WCHAR encoding[20];
2947 DWORD size;
2948
2949 if(request->status_code == HTTP_STATUS_NO_CONTENT || !wcscmp(request->verb, L"HEAD")) {
2950 request->contentLength = request->netconn_stream.content_length = 0;
2951 return ERROR_SUCCESS;
2952 }
2953
2954 size = sizeof(contentLength);
2955 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONTENT_LENGTH,
2956 contentLength, &size, NULL) != ERROR_SUCCESS ||
2957 !StrToInt64ExW(contentLength, STIF_DEFAULT, (LONGLONG*)&request->contentLength)) {
2958 request->contentLength = ~0;
2959 }
2960
2961 request->netconn_stream.content_length = request->contentLength;
2962 request->netconn_stream.content_read = request->read_size;
2963
2964 size = sizeof(encoding);
2965 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2966 !wcsicmp(encoding, L"chunked"))
2967 {
2968 chunked_stream_t *chunked_stream;
2969
2970 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2971 if(!chunked_stream)
2972 return ERROR_OUTOFMEMORY;
2973
2974 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2975 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2976 chunked_stream->chunk_size = 0;
2977 chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE;
2978
2979 if(request->read_size) {
2980 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2981 chunked_stream->buf_size = request->read_size;
2982 request->read_size = request->read_pos = 0;
2983 }
2984
2985 request->data_stream = &chunked_stream->data_stream;
2986 request->contentLength = ~0;
2987 }
2988
2989 if(request->hdr.decoding) {
2990 int encoding_idx;
2991
2992 EnterCriticalSection( &request->headers_section );
2993
2994 encoding_idx = HTTP_GetCustomHeaderIndex(request, L"Content-Encoding", 0, FALSE);
2995 if(encoding_idx != -1) {
2996 if(!wcsicmp(request->custHeaders[encoding_idx].lpszValue, L"gzip")) {
2997 HTTP_DeleteCustomHeader(request, encoding_idx);
2998 LeaveCriticalSection( &request->headers_section );
2999 return init_gzip_stream(request, TRUE);
3000 }
3001 if(!wcsicmp(request->custHeaders[encoding_idx].lpszValue, L"deflate")) {
3002 HTTP_DeleteCustomHeader(request, encoding_idx);
3003 LeaveCriticalSection( &request->headers_section );
3004 return init_gzip_stream(request, FALSE);
3005 }
3006 }
3007
3008 LeaveCriticalSection( &request->headers_section );
3009 }
3010
3011 return ERROR_SUCCESS;
3012 }
3013
send_request_complete(http_request_t * req,DWORD_PTR result,DWORD error)3014 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
3015 {
3016 INTERNET_ASYNC_RESULT iar;
3017
3018 iar.dwResult = result;
3019 iar.dwError = error;
3020
3021 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3022 sizeof(INTERNET_ASYNC_RESULT));
3023 }
3024
HTTP_ReceiveRequestData(http_request_t * req)3025 static void HTTP_ReceiveRequestData(http_request_t *req)
3026 {
3027 DWORD res, read = 0;
3028
3029 TRACE("%p\n", req);
3030
3031 EnterCriticalSection( &req->read_section );
3032
3033 res = refill_read_buffer(req, FALSE, &read);
3034 if(res == ERROR_SUCCESS)
3035 read += req->read_size;
3036
3037 LeaveCriticalSection( &req->read_section );
3038
3039 if(res != WSAEWOULDBLOCK && (res != ERROR_SUCCESS || !read)) {
3040 WARN("res %u read %u, closing connection\n", res, read);
3041 http_release_netconn(req, FALSE);
3042 }
3043
3044 if(res != ERROR_SUCCESS && res != WSAEWOULDBLOCK) {
3045 send_request_complete(req, 0, res);
3046 return;
3047 }
3048
3049 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, 0);
3050 }
3051
3052 /* read data from the http connection (the read section must be held) */
HTTPREQ_Read(http_request_t * req,void * buffer,DWORD size,DWORD * read,BOOL allow_blocking)3053 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL allow_blocking)
3054 {
3055 DWORD current_read = 0, ret_read = 0;
3056 DWORD res = ERROR_SUCCESS;
3057
3058 EnterCriticalSection( &req->read_section );
3059
3060 if(req->read_size) {
3061 ret_read = min(size, req->read_size);
3062 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
3063 req->read_size -= ret_read;
3064 req->read_pos += ret_read;
3065 allow_blocking = FALSE;
3066 }
3067
3068 if(ret_read < size) {
3069 res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, ¤t_read, allow_blocking);
3070 if(res == ERROR_SUCCESS)
3071 ret_read += current_read;
3072 else if(res == WSAEWOULDBLOCK && ret_read)
3073 res = ERROR_SUCCESS;
3074 }
3075
3076 LeaveCriticalSection( &req->read_section );
3077
3078 *read = ret_read;
3079 TRACE( "retrieved %u bytes (res %u)\n", ret_read, res );
3080
3081 if(res != WSAEWOULDBLOCK) {
3082 if(res != ERROR_SUCCESS)
3083 http_release_netconn(req, FALSE);
3084 else if(!ret_read && drain_content(req, FALSE) == ERROR_SUCCESS)
3085 http_release_netconn(req, TRUE);
3086 }
3087
3088 return res;
3089 }
3090
drain_content(http_request_t * req,BOOL blocking)3091 static DWORD drain_content(http_request_t *req, BOOL blocking)
3092 {
3093 DWORD res;
3094
3095 TRACE("%p\n", req->netconn);
3096
3097 if(!is_valid_netconn(req->netconn))
3098 return ERROR_NO_DATA;
3099
3100 if(!wcscmp(req->verb, L"HEAD"))
3101 return ERROR_SUCCESS;
3102
3103 EnterCriticalSection( &req->read_section );
3104 res = req->data_stream->vtbl->drain_content(req->data_stream, req, blocking);
3105 LeaveCriticalSection( &req->read_section );
3106 return res;
3107 }
3108
3109 typedef struct {
3110 task_header_t hdr;
3111 void *buf;
3112 DWORD size;
3113 DWORD read_pos;
3114 DWORD *ret_read;
3115 } read_file_task_t;
3116
async_read_file_proc(task_header_t * hdr)3117 static void async_read_file_proc(task_header_t *hdr)
3118 {
3119 read_file_task_t *task = (read_file_task_t*)hdr;
3120 http_request_t *req = (http_request_t*)task->hdr.hdr;
3121 DWORD res = ERROR_SUCCESS, read = task->read_pos, complete_arg = 0;
3122
3123 TRACE("req %p buf %p size %u read_pos %u ret_read %p\n", req, task->buf, task->size, task->read_pos, task->ret_read);
3124
3125 if(task->buf) {
3126 DWORD read_bytes;
3127 while (read < task->size) {
3128 res = HTTPREQ_Read(req, (char*)task->buf + read, task->size - read, &read_bytes, TRUE);
3129 if (res != ERROR_SUCCESS || !read_bytes)
3130 break;
3131 read += read_bytes;
3132 }
3133 }else {
3134 EnterCriticalSection(&req->read_section);
3135 res = refill_read_buffer(req, TRUE, &read);
3136 LeaveCriticalSection(&req->read_section);
3137
3138 if(task->ret_read)
3139 complete_arg = read; /* QueryDataAvailable reports read bytes in request complete notification */
3140 if(res != ERROR_SUCCESS || !read)
3141 http_release_netconn(req, drain_content(req, FALSE) == ERROR_SUCCESS);
3142 }
3143
3144 TRACE("res %u read %u\n", res, read);
3145
3146 if(task->ret_read)
3147 *task->ret_read = read;
3148
3149 /* FIXME: We should report bytes transferred before decoding content. */
3150 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, &read, sizeof(read));
3151
3152 if(res != ERROR_SUCCESS)
3153 complete_arg = res;
3154 send_request_complete(req, res == ERROR_SUCCESS, complete_arg);
3155 }
3156
async_read(http_request_t * req,void * buf,DWORD size,DWORD read_pos,DWORD * ret_read)3157 static DWORD async_read(http_request_t *req, void *buf, DWORD size, DWORD read_pos, DWORD *ret_read)
3158 {
3159 read_file_task_t *task;
3160
3161 task = alloc_async_task(&req->hdr, async_read_file_proc, sizeof(*task));
3162 if(!task)
3163 return ERROR_OUTOFMEMORY;
3164
3165 task->buf = buf;
3166 task->size = size;
3167 task->read_pos = read_pos;
3168 task->ret_read = ret_read;
3169
3170 INTERNET_AsyncCall(&task->hdr);
3171 return ERROR_IO_PENDING;
3172 }
3173
HTTPREQ_ReadFile(object_header_t * hdr,void * buf,DWORD size,DWORD * ret_read,DWORD flags,DWORD_PTR context)3174 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
3175 DWORD flags, DWORD_PTR context)
3176 {
3177 http_request_t *req = (http_request_t*)hdr;
3178 DWORD res = ERROR_SUCCESS, read = 0, cread, error = ERROR_SUCCESS;
3179 BOOL allow_blocking, notify_received = FALSE;
3180
3181 TRACE("(%p %p %u %x)\n", req, buf, size, flags);
3182
3183 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3184 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3185
3186 allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC);
3187
3188 if(allow_blocking || TryEnterCriticalSection(&req->read_section)) {
3189 if(allow_blocking)
3190 EnterCriticalSection(&req->read_section);
3191 if(hdr->dwError == ERROR_SUCCESS)
3192 hdr->dwError = INTERNET_HANDLE_IN_USE;
3193 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3194 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3195
3196 if(req->read_size) {
3197 read = min(size, req->read_size);
3198 memcpy(buf, req->read_buf + req->read_pos, read);
3199 req->read_size -= read;
3200 req->read_pos += read;
3201 }
3202
3203 if(read < size && (!read || !(flags & IRF_NO_WAIT)) && !end_of_read_data(req)) {
3204 LeaveCriticalSection(&req->read_section);
3205 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3206 EnterCriticalSection( &req->read_section );
3207 notify_received = TRUE;
3208
3209 while(read < size) {
3210 res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, allow_blocking);
3211 read += cread;
3212 if (res != ERROR_SUCCESS || !cread)
3213 break;
3214 }
3215 }
3216
3217 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3218 hdr->dwError = ERROR_SUCCESS;
3219 else
3220 error = hdr->dwError;
3221
3222 LeaveCriticalSection( &req->read_section );
3223 }else {
3224 res = WSAEWOULDBLOCK;
3225 }
3226
3227 if(res == WSAEWOULDBLOCK) {
3228 if(!(flags & IRF_NO_WAIT))
3229 return async_read(req, buf, size, read, ret_read);
3230 if(!read)
3231 return async_read(req, NULL, 0, 0, NULL);
3232 res = ERROR_SUCCESS;
3233 }
3234
3235 *ret_read = read;
3236 if (res != ERROR_SUCCESS)
3237 return res;
3238
3239 if(notify_received)
3240 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3241 &read, sizeof(read));
3242 return error;
3243 }
3244
HTTPREQ_WriteFile(object_header_t * hdr,const void * buffer,DWORD size,DWORD * written)3245 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
3246 {
3247 DWORD res;
3248 http_request_t *request = (http_request_t*)hdr;
3249
3250 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3251
3252 *written = 0;
3253 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3254 if (res == ERROR_SUCCESS)
3255 request->bytesWritten += *written;
3256
3257 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
3258 return res;
3259 }
3260
HTTPREQ_QueryDataAvailable(object_header_t * hdr,DWORD * available,DWORD flags,DWORD_PTR ctx)3261 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3262 {
3263 http_request_t *req = (http_request_t*)hdr;
3264 DWORD res = ERROR_SUCCESS, avail = 0, error = ERROR_SUCCESS;
3265 BOOL allow_blocking, notify_received = FALSE;
3266
3267 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3268
3269 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3270 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3271
3272 *available = 0;
3273 allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC);
3274
3275 if(allow_blocking || TryEnterCriticalSection(&req->read_section)) {
3276 if(allow_blocking)
3277 EnterCriticalSection(&req->read_section);
3278 if(hdr->dwError == ERROR_SUCCESS)
3279 hdr->dwError = INTERNET_HANDLE_IN_USE;
3280 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3281 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
3282
3283 avail = req->read_size;
3284
3285 if(!avail && !end_of_read_data(req)) {
3286 LeaveCriticalSection(&req->read_section);
3287 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3288 EnterCriticalSection( &req->read_section );
3289 notify_received = TRUE;
3290
3291 res = refill_read_buffer(req, allow_blocking, &avail);
3292 }
3293
3294 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3295 hdr->dwError = ERROR_SUCCESS;
3296 else
3297 error = hdr->dwError;
3298
3299 LeaveCriticalSection( &req->read_section );
3300 }else {
3301 res = WSAEWOULDBLOCK;
3302 }
3303
3304 if(res == WSAEWOULDBLOCK)
3305 return async_read(req, NULL, 0, 0, available);
3306
3307 if (res != ERROR_SUCCESS)
3308 return res;
3309
3310 *available = avail;
3311 if(notify_received)
3312 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
3313 &avail, sizeof(avail));
3314 return error;
3315 }
3316
HTTPREQ_LockRequestFile(object_header_t * hdr,req_file_t ** ret)3317 static DWORD HTTPREQ_LockRequestFile(object_header_t *hdr, req_file_t **ret)
3318 {
3319 http_request_t *req = (http_request_t*)hdr;
3320
3321 TRACE("(%p)\n", req);
3322
3323 if(!req->req_file) {
3324 WARN("No cache file name available\n");
3325 return ERROR_FILE_NOT_FOUND;
3326 }
3327
3328 *ret = req_file_addref(req->req_file);
3329 return ERROR_SUCCESS;
3330 }
3331
3332 static const object_vtbl_t HTTPREQVtbl = {
3333 HTTPREQ_Destroy,
3334 HTTPREQ_CloseConnection,
3335 HTTPREQ_QueryOption,
3336 HTTPREQ_SetOption,
3337 HTTPREQ_ReadFile,
3338 HTTPREQ_WriteFile,
3339 HTTPREQ_QueryDataAvailable,
3340 NULL,
3341 HTTPREQ_LockRequestFile
3342 };
3343
3344 /***********************************************************************
3345 * HTTP_HttpOpenRequestW (internal)
3346 *
3347 * Open a HTTP request handle
3348 *
3349 * RETURNS
3350 * HINTERNET a HTTP request handle on success
3351 * NULL on failure
3352 *
3353 */
HTTP_HttpOpenRequestW(http_session_t * session,LPCWSTR lpszVerb,LPCWSTR lpszObjectName,LPCWSTR lpszVersion,LPCWSTR lpszReferrer,LPCWSTR * lpszAcceptTypes,DWORD dwFlags,DWORD_PTR dwContext,HINTERNET * ret)3354 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3355 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3356 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3357 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3358 {
3359 appinfo_t *hIC = session->appInfo;
3360 http_request_t *request;
3361 DWORD port, len;
3362
3363 TRACE("-->\n");
3364
3365 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3366 if(!request)
3367 return ERROR_OUTOFMEMORY;
3368
3369 request->hdr.htype = WH_HHTTPREQ;
3370 request->hdr.dwFlags = dwFlags;
3371 request->hdr.dwContext = dwContext;
3372 request->hdr.decoding = session->hdr.decoding;
3373 request->contentLength = ~0;
3374
3375 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3376 request->data_stream = &request->netconn_stream.data_stream;
3377 request->connect_timeout = session->connect_timeout;
3378 request->send_timeout = session->send_timeout;
3379 request->receive_timeout = session->receive_timeout;
3380
3381 InitializeCriticalSection( &request->headers_section );
3382 request->headers_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.headers_section");
3383
3384 InitializeCriticalSection( &request->read_section );
3385 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3386
3387 WININET_AddRef( &session->hdr );
3388 request->session = session;
3389 list_add_head( &session->hdr.children, &request->hdr.entry );
3390
3391 port = session->hostPort;
3392 if (port == INTERNET_INVALID_PORT_NUMBER)
3393 port = (session->hdr.dwFlags & INTERNET_FLAG_SECURE) ?
3394 INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3395
3396 request->server = get_server(substrz(session->hostName), port, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3397 if(!request->server) {
3398 WININET_Release(&request->hdr);
3399 return ERROR_OUTOFMEMORY;
3400 }
3401
3402 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3403 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3404 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3405 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3406
3407 if (lpszObjectName && *lpszObjectName) {
3408 HRESULT rc;
3409 WCHAR dummy;
3410
3411 len = 1;
3412 rc = UrlCanonicalizeW(lpszObjectName, &dummy, &len, URL_ESCAPE_SPACES_ONLY);
3413 if (rc != E_POINTER)
3414 len = lstrlenW(lpszObjectName)+1;
3415 request->path = heap_alloc(len*sizeof(WCHAR));
3416 rc = UrlCanonicalizeW(lpszObjectName, request->path, &len,
3417 URL_ESCAPE_SPACES_ONLY);
3418 if (rc != S_OK)
3419 {
3420 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3421 lstrcpyW(request->path,lpszObjectName);
3422 }
3423 }else {
3424 request->path = heap_strdupW(L"/");
3425 }
3426
3427 if (lpszReferrer && *lpszReferrer)
3428 HTTP_ProcessHeader(request, L"Referer", lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3429
3430 if (lpszAcceptTypes)
3431 {
3432 int i;
3433 for (i = 0; lpszAcceptTypes[i]; i++)
3434 {
3435 if (!*lpszAcceptTypes[i]) continue;
3436 HTTP_ProcessHeader(request, L"Accept", lpszAcceptTypes[i],
3437 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3438 HTTP_ADDHDR_FLAG_REQ |
3439 (i == 0 ? (HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD) : 0));
3440 }
3441 }
3442
3443 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : L"GET");
3444 request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : L"HTTP/1.1");
3445
3446 if (hIC->proxy && hIC->proxy[0] && !HTTP_ShouldBypassProxy(hIC, session->hostName))
3447 HTTP_DealWithProxy( hIC, session, request );
3448
3449 INTERNET_SendCallback(&session->hdr, dwContext,
3450 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3451 sizeof(HINTERNET));
3452
3453 TRACE("<-- (%p)\n", request);
3454
3455 *ret = request->hdr.hInternet;
3456 return ERROR_SUCCESS;
3457 }
3458
3459 /***********************************************************************
3460 * HttpOpenRequestW (WININET.@)
3461 *
3462 * Open a HTTP request handle
3463 *
3464 * RETURNS
3465 * HINTERNET a HTTP request handle on success
3466 * NULL on failure
3467 *
3468 */
HttpOpenRequestW(HINTERNET hHttpSession,LPCWSTR lpszVerb,LPCWSTR lpszObjectName,LPCWSTR lpszVersion,LPCWSTR lpszReferrer,LPCWSTR * lpszAcceptTypes,DWORD dwFlags,DWORD_PTR dwContext)3469 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3470 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3471 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3472 DWORD dwFlags, DWORD_PTR dwContext)
3473 {
3474 http_session_t *session;
3475 HINTERNET handle = NULL;
3476 DWORD res;
3477
3478 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3479 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3480 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3481 dwFlags, dwContext);
3482 if(lpszAcceptTypes!=NULL)
3483 {
3484 int i;
3485 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3486 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3487 }
3488
3489 session = (http_session_t*) get_handle_object( hHttpSession );
3490 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3491 {
3492 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3493 goto lend;
3494 }
3495
3496 /*
3497 * My tests seem to show that the windows version does not
3498 * become asynchronous until after this point. And anyhow
3499 * if this call was asynchronous then how would you get the
3500 * necessary HINTERNET pointer returned by this function.
3501 *
3502 */
3503 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3504 lpszVersion, lpszReferrer, lpszAcceptTypes,
3505 dwFlags, dwContext, &handle);
3506 lend:
3507 if( session )
3508 WININET_Release( &session->hdr );
3509 TRACE("returning %p\n", handle);
3510 if(res != ERROR_SUCCESS)
3511 SetLastError(res);
3512 return handle;
3513 }
3514
3515 static const LPCWSTR header_lookup[] = {
3516 L"Mime-Version", /* HTTP_QUERY_MIME_VERSION = 0 */
3517 L"Content-Type", /* HTTP_QUERY_CONTENT_TYPE = 1 */
3518 L"Content-Transfer-Encoding", /* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3519 L"Content-ID", /* HTTP_QUERY_CONTENT_ID = 3 */
3520 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3521 L"Content-Length", /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3522 L"Content-Language", /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3523 L"Allow", /* HTTP_QUERY_ALLOW = 7 */
3524 L"Public", /* HTTP_QUERY_PUBLIC = 8 */
3525 L"Date", /* HTTP_QUERY_DATE = 9 */
3526 L"Expires", /* HTTP_QUERY_EXPIRES = 10 */
3527 L"Last-Modified", /* HTTP_QUERY_LAST_MODIFIED = 11 */
3528 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3529 L"URI", /* HTTP_QUERY_URI = 13 */
3530 L"From", /* HTTP_QUERY_DERIVED_FROM = 14 */
3531 NULL, /* HTTP_QUERY_COST = 15 */
3532 NULL, /* HTTP_QUERY_LINK = 16 */
3533 L"Pragma", /* HTTP_QUERY_PRAGMA = 17 */
3534 NULL, /* HTTP_QUERY_VERSION = 18 */
3535 L"Status", /* HTTP_QUERY_STATUS_CODE = 19 */
3536 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3537 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3538 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3539 L"Connection", /* HTTP_QUERY_CONNECTION = 23 */
3540 L"Accept", /* HTTP_QUERY_ACCEPT = 24 */
3541 L"Accept-Charset", /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3542 L"Accept-Encoding", /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3543 L"Accept-Language", /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3544 L"Authorization", /* HTTP_QUERY_AUTHORIZATION = 28 */
3545 L"Content-Encoding", /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3546 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3547 NULL, /* HTTP_QUERY_FROM = 31 */
3548 L"If-Modified-Since", /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3549 L"Location", /* HTTP_QUERY_LOCATION = 33 */
3550 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3551 L"Referer", /* HTTP_QUERY_REFERER = 35 */
3552 L"Retry-After", /* HTTP_QUERY_RETRY_AFTER = 36 */
3553 L"Server", /* HTTP_QUERY_SERVER = 37 */
3554 NULL, /* HTTP_TITLE = 38 */
3555 L"User-Agent", /* HTTP_QUERY_USER_AGENT = 39 */
3556 L"WWW-Authenticate", /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3557 L"Proxy-Authenticate", /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3558 L"Accept-Ranges", /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3559 L"Set-Cookie", /* HTTP_QUERY_SET_COOKIE = 43 */
3560 L"Cookie", /* HTTP_QUERY_COOKIE = 44 */
3561 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3562 NULL, /* HTTP_QUERY_REFRESH = 46 */
3563 L"Content-Disposition", /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3564 L"Age", /* HTTP_QUERY_AGE = 48 */
3565 L"Cache-Control", /* HTTP_QUERY_CACHE_CONTROL = 49 */
3566 L"Content-Base", /* HTTP_QUERY_CONTENT_BASE = 50 */
3567 L"Content-Location", /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3568 L"Content-MD5", /* HTTP_QUERY_CONTENT_MD5 = 52 */
3569 L"Content-Range", /* HTTP_QUERY_CONTENT_RANGE = 53 */
3570 L"ETag", /* HTTP_QUERY_ETAG = 54 */
3571 L"Host", /* HTTP_QUERY_HOST = 55 */
3572 L"If-Match", /* HTTP_QUERY_IF_MATCH = 56 */
3573 L"If-None-Match", /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3574 L"If-Range", /* HTTP_QUERY_IF_RANGE = 58 */
3575 L"If-Unmodified-Since", /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3576 L"Max-Forwards", /* HTTP_QUERY_MAX_FORWARDS = 60 */
3577 L"Proxy-Authorization", /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3578 L"Range", /* HTTP_QUERY_RANGE = 62 */
3579 L"Transfer-Encoding", /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3580 L"Upgrade", /* HTTP_QUERY_UPGRADE = 64 */
3581 L"Vary", /* HTTP_QUERY_VARY = 65 */
3582 L"Via", /* HTTP_QUERY_VIA = 66 */
3583 L"Warning", /* HTTP_QUERY_WARNING = 67 */
3584 L"Expect", /* HTTP_QUERY_EXPECT = 68 */
3585 L"Proxy-Connection", /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3586 L"Unless-Modified-Since", /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3587 };
3588
3589 /***********************************************************************
3590 * HTTP_HttpQueryInfoW (internal)
3591 */
HTTP_HttpQueryInfoW(http_request_t * request,DWORD dwInfoLevel,LPVOID lpBuffer,LPDWORD lpdwBufferLength,LPDWORD lpdwIndex)3592 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3593 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3594 {
3595 LPHTTPHEADERW lphttpHdr = NULL;
3596 BOOL request_only = !!(dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS);
3597 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3598 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3599 INT index = -1;
3600
3601 EnterCriticalSection( &request->headers_section );
3602
3603 /* Find requested header structure */
3604 switch (level)
3605 {
3606 case HTTP_QUERY_CUSTOM:
3607 if (!lpBuffer)
3608 {
3609 LeaveCriticalSection( &request->headers_section );
3610 return ERROR_INVALID_PARAMETER;
3611 }
3612 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3613 break;
3614 case HTTP_QUERY_RAW_HEADERS_CRLF:
3615 {
3616 LPWSTR headers;
3617 DWORD len = 0;
3618 DWORD res = ERROR_INVALID_PARAMETER;
3619
3620 if (request_only)
3621 headers = build_request_header(request, request->verb, request->path, request->version, TRUE);
3622 else
3623 headers = build_response_header(request, TRUE);
3624 if (!headers)
3625 {
3626 LeaveCriticalSection( &request->headers_section );
3627 return ERROR_OUTOFMEMORY;
3628 }
3629
3630 len = lstrlenW(headers) * sizeof(WCHAR);
3631 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3632 {
3633 len += sizeof(WCHAR);
3634 res = ERROR_INSUFFICIENT_BUFFER;
3635 }
3636 else if (lpBuffer)
3637 {
3638 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3639 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3640 res = ERROR_SUCCESS;
3641 }
3642 *lpdwBufferLength = len;
3643
3644 heap_free(headers);
3645 LeaveCriticalSection( &request->headers_section );
3646 return res;
3647 }
3648 case HTTP_QUERY_RAW_HEADERS:
3649 {
3650 LPWSTR headers;
3651 DWORD len;
3652
3653 if (request_only)
3654 headers = build_request_header(request, request->verb, request->path, request->version, FALSE);
3655 else
3656 headers = build_response_header(request, FALSE);
3657
3658 if (!headers)
3659 {
3660 LeaveCriticalSection( &request->headers_section );
3661 return ERROR_OUTOFMEMORY;
3662 }
3663
3664 len = lstrlenW(headers) * sizeof(WCHAR);
3665 if (len > *lpdwBufferLength)
3666 {
3667 *lpdwBufferLength = len;
3668 heap_free(headers);
3669 LeaveCriticalSection( &request->headers_section );
3670 return ERROR_INSUFFICIENT_BUFFER;
3671 }
3672
3673 if (lpBuffer)
3674 {
3675 DWORD i;
3676
3677 TRACE("returning data: %s\n", debugstr_wn(headers, len / sizeof(WCHAR)));
3678
3679 for (i = 0; i < len / sizeof(WCHAR); i++)
3680 {
3681 if (headers[i] == '\n')
3682 headers[i] = 0;
3683 }
3684 memcpy(lpBuffer, headers, len);
3685 }
3686 *lpdwBufferLength = len - sizeof(WCHAR);
3687
3688 heap_free(headers);
3689 LeaveCriticalSection( &request->headers_section );
3690 return ERROR_SUCCESS;
3691 }
3692 case HTTP_QUERY_STATUS_TEXT:
3693 if (request->statusText)
3694 {
3695 DWORD len = lstrlenW(request->statusText);
3696 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3697 {
3698 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3699 LeaveCriticalSection( &request->headers_section );
3700 return ERROR_INSUFFICIENT_BUFFER;
3701 }
3702 if (lpBuffer)
3703 {
3704 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3705 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3706 }
3707 *lpdwBufferLength = len * sizeof(WCHAR);
3708 LeaveCriticalSection( &request->headers_section );
3709 return ERROR_SUCCESS;
3710 }
3711 break;
3712 case HTTP_QUERY_VERSION:
3713 if (request->version)
3714 {
3715 DWORD len = lstrlenW(request->version);
3716 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3717 {
3718 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3719 LeaveCriticalSection( &request->headers_section );
3720 return ERROR_INSUFFICIENT_BUFFER;
3721 }
3722 if (lpBuffer)
3723 {
3724 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3725 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3726 }
3727 *lpdwBufferLength = len * sizeof(WCHAR);
3728 LeaveCriticalSection( &request->headers_section );
3729 return ERROR_SUCCESS;
3730 }
3731 break;
3732 case HTTP_QUERY_CONTENT_ENCODING:
3733 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3734 requested_index,request_only);
3735 break;
3736 case HTTP_QUERY_STATUS_CODE: {
3737 DWORD res = ERROR_SUCCESS;
3738
3739 if(request_only)
3740 {
3741 LeaveCriticalSection( &request->headers_section );
3742 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3743 }
3744
3745 if(requested_index)
3746 break;
3747
3748 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3749 if(*lpdwBufferLength >= sizeof(DWORD))
3750 *(DWORD*)lpBuffer = request->status_code;
3751 else
3752 res = ERROR_INSUFFICIENT_BUFFER;
3753 *lpdwBufferLength = sizeof(DWORD);
3754 }else {
3755 WCHAR buf[12];
3756 DWORD size;
3757
3758 size = swprintf(buf, ARRAY_SIZE(buf), L"%u", request->status_code) * sizeof(WCHAR);
3759
3760 if(size <= *lpdwBufferLength) {
3761 memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3762 }else {
3763 size += sizeof(WCHAR);
3764 res = ERROR_INSUFFICIENT_BUFFER;
3765 }
3766
3767 *lpdwBufferLength = size;
3768 }
3769 LeaveCriticalSection( &request->headers_section );
3770 return res;
3771 }
3772 default:
3773 assert (ARRAY_SIZE(header_lookup) == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3774
3775 if (level < ARRAY_SIZE(header_lookup) && header_lookup[level])
3776 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3777 requested_index,request_only);
3778 }
3779
3780 if (index >= 0)
3781 lphttpHdr = &request->custHeaders[index];
3782
3783 /* Ensure header satisfies requested attributes */
3784 if (!lphttpHdr ||
3785 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3786 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3787 {
3788 LeaveCriticalSection( &request->headers_section );
3789 return ERROR_HTTP_HEADER_NOT_FOUND;
3790 }
3791
3792 /* coalesce value to requested type */
3793 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3794 {
3795 unsigned long value;
3796
3797 if (*lpdwBufferLength != sizeof(DWORD))
3798 {
3799 LeaveCriticalSection( &request->headers_section );
3800 return ERROR_HTTP_INVALID_HEADER;
3801 }
3802
3803 errno = 0;
3804 value = wcstoul( lphttpHdr->lpszValue, NULL, 10 );
3805 if (value > UINT_MAX || (value == ULONG_MAX && errno == ERANGE))
3806 {
3807 LeaveCriticalSection( &request->headers_section );
3808 return ERROR_HTTP_INVALID_HEADER;
3809 }
3810
3811 *(DWORD *)lpBuffer = value;
3812 TRACE(" returning number: %u\n", *(DWORD *)lpBuffer);
3813 }
3814 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3815 {
3816 time_t tmpTime;
3817 struct tm tmpTM;
3818 SYSTEMTIME *STHook;
3819
3820 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3821
3822 tmpTM = *gmtime(&tmpTime);
3823 STHook = (SYSTEMTIME *)lpBuffer;
3824 STHook->wDay = tmpTM.tm_mday;
3825 STHook->wHour = tmpTM.tm_hour;
3826 STHook->wMilliseconds = 0;
3827 STHook->wMinute = tmpTM.tm_min;
3828 STHook->wDayOfWeek = tmpTM.tm_wday;
3829 STHook->wMonth = tmpTM.tm_mon + 1;
3830 STHook->wSecond = tmpTM.tm_sec;
3831 STHook->wYear = 1900+tmpTM.tm_year;
3832
3833 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3834 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3835 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3836 }
3837 else if (lphttpHdr->lpszValue)
3838 {
3839 DWORD len = (lstrlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3840
3841 if (len > *lpdwBufferLength)
3842 {
3843 *lpdwBufferLength = len;
3844 LeaveCriticalSection( &request->headers_section );
3845 return ERROR_INSUFFICIENT_BUFFER;
3846 }
3847 if (lpBuffer)
3848 {
3849 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3850 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3851 }
3852 *lpdwBufferLength = len - sizeof(WCHAR);
3853 }
3854 if (lpdwIndex) (*lpdwIndex)++;
3855
3856 LeaveCriticalSection( &request->headers_section );
3857 return ERROR_SUCCESS;
3858 }
3859
3860 /***********************************************************************
3861 * HttpQueryInfoW (WININET.@)
3862 *
3863 * Queries for information about an HTTP request
3864 *
3865 * RETURNS
3866 * TRUE on success
3867 * FALSE on failure
3868 *
3869 */
HttpQueryInfoW(HINTERNET hHttpRequest,DWORD dwInfoLevel,LPVOID lpBuffer,LPDWORD lpdwBufferLength,LPDWORD lpdwIndex)3870 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3871 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3872 {
3873 http_request_t *request;
3874 DWORD res;
3875
3876 if (TRACE_ON(wininet)) {
3877 #define FE(x) { x, #x }
3878 static const wininet_flag_info query_flags[] = {
3879 FE(HTTP_QUERY_MIME_VERSION),
3880 FE(HTTP_QUERY_CONTENT_TYPE),
3881 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3882 FE(HTTP_QUERY_CONTENT_ID),
3883 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3884 FE(HTTP_QUERY_CONTENT_LENGTH),
3885 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3886 FE(HTTP_QUERY_ALLOW),
3887 FE(HTTP_QUERY_PUBLIC),
3888 FE(HTTP_QUERY_DATE),
3889 FE(HTTP_QUERY_EXPIRES),
3890 FE(HTTP_QUERY_LAST_MODIFIED),
3891 FE(HTTP_QUERY_MESSAGE_ID),
3892 FE(HTTP_QUERY_URI),
3893 FE(HTTP_QUERY_DERIVED_FROM),
3894 FE(HTTP_QUERY_COST),
3895 FE(HTTP_QUERY_LINK),
3896 FE(HTTP_QUERY_PRAGMA),
3897 FE(HTTP_QUERY_VERSION),
3898 FE(HTTP_QUERY_STATUS_CODE),
3899 FE(HTTP_QUERY_STATUS_TEXT),
3900 FE(HTTP_QUERY_RAW_HEADERS),
3901 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3902 FE(HTTP_QUERY_CONNECTION),
3903 FE(HTTP_QUERY_ACCEPT),
3904 FE(HTTP_QUERY_ACCEPT_CHARSET),
3905 FE(HTTP_QUERY_ACCEPT_ENCODING),
3906 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3907 FE(HTTP_QUERY_AUTHORIZATION),
3908 FE(HTTP_QUERY_CONTENT_ENCODING),
3909 FE(HTTP_QUERY_FORWARDED),
3910 FE(HTTP_QUERY_FROM),
3911 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3912 FE(HTTP_QUERY_LOCATION),
3913 FE(HTTP_QUERY_ORIG_URI),
3914 FE(HTTP_QUERY_REFERER),
3915 FE(HTTP_QUERY_RETRY_AFTER),
3916 FE(HTTP_QUERY_SERVER),
3917 FE(HTTP_QUERY_TITLE),
3918 FE(HTTP_QUERY_USER_AGENT),
3919 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3920 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3921 FE(HTTP_QUERY_ACCEPT_RANGES),
3922 FE(HTTP_QUERY_SET_COOKIE),
3923 FE(HTTP_QUERY_COOKIE),
3924 FE(HTTP_QUERY_REQUEST_METHOD),
3925 FE(HTTP_QUERY_REFRESH),
3926 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3927 FE(HTTP_QUERY_AGE),
3928 FE(HTTP_QUERY_CACHE_CONTROL),
3929 FE(HTTP_QUERY_CONTENT_BASE),
3930 FE(HTTP_QUERY_CONTENT_LOCATION),
3931 FE(HTTP_QUERY_CONTENT_MD5),
3932 FE(HTTP_QUERY_CONTENT_RANGE),
3933 FE(HTTP_QUERY_ETAG),
3934 FE(HTTP_QUERY_HOST),
3935 FE(HTTP_QUERY_IF_MATCH),
3936 FE(HTTP_QUERY_IF_NONE_MATCH),
3937 FE(HTTP_QUERY_IF_RANGE),
3938 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3939 FE(HTTP_QUERY_MAX_FORWARDS),
3940 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3941 FE(HTTP_QUERY_RANGE),
3942 FE(HTTP_QUERY_TRANSFER_ENCODING),
3943 FE(HTTP_QUERY_UPGRADE),
3944 FE(HTTP_QUERY_VARY),
3945 FE(HTTP_QUERY_VIA),
3946 FE(HTTP_QUERY_WARNING),
3947 FE(HTTP_QUERY_CUSTOM)
3948 };
3949 static const wininet_flag_info modifier_flags[] = {
3950 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3951 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3952 FE(HTTP_QUERY_FLAG_NUMBER),
3953 FE(HTTP_QUERY_FLAG_COALESCE)
3954 };
3955 #undef FE
3956 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3957 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3958 DWORD i;
3959
3960 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3961 TRACE(" Attribute:");
3962 for (i = 0; i < ARRAY_SIZE(query_flags); i++) {
3963 if (query_flags[i].val == info) {
3964 TRACE(" %s", query_flags[i].name);
3965 break;
3966 }
3967 }
3968 if (i == ARRAY_SIZE(query_flags)) {
3969 TRACE(" Unknown (%08x)", info);
3970 }
3971
3972 TRACE(" Modifier:");
3973 for (i = 0; i < ARRAY_SIZE(modifier_flags); i++) {
3974 if (modifier_flags[i].val & info_mod) {
3975 TRACE(" %s", modifier_flags[i].name);
3976 info_mod &= ~ modifier_flags[i].val;
3977 }
3978 }
3979
3980 if (info_mod) {
3981 TRACE(" Unknown (%08x)", info_mod);
3982 }
3983 TRACE("\n");
3984 }
3985
3986 request = (http_request_t*) get_handle_object( hHttpRequest );
3987 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3988 {
3989 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3990 goto lend;
3991 }
3992
3993 if (lpBuffer == NULL)
3994 *lpdwBufferLength = 0;
3995 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3996 lpBuffer, lpdwBufferLength, lpdwIndex);
3997
3998 lend:
3999 if( request )
4000 WININET_Release( &request->hdr );
4001
4002 TRACE("%u <--\n", res);
4003
4004 SetLastError(res);
4005 return res == ERROR_SUCCESS;
4006 }
4007
4008 /***********************************************************************
4009 * HttpQueryInfoA (WININET.@)
4010 *
4011 * Queries for information about an HTTP request
4012 *
4013 * RETURNS
4014 * TRUE on success
4015 * FALSE on failure
4016 *
4017 */
HttpQueryInfoA(HINTERNET hHttpRequest,DWORD dwInfoLevel,LPVOID lpBuffer,LPDWORD lpdwBufferLength,LPDWORD lpdwIndex)4018 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
4019 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
4020 {
4021 BOOL result;
4022 DWORD len;
4023 WCHAR* bufferW;
4024
4025 TRACE("%p %x\n", hHttpRequest, dwInfoLevel);
4026
4027 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
4028 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
4029 {
4030 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
4031 lpdwBufferLength, lpdwIndex );
4032 }
4033
4034 if (lpBuffer)
4035 {
4036 DWORD alloclen;
4037 len = (*lpdwBufferLength)*sizeof(WCHAR);
4038 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
4039 {
4040 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
4041 if (alloclen < len)
4042 alloclen = len;
4043 }
4044 else
4045 alloclen = len;
4046 bufferW = heap_alloc(alloclen);
4047 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
4048 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
4049 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
4050 } else
4051 {
4052 bufferW = NULL;
4053 len = 0;
4054 }
4055
4056 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
4057 &len, lpdwIndex );
4058 if( result )
4059 {
4060 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
4061 lpBuffer, *lpdwBufferLength, NULL, NULL );
4062 *lpdwBufferLength = len - 1;
4063
4064 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
4065 }
4066 else
4067 /* since the strings being returned from HttpQueryInfoW should be
4068 * only ASCII characters, it is reasonable to assume that all of
4069 * the Unicode characters can be reduced to a single byte */
4070 *lpdwBufferLength = len / sizeof(WCHAR);
4071
4072 heap_free( bufferW );
4073 return result;
4074 }
4075
get_redirect_url(http_request_t * request)4076 static WCHAR *get_redirect_url(http_request_t *request)
4077 {
4078 static WCHAR szHttp[] = L"http";
4079 static WCHAR szHttps[] = L"https";
4080 http_session_t *session = request->session;
4081 URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
4082 WCHAR *orig_url = NULL, *redirect_url = NULL, *combined_url = NULL;
4083 DWORD url_length = 0, res;
4084 BOOL b;
4085
4086 url_length = 0;
4087 res = HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, redirect_url, &url_length, NULL);
4088 if(res == ERROR_INSUFFICIENT_BUFFER) {
4089 redirect_url = heap_alloc(url_length);
4090 res = HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, redirect_url, &url_length, NULL);
4091 }
4092 if(res != ERROR_SUCCESS) {
4093 heap_free(redirect_url);
4094 return NULL;
4095 }
4096
4097 urlComponents.dwSchemeLength = 1;
4098 b = InternetCrackUrlW(redirect_url, url_length / sizeof(WCHAR), 0, &urlComponents);
4099 if(b && urlComponents.dwSchemeLength &&
4100 urlComponents.nScheme != INTERNET_SCHEME_HTTP && urlComponents.nScheme != INTERNET_SCHEME_HTTPS) {
4101 TRACE("redirect to non-http URL\n");
4102 return NULL;
4103 }
4104
4105 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
4106 urlComponents.dwSchemeLength = 0;
4107 urlComponents.lpszHostName = request->server->name;
4108 urlComponents.nPort = request->server->port;
4109 urlComponents.lpszUserName = session->userName;
4110 urlComponents.lpszUrlPath = request->path;
4111
4112 b = InternetCreateUrlW(&urlComponents, 0, NULL, &url_length);
4113 if(!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
4114 orig_url = heap_alloc(url_length);
4115
4116 /* convert from bytes to characters */
4117 url_length = url_length / sizeof(WCHAR) - 1;
4118 b = InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length);
4119 }
4120
4121 if(b) {
4122 url_length = 0;
4123 b = InternetCombineUrlW(orig_url, redirect_url, NULL, &url_length, ICU_ENCODE_SPACES_ONLY);
4124 if(!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
4125 combined_url = heap_alloc(url_length * sizeof(WCHAR));
4126 b = InternetCombineUrlW(orig_url, redirect_url, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY);
4127 if(!b) {
4128 heap_free(combined_url);
4129 combined_url = NULL;
4130 }
4131 }
4132 }
4133
4134 heap_free(orig_url);
4135 heap_free(redirect_url);
4136 return combined_url;
4137 }
4138
4139
4140 /***********************************************************************
4141 * HTTP_HandleRedirect (internal)
4142 */
HTTP_HandleRedirect(http_request_t * request,WCHAR * url)4143 static DWORD HTTP_HandleRedirect(http_request_t *request, WCHAR *url)
4144 {
4145 URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
4146 http_session_t *session = request->session;
4147 size_t url_len = lstrlenW(url);
4148
4149 if(url[0] == '/')
4150 {
4151 /* if it's an absolute path, keep the same session info */
4152 urlComponents.lpszUrlPath = url;
4153 urlComponents.dwUrlPathLength = url_len;
4154 }
4155 else
4156 {
4157 urlComponents.dwHostNameLength = 1;
4158 urlComponents.dwUserNameLength = 1;
4159 urlComponents.dwUrlPathLength = 1;
4160 if(!InternetCrackUrlW(url, url_len, 0, &urlComponents))
4161 return INTERNET_GetLastError();
4162
4163 if(!urlComponents.dwHostNameLength)
4164 return ERROR_INTERNET_INVALID_URL;
4165 }
4166
4167 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4168 url, (url_len + 1) * sizeof(WCHAR));
4169
4170 if(urlComponents.dwHostNameLength) {
4171 BOOL custom_port = FALSE;
4172 substr_t host;
4173
4174 if(urlComponents.nScheme == INTERNET_SCHEME_HTTP) {
4175 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
4176 TRACE("redirect from secure page to non-secure page\n");
4177 /* FIXME: warn about from secure redirect to non-secure page */
4178 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
4179 }
4180
4181 custom_port = urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT;
4182 }else if(urlComponents.nScheme == INTERNET_SCHEME_HTTPS) {
4183 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
4184 TRACE("redirect from non-secure page to secure page\n");
4185 /* FIXME: notify about redirect to secure page */
4186 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
4187 }
4188
4189 custom_port = urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT;
4190 }
4191
4192 heap_free(session->hostName);
4193
4194 session->hostName = heap_strndupW(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
4195 session->hostPort = urlComponents.nPort;
4196
4197 heap_free(session->userName);
4198 session->userName = NULL;
4199 if (urlComponents.dwUserNameLength)
4200 session->userName = heap_strndupW(urlComponents.lpszUserName, urlComponents.dwUserNameLength);
4201
4202 reset_data_stream(request);
4203
4204 host = substr(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
4205
4206 if(host.len != lstrlenW(request->server->name) || wcsnicmp(request->server->name, host.str, host.len)
4207 || request->server->port != urlComponents.nPort) {
4208 server_t *new_server;
4209
4210 new_server = get_server(host, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
4211 server_release(request->server);
4212 request->server = new_server;
4213 }
4214
4215 if (custom_port)
4216 HTTP_ProcessHeader(request, L"Host", request->server->host_port,
4217 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4218 else
4219 HTTP_ProcessHeader(request, L"Host", request->server->name,
4220 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
4221 }
4222
4223 heap_free(request->path);
4224 request->path = NULL;
4225 if(urlComponents.dwUrlPathLength)
4226 {
4227 DWORD needed = 1;
4228 HRESULT rc;
4229 WCHAR dummy[] = L"";
4230 WCHAR *path;
4231
4232 path = heap_strndupW(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength);
4233 rc = UrlEscapeW(path, dummy, &needed, URL_ESCAPE_SPACES_ONLY);
4234 if (rc != E_POINTER)
4235 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4236 request->path = heap_alloc(needed*sizeof(WCHAR));
4237 rc = UrlEscapeW(path, request->path, &needed,
4238 URL_ESCAPE_SPACES_ONLY);
4239 if (rc != S_OK)
4240 {
4241 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
4242 lstrcpyW(request->path, path);
4243 }
4244 heap_free(path);
4245 }
4246
4247 /* Remove custom content-type/length headers on redirects. */
4248 remove_header(request, L"Content-Type", TRUE);
4249 remove_header(request, L"Content-Length", TRUE);
4250
4251 return ERROR_SUCCESS;
4252 }
4253
4254 /***********************************************************************
4255 * HTTP_build_req (internal)
4256 *
4257 * concatenate all the strings in the request together
4258 */
HTTP_build_req(LPCWSTR * list,int len)4259 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
4260 {
4261 LPCWSTR *t;
4262 LPWSTR str;
4263
4264 for( t = list; *t ; t++ )
4265 len += lstrlenW( *t );
4266 len++;
4267
4268 str = heap_alloc(len*sizeof(WCHAR));
4269 *str = 0;
4270
4271 for( t = list; *t ; t++ )
4272 lstrcatW( str, *t );
4273
4274 return str;
4275 }
4276
HTTP_InsertCookies(http_request_t * request)4277 static void HTTP_InsertCookies(http_request_t *request)
4278 {
4279 WCHAR *cookies;
4280 DWORD res;
4281
4282 res = get_cookie_header(request->server->name, request->path, &cookies);
4283 if(res != ERROR_SUCCESS || !cookies)
4284 return;
4285
4286 HTTP_HttpAddRequestHeadersW(request, cookies, lstrlenW(cookies),
4287 HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD);
4288 heap_free(cookies);
4289 }
4290
HTTP_ParseWkday(LPCWSTR day)4291 static WORD HTTP_ParseWkday(LPCWSTR day)
4292 {
4293 static const WCHAR days[7][4] = {L"sun",
4294 L"mon",
4295 L"tue",
4296 L"wed",
4297 L"thu",
4298 L"fri",
4299 L"sat"};
4300 unsigned int i;
4301 for (i = 0; i < ARRAY_SIZE(days); i++)
4302 if (!wcsicmp(day, days[i]))
4303 return i;
4304
4305 /* Invalid */
4306 return 7;
4307 }
4308
HTTP_ParseMonth(LPCWSTR month)4309 static WORD HTTP_ParseMonth(LPCWSTR month)
4310 {
4311 if (!wcsicmp(month, L"jan")) return 1;
4312 if (!wcsicmp(month, L"feb")) return 2;
4313 if (!wcsicmp(month, L"mar")) return 3;
4314 if (!wcsicmp(month, L"apr")) return 4;
4315 if (!wcsicmp(month, L"may")) return 5;
4316 if (!wcsicmp(month, L"jun")) return 6;
4317 if (!wcsicmp(month, L"jul")) return 7;
4318 if (!wcsicmp(month, L"aug")) return 8;
4319 if (!wcsicmp(month, L"sep")) return 9;
4320 if (!wcsicmp(month, L"oct")) return 10;
4321 if (!wcsicmp(month, L"nov")) return 11;
4322 if (!wcsicmp(month, L"dec")) return 12;
4323 /* Invalid */
4324 return 0;
4325 }
4326
4327 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4328 * optionally preceded by whitespace.
4329 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4330 * st, and sets *str to the first character after the time format.
4331 */
HTTP_ParseTime(SYSTEMTIME * st,LPCWSTR * str)4332 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4333 {
4334 LPCWSTR ptr = *str;
4335 WCHAR *nextPtr;
4336 unsigned long num;
4337
4338 while (iswspace(*ptr))
4339 ptr++;
4340
4341 num = wcstoul(ptr, &nextPtr, 10);
4342 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4343 {
4344 ERR("unexpected time format %s\n", debugstr_w(ptr));
4345 return FALSE;
4346 }
4347 if (num > 23)
4348 {
4349 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4350 return FALSE;
4351 }
4352 ptr = nextPtr + 1;
4353 st->wHour = (WORD)num;
4354 num = wcstoul(ptr, &nextPtr, 10);
4355 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4356 {
4357 ERR("unexpected time format %s\n", debugstr_w(ptr));
4358 return FALSE;
4359 }
4360 if (num > 59)
4361 {
4362 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4363 return FALSE;
4364 }
4365 ptr = nextPtr + 1;
4366 st->wMinute = (WORD)num;
4367 num = wcstoul(ptr, &nextPtr, 10);
4368 if (!nextPtr || nextPtr <= ptr)
4369 {
4370 ERR("unexpected time format %s\n", debugstr_w(ptr));
4371 return FALSE;
4372 }
4373 if (num > 59)
4374 {
4375 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4376 return FALSE;
4377 }
4378 *str = nextPtr;
4379 st->wSecond = (WORD)num;
4380 return TRUE;
4381 }
4382
HTTP_ParseDateAsAsctime(LPCWSTR value,FILETIME * ft)4383 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4384 {
4385 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4386 LPCWSTR ptr;
4387 SYSTEMTIME st = { 0 };
4388 unsigned long num;
4389
4390 for (ptr = value, dayPtr = day; *ptr && !iswspace(*ptr) &&
4391 dayPtr - day < ARRAY_SIZE(day) - 1; ptr++, dayPtr++)
4392 *dayPtr = *ptr;
4393 *dayPtr = 0;
4394 st.wDayOfWeek = HTTP_ParseWkday(day);
4395 if (st.wDayOfWeek >= 7)
4396 {
4397 ERR("unexpected weekday %s\n", debugstr_w(day));
4398 return FALSE;
4399 }
4400
4401 while (iswspace(*ptr))
4402 ptr++;
4403
4404 for (monthPtr = month; !iswspace(*ptr) && monthPtr - month < ARRAY_SIZE(month) - 1;
4405 monthPtr++, ptr++)
4406 *monthPtr = *ptr;
4407 *monthPtr = 0;
4408 st.wMonth = HTTP_ParseMonth(month);
4409 if (!st.wMonth || st.wMonth > 12)
4410 {
4411 ERR("unexpected month %s\n", debugstr_w(month));
4412 return FALSE;
4413 }
4414
4415 while (iswspace(*ptr))
4416 ptr++;
4417
4418 num = wcstoul(ptr, &nextPtr, 10);
4419 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4420 {
4421 ERR("unexpected day %s\n", debugstr_w(ptr));
4422 return FALSE;
4423 }
4424 ptr = nextPtr;
4425 st.wDay = (WORD)num;
4426
4427 while (iswspace(*ptr))
4428 ptr++;
4429
4430 if (!HTTP_ParseTime(&st, &ptr))
4431 return FALSE;
4432
4433 while (iswspace(*ptr))
4434 ptr++;
4435
4436 num = wcstoul(ptr, &nextPtr, 10);
4437 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4438 {
4439 ERR("unexpected year %s\n", debugstr_w(ptr));
4440 return FALSE;
4441 }
4442 ptr = nextPtr;
4443 st.wYear = (WORD)num;
4444
4445 while (iswspace(*ptr))
4446 ptr++;
4447
4448 /* asctime() doesn't report a timezone, but some web servers do, so accept
4449 * with or without GMT.
4450 */
4451 if (*ptr && wcscmp(ptr, L"GMT"))
4452 {
4453 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4454 return FALSE;
4455 }
4456 return SystemTimeToFileTime(&st, ft);
4457 }
4458
HTTP_ParseRfc1123Date(LPCWSTR value,FILETIME * ft)4459 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4460 {
4461 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4462 LPCWSTR ptr;
4463 unsigned long num;
4464 SYSTEMTIME st = { 0 };
4465
4466 ptr = wcschr(value, ',');
4467 if (!ptr)
4468 return FALSE;
4469 if (ptr - value != 3)
4470 {
4471 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4472 return FALSE;
4473 }
4474 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4475 day[3] = 0;
4476 st.wDayOfWeek = HTTP_ParseWkday(day);
4477 if (st.wDayOfWeek > 6)
4478 {
4479 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4480 return FALSE;
4481 }
4482 ptr++;
4483
4484 while (iswspace(*ptr))
4485 ptr++;
4486
4487 num = wcstoul(ptr, &nextPtr, 10);
4488 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4489 {
4490 WARN("unexpected day %s\n", debugstr_w(value));
4491 return FALSE;
4492 }
4493 ptr = nextPtr;
4494 st.wDay = (WORD)num;
4495
4496 while (iswspace(*ptr))
4497 ptr++;
4498
4499 for (monthPtr = month; !iswspace(*ptr) && monthPtr - month < ARRAY_SIZE(month) - 1;
4500 monthPtr++, ptr++)
4501 *monthPtr = *ptr;
4502 *monthPtr = 0;
4503 st.wMonth = HTTP_ParseMonth(month);
4504 if (!st.wMonth || st.wMonth > 12)
4505 {
4506 WARN("unexpected month %s\n", debugstr_w(month));
4507 return FALSE;
4508 }
4509
4510 while (iswspace(*ptr))
4511 ptr++;
4512
4513 num = wcstoul(ptr, &nextPtr, 10);
4514 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4515 {
4516 ERR("unexpected year %s\n", debugstr_w(value));
4517 return FALSE;
4518 }
4519 ptr = nextPtr;
4520 st.wYear = (WORD)num;
4521
4522 if (!HTTP_ParseTime(&st, &ptr))
4523 return FALSE;
4524
4525 while (iswspace(*ptr))
4526 ptr++;
4527
4528 if (wcscmp(ptr, L"GMT"))
4529 {
4530 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4531 return FALSE;
4532 }
4533 return SystemTimeToFileTime(&st, ft);
4534 }
4535
HTTP_ParseWeekday(LPCWSTR day)4536 static WORD HTTP_ParseWeekday(LPCWSTR day)
4537 {
4538 static const WCHAR days[7][10] = {L"sunday",
4539 L"monday",
4540 L"tuesday",
4541 L"wednesday",
4542 L"thursday",
4543 L"friday",
4544 L"saturday"};
4545 unsigned int i;
4546 for (i = 0; i < ARRAY_SIZE(days); i++)
4547 if (!wcsicmp(day, days[i]))
4548 return i;
4549
4550 /* Invalid */
4551 return 7;
4552 }
4553
HTTP_ParseRfc850Date(LPCWSTR value,FILETIME * ft)4554 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4555 {
4556 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4557 LPCWSTR ptr;
4558 unsigned long num;
4559 SYSTEMTIME st = { 0 };
4560
4561 ptr = wcschr(value, ',');
4562 if (!ptr)
4563 return FALSE;
4564 if (ptr - value == 3)
4565 {
4566 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4567 day[3] = 0;
4568 st.wDayOfWeek = HTTP_ParseWkday(day);
4569 if (st.wDayOfWeek > 6)
4570 {
4571 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4572 return FALSE;
4573 }
4574 }
4575 else if (ptr - value < ARRAY_SIZE(day))
4576 {
4577 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4578 day[ptr - value + 1] = 0;
4579 st.wDayOfWeek = HTTP_ParseWeekday(day);
4580 if (st.wDayOfWeek > 6)
4581 {
4582 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4583 return FALSE;
4584 }
4585 }
4586 else
4587 {
4588 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4589 return FALSE;
4590 }
4591 ptr++;
4592
4593 while (iswspace(*ptr))
4594 ptr++;
4595
4596 num = wcstoul(ptr, &nextPtr, 10);
4597 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4598 {
4599 ERR("unexpected day %s\n", debugstr_w(value));
4600 return FALSE;
4601 }
4602 ptr = nextPtr;
4603 st.wDay = (WORD)num;
4604
4605 if (*ptr != '-')
4606 {
4607 ERR("unexpected month format %s\n", debugstr_w(ptr));
4608 return FALSE;
4609 }
4610 ptr++;
4611
4612 for (monthPtr = month; *ptr != '-' && monthPtr - month < ARRAY_SIZE(month) - 1;
4613 monthPtr++, ptr++)
4614 *monthPtr = *ptr;
4615 *monthPtr = 0;
4616 st.wMonth = HTTP_ParseMonth(month);
4617 if (!st.wMonth || st.wMonth > 12)
4618 {
4619 ERR("unexpected month %s\n", debugstr_w(month));
4620 return FALSE;
4621 }
4622
4623 if (*ptr != '-')
4624 {
4625 ERR("unexpected year format %s\n", debugstr_w(ptr));
4626 return FALSE;
4627 }
4628 ptr++;
4629
4630 num = wcstoul(ptr, &nextPtr, 10);
4631 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4632 {
4633 ERR("unexpected year %s\n", debugstr_w(value));
4634 return FALSE;
4635 }
4636 ptr = nextPtr;
4637 st.wYear = (WORD)num;
4638
4639 if (!HTTP_ParseTime(&st, &ptr))
4640 return FALSE;
4641
4642 while (iswspace(*ptr))
4643 ptr++;
4644
4645 if (wcscmp(ptr, L"GMT"))
4646 {
4647 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4648 return FALSE;
4649 }
4650 return SystemTimeToFileTime(&st, ft);
4651 }
4652
HTTP_ParseDate(LPCWSTR value,FILETIME * ft)4653 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4654 {
4655 BOOL ret;
4656
4657 if (!wcscmp(value, L"0"))
4658 {
4659 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4660 ret = TRUE;
4661 }
4662 else if (wcschr(value, ','))
4663 {
4664 ret = HTTP_ParseRfc1123Date(value, ft);
4665 if (!ret)
4666 {
4667 ret = HTTP_ParseRfc850Date(value, ft);
4668 if (!ret)
4669 ERR("unexpected date format %s\n", debugstr_w(value));
4670 }
4671 }
4672 else
4673 {
4674 ret = HTTP_ParseDateAsAsctime(value, ft);
4675 if (!ret)
4676 ERR("unexpected date format %s\n", debugstr_w(value));
4677 }
4678 return ret;
4679 }
4680
HTTP_ProcessExpires(http_request_t * request)4681 static void HTTP_ProcessExpires(http_request_t *request)
4682 {
4683 BOOL expirationFound = FALSE;
4684 int headerIndex;
4685
4686 EnterCriticalSection( &request->headers_section );
4687
4688 /* Look for a Cache-Control header with a max-age directive, as it takes
4689 * precedence over the Expires header.
4690 */
4691 headerIndex = HTTP_GetCustomHeaderIndex(request, L"Cache-Control", 0, FALSE);
4692 if (headerIndex != -1)
4693 {
4694 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4695 LPWSTR ptr;
4696
4697 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4698 {
4699 LPWSTR comma = wcschr(ptr, ','), end, equal;
4700
4701 if (comma)
4702 end = comma;
4703 else
4704 end = ptr + lstrlenW(ptr);
4705 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4706 ;
4707 if (*equal == '=')
4708 {
4709 if (!wcsnicmp(ptr, L"max-age", equal - ptr - 1))
4710 {
4711 LPWSTR nextPtr;
4712 unsigned long age;
4713
4714 age = wcstoul(equal + 1, &nextPtr, 10);
4715 if (nextPtr > equal + 1)
4716 {
4717 LARGE_INTEGER ft;
4718
4719 NtQuerySystemTime( &ft );
4720 /* Age is in seconds, FILETIME resolution is in
4721 * 100 nanosecond intervals.
4722 */
4723 ft.QuadPart += age * (ULONGLONG)1000000;
4724 request->expires.dwLowDateTime = ft.u.LowPart;
4725 request->expires.dwHighDateTime = ft.u.HighPart;
4726 expirationFound = TRUE;
4727 }
4728 }
4729 }
4730 if (comma)
4731 {
4732 ptr = comma + 1;
4733 while (iswspace(*ptr))
4734 ptr++;
4735 }
4736 else
4737 ptr = NULL;
4738 }
4739 }
4740 if (!expirationFound)
4741 {
4742 headerIndex = HTTP_GetCustomHeaderIndex(request, L"Expires", 0, FALSE);
4743 if (headerIndex != -1)
4744 {
4745 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4746 FILETIME ft;
4747
4748 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4749 {
4750 expirationFound = TRUE;
4751 request->expires = ft;
4752 }
4753 }
4754 }
4755 if (!expirationFound)
4756 {
4757 LARGE_INTEGER t;
4758
4759 /* With no known age, default to 10 minutes until expiration. */
4760 NtQuerySystemTime( &t );
4761 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4762 request->expires.dwLowDateTime = t.u.LowPart;
4763 request->expires.dwHighDateTime = t.u.HighPart;
4764 }
4765
4766 LeaveCriticalSection( &request->headers_section );
4767 }
4768
HTTP_ProcessLastModified(http_request_t * request)4769 static void HTTP_ProcessLastModified(http_request_t *request)
4770 {
4771 int headerIndex;
4772
4773 EnterCriticalSection( &request->headers_section );
4774
4775 headerIndex = HTTP_GetCustomHeaderIndex(request, L"Last-Modified", 0, FALSE);
4776 if (headerIndex != -1)
4777 {
4778 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4779 FILETIME ft;
4780
4781 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4782 request->last_modified = ft;
4783 }
4784
4785 LeaveCriticalSection( &request->headers_section );
4786 }
4787
http_process_keep_alive(http_request_t * req)4788 static void http_process_keep_alive(http_request_t *req)
4789 {
4790 int index;
4791
4792 EnterCriticalSection( &req->headers_section );
4793
4794 if ((index = HTTP_GetCustomHeaderIndex(req, L"Connection", 0, FALSE)) != -1)
4795 req->netconn->keep_alive = !wcsicmp(req->custHeaders[index].lpszValue, L"Keep-Alive");
4796 else if ((index = HTTP_GetCustomHeaderIndex(req, L"Proxy-Connection", 0, FALSE)) != -1)
4797 req->netconn->keep_alive = !wcsicmp(req->custHeaders[index].lpszValue, L"Keep-Alive");
4798 else
4799 req->netconn->keep_alive = !wcsicmp(req->version, L"HTTP/1.1");
4800
4801 LeaveCriticalSection( &req->headers_section );
4802 }
4803
open_http_connection(http_request_t * request,BOOL * reusing)4804 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4805 {
4806 netconn_t *netconn = NULL;
4807 DWORD res;
4808
4809 if (request->netconn)
4810 {
4811 if (NETCON_is_alive(request->netconn) && drain_content(request, TRUE) == ERROR_SUCCESS)
4812 {
4813 reset_data_stream(request);
4814 *reusing = TRUE;
4815 return ERROR_SUCCESS;
4816 }
4817
4818 TRACE("freeing netconn\n");
4819 free_netconn(request->netconn);
4820 request->netconn = NULL;
4821 }
4822
4823 reset_data_stream(request);
4824
4825 res = HTTP_ResolveName(request);
4826 if(res != ERROR_SUCCESS)
4827 return res;
4828
4829 EnterCriticalSection(&connection_pool_cs);
4830
4831 while(!list_empty(&request->server->conn_pool)) {
4832 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4833 list_remove(&netconn->pool_entry);
4834
4835 if(is_valid_netconn(netconn) && NETCON_is_alive(netconn))
4836 break;
4837
4838 TRACE("connection %p closed during idle\n", netconn);
4839 free_netconn(netconn);
4840 netconn = NULL;
4841 }
4842
4843 LeaveCriticalSection(&connection_pool_cs);
4844
4845 if(netconn) {
4846 TRACE("<-- reusing %p netconn\n", netconn);
4847 request->netconn = netconn;
4848 *reusing = TRUE;
4849 return ERROR_SUCCESS;
4850 }
4851
4852 TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
4853 request->proxy ? debugstr_w(request->proxy->name) : "(null)");
4854
4855 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4856 INTERNET_STATUS_CONNECTING_TO_SERVER,
4857 request->server->addr_str,
4858 strlen(request->server->addr_str)+1);
4859
4860 res = create_netconn(request->proxy ? request->proxy : request->server, request->security_flags,
4861 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4862 request->connect_timeout, &netconn);
4863 if(res != ERROR_SUCCESS) {
4864 ERR("create_netconn failed: %u\n", res);
4865 return res;
4866 }
4867
4868 request->netconn = netconn;
4869
4870 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4871 INTERNET_STATUS_CONNECTED_TO_SERVER,
4872 request->server->addr_str, strlen(request->server->addr_str)+1);
4873
4874 *reusing = FALSE;
4875 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4876 return ERROR_SUCCESS;
4877 }
4878
build_ascii_request(const WCHAR * str,void * data,DWORD data_len,DWORD * out_len)4879 static char *build_ascii_request( const WCHAR *str, void *data, DWORD data_len, DWORD *out_len )
4880 {
4881 int len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
4882 char *ret;
4883
4884 if (!(ret = heap_alloc( len + data_len ))) return NULL;
4885 WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
4886 if (data_len) memcpy( ret + len - 1, data, data_len );
4887 *out_len = len + data_len - 1;
4888 ret[*out_len] = 0;
4889 return ret;
4890 }
4891
set_content_length_header(http_request_t * request,DWORD len,DWORD flags)4892 static void set_content_length_header( http_request_t *request, DWORD len, DWORD flags )
4893 {
4894 WCHAR buf[ARRAY_SIZE(L"Content-Length: %u\r\n") + 10];
4895
4896 swprintf( buf, ARRAY_SIZE(buf), L"Content-Length: %u\r\n", len );
4897 HTTP_HttpAddRequestHeadersW( request, buf, ~0u, flags );
4898 }
4899
4900 /***********************************************************************
4901 * HTTP_HttpSendRequestW (internal)
4902 *
4903 * Sends the specified request to the HTTP server
4904 *
4905 * RETURNS
4906 * ERROR_SUCCESS on success
4907 * win32 error code on failure
4908 *
4909 */
HTTP_HttpSendRequestW(http_request_t * request,LPCWSTR lpszHeaders,DWORD dwHeaderLength,LPVOID lpOptional,DWORD dwOptionalLength,DWORD dwContentLength,BOOL bEndRequest)4910 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4911 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4912 DWORD dwContentLength, BOOL bEndRequest)
4913 {
4914 BOOL redirected = FALSE, secure_proxy_connect = FALSE, loop_next;
4915 WCHAR *request_header = NULL;
4916 INT responseLen, cnt;
4917 DWORD res;
4918
4919 TRACE("--> %p\n", request);
4920
4921 assert(request->hdr.htype == WH_HHTTPREQ);
4922
4923 /* if the verb is NULL default to GET */
4924 if (!request->verb)
4925 request->verb = heap_strdupW(L"GET");
4926
4927 HTTP_ProcessHeader(request, L"Host", request->server->canon_host_port,
4928 HTTP_ADDREQ_FLAG_ADD_IF_NEW | HTTP_ADDHDR_FLAG_REQ);
4929
4930 if (dwContentLength || wcscmp(request->verb, L"GET"))
4931 {
4932 set_content_length_header(request, dwContentLength, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4933 request->bytesToWrite = dwContentLength;
4934 }
4935 if (request->session->appInfo->agent)
4936 {
4937 WCHAR *agent_header;
4938 int len;
4939
4940 len = lstrlenW(request->session->appInfo->agent) + lstrlenW(L"User-Agent: %s\r\n");
4941 agent_header = heap_alloc(len * sizeof(WCHAR));
4942 swprintf(agent_header, len, L"User-Agent: %s\r\n", request->session->appInfo->agent);
4943
4944 HTTP_HttpAddRequestHeadersW(request, agent_header, lstrlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4945 heap_free(agent_header);
4946 }
4947 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4948 {
4949 HTTP_HttpAddRequestHeadersW(request, L"Pragma: no-cache\r\n",
4950 lstrlenW(L"Pragma: no-cache\r\n"), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4951 }
4952 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && wcscmp(request->verb, L"GET"))
4953 {
4954 HTTP_HttpAddRequestHeadersW(request, L"Cache-Control: no-cache\r\n",
4955 lstrlenW(L"Cache-Control: no-cache\r\n"), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4956 }
4957
4958 /* add the headers the caller supplied */
4959 if( lpszHeaders && dwHeaderLength )
4960 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4961
4962 do
4963 {
4964 DWORD len, data_len = dwOptionalLength;
4965 BOOL reusing_connection;
4966 char *ascii_req;
4967
4968 loop_next = FALSE;
4969
4970 if(redirected) {
4971 request->contentLength = ~0;
4972 request->bytesToWrite = 0;
4973 }
4974
4975 if (TRACE_ON(wininet))
4976 {
4977 HTTPHEADERW *host;
4978
4979 EnterCriticalSection( &request->headers_section );
4980 host = HTTP_GetHeader(request, L"Host");
4981 TRACE("Going to url %s %s\n", debugstr_w(host->lpszValue), debugstr_w(request->path));
4982 LeaveCriticalSection( &request->headers_section );
4983 }
4984
4985 HTTP_FixURL(request);
4986 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4987 {
4988 HTTP_ProcessHeader(request, L"Connection", L"Keep-Alive",
4989 HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD);
4990 }
4991 HTTP_InsertAuthorization(request, request->authInfo, L"Authorization");
4992 HTTP_InsertAuthorization(request, request->proxyAuthInfo, L"Proxy-Authorization");
4993
4994 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4995 HTTP_InsertCookies(request);
4996
4997 res = open_http_connection(request, &reusing_connection);
4998 if (res != ERROR_SUCCESS)
4999 break;
5000
5001 if (!reusing_connection && (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
5002 {
5003 if (request->proxy) secure_proxy_connect = TRUE;
5004 else
5005 {
5006 res = NETCON_secure_connect(request->netconn, request->server);
5007 if (res != ERROR_SUCCESS)
5008 {
5009 WARN("failed to upgrade to secure connection\n");
5010 http_release_netconn(request, FALSE);
5011 break;
5012 }
5013 }
5014 }
5015 if (secure_proxy_connect)
5016 {
5017 const WCHAR *target = request->server->host_port;
5018
5019 if (HTTP_GetCustomHeaderIndex(request, L"Content-Length", 0, TRUE) >= 0)
5020 set_content_length_header(request, 0, HTTP_ADDREQ_FLAG_REPLACE);
5021
5022 request_header = build_request_header(request, L"CONNECT", target, L"HTTP/1.1", TRUE);
5023 }
5024 else if (request->proxy && !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
5025 {
5026 WCHAR *url = build_proxy_path_url(request);
5027 request_header = build_request_header(request, request->verb, url, request->version, TRUE);
5028 heap_free(url);
5029 }
5030 else
5031 {
5032 if (request->proxy && HTTP_GetCustomHeaderIndex(request, L"Content-Length", 0, TRUE) >= 0)
5033 set_content_length_header(request, dwContentLength, HTTP_ADDREQ_FLAG_REPLACE);
5034
5035 request_header = build_request_header(request, request->verb, request->path, request->version, TRUE);
5036 }
5037
5038 TRACE("Request header -> %s\n", debugstr_w(request_header) );
5039
5040 /* send the request as ASCII, tack on the optional data */
5041 if (!lpOptional || redirected || secure_proxy_connect)
5042 data_len = 0;
5043
5044 ascii_req = build_ascii_request(request_header, lpOptional, data_len, &len);
5045 heap_free(request_header);
5046 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
5047
5048 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5049 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
5050
5051 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
5052 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
5053 heap_free( ascii_req );
5054 if(res != ERROR_SUCCESS) {
5055 TRACE("send failed: %u\n", res);
5056 if(!reusing_connection)
5057 break;
5058 http_release_netconn(request, FALSE);
5059 loop_next = TRUE;
5060 continue;
5061 }
5062
5063 request->bytesWritten = data_len;
5064
5065 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5066 INTERNET_STATUS_REQUEST_SENT,
5067 &len, sizeof(DWORD));
5068
5069 if (bEndRequest)
5070 {
5071 DWORD dwBufferSize;
5072
5073 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5074 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5075
5076 if (HTTP_GetResponseHeaders(request, &responseLen))
5077 {
5078 http_release_netconn(request, FALSE);
5079 res = ERROR_INTERNET_CONNECTION_ABORTED;
5080 goto lend;
5081 }
5082 /* FIXME: We should know that connection is closed before sending
5083 * headers. Otherwise wrong callbacks are executed */
5084 if(!responseLen && reusing_connection) {
5085 TRACE("Connection closed by server, reconnecting\n");
5086 http_release_netconn(request, FALSE);
5087 loop_next = TRUE;
5088 continue;
5089 }
5090
5091 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5092 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
5093 sizeof(DWORD));
5094
5095 http_process_keep_alive(request);
5096 HTTP_ProcessCookies(request);
5097 HTTP_ProcessExpires(request);
5098 HTTP_ProcessLastModified(request);
5099
5100 res = set_content_length(request);
5101 if(res != ERROR_SUCCESS)
5102 goto lend;
5103 if(!request->contentLength && !secure_proxy_connect)
5104 http_release_netconn(request, TRUE);
5105
5106 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
5107 {
5108 WCHAR *new_url;
5109
5110 switch(request->status_code) {
5111 case HTTP_STATUS_REDIRECT:
5112 case HTTP_STATUS_MOVED:
5113 case HTTP_STATUS_REDIRECT_KEEP_VERB:
5114 case HTTP_STATUS_REDIRECT_METHOD:
5115 new_url = get_redirect_url(request);
5116 if(!new_url)
5117 break;
5118
5119 if (wcscmp(request->verb, L"GET") && wcscmp(request->verb, L"HEAD") &&
5120 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5121 {
5122 heap_free(request->verb);
5123 request->verb = heap_strdupW(L"GET");
5124 }
5125 http_release_netconn(request, drain_content(request, FALSE) == ERROR_SUCCESS);
5126 res = HTTP_HandleRedirect(request, new_url);
5127 heap_free(new_url);
5128 if (res == ERROR_SUCCESS)
5129 loop_next = TRUE;
5130 redirected = TRUE;
5131 }
5132 }
5133 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
5134 {
5135 WCHAR szAuthValue[2048];
5136 dwBufferSize=2048;
5137 if (request->status_code == HTTP_STATUS_DENIED)
5138 {
5139 WCHAR *host = heap_strdupW( request->server->canon_host_port );
5140 DWORD dwIndex = 0;
5141 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5142 {
5143 if (HTTP_DoAuthorization(request, szAuthValue,
5144 &request->authInfo,
5145 request->session->userName,
5146 request->session->password, host))
5147 {
5148 if (drain_content(request, TRUE) != ERROR_SUCCESS)
5149 {
5150 FIXME("Could not drain content\n");
5151 http_release_netconn(request, FALSE);
5152 }
5153 loop_next = TRUE;
5154 break;
5155 }
5156 }
5157 heap_free( host );
5158
5159 if(!loop_next) {
5160 TRACE("Cleaning wrong authorization data\n");
5161 destroy_authinfo(request->authInfo);
5162 request->authInfo = NULL;
5163 }
5164 }
5165 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
5166 {
5167 DWORD dwIndex = 0;
5168 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
5169 {
5170 if (HTTP_DoAuthorization(request, szAuthValue,
5171 &request->proxyAuthInfo,
5172 request->session->appInfo->proxyUsername,
5173 request->session->appInfo->proxyPassword,
5174 NULL))
5175 {
5176 if (drain_content(request, TRUE) != ERROR_SUCCESS)
5177 {
5178 FIXME("Could not drain content\n");
5179 http_release_netconn(request, FALSE);
5180 }
5181 loop_next = TRUE;
5182 break;
5183 }
5184 }
5185
5186 if(!loop_next) {
5187 TRACE("Cleaning wrong proxy authorization data\n");
5188 destroy_authinfo(request->proxyAuthInfo);
5189 request->proxyAuthInfo = NULL;
5190 }
5191 }
5192 }
5193 if (secure_proxy_connect && request->status_code == HTTP_STATUS_OK)
5194 {
5195 res = NETCON_secure_connect(request->netconn, request->server);
5196 if (res != ERROR_SUCCESS)
5197 {
5198 WARN("failed to upgrade to secure proxy connection\n");
5199 http_release_netconn( request, FALSE );
5200 break;
5201 }
5202 remove_header(request, L"Proxy-Authorization", TRUE);
5203 destroy_authinfo(request->proxyAuthInfo);
5204 request->proxyAuthInfo = NULL;
5205 request->contentLength = 0;
5206 request->netconn_stream.content_length = 0;
5207
5208 secure_proxy_connect = FALSE;
5209 loop_next = TRUE;
5210 }
5211 }
5212 else
5213 res = ERROR_SUCCESS;
5214 }
5215 while (loop_next);
5216
5217 lend:
5218 /* TODO: send notification for P3P header */
5219
5220 if(res == ERROR_SUCCESS)
5221 create_cache_entry(request);
5222
5223 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5224 {
5225 if (res == ERROR_SUCCESS) {
5226 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
5227 HTTP_ReceiveRequestData(request);
5228 else
5229 send_request_complete(request,
5230 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
5231 }else {
5232 send_request_complete(request, 0, res);
5233 }
5234 }
5235
5236 TRACE("<--\n");
5237 return res;
5238 }
5239
5240 typedef struct {
5241 task_header_t hdr;
5242 WCHAR *headers;
5243 DWORD headers_len;
5244 void *optional;
5245 DWORD optional_len;
5246 DWORD content_len;
5247 BOOL end_request;
5248 } send_request_task_t;
5249
5250 /***********************************************************************
5251 *
5252 * Helper functions for the HttpSendRequest(Ex) functions
5253 *
5254 */
AsyncHttpSendRequestProc(task_header_t * hdr)5255 static void AsyncHttpSendRequestProc(task_header_t *hdr)
5256 {
5257 send_request_task_t *task = (send_request_task_t*)hdr;
5258 http_request_t *request = (http_request_t*)task->hdr.hdr;
5259
5260 TRACE("%p\n", request);
5261
5262 HTTP_HttpSendRequestW(request, task->headers, task->headers_len, task->optional,
5263 task->optional_len, task->content_len, task->end_request);
5264
5265 heap_free(task->headers);
5266 }
5267
5268
HTTP_HttpEndRequestW(http_request_t * request,DWORD dwFlags,DWORD_PTR dwContext)5269 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5270 {
5271 INT responseLen;
5272 DWORD res = ERROR_SUCCESS;
5273
5274 if(!is_valid_netconn(request->netconn)) {
5275 WARN("Not connected\n");
5276 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5277 return ERROR_INTERNET_OPERATION_CANCELLED;
5278 }
5279
5280 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5281 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5282
5283 if (HTTP_GetResponseHeaders(request, &responseLen) || !responseLen)
5284 res = ERROR_HTTP_HEADER_NOT_FOUND;
5285
5286 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5287 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5288
5289 /* process cookies here. Is this right? */
5290 http_process_keep_alive(request);
5291 HTTP_ProcessCookies(request);
5292 HTTP_ProcessExpires(request);
5293 HTTP_ProcessLastModified(request);
5294
5295 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5296 if(!request->contentLength)
5297 http_release_netconn(request, TRUE);
5298 }
5299
5300 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5301 {
5302 switch(request->status_code) {
5303 case HTTP_STATUS_REDIRECT:
5304 case HTTP_STATUS_MOVED:
5305 case HTTP_STATUS_REDIRECT_METHOD:
5306 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5307 WCHAR *new_url;
5308
5309 new_url = get_redirect_url(request);
5310 if(!new_url)
5311 break;
5312
5313 if (wcscmp(request->verb, L"GET") && wcscmp(request->verb, L"HEAD") &&
5314 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5315 {
5316 heap_free(request->verb);
5317 request->verb = heap_strdupW(L"GET");
5318 }
5319 http_release_netconn(request, drain_content(request, FALSE) == ERROR_SUCCESS);
5320 res = HTTP_HandleRedirect(request, new_url);
5321 heap_free(new_url);
5322 if (res == ERROR_SUCCESS)
5323 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5324 }
5325 }
5326 }
5327
5328 if(res == ERROR_SUCCESS)
5329 create_cache_entry(request);
5330
5331 if (res == ERROR_SUCCESS && request->contentLength)
5332 HTTP_ReceiveRequestData(request);
5333 else
5334 send_request_complete(request, res == ERROR_SUCCESS, res);
5335
5336 return res;
5337 }
5338
5339 /***********************************************************************
5340 * HttpEndRequestA (WININET.@)
5341 *
5342 * Ends an HTTP request that was started by HttpSendRequestEx
5343 *
5344 * RETURNS
5345 * TRUE if successful
5346 * FALSE on failure
5347 *
5348 */
HttpEndRequestA(HINTERNET hRequest,LPINTERNET_BUFFERSA lpBuffersOut,DWORD dwFlags,DWORD_PTR dwContext)5349 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5350 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5351 {
5352 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5353
5354 if (lpBuffersOut)
5355 {
5356 SetLastError(ERROR_INVALID_PARAMETER);
5357 return FALSE;
5358 }
5359
5360 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5361 }
5362
5363 typedef struct {
5364 task_header_t hdr;
5365 DWORD flags;
5366 DWORD context;
5367 } end_request_task_t;
5368
AsyncHttpEndRequestProc(task_header_t * hdr)5369 static void AsyncHttpEndRequestProc(task_header_t *hdr)
5370 {
5371 end_request_task_t *task = (end_request_task_t*)hdr;
5372 http_request_t *req = (http_request_t*)task->hdr.hdr;
5373
5374 TRACE("%p\n", req);
5375
5376 HTTP_HttpEndRequestW(req, task->flags, task->context);
5377 }
5378
5379 /***********************************************************************
5380 * HttpEndRequestW (WININET.@)
5381 *
5382 * Ends an HTTP request that was started by HttpSendRequestEx
5383 *
5384 * RETURNS
5385 * TRUE if successful
5386 * FALSE on failure
5387 *
5388 */
HttpEndRequestW(HINTERNET hRequest,LPINTERNET_BUFFERSW lpBuffersOut,DWORD dwFlags,DWORD_PTR dwContext)5389 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5390 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5391 {
5392 http_request_t *request;
5393 DWORD res;
5394
5395 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5396
5397 if (lpBuffersOut)
5398 {
5399 SetLastError(ERROR_INVALID_PARAMETER);
5400 return FALSE;
5401 }
5402
5403 request = (http_request_t*) get_handle_object( hRequest );
5404
5405 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5406 {
5407 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5408 if (request)
5409 WININET_Release( &request->hdr );
5410 return FALSE;
5411 }
5412 request->hdr.dwFlags |= dwFlags;
5413
5414 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5415 {
5416 end_request_task_t *task;
5417
5418 task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
5419 task->flags = dwFlags;
5420 task->context = dwContext;
5421
5422 INTERNET_AsyncCall(&task->hdr);
5423 res = ERROR_IO_PENDING;
5424 }
5425 else
5426 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5427
5428 WININET_Release( &request->hdr );
5429 TRACE("%u <--\n", res);
5430 if(res != ERROR_SUCCESS)
5431 SetLastError(res);
5432 return res == ERROR_SUCCESS;
5433 }
5434
5435 /***********************************************************************
5436 * HttpSendRequestExA (WININET.@)
5437 *
5438 * Sends the specified request to the HTTP server and allows chunked
5439 * transfers.
5440 *
5441 * RETURNS
5442 * Success: TRUE
5443 * Failure: FALSE, call GetLastError() for more information.
5444 */
HttpSendRequestExA(HINTERNET hRequest,LPINTERNET_BUFFERSA lpBuffersIn,LPINTERNET_BUFFERSA lpBuffersOut,DWORD dwFlags,DWORD_PTR dwContext)5445 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5446 LPINTERNET_BUFFERSA lpBuffersIn,
5447 LPINTERNET_BUFFERSA lpBuffersOut,
5448 DWORD dwFlags, DWORD_PTR dwContext)
5449 {
5450 INTERNET_BUFFERSW BuffersInW;
5451 BOOL rc = FALSE;
5452 DWORD headerlen;
5453 LPWSTR header = NULL;
5454
5455 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5456 lpBuffersOut, dwFlags, dwContext);
5457
5458 if (lpBuffersIn)
5459 {
5460 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5461 if (lpBuffersIn->lpcszHeader)
5462 {
5463 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5464 lpBuffersIn->dwHeadersLength,0,0);
5465 header = heap_alloc(headerlen*sizeof(WCHAR));
5466 if (!(BuffersInW.lpcszHeader = header))
5467 {
5468 SetLastError(ERROR_OUTOFMEMORY);
5469 return FALSE;
5470 }
5471 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5472 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5473 header, headerlen);
5474 }
5475 else
5476 BuffersInW.lpcszHeader = NULL;
5477 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5478 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5479 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5480 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5481 BuffersInW.Next = NULL;
5482 }
5483
5484 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5485
5486 heap_free(header);
5487 return rc;
5488 }
5489
5490 /***********************************************************************
5491 * HttpSendRequestExW (WININET.@)
5492 *
5493 * Sends the specified request to the HTTP server and allows chunked
5494 * transfers
5495 *
5496 * RETURNS
5497 * Success: TRUE
5498 * Failure: FALSE, call GetLastError() for more information.
5499 */
HttpSendRequestExW(HINTERNET hRequest,LPINTERNET_BUFFERSW lpBuffersIn,LPINTERNET_BUFFERSW lpBuffersOut,DWORD dwFlags,DWORD_PTR dwContext)5500 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5501 LPINTERNET_BUFFERSW lpBuffersIn,
5502 LPINTERNET_BUFFERSW lpBuffersOut,
5503 DWORD dwFlags, DWORD_PTR dwContext)
5504 {
5505 http_request_t *request;
5506 http_session_t *session;
5507 appinfo_t *hIC;
5508 DWORD res;
5509
5510 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5511 lpBuffersOut, dwFlags, dwContext);
5512
5513 request = (http_request_t*) get_handle_object( hRequest );
5514
5515 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5516 {
5517 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5518 goto lend;
5519 }
5520
5521 session = request->session;
5522 assert(session->hdr.htype == WH_HHTTPSESSION);
5523 hIC = session->appInfo;
5524 assert(hIC->hdr.htype == WH_HINIT);
5525
5526 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5527 {
5528 send_request_task_t *task;
5529
5530 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5531 if (lpBuffersIn)
5532 {
5533 DWORD size = 0;
5534
5535 if (lpBuffersIn->lpcszHeader)
5536 {
5537 if (lpBuffersIn->dwHeadersLength == ~0u)
5538 size = (lstrlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5539 else
5540 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5541
5542 task->headers = heap_alloc(size);
5543 memcpy(task->headers, lpBuffersIn->lpcszHeader, size);
5544 }
5545 else task->headers = NULL;
5546
5547 task->headers_len = size / sizeof(WCHAR);
5548 task->optional = lpBuffersIn->lpvBuffer;
5549 task->optional_len = lpBuffersIn->dwBufferLength;
5550 task->content_len = lpBuffersIn->dwBufferTotal;
5551 }
5552 else
5553 {
5554 task->headers = NULL;
5555 task->headers_len = 0;
5556 task->optional = NULL;
5557 task->optional_len = 0;
5558 task->content_len = 0;
5559 }
5560
5561 task->end_request = FALSE;
5562
5563 INTERNET_AsyncCall(&task->hdr);
5564 res = ERROR_IO_PENDING;
5565 }
5566 else
5567 {
5568 if (lpBuffersIn)
5569 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5570 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5571 lpBuffersIn->dwBufferTotal, FALSE);
5572 else
5573 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5574 }
5575
5576 lend:
5577 if ( request )
5578 WININET_Release( &request->hdr );
5579
5580 TRACE("<---\n");
5581 SetLastError(res);
5582 return res == ERROR_SUCCESS;
5583 }
5584
5585 /***********************************************************************
5586 * HttpSendRequestW (WININET.@)
5587 *
5588 * Sends the specified request to the HTTP server
5589 *
5590 * RETURNS
5591 * TRUE on success
5592 * FALSE on failure
5593 *
5594 */
HttpSendRequestW(HINTERNET hHttpRequest,LPCWSTR lpszHeaders,DWORD dwHeaderLength,LPVOID lpOptional,DWORD dwOptionalLength)5595 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5596 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5597 {
5598 http_request_t *request;
5599 http_session_t *session = NULL;
5600 appinfo_t *hIC = NULL;
5601 DWORD res = ERROR_SUCCESS;
5602
5603 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5604 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5605
5606 request = (http_request_t*) get_handle_object( hHttpRequest );
5607 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5608 {
5609 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5610 goto lend;
5611 }
5612
5613 session = request->session;
5614 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5615 {
5616 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5617 goto lend;
5618 }
5619
5620 hIC = session->appInfo;
5621 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5622 {
5623 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5624 goto lend;
5625 }
5626
5627 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5628 {
5629 send_request_task_t *task;
5630
5631 task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
5632 if (lpszHeaders)
5633 {
5634 DWORD size;
5635
5636 if (dwHeaderLength == ~0u) size = (lstrlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5637 else size = dwHeaderLength * sizeof(WCHAR);
5638
5639 task->headers = heap_alloc(size);
5640 memcpy(task->headers, lpszHeaders, size);
5641 }
5642 else
5643 task->headers = NULL;
5644 task->headers_len = dwHeaderLength;
5645 task->optional = lpOptional;
5646 task->optional_len = dwOptionalLength;
5647 task->content_len = dwOptionalLength;
5648 task->end_request = TRUE;
5649
5650 INTERNET_AsyncCall(&task->hdr);
5651 res = ERROR_IO_PENDING;
5652 }
5653 else
5654 {
5655 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5656 dwHeaderLength, lpOptional, dwOptionalLength,
5657 dwOptionalLength, TRUE);
5658 }
5659 lend:
5660 if( request )
5661 WININET_Release( &request->hdr );
5662
5663 SetLastError(res);
5664 return res == ERROR_SUCCESS;
5665 }
5666
5667 /***********************************************************************
5668 * HttpSendRequestA (WININET.@)
5669 *
5670 * Sends the specified request to the HTTP server
5671 *
5672 * RETURNS
5673 * TRUE on success
5674 * FALSE on failure
5675 *
5676 */
HttpSendRequestA(HINTERNET hHttpRequest,LPCSTR lpszHeaders,DWORD dwHeaderLength,LPVOID lpOptional,DWORD dwOptionalLength)5677 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5678 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5679 {
5680 BOOL result;
5681 LPWSTR szHeaders=NULL;
5682 DWORD nLen=dwHeaderLength;
5683 if(lpszHeaders!=NULL)
5684 {
5685 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5686 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5687 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5688 }
5689 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5690 heap_free(szHeaders);
5691 return result;
5692 }
5693
5694 /***********************************************************************
5695 * HTTPSESSION_Destroy (internal)
5696 *
5697 * Deallocate session handle
5698 *
5699 */
HTTPSESSION_Destroy(object_header_t * hdr)5700 static void HTTPSESSION_Destroy(object_header_t *hdr)
5701 {
5702 http_session_t *session = (http_session_t*) hdr;
5703
5704 TRACE("%p\n", session);
5705
5706 WININET_Release(&session->appInfo->hdr);
5707
5708 heap_free(session->hostName);
5709 heap_free(session->password);
5710 heap_free(session->userName);
5711 }
5712
HTTPSESSION_QueryOption(object_header_t * hdr,DWORD option,void * buffer,DWORD * size,BOOL unicode)5713 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5714 {
5715 http_session_t *ses = (http_session_t *)hdr;
5716
5717 switch(option) {
5718 case INTERNET_OPTION_HANDLE_TYPE:
5719 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5720
5721 if (*size < sizeof(ULONG))
5722 return ERROR_INSUFFICIENT_BUFFER;
5723
5724 *size = sizeof(DWORD);
5725 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5726 return ERROR_SUCCESS;
5727 case INTERNET_OPTION_CONNECT_TIMEOUT:
5728 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5729
5730 if (*size < sizeof(DWORD))
5731 return ERROR_INSUFFICIENT_BUFFER;
5732
5733 *size = sizeof(DWORD);
5734 *(DWORD *)buffer = ses->connect_timeout;
5735 return ERROR_SUCCESS;
5736
5737 case INTERNET_OPTION_SEND_TIMEOUT:
5738 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5739
5740 if (*size < sizeof(DWORD))
5741 return ERROR_INSUFFICIENT_BUFFER;
5742
5743 *size = sizeof(DWORD);
5744 *(DWORD *)buffer = ses->send_timeout;
5745 return ERROR_SUCCESS;
5746
5747 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5748 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5749
5750 if (*size < sizeof(DWORD))
5751 return ERROR_INSUFFICIENT_BUFFER;
5752
5753 *size = sizeof(DWORD);
5754 *(DWORD *)buffer = ses->receive_timeout;
5755 return ERROR_SUCCESS;
5756 }
5757
5758 return INET_QueryOption(hdr, option, buffer, size, unicode);
5759 }
5760
HTTPSESSION_SetOption(object_header_t * hdr,DWORD option,void * buffer,DWORD size)5761 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5762 {
5763 http_session_t *ses = (http_session_t*)hdr;
5764
5765 switch(option) {
5766 case INTERNET_OPTION_USERNAME:
5767 {
5768 heap_free(ses->userName);
5769 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5770 return ERROR_SUCCESS;
5771 }
5772 case INTERNET_OPTION_PASSWORD:
5773 {
5774 heap_free(ses->password);
5775 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5776 return ERROR_SUCCESS;
5777 }
5778 case INTERNET_OPTION_PROXY_USERNAME:
5779 {
5780 heap_free(ses->appInfo->proxyUsername);
5781 if (!(ses->appInfo->proxyUsername = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5782 return ERROR_SUCCESS;
5783 }
5784 case INTERNET_OPTION_PROXY_PASSWORD:
5785 {
5786 heap_free(ses->appInfo->proxyPassword);
5787 if (!(ses->appInfo->proxyPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5788 return ERROR_SUCCESS;
5789 }
5790 case INTERNET_OPTION_CONNECT_TIMEOUT:
5791 {
5792 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5793 ses->connect_timeout = *(DWORD *)buffer;
5794 return ERROR_SUCCESS;
5795 }
5796 case INTERNET_OPTION_SEND_TIMEOUT:
5797 {
5798 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5799 ses->send_timeout = *(DWORD *)buffer;
5800 return ERROR_SUCCESS;
5801 }
5802 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5803 {
5804 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5805 ses->receive_timeout = *(DWORD *)buffer;
5806 return ERROR_SUCCESS;
5807 }
5808 default: break;
5809 }
5810
5811 return INET_SetOption(hdr, option, buffer, size);
5812 }
5813
5814 static const object_vtbl_t HTTPSESSIONVtbl = {
5815 HTTPSESSION_Destroy,
5816 NULL,
5817 HTTPSESSION_QueryOption,
5818 HTTPSESSION_SetOption,
5819 NULL,
5820 NULL,
5821 NULL,
5822 NULL
5823 };
5824
5825
5826 /***********************************************************************
5827 * HTTP_Connect (internal)
5828 *
5829 * Create http session handle
5830 *
5831 * RETURNS
5832 * HINTERNET a session handle on success
5833 * NULL on failure
5834 *
5835 */
HTTP_Connect(appinfo_t * hIC,LPCWSTR lpszServerName,INTERNET_PORT serverPort,LPCWSTR lpszUserName,LPCWSTR lpszPassword,DWORD dwFlags,DWORD_PTR dwContext,DWORD dwInternalFlags,HINTERNET * ret)5836 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5837 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5838 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5839 DWORD dwInternalFlags, HINTERNET *ret)
5840 {
5841 http_session_t *session = NULL;
5842
5843 TRACE("-->\n");
5844
5845 if (!lpszServerName || !lpszServerName[0])
5846 return ERROR_INVALID_PARAMETER;
5847
5848 assert( hIC->hdr.htype == WH_HINIT );
5849
5850 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5851 if (!session)
5852 return ERROR_OUTOFMEMORY;
5853
5854 /*
5855 * According to my tests. The name is not resolved until a request is sent
5856 */
5857
5858 session->hdr.htype = WH_HHTTPSESSION;
5859 session->hdr.dwFlags = dwFlags;
5860 session->hdr.dwContext = dwContext;
5861 session->hdr.dwInternalFlags |= dwInternalFlags;
5862 session->hdr.decoding = hIC->hdr.decoding;
5863
5864 WININET_AddRef( &hIC->hdr );
5865 session->appInfo = hIC;
5866 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5867
5868 session->hostName = heap_strdupW(lpszServerName);
5869 if (lpszUserName && lpszUserName[0])
5870 session->userName = heap_strdupW(lpszUserName);
5871 session->password = heap_strdupW(lpszPassword);
5872 session->hostPort = serverPort;
5873 session->connect_timeout = hIC->connect_timeout;
5874 session->send_timeout = 0;
5875 session->receive_timeout = 0;
5876
5877 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5878 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5879 {
5880 INTERNET_SendCallback(&hIC->hdr, dwContext,
5881 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5882 sizeof(HINTERNET));
5883 }
5884
5885 /*
5886 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5887 * windows
5888 */
5889
5890 TRACE("%p --> %p\n", hIC, session);
5891
5892 *ret = session->hdr.hInternet;
5893 return ERROR_SUCCESS;
5894 }
5895
5896 /***********************************************************************
5897 * HTTP_clear_response_headers (internal)
5898 *
5899 * clear out any old response headers
5900 */
HTTP_clear_response_headers(http_request_t * request)5901 static void HTTP_clear_response_headers( http_request_t *request )
5902 {
5903 DWORD i;
5904
5905 EnterCriticalSection( &request->headers_section );
5906
5907 for( i=0; i<request->nCustHeaders; i++)
5908 {
5909 if( !request->custHeaders[i].lpszField )
5910 continue;
5911 if( !request->custHeaders[i].lpszValue )
5912 continue;
5913 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5914 continue;
5915 HTTP_DeleteCustomHeader( request, i );
5916 i--;
5917 }
5918
5919 LeaveCriticalSection( &request->headers_section );
5920 }
5921
5922 /***********************************************************************
5923 * HTTP_GetResponseHeaders (internal)
5924 *
5925 * Read server response
5926 *
5927 * RETURNS
5928 *
5929 * TRUE on success
5930 * FALSE on error
5931 */
HTTP_GetResponseHeaders(http_request_t * request,INT * len)5932 static DWORD HTTP_GetResponseHeaders(http_request_t *request, INT *len)
5933 {
5934 INT cbreaks = 0;
5935 WCHAR buffer[MAX_REPLY_LEN];
5936 DWORD buflen = MAX_REPLY_LEN;
5937 INT rc = 0;
5938 char bufferA[MAX_REPLY_LEN];
5939 LPWSTR status_code = NULL, status_text = NULL;
5940 DWORD res = ERROR_HTTP_INVALID_SERVER_RESPONSE;
5941 BOOL codeHundred = FALSE;
5942
5943 TRACE("-->\n");
5944
5945 if(!is_valid_netconn(request->netconn))
5946 goto lend;
5947
5948 /* clear old response headers (eg. from a redirect response) */
5949 HTTP_clear_response_headers( request );
5950
5951 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5952 do {
5953 /*
5954 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5955 */
5956 buflen = MAX_REPLY_LEN;
5957 if ((res = read_line(request, bufferA, &buflen)))
5958 goto lend;
5959
5960 if (!buflen) goto lend;
5961
5962 rc += buflen;
5963 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5964 /* check is this a status code line? */
5965 if (!wcsncmp(buffer, L"HTTP/1.0", 4))
5966 {
5967 /* split the version from the status code */
5968 status_code = wcschr( buffer, ' ' );
5969 if( !status_code )
5970 goto lend;
5971 *status_code++=0;
5972
5973 /* split the status code from the status text */
5974 status_text = wcschr( status_code, ' ' );
5975 if( status_text )
5976 *status_text++=0;
5977
5978 request->status_code = wcstol(status_code, NULL, 10);
5979
5980 TRACE("version [%s] status code [%s] status text [%s]\n",
5981 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5982
5983 codeHundred = request->status_code == HTTP_STATUS_CONTINUE;
5984 }
5985 else if (!codeHundred)
5986 {
5987 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5988
5989 heap_free(request->version);
5990 heap_free(request->statusText);
5991
5992 request->status_code = HTTP_STATUS_OK;
5993 request->version = heap_strdupW(L"HTTP/1.0");
5994 request->statusText = heap_strdupW(L"OK");
5995
5996 goto lend;
5997 }
5998 } while (codeHundred);
5999
6000 /* Add status code */
6001 HTTP_ProcessHeader(request, L"Status", status_code,
6002 HTTP_ADDHDR_FLAG_REPLACE | HTTP_ADDHDR_FLAG_ADD);
6003
6004 heap_free(request->version);
6005 heap_free(request->statusText);
6006
6007 request->version = heap_strdupW(buffer);
6008 request->statusText = heap_strdupW(status_text ? status_text : L"");
6009
6010 /* Restore the spaces */
6011 *(status_code-1) = ' ';
6012 if (status_text)
6013 *(status_text-1) = ' ';
6014
6015 /* Parse each response line */
6016 do
6017 {
6018 buflen = MAX_REPLY_LEN;
6019 if (!read_line(request, bufferA, &buflen) && buflen)
6020 {
6021 LPWSTR * pFieldAndValue;
6022
6023 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
6024
6025 if (!bufferA[0]) break;
6026 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
6027
6028 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
6029 if (pFieldAndValue)
6030 {
6031 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
6032 HTTP_ADDREQ_FLAG_ADD );
6033 HTTP_FreeTokens(pFieldAndValue);
6034 }
6035 }
6036 else
6037 {
6038 cbreaks++;
6039 if (cbreaks >= 2)
6040 break;
6041 }
6042 }while(1);
6043
6044 res = ERROR_SUCCESS;
6045
6046 lend:
6047
6048 *len = rc;
6049 TRACE("<--\n");
6050 return res;
6051 }
6052
6053 /***********************************************************************
6054 * HTTP_InterpretHttpHeader (internal)
6055 *
6056 * Parse server response
6057 *
6058 * RETURNS
6059 *
6060 * Pointer to array of field, value, NULL on success.
6061 * NULL on error.
6062 */
HTTP_InterpretHttpHeader(LPCWSTR buffer)6063 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
6064 {
6065 LPWSTR * pTokenPair;
6066 LPWSTR pszColon;
6067 INT len;
6068
6069 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
6070
6071 pszColon = wcschr(buffer, ':');
6072 /* must have two tokens */
6073 if (!pszColon)
6074 {
6075 HTTP_FreeTokens(pTokenPair);
6076 if (buffer[0])
6077 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
6078 return NULL;
6079 }
6080
6081 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
6082 if (!pTokenPair[0])
6083 {
6084 HTTP_FreeTokens(pTokenPair);
6085 return NULL;
6086 }
6087 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
6088 pTokenPair[0][pszColon - buffer] = '\0';
6089
6090 /* skip colon */
6091 pszColon++;
6092 len = lstrlenW(pszColon);
6093 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
6094 if (!pTokenPair[1])
6095 {
6096 HTTP_FreeTokens(pTokenPair);
6097 return NULL;
6098 }
6099 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
6100
6101 strip_spaces(pTokenPair[0]);
6102 strip_spaces(pTokenPair[1]);
6103
6104 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
6105 return pTokenPair;
6106 }
6107
6108 /***********************************************************************
6109 * HTTP_ProcessHeader (internal)
6110 *
6111 * Stuff header into header tables according to <dwModifier>
6112 *
6113 */
6114
6115 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6116
HTTP_ProcessHeader(http_request_t * request,LPCWSTR field,LPCWSTR value,DWORD dwModifier)6117 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
6118 {
6119 LPHTTPHEADERW lphttpHdr;
6120 INT index;
6121 BOOL request_only = !!(dwModifier & HTTP_ADDHDR_FLAG_REQ);
6122 DWORD res = ERROR_SUCCESS;
6123
6124 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
6125
6126 EnterCriticalSection( &request->headers_section );
6127
6128 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
6129 if (index >= 0)
6130 {
6131 lphttpHdr = &request->custHeaders[index];
6132
6133 /* replace existing header if FLAG_REPLACE is given */
6134 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
6135 {
6136 HTTP_DeleteCustomHeader( request, index );
6137
6138 if (value && value[0])
6139 {
6140 HTTPHEADERW hdr;
6141
6142 hdr.lpszField = (LPWSTR)field;
6143 hdr.lpszValue = (LPWSTR)value;
6144 hdr.wFlags = hdr.wCount = 0;
6145
6146 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6147 hdr.wFlags |= HDR_ISREQUEST;
6148
6149 res = HTTP_InsertCustomHeader( request, &hdr );
6150 }
6151
6152 goto out;
6153 }
6154
6155 /* do not add new header if FLAG_ADD_IF_NEW is set */
6156 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
6157 {
6158 res = ERROR_HTTP_INVALID_HEADER; /* FIXME */
6159 goto out;
6160 }
6161
6162 /* handle appending to existing header */
6163 if (dwModifier & COALESCEFLAGS)
6164 {
6165 LPWSTR lpsztmp;
6166 WCHAR ch = 0;
6167 INT len = 0;
6168 INT origlen = lstrlenW(lphttpHdr->lpszValue);
6169 INT valuelen = lstrlenW(value);
6170
6171 /* FIXME: Should it really clear HDR_ISREQUEST? */
6172 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6173 lphttpHdr->wFlags |= HDR_ISREQUEST;
6174 else
6175 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
6176
6177 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6178 {
6179 ch = ',';
6180 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6181 }
6182 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6183 {
6184 ch = ';';
6185 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6186 }
6187
6188 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6189
6190 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6191 if (lpsztmp)
6192 {
6193 lphttpHdr->lpszValue = lpsztmp;
6194 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6195 if (ch > 0)
6196 {
6197 lphttpHdr->lpszValue[origlen] = ch;
6198 origlen++;
6199 lphttpHdr->lpszValue[origlen] = ' ';
6200 origlen++;
6201 }
6202
6203 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6204 lphttpHdr->lpszValue[len] = '\0';
6205 }
6206 else
6207 {
6208 WARN("heap_realloc (%d bytes) failed\n",len+1);
6209 res = ERROR_OUTOFMEMORY;
6210 }
6211
6212 goto out;
6213 }
6214 }
6215
6216 /* FIXME: What about other combinations? */
6217 if ((dwModifier & ~HTTP_ADDHDR_FLAG_REQ) == HTTP_ADDHDR_FLAG_REPLACE)
6218 {
6219 res = ERROR_HTTP_HEADER_NOT_FOUND;
6220 goto out;
6221 }
6222
6223 /* FIXME: What if value == ""? */
6224 if (value)
6225 {
6226 HTTPHEADERW hdr;
6227
6228 hdr.lpszField = (LPWSTR)field;
6229 hdr.lpszValue = (LPWSTR)value;
6230 hdr.wFlags = hdr.wCount = 0;
6231
6232 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
6233 hdr.wFlags |= HDR_ISREQUEST;
6234
6235 res = HTTP_InsertCustomHeader( request, &hdr );
6236 goto out;
6237 }
6238
6239 /* FIXME: What if value == NULL? */
6240 out:
6241 TRACE("<-- %d\n", res);
6242 LeaveCriticalSection( &request->headers_section );
6243 return res;
6244 }
6245
6246 /***********************************************************************
6247 * HTTP_GetCustomHeaderIndex (internal)
6248 *
6249 * Return index of custom header from header array
6250 * Headers section must be held
6251 */
HTTP_GetCustomHeaderIndex(http_request_t * request,LPCWSTR lpszField,int requested_index,BOOL request_only)6252 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6253 int requested_index, BOOL request_only)
6254 {
6255 DWORD index;
6256
6257 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6258
6259 for (index = 0; index < request->nCustHeaders; index++)
6260 {
6261 if (wcsicmp(request->custHeaders[index].lpszField, lpszField))
6262 continue;
6263
6264 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6265 continue;
6266
6267 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6268 continue;
6269
6270 if (requested_index == 0)
6271 break;
6272 requested_index --;
6273 }
6274
6275 if (index >= request->nCustHeaders)
6276 index = -1;
6277
6278 TRACE("Return: %d\n", index);
6279 return index;
6280 }
6281
6282
6283 /***********************************************************************
6284 * HTTP_InsertCustomHeader (internal)
6285 *
6286 * Insert header into array
6287 * Headers section must be held
6288 */
HTTP_InsertCustomHeader(http_request_t * request,LPHTTPHEADERW lpHdr)6289 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6290 {
6291 INT count;
6292 LPHTTPHEADERW lph = NULL;
6293
6294 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6295 count = request->nCustHeaders + 1;
6296 if (count > 1)
6297 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6298 else
6299 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6300
6301 if (!lph)
6302 return ERROR_OUTOFMEMORY;
6303
6304 request->custHeaders = lph;
6305 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6306 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6307 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6308 request->custHeaders[count-1].wCount= lpHdr->wCount;
6309 request->nCustHeaders++;
6310
6311 return ERROR_SUCCESS;
6312 }
6313
6314
6315 /***********************************************************************
6316 * HTTP_DeleteCustomHeader (internal)
6317 *
6318 * Delete header from array
6319 * If this function is called, the index may change.
6320 * Headers section must be held
6321 */
HTTP_DeleteCustomHeader(http_request_t * request,DWORD index)6322 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6323 {
6324 if( request->nCustHeaders <= 0 )
6325 return FALSE;
6326 if( index >= request->nCustHeaders )
6327 return FALSE;
6328 request->nCustHeaders--;
6329
6330 heap_free(request->custHeaders[index].lpszField);
6331 heap_free(request->custHeaders[index].lpszValue);
6332
6333 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6334 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6335 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6336
6337 return TRUE;
6338 }
6339
6340
6341 /***********************************************************************
6342 * IsHostInProxyBypassList (@)
6343 *
6344 * Undocumented
6345 *
6346 */
IsHostInProxyBypassList(DWORD flags,LPCSTR szHost,DWORD length)6347 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6348 {
6349 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6350 return FALSE;
6351 }
6352