xref: /reactos/dll/win32/winhttp/session.c (revision 682f85ad)
1 /*
2  * Copyright 2008 Hans Leidekker for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "config.h"
20 #include <stdarg.h>
21 #include <stdlib.h>
22 
23 #ifdef HAVE_CORESERVICES_CORESERVICES_H
24 #define GetCurrentThread MacGetCurrentThread
25 #define LoadResource MacLoadResource
26 #include <CoreServices/CoreServices.h>
27 #undef GetCurrentThread
28 #undef LoadResource
29 #endif
30 
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winsock2.h"
34 #include "ws2ipdef.h"
35 #include "ws2tcpip.h"
36 #include "winhttp.h"
37 #include "winreg.h"
38 #include "wine/winternl.h"
39 #define COBJMACROS
40 #include "ole2.h"
41 #include "dispex.h"
42 #include "activscp.h"
43 
44 #include "wine/debug.h"
45 #include "winhttp_private.h"
46 
47 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
48 
49 #define DEFAULT_RESOLVE_TIMEOUT             0
50 #define DEFAULT_CONNECT_TIMEOUT             20000
51 #define DEFAULT_SEND_TIMEOUT                30000
52 #define DEFAULT_RECEIVE_TIMEOUT             30000
53 #define DEFAULT_RECEIVE_RESPONSE_TIMEOUT    ~0u
54 
55 void send_callback( struct object_header *hdr, DWORD status, void *info, DWORD buflen )
56 {
57     if (hdr->callback && (hdr->notify_mask & status))
58     {
59         TRACE("%p, 0x%08x, %p, %u\n", hdr, status, info, buflen);
60         hdr->callback( hdr->handle, hdr->context, status, info, buflen );
61         TRACE("returning from 0x%08x callback\n", status);
62     }
63 }
64 
65 /***********************************************************************
66  *          WinHttpCheckPlatform (winhttp.@)
67  */
68 BOOL WINAPI WinHttpCheckPlatform( void )
69 {
70     TRACE("\n");
71     return TRUE;
72 }
73 
74 /***********************************************************************
75  *          session_destroy (internal)
76  */
77 static void session_destroy( struct object_header *hdr )
78 {
79     struct session *session = (struct session *)hdr;
80 
81     TRACE("%p\n", session);
82 
83     if (session->unload_event) SetEvent( session->unload_event );
84     destroy_cookies( session );
85 
86     session->cs.DebugInfo->Spare[0] = 0;
87     DeleteCriticalSection( &session->cs );
88     heap_free( session->agent );
89     heap_free( session->proxy_server );
90     heap_free( session->proxy_bypass );
91     heap_free( session->proxy_username );
92     heap_free( session->proxy_password );
93     heap_free( session );
94 }
95 
96 static BOOL session_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
97 {
98     struct session *session = (struct session *)hdr;
99 
100     switch (option)
101     {
102     case WINHTTP_OPTION_REDIRECT_POLICY:
103     {
104         if (!buffer || *buflen < sizeof(DWORD))
105         {
106             *buflen = sizeof(DWORD);
107             SetLastError( ERROR_INSUFFICIENT_BUFFER );
108             return FALSE;
109         }
110 
111         *(DWORD *)buffer = hdr->redirect_policy;
112         *buflen = sizeof(DWORD);
113         return TRUE;
114     }
115     case WINHTTP_OPTION_RESOLVE_TIMEOUT:
116         *(DWORD *)buffer = session->resolve_timeout;
117         *buflen = sizeof(DWORD);
118         return TRUE;
119 
120     case WINHTTP_OPTION_CONNECT_TIMEOUT:
121         *(DWORD *)buffer = session->connect_timeout;
122         *buflen = sizeof(DWORD);
123         return TRUE;
124 
125     case WINHTTP_OPTION_SEND_TIMEOUT:
126         *(DWORD *)buffer = session->send_timeout;
127         *buflen = sizeof(DWORD);
128         return TRUE;
129 
130     case WINHTTP_OPTION_RECEIVE_TIMEOUT:
131         *(DWORD *)buffer = session->receive_timeout;
132         *buflen = sizeof(DWORD);
133         return TRUE;
134 
135     case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
136         *(DWORD *)buffer = session->receive_response_timeout;
137         *buflen = sizeof(DWORD);
138         return TRUE;
139 
140     default:
141         FIXME("unimplemented option %u\n", option);
142         SetLastError( ERROR_INVALID_PARAMETER );
143         return FALSE;
144     }
145 }
146 
147 static BOOL session_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
148 {
149     struct session *session = (struct session *)hdr;
150 
151     switch (option)
152     {
153     case WINHTTP_OPTION_PROXY:
154     {
155         WINHTTP_PROXY_INFO *pi = buffer;
156 
157         FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass));
158         return TRUE;
159     }
160     case WINHTTP_OPTION_REDIRECT_POLICY:
161     {
162         DWORD policy;
163 
164         if (buflen != sizeof(policy))
165         {
166             SetLastError( ERROR_INSUFFICIENT_BUFFER );
167             return FALSE;
168         }
169 
170         policy = *(DWORD *)buffer;
171         TRACE("0x%x\n", policy);
172         hdr->redirect_policy = policy;
173         return TRUE;
174     }
175     case WINHTTP_OPTION_SECURE_PROTOCOLS:
176     {
177         if (buflen != sizeof(session->secure_protocols))
178         {
179             SetLastError( ERROR_INSUFFICIENT_BUFFER );
180             return FALSE;
181         }
182         EnterCriticalSection( &session->cs );
183         session->secure_protocols = *(DWORD *)buffer;
184         LeaveCriticalSection( &session->cs );
185         TRACE("0x%x\n", session->secure_protocols);
186         return TRUE;
187     }
188     case WINHTTP_OPTION_DISABLE_FEATURE:
189         SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
190         return FALSE;
191 
192     case WINHTTP_OPTION_RESOLVE_TIMEOUT:
193         session->resolve_timeout = *(DWORD *)buffer;
194         return TRUE;
195 
196     case WINHTTP_OPTION_CONNECT_TIMEOUT:
197         session->connect_timeout = *(DWORD *)buffer;
198         return TRUE;
199 
200     case WINHTTP_OPTION_SEND_TIMEOUT:
201         session->send_timeout = *(DWORD *)buffer;
202         return TRUE;
203 
204     case WINHTTP_OPTION_RECEIVE_TIMEOUT:
205         session->receive_timeout = *(DWORD *)buffer;
206         return TRUE;
207 
208     case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
209         session->receive_response_timeout = *(DWORD *)buffer;
210         return TRUE;
211 
212     case WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH:
213         session->passport_flags = *(DWORD *)buffer;
214         return TRUE;
215 
216     case WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT:
217         TRACE("WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT: %p\n", *(HANDLE *)buffer);
218         session->unload_event = *(HANDLE *)buffer;
219         return TRUE;
220 
221     case WINHTTP_OPTION_MAX_CONNS_PER_SERVER:
222         FIXME("WINHTTP_OPTION_MAX_CONNS_PER_SERVER: %d\n", *(DWORD *)buffer);
223         return TRUE;
224 
225     case WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER:
226         FIXME("WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER: %d\n", *(DWORD *)buffer);
227         return TRUE;
228 
229     default:
230         FIXME("unimplemented option %u\n", option);
231         SetLastError( ERROR_WINHTTP_INVALID_OPTION );
232         return FALSE;
233     }
234 }
235 
236 static const struct object_vtbl session_vtbl =
237 {
238     session_destroy,
239     session_query_option,
240     session_set_option
241 };
242 
243 #ifdef __REACTOS__
244 void winsock_init(void);
245 #endif
246 
247 /***********************************************************************
248  *          WinHttpOpen (winhttp.@)
249  */
250 HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWSTR bypass, DWORD flags )
251 {
252     struct session *session;
253     HINTERNET handle = NULL;
254 
255     TRACE("%s, %u, %s, %s, 0x%08x\n", debugstr_w(agent), access, debugstr_w(proxy), debugstr_w(bypass), flags);
256 
257     if (!(session = heap_alloc_zero( sizeof(struct session) ))) return NULL;
258 
259     session->hdr.type = WINHTTP_HANDLE_TYPE_SESSION;
260     session->hdr.vtbl = &session_vtbl;
261     session->hdr.flags = flags;
262     session->hdr.refs = 1;
263     session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
264     list_init( &session->hdr.children );
265     session->resolve_timeout = DEFAULT_RESOLVE_TIMEOUT;
266     session->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
267     session->send_timeout = DEFAULT_SEND_TIMEOUT;
268     session->receive_timeout = DEFAULT_RECEIVE_TIMEOUT;
269     session->receive_response_timeout = DEFAULT_RECEIVE_RESPONSE_TIMEOUT;
270     list_init( &session->cookie_cache );
271     InitializeCriticalSection( &session->cs );
272     session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": session.cs");
273 
274     if (agent && !(session->agent = strdupW( agent ))) goto end;
275     if (access == WINHTTP_ACCESS_TYPE_DEFAULT_PROXY)
276     {
277         WINHTTP_PROXY_INFO info;
278 
279         WinHttpGetDefaultProxyConfiguration( &info );
280         session->access = info.dwAccessType;
281         if (info.lpszProxy && !(session->proxy_server = strdupW( info.lpszProxy )))
282         {
283             GlobalFree( (LPWSTR)info.lpszProxy );
284             GlobalFree( (LPWSTR)info.lpszProxyBypass );
285             goto end;
286         }
287         if (info.lpszProxyBypass && !(session->proxy_bypass = strdupW( info.lpszProxyBypass )))
288         {
289             GlobalFree( (LPWSTR)info.lpszProxy );
290             GlobalFree( (LPWSTR)info.lpszProxyBypass );
291             goto end;
292         }
293     }
294     else if (access == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
295     {
296         session->access = access;
297         if (proxy && !(session->proxy_server = strdupW( proxy ))) goto end;
298         if (bypass && !(session->proxy_bypass = strdupW( bypass ))) goto end;
299     }
300 
301     if (!(handle = alloc_handle( &session->hdr ))) goto end;
302     session->hdr.handle = handle;
303 
304 #ifdef __REACTOS__
305     winsock_init();
306 #endif
307 
308 end:
309     release_object( &session->hdr );
310     TRACE("returning %p\n", handle);
311     if (handle) SetLastError( ERROR_SUCCESS );
312     return handle;
313 }
314 
315 /***********************************************************************
316  *          connect_destroy (internal)
317  */
318 static void connect_destroy( struct object_header *hdr )
319 {
320     struct connect *connect = (struct connect *)hdr;
321 
322     TRACE("%p\n", connect);
323 
324     release_object( &connect->session->hdr );
325 
326     heap_free( connect->hostname );
327     heap_free( connect->servername );
328     heap_free( connect->username );
329     heap_free( connect->password );
330     heap_free( connect );
331 }
332 
333 static BOOL connect_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
334 {
335     struct connect *connect = (struct connect *)hdr;
336 
337     switch (option)
338     {
339     case WINHTTP_OPTION_PARENT_HANDLE:
340     {
341         if (!buffer || *buflen < sizeof(HINTERNET))
342         {
343             *buflen = sizeof(HINTERNET);
344             SetLastError( ERROR_INSUFFICIENT_BUFFER );
345             return FALSE;
346         }
347 
348         *(HINTERNET *)buffer = ((struct object_header *)connect->session)->handle;
349         *buflen = sizeof(HINTERNET);
350         return TRUE;
351     }
352     case WINHTTP_OPTION_RESOLVE_TIMEOUT:
353         *(DWORD *)buffer = connect->session->resolve_timeout;
354         *buflen = sizeof(DWORD);
355         return TRUE;
356 
357     case WINHTTP_OPTION_CONNECT_TIMEOUT:
358         *(DWORD *)buffer = connect->session->connect_timeout;
359         *buflen = sizeof(DWORD);
360         return TRUE;
361 
362     case WINHTTP_OPTION_SEND_TIMEOUT:
363         *(DWORD *)buffer = connect->session->send_timeout;
364         *buflen = sizeof(DWORD);
365         return TRUE;
366 
367     case WINHTTP_OPTION_RECEIVE_TIMEOUT:
368         *(DWORD *)buffer = connect->session->receive_timeout;
369         *buflen = sizeof(DWORD);
370         return TRUE;
371 
372     case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
373         *(DWORD *)buffer = connect->session->receive_response_timeout;
374         *buflen = sizeof(DWORD);
375         return TRUE;
376 
377     default:
378         FIXME("unimplemented option %u\n", option);
379         SetLastError( ERROR_INVALID_PARAMETER );
380         return FALSE;
381     }
382 }
383 
384 static const struct object_vtbl connect_vtbl =
385 {
386     connect_destroy,
387     connect_query_option,
388     NULL
389 };
390 
391 static BOOL domain_matches(LPCWSTR server, LPCWSTR domain)
392 {
393     static const WCHAR localW[] = { '<','l','o','c','a','l','>',0 };
394     BOOL ret = FALSE;
395 
396     if (!strcmpiW( domain, localW ) && !strchrW( server, '.' ))
397         ret = TRUE;
398     else if (*domain == '*')
399     {
400         if (domain[1] == '.')
401         {
402             LPCWSTR dot;
403 
404             /* For a hostname to match a wildcard, the last domain must match
405              * the wildcard exactly.  E.g. if the wildcard is *.a.b, and the
406              * hostname is www.foo.a.b, it matches, but a.b does not.
407              */
408             dot = strchrW( server, '.' );
409             if (dot)
410             {
411                 int len = strlenW( dot + 1 );
412 
413                 if (len > strlenW( domain + 2 ))
414                 {
415                     LPCWSTR ptr;
416 
417                     /* The server's domain is longer than the wildcard, so it
418                      * could be a subdomain.  Compare the last portion of the
419                      * server's domain.
420                      */
421                     ptr = dot + len + 1 - strlenW( domain + 2 );
422                     if (!strcmpiW( ptr, domain + 2 ))
423                     {
424                         /* This is only a match if the preceding character is
425                          * a '.', i.e. that it is a matching domain.  E.g.
426                          * if domain is '*.b.c' and server is 'www.ab.c' they
427                          * do not match.
428                          */
429                         ret = *(ptr - 1) == '.';
430                     }
431                 }
432                 else
433                     ret = !strcmpiW( dot + 1, domain + 2 );
434             }
435         }
436     }
437     else
438         ret = !strcmpiW( server, domain );
439     return ret;
440 }
441 
442 /* Matches INTERNET_MAX_HOST_NAME_LENGTH in wininet.h, also RFC 1035 */
443 #define MAX_HOST_NAME_LENGTH 256
444 
445 static BOOL should_bypass_proxy(struct session *session, LPCWSTR server)
446 {
447     LPCWSTR ptr;
448     BOOL ret = FALSE;
449 
450     if (!session->proxy_bypass) return FALSE;
451     ptr = session->proxy_bypass;
452     do {
453         LPCWSTR tmp = ptr;
454 
455         ptr = strchrW( ptr, ';' );
456         if (!ptr)
457             ptr = strchrW( tmp, ' ' );
458         if (ptr)
459         {
460             if (ptr - tmp < MAX_HOST_NAME_LENGTH)
461             {
462                 WCHAR domain[MAX_HOST_NAME_LENGTH];
463 
464                 memcpy( domain, tmp, (ptr - tmp) * sizeof(WCHAR) );
465                 domain[ptr - tmp] = 0;
466                 ret = domain_matches( server, domain );
467             }
468             ptr += 1;
469         }
470         else if (*tmp)
471             ret = domain_matches( server, tmp );
472     } while (ptr && !ret);
473     return ret;
474 }
475 
476 BOOL set_server_for_hostname( struct connect *connect, const WCHAR *server, INTERNET_PORT port )
477 {
478     struct session *session = connect->session;
479     BOOL ret = TRUE;
480 
481     if (session->proxy_server && !should_bypass_proxy(session, server))
482     {
483         LPCWSTR colon;
484 
485         if ((colon = strchrW( session->proxy_server, ':' )))
486         {
487             if (!connect->servername || strncmpiW( connect->servername,
488                 session->proxy_server, colon - session->proxy_server - 1 ))
489             {
490                 heap_free( connect->servername );
491                 connect->resolved = FALSE;
492                 if (!(connect->servername = heap_alloc(
493                     (colon - session->proxy_server + 1) * sizeof(WCHAR) )))
494                 {
495                     ret = FALSE;
496                     goto end;
497                 }
498                 memcpy( connect->servername, session->proxy_server,
499                     (colon - session->proxy_server) * sizeof(WCHAR) );
500                 connect->servername[colon - session->proxy_server] = 0;
501                 if (*(colon + 1))
502                     connect->serverport = atoiW( colon + 1 );
503                 else
504                     connect->serverport = INTERNET_DEFAULT_PORT;
505             }
506         }
507         else
508         {
509             if (!connect->servername || strcmpiW( connect->servername,
510                 session->proxy_server ))
511             {
512                 heap_free( connect->servername );
513                 connect->resolved = FALSE;
514                 if (!(connect->servername = strdupW( session->proxy_server )))
515                 {
516                     ret = FALSE;
517                     goto end;
518                 }
519                 connect->serverport = INTERNET_DEFAULT_PORT;
520             }
521         }
522     }
523     else if (server)
524     {
525         heap_free( connect->servername );
526         connect->resolved = FALSE;
527         if (!(connect->servername = strdupW( server )))
528         {
529             ret = FALSE;
530             goto end;
531         }
532         connect->serverport = port;
533     }
534 end:
535     return ret;
536 }
537 
538 /***********************************************************************
539  *          WinHttpConnect (winhttp.@)
540  */
541 HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PORT port, DWORD reserved )
542 {
543     struct connect *connect;
544     struct session *session;
545     HINTERNET hconnect = NULL;
546 
547     TRACE("%p, %s, %u, %x\n", hsession, debugstr_w(server), port, reserved);
548 
549     if (!server)
550     {
551         SetLastError( ERROR_INVALID_PARAMETER );
552         return NULL;
553     }
554     if (!(session = (struct session *)grab_object( hsession )))
555     {
556         SetLastError( ERROR_INVALID_HANDLE );
557         return NULL;
558     }
559     if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION)
560     {
561         release_object( &session->hdr );
562         SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
563         return NULL;
564     }
565     if (!(connect = heap_alloc_zero( sizeof(struct connect) )))
566     {
567         release_object( &session->hdr );
568         return NULL;
569     }
570     connect->hdr.type = WINHTTP_HANDLE_TYPE_CONNECT;
571     connect->hdr.vtbl = &connect_vtbl;
572     connect->hdr.refs = 1;
573     connect->hdr.flags = session->hdr.flags;
574     connect->hdr.callback = session->hdr.callback;
575     connect->hdr.notify_mask = session->hdr.notify_mask;
576     connect->hdr.context = session->hdr.context;
577     connect->hdr.redirect_policy = session->hdr.redirect_policy;
578     list_init( &connect->hdr.children );
579 
580     addref_object( &session->hdr );
581     connect->session = session;
582     list_add_head( &session->hdr.children, &connect->hdr.entry );
583 
584     if (!(connect->hostname = strdupW( server ))) goto end;
585     connect->hostport = port;
586     if (!set_server_for_hostname( connect, server, port )) goto end;
587 
588     if (!(hconnect = alloc_handle( &connect->hdr ))) goto end;
589     connect->hdr.handle = hconnect;
590 
591     send_callback( &session->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hconnect, sizeof(hconnect) );
592 
593 end:
594     release_object( &connect->hdr );
595     release_object( &session->hdr );
596     TRACE("returning %p\n", hconnect);
597     if (hconnect) SetLastError( ERROR_SUCCESS );
598     return hconnect;
599 }
600 
601 /***********************************************************************
602  *          request_destroy (internal)
603  */
604 static void request_destroy( struct object_header *hdr )
605 {
606     struct request *request = (struct request *)hdr;
607     unsigned int i, j;
608 
609     TRACE("%p\n", request);
610 
611 #ifdef __REACTOS__
612     if (request->task_thread)
613 #else
614     if (request->task_proc_running)
615 #endif
616     {
617         /* Signal to the task proc to quit. It will call this again when it does. */
618 #ifdef __REACTOS__
619         HANDLE thread = request->task_thread;
620         request->task_thread = 0;
621         SetEvent( request->task_cancel );
622         CloseHandle( thread );
623 #else
624         request->task_proc_running = FALSE;
625         SetEvent( request->task_cancel );
626 #endif
627         return;
628     }
629     release_object( &request->connect->hdr );
630 
631     if (request->cred_handle_initialized) FreeCredentialsHandle( &request->cred_handle );
632     CertFreeCertificateContext( request->server_cert );
633     CertFreeCertificateContext( request->client_cert );
634 
635     destroy_authinfo( request->authinfo );
636     destroy_authinfo( request->proxy_authinfo );
637 
638     heap_free( request->verb );
639     heap_free( request->path );
640     heap_free( request->version );
641     heap_free( request->raw_headers );
642     heap_free( request->status_text );
643     for (i = 0; i < request->num_headers; i++)
644     {
645         heap_free( request->headers[i].field );
646         heap_free( request->headers[i].value );
647     }
648     heap_free( request->headers );
649     for (i = 0; i < TARGET_MAX; i++)
650     {
651         for (j = 0; j < SCHEME_MAX; j++)
652         {
653             heap_free( request->creds[i][j].username );
654             heap_free( request->creds[i][j].password );
655         }
656     }
657     heap_free( request );
658 }
659 
660 static void str_to_buffer( WCHAR *buffer, const WCHAR *str, LPDWORD buflen )
661 {
662     int len = 0;
663     if (str) len = strlenW( str );
664     if (buffer && *buflen > len)
665     {
666         if (str) memcpy( buffer, str, len * sizeof(WCHAR) );
667         buffer[len] = 0;
668     }
669     *buflen = len * sizeof(WCHAR);
670 }
671 
672 static WCHAR *blob_to_str( DWORD encoding, CERT_NAME_BLOB *blob )
673 {
674     WCHAR *ret;
675     DWORD size, format = CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG;
676 
677     size = CertNameToStrW( encoding, blob, format, NULL, 0 );
678     if ((ret = LocalAlloc( 0, size * sizeof(WCHAR) )))
679         CertNameToStrW( encoding, blob, format, ret, size );
680 
681     return ret;
682 }
683 
684 static BOOL copy_sockaddr( const struct sockaddr *addr, SOCKADDR_STORAGE *addr_storage )
685 {
686     switch (addr->sa_family)
687     {
688     case AF_INET:
689     {
690         struct sockaddr_in *addr_in = (struct sockaddr_in *)addr_storage;
691 
692         memcpy( addr_in, addr, sizeof(*addr_in) );
693         memset( addr_in + 1, 0, sizeof(*addr_storage) - sizeof(*addr_in) );
694         return TRUE;
695     }
696     case AF_INET6:
697     {
698         struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr_storage;
699 
700         memcpy( addr_in6, addr, sizeof(*addr_in6) );
701         memset( addr_in6 + 1, 0, sizeof(*addr_storage) - sizeof(*addr_in6) );
702         return TRUE;
703     }
704     default:
705         ERR("unhandled family %u\n", addr->sa_family);
706         return FALSE;
707     }
708 }
709 
710 static BOOL request_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
711 {
712     struct request *request = (struct request *)hdr;
713 
714     switch (option)
715     {
716     case WINHTTP_OPTION_SECURITY_FLAGS:
717     {
718         DWORD flags;
719         int bits;
720 
721         if (!buffer || *buflen < sizeof(flags))
722         {
723             *buflen = sizeof(flags);
724             SetLastError( ERROR_INSUFFICIENT_BUFFER );
725             return FALSE;
726         }
727 
728         flags = request->security_flags;
729         if (request->netconn)
730         {
731             bits = netconn_get_cipher_strength( request->netconn );
732             if (bits >= 128)
733                 flags |= SECURITY_FLAG_STRENGTH_STRONG;
734             else if (bits >= 56)
735                 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
736             else
737                 flags |= SECURITY_FLAG_STRENGTH_WEAK;
738         }
739         *(DWORD *)buffer = flags;
740         *buflen = sizeof(flags);
741         return TRUE;
742     }
743     case WINHTTP_OPTION_SERVER_CERT_CONTEXT:
744     {
745         const CERT_CONTEXT *cert;
746 
747         if (!buffer || *buflen < sizeof(cert))
748         {
749             *buflen = sizeof(cert);
750             SetLastError( ERROR_INSUFFICIENT_BUFFER );
751             return FALSE;
752         }
753 
754         if (!(cert = CertDuplicateCertificateContext( request->server_cert ))) return FALSE;
755         *(CERT_CONTEXT **)buffer = (CERT_CONTEXT *)cert;
756         *buflen = sizeof(cert);
757         return TRUE;
758     }
759     case WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT:
760     {
761         const CERT_CONTEXT *cert = request->server_cert;
762         const CRYPT_OID_INFO *oidInfo;
763         WINHTTP_CERTIFICATE_INFO *ci = buffer;
764 
765         FIXME("partial stub\n");
766 
767         if (!buffer || *buflen < sizeof(*ci))
768         {
769             *buflen = sizeof(*ci);
770             SetLastError( ERROR_INSUFFICIENT_BUFFER );
771             return FALSE;
772         }
773         if (!cert) return FALSE;
774 
775         ci->ftExpiry = cert->pCertInfo->NotAfter;
776         ci->ftStart  = cert->pCertInfo->NotBefore;
777         ci->lpszSubjectInfo = blob_to_str( cert->dwCertEncodingType, &cert->pCertInfo->Subject );
778         ci->lpszIssuerInfo  = blob_to_str( cert->dwCertEncodingType, &cert->pCertInfo->Issuer );
779         ci->lpszProtocolName      = NULL;
780         oidInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, cert->pCertInfo->SignatureAlgorithm.pszObjId, 0 );
781         if (oidInfo)
782             ci->lpszSignatureAlgName = (LPWSTR)oidInfo->pwszName;
783         else
784             ci->lpszSignatureAlgName  = NULL;
785         ci->lpszEncryptionAlgName = NULL;
786         ci->dwKeySize = request->netconn ? netconn_get_cipher_strength( request->netconn ) : 0;
787 
788         *buflen = sizeof(*ci);
789         return TRUE;
790     }
791     case WINHTTP_OPTION_SECURITY_KEY_BITNESS:
792     {
793         if (!buffer || *buflen < sizeof(DWORD))
794         {
795             *buflen = sizeof(DWORD);
796             SetLastError( ERROR_INSUFFICIENT_BUFFER );
797             return FALSE;
798         }
799 
800         *(DWORD *)buffer = request->netconn ? netconn_get_cipher_strength( request->netconn ) : 0;
801         *buflen = sizeof(DWORD);
802         return TRUE;
803     }
804     case WINHTTP_OPTION_CONNECTION_INFO:
805     {
806         WINHTTP_CONNECTION_INFO *info = buffer;
807         struct sockaddr local;
808         socklen_t len = sizeof(local);
809         const struct sockaddr *remote = (const struct sockaddr *)&request->connect->sockaddr;
810 
811         if (!buffer || *buflen < sizeof(*info))
812         {
813             *buflen = sizeof(*info);
814             SetLastError( ERROR_INSUFFICIENT_BUFFER );
815             return FALSE;
816         }
817         if (!request->netconn)
818         {
819             SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_STATE );
820             return FALSE;
821         }
822         if (getsockname( request->netconn->socket, &local, &len )) return FALSE;
823         if (!copy_sockaddr( &local, &info->LocalAddress )) return FALSE;
824         if (!copy_sockaddr( remote, &info->RemoteAddress )) return FALSE;
825         info->cbSize = sizeof(*info);
826         return TRUE;
827     }
828     case WINHTTP_OPTION_RESOLVE_TIMEOUT:
829         *(DWORD *)buffer = request->resolve_timeout;
830         *buflen = sizeof(DWORD);
831         return TRUE;
832 
833     case WINHTTP_OPTION_CONNECT_TIMEOUT:
834         *(DWORD *)buffer = request->connect_timeout;
835         *buflen = sizeof(DWORD);
836         return TRUE;
837 
838     case WINHTTP_OPTION_SEND_TIMEOUT:
839         *(DWORD *)buffer = request->send_timeout;
840         *buflen = sizeof(DWORD);
841         return TRUE;
842 
843     case WINHTTP_OPTION_RECEIVE_TIMEOUT:
844         *(DWORD *)buffer = request->receive_timeout;
845         *buflen = sizeof(DWORD);
846         return TRUE;
847 
848     case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
849         *(DWORD *)buffer = request->receive_response_timeout;
850         *buflen = sizeof(DWORD);
851         return TRUE;
852 
853     case WINHTTP_OPTION_USERNAME:
854         str_to_buffer( buffer, request->connect->username, buflen );
855         return TRUE;
856 
857     case WINHTTP_OPTION_PASSWORD:
858         str_to_buffer( buffer, request->connect->password, buflen );
859         return TRUE;
860 
861     case WINHTTP_OPTION_PROXY_USERNAME:
862         str_to_buffer( buffer, request->connect->session->proxy_username, buflen );
863         return TRUE;
864 
865     case WINHTTP_OPTION_PROXY_PASSWORD:
866         str_to_buffer( buffer, request->connect->session->proxy_password, buflen );
867         return TRUE;
868 
869     default:
870         FIXME("unimplemented option %u\n", option);
871         SetLastError( ERROR_INVALID_PARAMETER );
872         return FALSE;
873     }
874 }
875 
876 static WCHAR *buffer_to_str( WCHAR *buffer, DWORD buflen )
877 {
878     WCHAR *ret;
879     if ((ret = heap_alloc( (buflen + 1) * sizeof(WCHAR))))
880     {
881         memcpy( ret, buffer, buflen * sizeof(WCHAR) );
882         ret[buflen] = 0;
883         return ret;
884     }
885     SetLastError( ERROR_OUTOFMEMORY );
886     return NULL;
887 }
888 
889 static BOOL request_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
890 {
891     struct request *request = (struct request *)hdr;
892 
893     switch (option)
894     {
895     case WINHTTP_OPTION_PROXY:
896     {
897         WINHTTP_PROXY_INFO *pi = buffer;
898 
899         FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass));
900         return TRUE;
901     }
902     case WINHTTP_OPTION_DISABLE_FEATURE:
903     {
904         DWORD disable;
905 
906         if (buflen != sizeof(DWORD))
907         {
908             SetLastError( ERROR_INSUFFICIENT_BUFFER );
909             return FALSE;
910         }
911 
912         disable = *(DWORD *)buffer;
913         TRACE("0x%x\n", disable);
914         hdr->disable_flags |= disable;
915         return TRUE;
916     }
917     case WINHTTP_OPTION_AUTOLOGON_POLICY:
918     {
919         DWORD policy;
920 
921         if (buflen != sizeof(DWORD))
922         {
923             SetLastError( ERROR_INSUFFICIENT_BUFFER );
924             return FALSE;
925         }
926 
927         policy = *(DWORD *)buffer;
928         TRACE("0x%x\n", policy);
929         hdr->logon_policy = policy;
930         return TRUE;
931     }
932     case WINHTTP_OPTION_REDIRECT_POLICY:
933     {
934         DWORD policy;
935 
936         if (buflen != sizeof(DWORD))
937         {
938             SetLastError( ERROR_INSUFFICIENT_BUFFER );
939             return FALSE;
940         }
941 
942         policy = *(DWORD *)buffer;
943         TRACE("0x%x\n", policy);
944         hdr->redirect_policy = policy;
945         return TRUE;
946     }
947     case WINHTTP_OPTION_SECURITY_FLAGS:
948     {
949         DWORD flags;
950         static const DWORD accepted = SECURITY_FLAG_IGNORE_CERT_CN_INVALID   |
951                                       SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
952                                       SECURITY_FLAG_IGNORE_UNKNOWN_CA        |
953                                       SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
954 
955         if (buflen < sizeof(DWORD))
956         {
957             SetLastError( ERROR_INSUFFICIENT_BUFFER );
958             return FALSE;
959         }
960         flags = *(DWORD *)buffer;
961         TRACE("0x%x\n", flags);
962         if (flags && (flags & ~accepted))
963         {
964             SetLastError( ERROR_INVALID_PARAMETER );
965             return FALSE;
966         }
967         request->security_flags = flags;
968         return TRUE;
969     }
970     case WINHTTP_OPTION_RESOLVE_TIMEOUT:
971         request->resolve_timeout = *(DWORD *)buffer;
972         return TRUE;
973 
974     case WINHTTP_OPTION_CONNECT_TIMEOUT:
975         request->connect_timeout = *(DWORD *)buffer;
976         return TRUE;
977 
978     case WINHTTP_OPTION_SEND_TIMEOUT:
979         request->send_timeout = *(DWORD *)buffer;
980         return TRUE;
981 
982     case WINHTTP_OPTION_RECEIVE_TIMEOUT:
983         request->receive_timeout = *(DWORD *)buffer;
984         return TRUE;
985 
986     case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
987         request->receive_response_timeout = *(DWORD *)buffer;
988         return TRUE;
989 
990     case WINHTTP_OPTION_USERNAME:
991     {
992         struct connect *connect = request->connect;
993 
994         heap_free( connect->username );
995         if (!(connect->username = buffer_to_str( buffer, buflen ))) return FALSE;
996         return TRUE;
997     }
998     case WINHTTP_OPTION_PASSWORD:
999     {
1000         struct connect *connect = request->connect;
1001 
1002         heap_free( connect->password );
1003         if (!(connect->password = buffer_to_str( buffer, buflen ))) return FALSE;
1004         return TRUE;
1005     }
1006     case WINHTTP_OPTION_PROXY_USERNAME:
1007     {
1008         struct session *session = request->connect->session;
1009 
1010         heap_free( session->proxy_username );
1011         if (!(session->proxy_username = buffer_to_str( buffer, buflen ))) return FALSE;
1012         return TRUE;
1013     }
1014     case WINHTTP_OPTION_PROXY_PASSWORD:
1015     {
1016         struct session *session = request->connect->session;
1017 
1018         heap_free( session->proxy_password );
1019         if (!(session->proxy_password = buffer_to_str( buffer, buflen ))) return FALSE;
1020         return TRUE;
1021     }
1022     case WINHTTP_OPTION_CLIENT_CERT_CONTEXT:
1023     {
1024         const CERT_CONTEXT *cert;
1025 
1026         if (!(hdr->flags & WINHTTP_FLAG_SECURE))
1027         {
1028             SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_STATE );
1029             return FALSE;
1030         }
1031         if (!buffer)
1032         {
1033             CertFreeCertificateContext( request->client_cert );
1034             request->client_cert = NULL;
1035         }
1036         else if (buflen >= sizeof(cert))
1037         {
1038             if (!(cert = CertDuplicateCertificateContext( buffer ))) return FALSE;
1039             CertFreeCertificateContext( request->client_cert );
1040             request->client_cert = cert;
1041         }
1042         else
1043         {
1044             SetLastError( ERROR_INVALID_PARAMETER );
1045             return FALSE;
1046         }
1047 
1048         if (request->cred_handle_initialized)
1049         {
1050             FreeCredentialsHandle( &request->cred_handle );
1051             request->cred_handle_initialized = FALSE;
1052         }
1053 
1054         return TRUE;
1055     }
1056     case WINHTTP_OPTION_ENABLE_FEATURE:
1057         if(buflen == sizeof( DWORD ) && *(DWORD *)buffer == WINHTTP_ENABLE_SSL_REVOCATION)
1058         {
1059             request->check_revocation = TRUE;
1060             SetLastError( NO_ERROR );
1061             return TRUE;
1062         }
1063         else
1064         {
1065             SetLastError( ERROR_INVALID_PARAMETER );
1066             return FALSE;
1067         }
1068 
1069     case WINHTTP_OPTION_CONNECT_RETRIES:
1070         FIXME("WINHTTP_OPTION_CONNECT_RETRIES\n");
1071         return TRUE;
1072 
1073     default:
1074         FIXME("unimplemented option %u\n", option);
1075         SetLastError( ERROR_WINHTTP_INVALID_OPTION );
1076         return FALSE;
1077     }
1078 }
1079 
1080 static const struct object_vtbl request_vtbl =
1081 {
1082     request_destroy,
1083     request_query_option,
1084     request_set_option
1085 };
1086 
1087 static BOOL add_accept_types_header( struct request *request, const WCHAR **types )
1088 {
1089     static const WCHAR acceptW[] = {'A','c','c','e','p','t',0};
1090     static const DWORD flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA;
1091 
1092     if (!types) return TRUE;
1093     while (*types)
1094     {
1095         if (!process_header( request, acceptW, *types, flags, TRUE )) return FALSE;
1096         types++;
1097     }
1098     return TRUE;
1099 }
1100 
1101 static WCHAR *get_request_path( const WCHAR *object )
1102 {
1103     int len = object ? strlenW(object) : 0;
1104     WCHAR *p, *ret;
1105 
1106     if (!object || object[0] != '/') len++;
1107     if (!(p = ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
1108     if (!object || object[0] != '/') *p++ = '/';
1109     if (object) strcpyW( p, object );
1110     ret[len] = 0;
1111     return ret;
1112 }
1113 
1114 /***********************************************************************
1115  *          WinHttpOpenRequest (winhttp.@)
1116  */
1117 HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR object, LPCWSTR version,
1118                                      LPCWSTR referrer, LPCWSTR *types, DWORD flags )
1119 {
1120     struct request *request;
1121     struct connect *connect;
1122     HINTERNET hrequest = NULL;
1123 
1124     TRACE("%p, %s, %s, %s, %s, %p, 0x%08x\n", hconnect, debugstr_w(verb), debugstr_w(object),
1125           debugstr_w(version), debugstr_w(referrer), types, flags);
1126 
1127     if (types && TRACE_ON(winhttp))
1128     {
1129         const WCHAR **iter;
1130         TRACE("accept types:\n");
1131         for (iter = types; *iter; iter++) TRACE("    %s\n", debugstr_w(*iter));
1132     }
1133 
1134     if (!(connect = (struct connect *)grab_object( hconnect )))
1135     {
1136         SetLastError( ERROR_INVALID_HANDLE );
1137         return NULL;
1138     }
1139     if (connect->hdr.type != WINHTTP_HANDLE_TYPE_CONNECT)
1140     {
1141         release_object( &connect->hdr );
1142         SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1143         return NULL;
1144     }
1145     if (!(request = heap_alloc_zero( sizeof(struct request) )))
1146     {
1147         release_object( &connect->hdr );
1148         return NULL;
1149     }
1150     request->hdr.type = WINHTTP_HANDLE_TYPE_REQUEST;
1151     request->hdr.vtbl = &request_vtbl;
1152     request->hdr.refs = 1;
1153     request->hdr.flags = flags;
1154     request->hdr.callback = connect->hdr.callback;
1155     request->hdr.notify_mask = connect->hdr.notify_mask;
1156     request->hdr.context = connect->hdr.context;
1157     request->hdr.redirect_policy = connect->hdr.redirect_policy;
1158     list_init( &request->hdr.children );
1159     list_init( &request->task_queue );
1160 
1161     addref_object( &connect->hdr );
1162     request->connect = connect;
1163     list_add_head( &connect->hdr.children, &request->hdr.entry );
1164 
1165     request->resolve_timeout = connect->session->resolve_timeout;
1166     request->connect_timeout = connect->session->connect_timeout;
1167     request->send_timeout = connect->session->send_timeout;
1168     request->receive_timeout = connect->session->receive_timeout;
1169     request->receive_response_timeout = connect->session->receive_response_timeout;
1170 
1171     if (!verb || !verb[0]) verb = getW;
1172     if (!(request->verb = strdupW( verb ))) goto end;
1173     if (!(request->path = get_request_path( object ))) goto end;
1174 
1175     if (!version || !version[0]) version = http1_1;
1176     if (!(request->version = strdupW( version ))) goto end;
1177     if (!(add_accept_types_header( request, types ))) goto end;
1178 
1179     if (!(hrequest = alloc_handle( &request->hdr ))) goto end;
1180     request->hdr.handle = hrequest;
1181 
1182     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hrequest, sizeof(hrequest) );
1183 
1184 end:
1185     release_object( &request->hdr );
1186     release_object( &connect->hdr );
1187     TRACE("returning %p\n", hrequest);
1188     if (hrequest) SetLastError( ERROR_SUCCESS );
1189     return hrequest;
1190 }
1191 
1192 /***********************************************************************
1193  *          WinHttpCloseHandle (winhttp.@)
1194  */
1195 BOOL WINAPI WinHttpCloseHandle( HINTERNET handle )
1196 {
1197     struct object_header *hdr;
1198 
1199     TRACE("%p\n", handle);
1200 
1201     if (!(hdr = grab_object( handle )))
1202     {
1203         SetLastError( ERROR_INVALID_HANDLE );
1204         return FALSE;
1205     }
1206     release_object( hdr );
1207     free_handle( handle );
1208     SetLastError( ERROR_SUCCESS );
1209     return TRUE;
1210 }
1211 
1212 static BOOL query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
1213 {
1214     BOOL ret = FALSE;
1215 
1216     if (!buflen)
1217     {
1218         SetLastError( ERROR_INVALID_PARAMETER );
1219         return FALSE;
1220     }
1221 
1222     switch (option)
1223     {
1224     case WINHTTP_OPTION_CONTEXT_VALUE:
1225     {
1226         if (!buffer || *buflen < sizeof(DWORD_PTR))
1227         {
1228             *buflen = sizeof(DWORD_PTR);
1229             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1230             return FALSE;
1231         }
1232 
1233         *(DWORD_PTR *)buffer = hdr->context;
1234         *buflen = sizeof(DWORD_PTR);
1235         return TRUE;
1236     }
1237     default:
1238         if (hdr->vtbl->query_option) ret = hdr->vtbl->query_option( hdr, option, buffer, buflen );
1239         else
1240         {
1241             FIXME("unimplemented option %u\n", option);
1242             SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1243             return FALSE;
1244         }
1245         break;
1246     }
1247     return ret;
1248 }
1249 
1250 /***********************************************************************
1251  *          WinHttpQueryOption (winhttp.@)
1252  */
1253 BOOL WINAPI WinHttpQueryOption( HINTERNET handle, DWORD option, LPVOID buffer, LPDWORD buflen )
1254 {
1255     BOOL ret = FALSE;
1256     struct object_header *hdr;
1257 
1258     TRACE("%p, %u, %p, %p\n", handle, option, buffer, buflen);
1259 
1260     if (!(hdr = grab_object( handle )))
1261     {
1262         SetLastError( ERROR_INVALID_HANDLE );
1263         return FALSE;
1264     }
1265 
1266     ret = query_option( hdr, option, buffer, buflen );
1267 
1268     release_object( hdr );
1269     if (ret) SetLastError( ERROR_SUCCESS );
1270     return ret;
1271 }
1272 
1273 static BOOL set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
1274 {
1275     BOOL ret = TRUE;
1276 
1277     if (!buffer && buflen)
1278     {
1279         SetLastError( ERROR_INVALID_PARAMETER );
1280         return FALSE;
1281     }
1282 
1283     switch (option)
1284     {
1285     case WINHTTP_OPTION_CONTEXT_VALUE:
1286     {
1287         if (buflen != sizeof(DWORD_PTR))
1288         {
1289             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1290             return FALSE;
1291         }
1292 
1293         hdr->context = *(DWORD_PTR *)buffer;
1294         return TRUE;
1295     }
1296     default:
1297         if (hdr->vtbl->set_option) ret = hdr->vtbl->set_option( hdr, option, buffer, buflen );
1298         else
1299         {
1300             FIXME("unimplemented option %u\n", option);
1301             SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1302             return FALSE;
1303         }
1304         break;
1305     }
1306     return ret;
1307 }
1308 
1309 /***********************************************************************
1310  *          WinHttpSetOption (winhttp.@)
1311  */
1312 BOOL WINAPI WinHttpSetOption( HINTERNET handle, DWORD option, LPVOID buffer, DWORD buflen )
1313 {
1314     BOOL ret = FALSE;
1315     struct object_header *hdr;
1316 
1317     TRACE("%p, %u, %p, %u\n", handle, option, buffer, buflen);
1318 
1319     if (!(hdr = grab_object( handle )))
1320     {
1321         SetLastError( ERROR_INVALID_HANDLE );
1322         return FALSE;
1323     }
1324 
1325     ret = set_option( hdr, option, buffer, buflen );
1326 
1327     release_object( hdr );
1328     if (ret) SetLastError( ERROR_SUCCESS );
1329     return ret;
1330 }
1331 
1332 static char *get_computer_name( COMPUTER_NAME_FORMAT format )
1333 {
1334     char *ret;
1335     DWORD size = 0;
1336 
1337     GetComputerNameExA( format, NULL, &size );
1338     if (GetLastError() != ERROR_MORE_DATA) return NULL;
1339     if (!(ret = heap_alloc( size ))) return NULL;
1340     if (!GetComputerNameExA( format, ret, &size ))
1341     {
1342         heap_free( ret );
1343         return NULL;
1344     }
1345     return ret;
1346 }
1347 
1348 static BOOL is_domain_suffix( const char *domain, const char *suffix )
1349 {
1350     int len_domain = strlen( domain ), len_suffix = strlen( suffix );
1351 
1352     if (len_suffix > len_domain) return FALSE;
1353     if (!_strnicmp( domain + len_domain - len_suffix, suffix, -1 )) return TRUE;
1354     return FALSE;
1355 }
1356 
1357 static int reverse_lookup( const struct addrinfo *ai, char *hostname, size_t len )
1358 {
1359     return getnameinfo( ai->ai_addr, ai->ai_addrlen, hostname, len, NULL, 0, 0 );
1360 }
1361 
1362 static WCHAR *build_wpad_url( const char *hostname, const struct addrinfo *ai )
1363 {
1364     static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1365     static const WCHAR wpadW[] = {'/','w','p','a','d','.','d','a','t',0};
1366     char name[NI_MAXHOST];
1367     WCHAR *ret, *p;
1368     int len;
1369 
1370     while (ai && ai->ai_family != AF_INET && ai->ai_family != AF_INET6) ai = ai->ai_next;
1371     if (!ai) return NULL;
1372 
1373     if (!reverse_lookup( ai, name, sizeof(name) )) hostname = name;
1374 
1375     len = strlenW( httpW ) + strlen( hostname ) + strlenW( wpadW );
1376     if (!(ret = p = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) return NULL;
1377     strcpyW( p, httpW );
1378     p += strlenW( httpW );
1379     while (*hostname) { *p++ = *hostname++; }
1380     strcpyW( p, wpadW );
1381     return ret;
1382 }
1383 
1384 static BOOL get_system_proxy_autoconfig_url( char *buf, DWORD buflen )
1385 {
1386 #if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1387     CFDictionaryRef settings = CFNetworkCopySystemProxySettings();
1388     const void *ref;
1389     BOOL ret = FALSE;
1390 
1391     if (!settings) return FALSE;
1392 
1393     if (!(ref = CFDictionaryGetValue( settings, kCFNetworkProxiesProxyAutoConfigURLString )))
1394     {
1395         CFRelease( settings );
1396         return FALSE;
1397     }
1398     if (CFStringGetCString( ref, buf, buflen, kCFStringEncodingASCII ))
1399     {
1400         TRACE( "returning %s\n", debugstr_a(buf) );
1401         ret = TRUE;
1402     }
1403     CFRelease( settings );
1404     return ret;
1405 #else
1406     static BOOL first = TRUE;
1407     if (first)
1408     {
1409         FIXME( "no support on this platform\n" );
1410         first = FALSE;
1411     }
1412     else
1413         TRACE( "no support on this platform\n" );
1414     return FALSE;
1415 #endif
1416 }
1417 
1418 #define INTERNET_MAX_URL_LENGTH 2084
1419 
1420 /***********************************************************************
1421  *          WinHttpDetectAutoProxyConfigUrl (winhttp.@)
1422  */
1423 BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, LPWSTR *url )
1424 {
1425     BOOL ret = FALSE;
1426     char system_url[INTERNET_MAX_URL_LENGTH + 1];
1427 
1428     TRACE("0x%08x, %p\n", flags, url);
1429 
1430     if (!flags || !url)
1431     {
1432         SetLastError( ERROR_INVALID_PARAMETER );
1433         return FALSE;
1434     }
1435     if (get_system_proxy_autoconfig_url( system_url, sizeof(system_url) ))
1436     {
1437         WCHAR *urlW;
1438 
1439         if (!(urlW = strdupAW( system_url ))) return FALSE;
1440         *url = urlW;
1441         SetLastError( ERROR_SUCCESS );
1442         return TRUE;
1443     }
1444     if (flags & WINHTTP_AUTO_DETECT_TYPE_DHCP)
1445     {
1446         static int fixme_shown;
1447         if (!fixme_shown++) FIXME("discovery via DHCP not supported\n");
1448     }
1449     if (flags & WINHTTP_AUTO_DETECT_TYPE_DNS_A)
1450     {
1451         char *fqdn, *domain, *p;
1452 
1453         if (!(fqdn = get_computer_name( ComputerNamePhysicalDnsFullyQualified ))) return FALSE;
1454         if (!(domain = get_computer_name( ComputerNamePhysicalDnsDomain )))
1455         {
1456             heap_free( fqdn );
1457             return FALSE;
1458         }
1459         p = fqdn;
1460         while ((p = strchr( p, '.' )) && is_domain_suffix( p + 1, domain ))
1461         {
1462             struct addrinfo *ai;
1463             char *name;
1464             int res;
1465 
1466             if (!(name = heap_alloc( sizeof("wpad") + strlen(p) )))
1467             {
1468                 heap_free( fqdn );
1469                 heap_free( domain );
1470                 return FALSE;
1471             }
1472             strcpy( name, "wpad" );
1473             strcat( name, p );
1474             res = getaddrinfo( name, NULL, NULL, &ai );
1475             if (!res)
1476             {
1477                 *url = build_wpad_url( name, ai );
1478                 freeaddrinfo( ai );
1479                 if (*url)
1480                 {
1481                     TRACE("returning %s\n", debugstr_w(*url));
1482                     heap_free( name );
1483                     ret = TRUE;
1484                     break;
1485                 }
1486             }
1487             heap_free( name );
1488             p++;
1489         }
1490         heap_free( domain );
1491         heap_free( fqdn );
1492     }
1493     if (!ret)
1494     {
1495         SetLastError( ERROR_WINHTTP_AUTODETECTION_FAILED );
1496         *url = NULL;
1497     }
1498     else SetLastError( ERROR_SUCCESS );
1499     return ret;
1500 }
1501 
1502 static const WCHAR Connections[] = {
1503     'S','o','f','t','w','a','r','e','\\',
1504     'M','i','c','r','o','s','o','f','t','\\',
1505     'W','i','n','d','o','w','s','\\',
1506     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1507     'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
1508     'C','o','n','n','e','c','t','i','o','n','s',0 };
1509 static const WCHAR WinHttpSettings[] = {
1510     'W','i','n','H','t','t','p','S','e','t','t','i','n','g','s',0 };
1511 static const DWORD WINHTTP_SETTINGS_MAGIC = 0x18;
1512 static const DWORD WININET_SETTINGS_MAGIC = 0x46;
1513 static const DWORD PROXY_TYPE_DIRECT         = 1;
1514 static const DWORD PROXY_TYPE_PROXY          = 2;
1515 static const DWORD PROXY_USE_PAC_SCRIPT      = 4;
1516 static const DWORD PROXY_AUTODETECT_SETTINGS = 8;
1517 
1518 struct connection_settings_header
1519 {
1520     DWORD magic;
1521     DWORD unknown; /* always zero? */
1522     DWORD flags;   /* one or more of PROXY_* */
1523 };
1524 
1525 static inline void copy_char_to_wchar_sz(const BYTE *src, DWORD len, WCHAR *dst)
1526 {
1527     const BYTE *begin;
1528 
1529     for (begin = src; src - begin < len; src++, dst++)
1530         *dst = *src;
1531     *dst = 0;
1532 }
1533 
1534 /***********************************************************************
1535  *          WinHttpGetDefaultProxyConfiguration (winhttp.@)
1536  */
1537 BOOL WINAPI WinHttpGetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info )
1538 {
1539     LONG l;
1540     HKEY key;
1541     BOOL got_from_reg = FALSE, direct = TRUE;
1542     char *envproxy;
1543 
1544     TRACE("%p\n", info);
1545 
1546     l = RegOpenKeyExW( HKEY_LOCAL_MACHINE, Connections, 0, KEY_READ, &key );
1547     if (!l)
1548     {
1549         DWORD type, size = 0;
1550 
1551         l = RegQueryValueExW( key, WinHttpSettings, NULL, &type, NULL, &size );
1552         if (!l && type == REG_BINARY &&
1553             size >= sizeof(struct connection_settings_header) + 2 * sizeof(DWORD))
1554         {
1555             BYTE *buf = heap_alloc( size );
1556 
1557             if (buf)
1558             {
1559                 struct connection_settings_header *hdr =
1560                     (struct connection_settings_header *)buf;
1561                 DWORD *len = (DWORD *)(hdr + 1);
1562 
1563                 l = RegQueryValueExW( key, WinHttpSettings, NULL, NULL, buf,
1564                     &size );
1565                 if (!l && hdr->magic == WINHTTP_SETTINGS_MAGIC &&
1566                     hdr->unknown == 0)
1567                 {
1568                     if (hdr->flags & PROXY_TYPE_PROXY)
1569                     {
1570                        BOOL sane = FALSE;
1571                        LPWSTR proxy = NULL;
1572                        LPWSTR proxy_bypass = NULL;
1573 
1574                         /* Sanity-check length of proxy string */
1575                         if ((BYTE *)len - buf + *len <= size)
1576                         {
1577                             sane = TRUE;
1578                             proxy = GlobalAlloc( 0, (*len + 1) * sizeof(WCHAR) );
1579                             if (proxy)
1580                                 copy_char_to_wchar_sz( (BYTE *)(len + 1), *len, proxy );
1581                             len = (DWORD *)((BYTE *)(len + 1) + *len);
1582                         }
1583                         if (sane)
1584                         {
1585                             /* Sanity-check length of proxy bypass string */
1586                             if ((BYTE *)len - buf + *len <= size)
1587                             {
1588                                 proxy_bypass = GlobalAlloc( 0, (*len + 1) * sizeof(WCHAR) );
1589                                 if (proxy_bypass)
1590                                     copy_char_to_wchar_sz( (BYTE *)(len + 1), *len, proxy_bypass );
1591                             }
1592                             else
1593                             {
1594                                 sane = FALSE;
1595                                 GlobalFree( proxy );
1596                                 proxy = NULL;
1597                             }
1598                         }
1599                         info->lpszProxy = proxy;
1600                         info->lpszProxyBypass = proxy_bypass;
1601                         if (sane)
1602                         {
1603                             got_from_reg = TRUE;
1604                             direct = FALSE;
1605                             info->dwAccessType =
1606                                 WINHTTP_ACCESS_TYPE_NAMED_PROXY;
1607                             TRACE("http proxy (from registry) = %s, bypass = %s\n",
1608                                 debugstr_w(info->lpszProxy),
1609                                 debugstr_w(info->lpszProxyBypass));
1610                         }
1611                     }
1612                 }
1613                 heap_free( buf );
1614             }
1615         }
1616         RegCloseKey( key );
1617     }
1618     if (!got_from_reg && (envproxy = getenv( "http_proxy" )))
1619     {
1620         char *colon, *http_proxy = NULL;
1621 
1622         if (!(colon = strchr( envproxy, ':' ))) http_proxy = envproxy;
1623         else
1624         {
1625             if (*(colon + 1) == '/' && *(colon + 2) == '/')
1626             {
1627                 /* It's a scheme, check that it's http */
1628                 if (!strncmp( envproxy, "http://", 7 )) http_proxy = envproxy + 7;
1629                 else WARN("unsupported scheme in $http_proxy: %s\n", envproxy);
1630             }
1631             else http_proxy = envproxy;
1632         }
1633 
1634         if (http_proxy && http_proxy[0])
1635         {
1636             WCHAR *http_proxyW;
1637             int len;
1638 
1639             len = MultiByteToWideChar( CP_UNIXCP, 0, http_proxy, -1, NULL, 0 );
1640             if ((http_proxyW = GlobalAlloc( 0, len * sizeof(WCHAR))))
1641             {
1642                 MultiByteToWideChar( CP_UNIXCP, 0, http_proxy, -1, http_proxyW, len );
1643                 direct = FALSE;
1644                 info->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
1645                 info->lpszProxy = http_proxyW;
1646                 info->lpszProxyBypass = NULL;
1647                 TRACE("http proxy (from environment) = %s\n", debugstr_w(info->lpszProxy));
1648             }
1649         }
1650     }
1651     if (direct)
1652     {
1653         info->dwAccessType    = WINHTTP_ACCESS_TYPE_NO_PROXY;
1654         info->lpszProxy       = NULL;
1655         info->lpszProxyBypass = NULL;
1656     }
1657     SetLastError( ERROR_SUCCESS );
1658     return TRUE;
1659 }
1660 
1661 /***********************************************************************
1662  *          WinHttpGetIEProxyConfigForCurrentUser (winhttp.@)
1663  */
1664 BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *config )
1665 {
1666     static const WCHAR settingsW[] =
1667         {'D','e','f','a','u','l','t','C','o','n','n','e','c','t','i','o','n','S','e','t','t','i','n','g','s',0};
1668     HKEY hkey = NULL;
1669     struct connection_settings_header *hdr = NULL;
1670     DWORD type, offset, len, size = 0;
1671     BOOL ret = FALSE;
1672 
1673     TRACE("%p\n", config);
1674 
1675     if (!config)
1676     {
1677         SetLastError( ERROR_INVALID_PARAMETER );
1678         return FALSE;
1679     }
1680     memset( config, 0, sizeof(*config) );
1681     config->fAutoDetect = TRUE;
1682 
1683     if (RegOpenKeyExW( HKEY_CURRENT_USER, Connections, 0, KEY_READ, &hkey ) ||
1684         RegQueryValueExW( hkey, settingsW, NULL, &type, NULL, &size ) ||
1685         type != REG_BINARY || size < sizeof(struct connection_settings_header))
1686     {
1687         ret = TRUE;
1688         goto done;
1689     }
1690     if (!(hdr = heap_alloc( size ))) goto done;
1691     if (RegQueryValueExW( hkey, settingsW, NULL, &type, (BYTE *)hdr, &size ) ||
1692         hdr->magic != WININET_SETTINGS_MAGIC)
1693     {
1694         ret = TRUE;
1695         goto done;
1696     }
1697 
1698     config->fAutoDetect = (hdr->flags & PROXY_AUTODETECT_SETTINGS) != 0;
1699     offset = sizeof(*hdr);
1700     if (offset + sizeof(DWORD) > size) goto done;
1701     len = *(DWORD *)((char *)hdr + offset);
1702     offset += sizeof(DWORD);
1703     if (len && hdr->flags & PROXY_TYPE_PROXY)
1704     {
1705         if (!(config->lpszProxy = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done;
1706         copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszProxy );
1707     }
1708     offset += len;
1709     if (offset + sizeof(DWORD) > size) goto done;
1710     len = *(DWORD *)((char *)hdr + offset);
1711     offset += sizeof(DWORD);
1712     if (len && (hdr->flags & PROXY_TYPE_PROXY))
1713     {
1714         if (!(config->lpszProxyBypass = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done;
1715         copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszProxyBypass );
1716     }
1717     offset += len;
1718     if (offset + sizeof(DWORD) > size) goto done;
1719     len = *(DWORD *)((char *)hdr + offset);
1720     offset += sizeof(DWORD);
1721     if (len && (hdr->flags & PROXY_USE_PAC_SCRIPT))
1722     {
1723         if (!(config->lpszAutoConfigUrl = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done;
1724         copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszAutoConfigUrl );
1725     }
1726     ret = TRUE;
1727 
1728 done:
1729     RegCloseKey( hkey );
1730     heap_free( hdr );
1731     if (!ret)
1732     {
1733         GlobalFree( config->lpszAutoConfigUrl );
1734         config->lpszAutoConfigUrl = NULL;
1735         GlobalFree( config->lpszProxy );
1736         config->lpszProxy = NULL;
1737         GlobalFree( config->lpszProxyBypass );
1738         config->lpszProxyBypass = NULL;
1739     }
1740     else SetLastError( ERROR_SUCCESS );
1741     return ret;
1742 }
1743 
1744 static BOOL parse_script_result( const char *result, WINHTTP_PROXY_INFO *info )
1745 {
1746     const char *p;
1747     WCHAR *q;
1748     int len;
1749 
1750     info->dwAccessType    = WINHTTP_ACCESS_TYPE_NO_PROXY;
1751     info->lpszProxy       = NULL;
1752     info->lpszProxyBypass = NULL;
1753 
1754     TRACE("%s\n", debugstr_a( result ));
1755 
1756     p = result;
1757     while (*p == ' ') p++;
1758     len = strlen( p );
1759     if (len >= 5 && !_strnicmp( p, "PROXY", sizeof("PROXY") - 1 ))
1760     {
1761         p += 5;
1762         while (*p == ' ') p++;
1763         if (!*p || *p == ';') return TRUE;
1764         if (!(info->lpszProxy = q = strdupAW( p ))) return FALSE;
1765         info->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
1766         for (; *q; q++)
1767         {
1768             if (*q == ' ' || *q == ';')
1769             {
1770                 *q = 0;
1771                 break;
1772             }
1773         }
1774     }
1775     return TRUE;
1776 }
1777 
1778 static char *download_script( const WCHAR *url, DWORD *out_size )
1779 {
1780     static const WCHAR typeW[] = {'*','/','*',0};
1781     static const WCHAR *acceptW[] = {typeW, NULL};
1782     HINTERNET ses, con = NULL, req = NULL;
1783     WCHAR *hostname;
1784     URL_COMPONENTSW uc;
1785     DWORD status, size = sizeof(status), offset, to_read, bytes_read, flags = 0;
1786     char *tmp, *buffer = NULL;
1787 
1788     *out_size = 0;
1789 
1790     memset( &uc, 0, sizeof(uc) );
1791     uc.dwStructSize = sizeof(uc);
1792     uc.dwHostNameLength = -1;
1793     uc.dwUrlPathLength = -1;
1794     if (!WinHttpCrackUrl( url, 0, 0, &uc )) return NULL;
1795     if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) return NULL;
1796     memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
1797     hostname[uc.dwHostNameLength] = 0;
1798 
1799     if (!(ses = WinHttpOpen( NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0 ))) goto done;
1800     if (!(con = WinHttpConnect( ses, hostname, uc.nPort, 0 ))) goto done;
1801     if (uc.nScheme == INTERNET_SCHEME_HTTPS) flags |= WINHTTP_FLAG_SECURE;
1802     if (!(req = WinHttpOpenRequest( con, NULL, uc.lpszUrlPath, NULL, NULL, acceptW, flags ))) goto done;
1803     if (!WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 )) goto done;
1804 
1805     if (!(WinHttpReceiveResponse( req, 0 ))) goto done;
1806     if (!WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status,
1807         &size, NULL ) || status != HTTP_STATUS_OK) goto done;
1808 
1809     size = 4096;
1810     if (!(buffer = heap_alloc( size ))) goto done;
1811     to_read = size;
1812     offset = 0;
1813     for (;;)
1814     {
1815         if (!WinHttpReadData( req, buffer + offset, to_read, &bytes_read )) goto done;
1816         if (!bytes_read) break;
1817         to_read -= bytes_read;
1818         offset += bytes_read;
1819         *out_size += bytes_read;
1820         if (!to_read)
1821         {
1822             to_read = size;
1823             size *= 2;
1824             if (!(tmp = heap_realloc( buffer, size ))) goto done;
1825             buffer = tmp;
1826         }
1827     }
1828 
1829 done:
1830     WinHttpCloseHandle( req );
1831     WinHttpCloseHandle( con );
1832     WinHttpCloseHandle( ses );
1833     heap_free( hostname );
1834     if (!buffer) SetLastError( ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT );
1835     return buffer;
1836 }
1837 
1838 struct AUTO_PROXY_SCRIPT_BUFFER
1839 {
1840     DWORD dwStructSize;
1841     LPSTR lpszScriptBuffer;
1842     DWORD dwScriptBufferSize;
1843 };
1844 
1845 BOOL WINAPI InternetDeInitializeAutoProxyDll(LPSTR, DWORD);
1846 BOOL WINAPI InternetGetProxyInfo(LPCSTR, DWORD, LPSTR, DWORD, LPSTR *, LPDWORD);
1847 BOOL WINAPI InternetInitializeAutoProxyDll(DWORD, LPSTR, LPSTR, void *, struct AUTO_PROXY_SCRIPT_BUFFER *);
1848 
1849 static BOOL run_script( char *script, DWORD size, const WCHAR *url, WINHTTP_PROXY_INFO *info )
1850 {
1851     BOOL ret;
1852     char *result, *urlA;
1853     DWORD len_result;
1854     struct AUTO_PROXY_SCRIPT_BUFFER buffer;
1855     URL_COMPONENTSW uc;
1856 
1857     buffer.dwStructSize = sizeof(buffer);
1858     buffer.lpszScriptBuffer = script;
1859     buffer.dwScriptBufferSize = size;
1860 
1861     if (!(urlA = strdupWA( url ))) return FALSE;
1862     if (!(ret = InternetInitializeAutoProxyDll( 0, NULL, NULL, NULL, &buffer )))
1863     {
1864         heap_free( urlA );
1865         return FALSE;
1866     }
1867 
1868     memset( &uc, 0, sizeof(uc) );
1869     uc.dwStructSize = sizeof(uc);
1870     uc.dwHostNameLength = -1;
1871 
1872     if (WinHttpCrackUrl( url, 0, 0, &uc ))
1873     {
1874         char *hostnameA = strdupWA_sized( uc.lpszHostName, uc.dwHostNameLength );
1875 
1876         if ((ret = InternetGetProxyInfo( urlA, strlen(urlA),
1877                         hostnameA, strlen(hostnameA), &result, &len_result )))
1878         {
1879             ret = parse_script_result( result, info );
1880             heap_free( result );
1881         }
1882 
1883         heap_free( hostnameA );
1884     }
1885     heap_free( urlA );
1886     return InternetDeInitializeAutoProxyDll( NULL, 0 );
1887 }
1888 
1889 /***********************************************************************
1890  *          WinHttpGetProxyForUrl (winhttp.@)
1891  */
1892 BOOL WINAPI WinHttpGetProxyForUrl( HINTERNET hsession, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options,
1893                                    WINHTTP_PROXY_INFO *info )
1894 {
1895     WCHAR *detected_pac_url = NULL;
1896     const WCHAR *pac_url;
1897     struct session *session;
1898     char *script;
1899     DWORD size;
1900     BOOL ret = FALSE;
1901 
1902     TRACE("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info);
1903 
1904     if (!(session = (struct session *)grab_object( hsession )))
1905     {
1906         SetLastError( ERROR_INVALID_HANDLE );
1907         return FALSE;
1908     }
1909     if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION)
1910     {
1911         release_object( &session->hdr );
1912         SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1913         return FALSE;
1914     }
1915     if (!url || !options || !info ||
1916         !(options->dwFlags & (WINHTTP_AUTOPROXY_AUTO_DETECT|WINHTTP_AUTOPROXY_CONFIG_URL)) ||
1917         ((options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) && !options->dwAutoDetectFlags) ||
1918         ((options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) &&
1919          (options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL)))
1920     {
1921         release_object( &session->hdr );
1922         SetLastError( ERROR_INVALID_PARAMETER );
1923         return FALSE;
1924     }
1925     if (options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT &&
1926         !WinHttpDetectAutoProxyConfigUrl( options->dwAutoDetectFlags, &detected_pac_url ))
1927         goto done;
1928 
1929     if (options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL) pac_url = options->lpszAutoConfigUrl;
1930     else pac_url = detected_pac_url;
1931 
1932     if ((script = download_script( pac_url, &size )))
1933     {
1934         ret = run_script( script, size, url, info );
1935         heap_free( script );
1936     }
1937 
1938 done:
1939     GlobalFree( detected_pac_url );
1940     release_object( &session->hdr );
1941     if (ret) SetLastError( ERROR_SUCCESS );
1942     return ret;
1943 }
1944 
1945 /***********************************************************************
1946  *          WinHttpSetDefaultProxyConfiguration (winhttp.@)
1947  */
1948 BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info )
1949 {
1950     LONG l;
1951     HKEY key;
1952     BOOL ret = FALSE;
1953     const WCHAR *src;
1954 
1955     TRACE("%p\n", info);
1956 
1957     if (!info)
1958     {
1959         SetLastError( ERROR_INVALID_PARAMETER );
1960         return FALSE;
1961     }
1962     switch (info->dwAccessType)
1963     {
1964     case WINHTTP_ACCESS_TYPE_NO_PROXY:
1965         break;
1966     case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
1967         if (!info->lpszProxy)
1968         {
1969             SetLastError( ERROR_INVALID_PARAMETER );
1970             return FALSE;
1971         }
1972         /* Only ASCII characters are allowed */
1973         for (src = info->lpszProxy; *src; src++)
1974             if (*src > 0x7f)
1975             {
1976                 SetLastError( ERROR_INVALID_PARAMETER );
1977                 return FALSE;
1978             }
1979         if (info->lpszProxyBypass)
1980         {
1981             for (src = info->lpszProxyBypass; *src; src++)
1982                 if (*src > 0x7f)
1983                 {
1984                     SetLastError( ERROR_INVALID_PARAMETER );
1985                     return FALSE;
1986                 }
1987         }
1988         break;
1989     default:
1990         SetLastError( ERROR_INVALID_PARAMETER );
1991         return FALSE;
1992     }
1993 
1994     l = RegCreateKeyExW( HKEY_LOCAL_MACHINE, Connections, 0, NULL, 0,
1995         KEY_WRITE, NULL, &key, NULL );
1996     if (!l)
1997     {
1998         DWORD size = sizeof(struct connection_settings_header) + 2 * sizeof(DWORD);
1999         BYTE *buf;
2000 
2001         if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
2002         {
2003             size += strlenW( info->lpszProxy );
2004             if (info->lpszProxyBypass)
2005                 size += strlenW( info->lpszProxyBypass );
2006         }
2007         buf = heap_alloc( size );
2008         if (buf)
2009         {
2010             struct connection_settings_header *hdr =
2011                 (struct connection_settings_header *)buf;
2012             DWORD *len = (DWORD *)(hdr + 1);
2013 
2014             hdr->magic = WINHTTP_SETTINGS_MAGIC;
2015             hdr->unknown = 0;
2016             if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
2017             {
2018                 BYTE *dst;
2019 
2020                 hdr->flags = PROXY_TYPE_PROXY;
2021                 *len++ = strlenW( info->lpszProxy );
2022                 for (dst = (BYTE *)len, src = info->lpszProxy; *src;
2023                     src++, dst++)
2024                     *dst = *src;
2025                 len = (DWORD *)dst;
2026                 if (info->lpszProxyBypass)
2027                 {
2028                     *len++ = strlenW( info->lpszProxyBypass );
2029                     for (dst = (BYTE *)len, src = info->lpszProxyBypass; *src;
2030                         src++, dst++)
2031                         *dst = *src;
2032                 }
2033                 else
2034                     *len++ = 0;
2035             }
2036             else
2037             {
2038                 hdr->flags = PROXY_TYPE_DIRECT;
2039                 *len++ = 0;
2040                 *len++ = 0;
2041             }
2042             l = RegSetValueExW( key, WinHttpSettings, 0, REG_BINARY, buf, size );
2043             if (!l)
2044                 ret = TRUE;
2045             heap_free( buf );
2046         }
2047         RegCloseKey( key );
2048     }
2049     if (ret) SetLastError( ERROR_SUCCESS );
2050     return ret;
2051 }
2052 
2053 /***********************************************************************
2054  *          WinHttpSetStatusCallback (winhttp.@)
2055  */
2056 WINHTTP_STATUS_CALLBACK WINAPI WinHttpSetStatusCallback( HINTERNET handle, WINHTTP_STATUS_CALLBACK callback,
2057                                                          DWORD flags, DWORD_PTR reserved )
2058 {
2059     struct object_header *hdr;
2060     WINHTTP_STATUS_CALLBACK ret;
2061 
2062     TRACE("%p, %p, 0x%08x, 0x%lx\n", handle, callback, flags, reserved);
2063 
2064     if (!(hdr = grab_object( handle )))
2065     {
2066         SetLastError( ERROR_INVALID_HANDLE );
2067         return WINHTTP_INVALID_STATUS_CALLBACK;
2068     }
2069     ret = hdr->callback;
2070     hdr->callback = callback;
2071     hdr->notify_mask = flags;
2072 
2073     release_object( hdr );
2074     SetLastError( ERROR_SUCCESS );
2075     return ret;
2076 }
2077 
2078 /***********************************************************************
2079  *          WinHttpSetTimeouts (winhttp.@)
2080  */
2081 BOOL WINAPI WinHttpSetTimeouts( HINTERNET handle, int resolve, int connect, int send, int receive )
2082 {
2083     BOOL ret = TRUE;
2084     struct object_header *hdr;
2085 
2086     TRACE("%p, %d, %d, %d, %d\n", handle, resolve, connect, send, receive);
2087 
2088     if (resolve < -1 || connect < -1 || send < -1 || receive < -1)
2089     {
2090         SetLastError( ERROR_INVALID_PARAMETER );
2091         return FALSE;
2092     }
2093 
2094     if (!(hdr = grab_object( handle )))
2095     {
2096         SetLastError( ERROR_INVALID_HANDLE );
2097         return FALSE;
2098     }
2099 
2100     switch(hdr->type)
2101     {
2102     case WINHTTP_HANDLE_TYPE_REQUEST:
2103     {
2104         struct request *request = (struct request *)hdr;
2105         request->connect_timeout = connect;
2106 
2107         if (resolve < 0) resolve = 0;
2108         request->resolve_timeout = resolve;
2109 
2110         if (send < 0) send = 0;
2111         request->send_timeout = send;
2112 
2113         if (receive < 0) receive = 0;
2114         request->receive_timeout = receive;
2115 
2116         if (request->netconn)
2117         {
2118             if (netconn_set_timeout( request->netconn, TRUE, send )) ret = FALSE;
2119             if (netconn_set_timeout( request->netconn, FALSE, receive )) ret = FALSE;
2120         }
2121         break;
2122     }
2123     case WINHTTP_HANDLE_TYPE_SESSION:
2124     {
2125         struct session *session = (struct session *)hdr;
2126         session->connect_timeout = connect;
2127 
2128         if (resolve < 0) resolve = 0;
2129         session->resolve_timeout = resolve;
2130 
2131         if (send < 0) send = 0;
2132         session->send_timeout = send;
2133 
2134         if (receive < 0) receive = 0;
2135         session->receive_timeout = receive;
2136         break;
2137     }
2138     default:
2139         SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2140         ret = FALSE;
2141     }
2142     release_object( hdr );
2143     if (ret) SetLastError( ERROR_SUCCESS );
2144     return ret;
2145 }
2146 
2147 static const WCHAR wkday[7][4] =
2148     {{'S','u','n', 0}, {'M','o','n', 0}, {'T','u','e', 0}, {'W','e','d', 0},
2149      {'T','h','u', 0}, {'F','r','i', 0}, {'S','a','t', 0}};
2150 static const WCHAR month[12][4] =
2151     {{'J','a','n', 0}, {'F','e','b', 0}, {'M','a','r', 0}, {'A','p','r', 0},
2152      {'M','a','y', 0}, {'J','u','n', 0}, {'J','u','l', 0}, {'A','u','g', 0},
2153      {'S','e','p', 0}, {'O','c','t', 0}, {'N','o','v', 0}, {'D','e','c', 0}};
2154 
2155 /***********************************************************************
2156  *           WinHttpTimeFromSystemTime (WININET.@)
2157  */
2158 BOOL WINAPI WinHttpTimeFromSystemTime( const SYSTEMTIME *time, LPWSTR string )
2159 {
2160     static const WCHAR format[] =
2161         {'%','s',',',' ','%','0','2','d',' ','%','s',' ','%','4','d',' ','%','0',
2162          '2','d',':','%','0','2','d',':','%','0','2','d',' ','G','M','T', 0};
2163 
2164     TRACE("%p, %p\n", time, string);
2165 
2166     if (!time || !string)
2167     {
2168         SetLastError( ERROR_INVALID_PARAMETER );
2169         return FALSE;
2170     }
2171 
2172     sprintfW( string, format,
2173               wkday[time->wDayOfWeek],
2174               time->wDay,
2175               month[time->wMonth - 1],
2176               time->wYear,
2177               time->wHour,
2178               time->wMinute,
2179               time->wSecond );
2180 
2181     SetLastError( ERROR_SUCCESS );
2182     return TRUE;
2183 }
2184 
2185 /***********************************************************************
2186  *           WinHttpTimeToSystemTime (WININET.@)
2187  */
2188 BOOL WINAPI WinHttpTimeToSystemTime( LPCWSTR string, SYSTEMTIME *time )
2189 {
2190     unsigned int i;
2191     const WCHAR *s = string;
2192     WCHAR *end;
2193 
2194     TRACE("%s, %p\n", debugstr_w(string), time);
2195 
2196     if (!string || !time)
2197     {
2198         SetLastError( ERROR_INVALID_PARAMETER );
2199         return FALSE;
2200     }
2201 
2202     /* Windows does this too */
2203     GetSystemTime( time );
2204 
2205     /*  Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
2206      *  a SYSTEMTIME structure.
2207      */
2208 
2209     SetLastError( ERROR_SUCCESS );
2210 
2211     while (*s && !isalphaW( *s )) s++;
2212     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
2213     time->wDayOfWeek = 7;
2214 
2215     for (i = 0; i < 7; i++)
2216     {
2217         if (toupperW( wkday[i][0] ) == toupperW( s[0] ) &&
2218             toupperW( wkday[i][1] ) == toupperW( s[1] ) &&
2219             toupperW( wkday[i][2] ) == toupperW( s[2] ) )
2220         {
2221             time->wDayOfWeek = i;
2222             break;
2223         }
2224     }
2225 
2226     if (time->wDayOfWeek > 6) return TRUE;
2227     while (*s && !isdigitW( *s )) s++;
2228     time->wDay = strtolW( s, &end, 10 );
2229     s = end;
2230 
2231     while (*s && !isalphaW( *s )) s++;
2232     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
2233     time->wMonth = 0;
2234 
2235     for (i = 0; i < 12; i++)
2236     {
2237         if (toupperW( month[i][0]) == toupperW( s[0] ) &&
2238             toupperW( month[i][1]) == toupperW( s[1] ) &&
2239             toupperW( month[i][2]) == toupperW( s[2] ) )
2240         {
2241             time->wMonth = i + 1;
2242             break;
2243         }
2244     }
2245     if (time->wMonth == 0) return TRUE;
2246 
2247     while (*s && !isdigitW( *s )) s++;
2248     if (*s == '\0') return TRUE;
2249     time->wYear = strtolW( s, &end, 10 );
2250     s = end;
2251 
2252     while (*s && !isdigitW( *s )) s++;
2253     if (*s == '\0') return TRUE;
2254     time->wHour = strtolW( s, &end, 10 );
2255     s = end;
2256 
2257     while (*s && !isdigitW( *s )) s++;
2258     if (*s == '\0') return TRUE;
2259     time->wMinute = strtolW( s, &end, 10 );
2260     s = end;
2261 
2262     while (*s && !isdigitW( *s )) s++;
2263     if (*s == '\0') return TRUE;
2264     time->wSecond = strtolW( s, &end, 10 );
2265 
2266     time->wMilliseconds = 0;
2267     return TRUE;
2268 }
2269