winhttp: Adjust connect timeout to the value observed on Windows.
[wine] / dlls / winhttp / session.c
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 "wine/port.h"
21 #include "wine/debug.h"
22
23 #include <stdarg.h>
24 #include <stdlib.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winhttp.h"
29 #include "wincrypt.h"
30 #include "winreg.h"
31
32 #include "winhttp_private.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
35
36 #define DEFAULT_CONNECT_TIMEOUT     20000
37 #define DEFAULT_SEND_TIMEOUT        30000
38 #define DEFAULT_RECEIVE_TIMEOUT     30000
39
40 void set_last_error( DWORD error )
41 {
42     /* FIXME */
43     SetLastError( error );
44 }
45
46 DWORD get_last_error( void )
47 {
48     /* FIXME */
49     return GetLastError();
50 }
51
52 void send_callback( object_header_t *hdr, DWORD status, LPVOID info, DWORD buflen )
53 {
54     TRACE("%p, 0x%08x, %p, %u\n", hdr, status, info, buflen);
55
56     if (hdr->callback && (hdr->notify_mask & status)) hdr->callback( hdr->handle, hdr->context, status, info, buflen );
57 }
58
59 /***********************************************************************
60  *          WinHttpCheckPlatform (winhttp.@)
61  */
62 BOOL WINAPI WinHttpCheckPlatform( void )
63 {
64     TRACE("\n");
65     return TRUE;
66 }
67
68 /***********************************************************************
69  *          session_destroy (internal)
70  */
71 static void session_destroy( object_header_t *hdr )
72 {
73     session_t *session = (session_t *)hdr;
74     struct list *item, *next;
75     domain_t *domain;
76
77     TRACE("%p\n", session);
78
79     LIST_FOR_EACH_SAFE( item, next, &session->cookie_cache )
80     {
81         domain = LIST_ENTRY( item, domain_t, entry );
82         delete_domain( domain );
83     }
84     heap_free( session->agent );
85     heap_free( session->proxy_server );
86     heap_free( session->proxy_bypass );
87     heap_free( session->proxy_username );
88     heap_free( session->proxy_password );
89     heap_free( session );
90 }
91
92 static BOOL session_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
93 {
94     switch (option)
95     {
96     case WINHTTP_OPTION_REDIRECT_POLICY:
97     {
98         if (!buffer || *buflen < sizeof(DWORD))
99         {
100             *buflen = sizeof(DWORD);
101             set_last_error( ERROR_INSUFFICIENT_BUFFER );
102             return FALSE;
103         }
104
105         *(DWORD *)buffer = hdr->redirect_policy;
106         *buflen = sizeof(DWORD);
107         return TRUE;
108     }
109     default:
110         FIXME("unimplemented option %u\n", option);
111         set_last_error( ERROR_INVALID_PARAMETER );
112         return FALSE;
113     }
114 }
115
116 static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
117 {
118     switch (option)
119     {
120     case WINHTTP_OPTION_PROXY:
121     {
122         WINHTTP_PROXY_INFO *pi = buffer;
123
124         FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass));
125         return TRUE;
126     }
127     case WINHTTP_OPTION_REDIRECT_POLICY:
128     {
129         DWORD policy;
130
131         if (buflen != sizeof(policy))
132         {
133             set_last_error( ERROR_INSUFFICIENT_BUFFER );
134             return FALSE;
135         }
136
137         policy = *(DWORD *)buffer;
138         TRACE("0x%x\n", policy);
139         hdr->redirect_policy = policy;
140         return TRUE;
141     }
142     case WINHTTP_OPTION_DISABLE_FEATURE:
143         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
144         return FALSE;
145     default:
146         FIXME("unimplemented option %u\n", option);
147         set_last_error( ERROR_INVALID_PARAMETER );
148         return FALSE;
149     }
150 }
151
152 static const object_vtbl_t session_vtbl =
153 {
154     session_destroy,
155     session_query_option,
156     session_set_option
157 };
158
159 /***********************************************************************
160  *          WinHttpOpen (winhttp.@)
161  */
162 HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWSTR bypass, DWORD flags )
163 {
164     session_t *session;
165     HINTERNET handle = NULL;
166
167     TRACE("%s, %u, %s, %s, 0x%08x\n", debugstr_w(agent), access, debugstr_w(proxy), debugstr_w(bypass), flags);
168
169     if (!(session = heap_alloc_zero( sizeof(session_t) ))) return NULL;
170
171     session->hdr.type = WINHTTP_HANDLE_TYPE_SESSION;
172     session->hdr.vtbl = &session_vtbl;
173     session->hdr.flags = flags;
174     session->hdr.refs = 1;
175     session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
176     list_init( &session->cookie_cache );
177
178     if (agent && !(session->agent = strdupW( agent ))) goto end;
179     if (access == WINHTTP_ACCESS_TYPE_DEFAULT_PROXY)
180     {
181         WINHTTP_PROXY_INFO info;
182
183         WinHttpGetDefaultProxyConfiguration( &info );
184         session->access = info.dwAccessType;
185         if (info.lpszProxy && !(session->proxy_server = strdupW( info.lpszProxy )))
186         {
187             GlobalFree( (LPWSTR)info.lpszProxy );
188             GlobalFree( (LPWSTR)info.lpszProxyBypass );
189             goto end;
190         }
191         if (info.lpszProxyBypass && !(session->proxy_bypass = strdupW( info.lpszProxyBypass )))
192         {
193             GlobalFree( (LPWSTR)info.lpszProxy );
194             GlobalFree( (LPWSTR)info.lpszProxyBypass );
195             goto end;
196         }
197     }
198     else if (access == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
199     {
200         session->access = access;
201         if (proxy && !(session->proxy_server = strdupW( proxy ))) goto end;
202         if (bypass && !(session->proxy_bypass = strdupW( bypass ))) goto end;
203     }
204
205     if (!(handle = alloc_handle( &session->hdr ))) goto end;
206     session->hdr.handle = handle;
207
208 end:
209     release_object( &session->hdr );
210     TRACE("returning %p\n", handle);
211     return handle;
212 }
213
214 /***********************************************************************
215  *          connect_destroy (internal)
216  */
217 static void connect_destroy( object_header_t *hdr )
218 {
219     connect_t *connect = (connect_t *)hdr;
220
221     TRACE("%p\n", connect);
222
223     release_object( &connect->session->hdr );
224
225     heap_free( connect->hostname );
226     heap_free( connect->servername );
227     heap_free( connect->username );
228     heap_free( connect->password );
229     heap_free( connect );
230 }
231
232 static BOOL connect_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
233 {
234     connect_t *connect = (connect_t *)hdr;
235
236     switch (option)
237     {
238     case WINHTTP_OPTION_PARENT_HANDLE:
239     {
240         if (!buffer || *buflen < sizeof(HINTERNET))
241         {
242             *buflen = sizeof(HINTERNET);
243             set_last_error( ERROR_INSUFFICIENT_BUFFER );
244             return FALSE;
245         }
246
247         *(HINTERNET *)buffer = ((object_header_t *)connect->session)->handle;
248         *buflen = sizeof(HINTERNET);
249         return TRUE;
250     }
251     default:
252         FIXME("unimplemented option %u\n", option);
253         set_last_error( ERROR_INVALID_PARAMETER );
254         return FALSE;
255     }
256 }
257
258 static const object_vtbl_t connect_vtbl =
259 {
260     connect_destroy,
261     connect_query_option,
262     NULL
263 };
264
265 static BOOL domain_matches(LPCWSTR server, LPCWSTR domain)
266 {
267     static const WCHAR localW[] = { '<','l','o','c','a','l','>',0 };
268     BOOL ret = FALSE;
269
270     if (!strcmpiW( domain, localW ) && !strchrW( server, '.' ))
271         ret = TRUE;
272     else if (*domain == '*')
273     {
274         if (domain[1] == '.')
275         {
276             LPCWSTR dot;
277
278             /* For a hostname to match a wildcard, the last domain must match
279              * the wildcard exactly.  E.g. if the wildcard is *.a.b, and the
280              * hostname is www.foo.a.b, it matches, but a.b does not.
281              */
282             dot = strchrW( server, '.' );
283             if (dot)
284             {
285                 int len = strlenW( dot + 1 );
286
287                 if (len > strlenW( domain + 2 ))
288                 {
289                     LPCWSTR ptr;
290
291                     /* The server's domain is longer than the wildcard, so it
292                      * could be a subdomain.  Compare the last portion of the
293                      * server's domain.
294                      */
295                     ptr = dot + len + 1 - strlenW( domain + 2 );
296                     if (!strcmpiW( ptr, domain + 2 ))
297                     {
298                         /* This is only a match if the preceding character is
299                          * a '.', i.e. that it is a matching domain.  E.g.
300                          * if domain is '*.b.c' and server is 'www.ab.c' they
301                          * do not match.
302                          */
303                         ret = *(ptr - 1) == '.';
304                     }
305                 }
306                 else
307                     ret = !strcmpiW( dot + 1, domain + 2 );
308             }
309         }
310     }
311     else
312         ret = !strcmpiW( server, domain );
313     return ret;
314 }
315
316 /* Matches INTERNET_MAX_HOST_NAME_LENGTH in wininet.h, also RFC 1035 */
317 #define MAX_HOST_NAME_LENGTH 256
318
319 static BOOL should_bypass_proxy(session_t *session, LPCWSTR server)
320 {
321     LPCWSTR ptr;
322     BOOL ret = FALSE;
323
324     if (!session->proxy_bypass) return FALSE;
325     ptr = session->proxy_bypass;
326     do {
327         LPCWSTR tmp = ptr;
328
329         ptr = strchrW( ptr, ';' );
330         if (!ptr)
331             ptr = strchrW( tmp, ' ' );
332         if (ptr)
333         {
334             if (ptr - tmp < MAX_HOST_NAME_LENGTH)
335             {
336                 WCHAR domain[MAX_HOST_NAME_LENGTH];
337
338                 memcpy( domain, tmp, (ptr - tmp) * sizeof(WCHAR) );
339                 domain[ptr - tmp] = 0;
340                 ret = domain_matches( server, domain );
341             }
342             ptr += 1;
343         }
344         else if (*tmp)
345             ret = domain_matches( server, tmp );
346     } while (ptr && !ret);
347     return ret;
348 }
349
350 BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT port )
351 {
352     session_t *session = connect->session;
353     BOOL ret = TRUE;
354
355     if (session->proxy_server && !should_bypass_proxy(session, server))
356     {
357         LPCWSTR colon;
358
359         if ((colon = strchrW( session->proxy_server, ':' )))
360         {
361             if (!connect->servername || strncmpiW( connect->servername,
362                 session->proxy_server, colon - session->proxy_server - 1 ))
363             {
364                 heap_free( connect->servername );
365                 if (!(connect->servername = heap_alloc(
366                     (colon - session->proxy_server + 1) * sizeof(WCHAR) )))
367                 {
368                     ret = FALSE;
369                     goto end;
370                 }
371                 memcpy( connect->servername, session->proxy_server,
372                     (colon - session->proxy_server) * sizeof(WCHAR) );
373                 connect->servername[colon - session->proxy_server] = 0;
374                 if (*(colon + 1))
375                     connect->serverport = atoiW( colon + 1 );
376                 else
377                     connect->serverport = INTERNET_DEFAULT_PORT;
378             }
379         }
380         else
381         {
382             if (!connect->servername || strcmpiW( connect->servername,
383                 session->proxy_server ))
384             {
385                 heap_free( connect->servername );
386                 if (!(connect->servername = strdupW( session->proxy_server )))
387                 {
388                     ret = FALSE;
389                     goto end;
390                 }
391                 connect->serverport = INTERNET_DEFAULT_PORT;
392             }
393         }
394     }
395     else if (server)
396     {
397         heap_free( connect->servername );
398         if (!(connect->servername = strdupW( server )))
399         {
400             ret = FALSE;
401             goto end;
402         }
403         connect->serverport = port;
404     }
405 end:
406     return ret;
407 }
408
409 /***********************************************************************
410  *          WinHttpConnect (winhttp.@)
411  */
412 HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PORT port, DWORD reserved )
413 {
414     connect_t *connect;
415     session_t *session;
416     HINTERNET hconnect = NULL;
417
418     TRACE("%p, %s, %u, %x\n", hsession, debugstr_w(server), port, reserved);
419
420     if (!server)
421     {
422         set_last_error( ERROR_INVALID_PARAMETER );
423         return NULL;
424     }
425     if (!(session = (session_t *)grab_object( hsession )))
426     {
427         set_last_error( ERROR_INVALID_HANDLE );
428         return NULL;
429     }
430     if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION)
431     {
432         release_object( &session->hdr );
433         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
434         return NULL;
435     }
436     if (!(connect = heap_alloc_zero( sizeof(connect_t) )))
437     {
438         release_object( &session->hdr );
439         return NULL;
440     }
441     connect->hdr.type = WINHTTP_HANDLE_TYPE_CONNECT;
442     connect->hdr.vtbl = &connect_vtbl;
443     connect->hdr.refs = 1;
444     connect->hdr.flags = session->hdr.flags;
445     connect->hdr.callback = session->hdr.callback;
446     connect->hdr.notify_mask = session->hdr.notify_mask;
447     connect->hdr.context = session->hdr.context;
448
449     addref_object( &session->hdr );
450     connect->session = session;
451     list_add_head( &session->hdr.children, &connect->hdr.entry );
452
453     if (server && !(connect->hostname = strdupW( server ))) goto end;
454     connect->hostport = port;
455
456     if (!set_server_for_hostname( connect, server, port ))
457         goto end;
458
459     if (!(hconnect = alloc_handle( &connect->hdr ))) goto end;
460     connect->hdr.handle = hconnect;
461
462     send_callback( &session->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hconnect, sizeof(hconnect) );
463
464 end:
465     release_object( &connect->hdr );
466
467     TRACE("returning %p\n", hconnect);
468     return hconnect;
469 }
470
471 /***********************************************************************
472  *          request_destroy (internal)
473  */
474 static void request_destroy( object_header_t *hdr )
475 {
476     request_t *request = (request_t *)hdr;
477     DWORD i;
478
479     TRACE("%p\n", request);
480
481     release_object( &request->connect->hdr );
482
483     heap_free( request->verb );
484     heap_free( request->path );
485     heap_free( request->version );
486     heap_free( request->raw_headers );
487     heap_free( request->status_text );
488     for (i = 0; i < request->num_headers; i++)
489     {
490         heap_free( request->headers[i].field );
491         heap_free( request->headers[i].value );
492     }
493     heap_free( request->headers );
494     heap_free( request );
495 }
496
497 static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
498 {
499     switch (option)
500     {
501     case WINHTTP_OPTION_SECURITY_FLAGS:
502     {
503         DWORD flags;
504
505         if (!buffer || *buflen < sizeof(flags))
506         {
507             *buflen = sizeof(flags);
508             set_last_error( ERROR_INSUFFICIENT_BUFFER );
509             return FALSE;
510         }
511
512         flags = 0;
513         if (hdr->flags & WINHTTP_FLAG_SECURE) flags |= SECURITY_FLAG_SECURE;
514         *(DWORD *)buffer = flags;
515         *buflen = sizeof(flags);
516         return TRUE;
517     }
518     case WINHTTP_OPTION_SERVER_CERT_CONTEXT:
519     {
520         const CERT_CONTEXT *cert;
521         request_t *request = (request_t *)hdr;
522
523         if (!buffer || *buflen < sizeof(cert))
524         {
525             *buflen = sizeof(cert);
526             set_last_error( ERROR_INSUFFICIENT_BUFFER );
527             return FALSE;
528         }
529
530         if (!(cert = netconn_get_certificate( &request->netconn ))) return FALSE;
531         *(CERT_CONTEXT **)buffer = (CERT_CONTEXT *)cert;
532         *buflen = sizeof(cert);
533         return TRUE;
534     }
535     case WINHTTP_OPTION_SECURITY_KEY_BITNESS:
536     {
537         if (!buffer || *buflen < sizeof(DWORD))
538         {
539             *buflen = sizeof(DWORD);
540             set_last_error( ERROR_INSUFFICIENT_BUFFER );
541             return FALSE;
542         }
543
544         *(DWORD *)buffer = 128; /* FIXME */
545         *buflen = sizeof(DWORD);
546         return TRUE;
547     }
548     default:
549         FIXME("unimplemented option %u\n", option);
550         set_last_error( ERROR_INVALID_PARAMETER );
551         return FALSE;
552     }
553 }
554
555 static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
556 {
557     switch (option)
558     {
559     case WINHTTP_OPTION_PROXY:
560     {
561         WINHTTP_PROXY_INFO *pi = buffer;
562
563         FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass));
564         return TRUE;
565     }
566     case WINHTTP_OPTION_DISABLE_FEATURE:
567     {
568         DWORD disable;
569
570         if (buflen != sizeof(DWORD))
571         {
572             set_last_error( ERROR_INSUFFICIENT_BUFFER );
573             return FALSE;
574         }
575
576         disable = *(DWORD *)buffer;
577         TRACE("0x%x\n", disable);
578         hdr->disable_flags |= disable;
579         return TRUE;
580     }
581     case WINHTTP_OPTION_AUTOLOGON_POLICY:
582     {
583         DWORD policy;
584
585         if (buflen != sizeof(DWORD))
586         {
587             set_last_error( ERROR_INSUFFICIENT_BUFFER );
588             return FALSE;
589         }
590
591         policy = *(DWORD *)buffer;
592         TRACE("0x%x\n", policy);
593         hdr->logon_policy = policy;
594         return TRUE;
595     }
596     case WINHTTP_OPTION_REDIRECT_POLICY:
597     {
598         DWORD policy;
599
600         if (buflen != sizeof(DWORD))
601         {
602             set_last_error( ERROR_INSUFFICIENT_BUFFER );
603             return FALSE;
604         }
605
606         policy = *(DWORD *)buffer;
607         TRACE("0x%x\n", policy);
608         hdr->redirect_policy = policy;
609         return TRUE;
610     }
611     case WINHTTP_OPTION_SECURITY_FLAGS:
612         FIXME("WINHTTP_OPTION_SECURITY_FLAGS unimplemented (%08x)\n",
613               *(DWORD *)buffer);
614         return TRUE;
615     default:
616         FIXME("unimplemented option %u\n", option);
617         set_last_error( ERROR_INVALID_PARAMETER );
618         return TRUE;
619     }
620 }
621
622 static const object_vtbl_t request_vtbl =
623 {
624     request_destroy,
625     request_query_option,
626     request_set_option
627 };
628
629 /***********************************************************************
630  *          WinHttpOpenRequest (winhttp.@)
631  */
632 HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR object, LPCWSTR version,
633                                      LPCWSTR referrer, LPCWSTR *types, DWORD flags )
634 {
635     request_t *request;
636     connect_t *connect;
637     HINTERNET hrequest = NULL;
638
639     TRACE("%p, %s, %s, %s, %s, %p, 0x%08x\n", hconnect, debugstr_w(verb), debugstr_w(object),
640           debugstr_w(version), debugstr_w(referrer), types, flags);
641
642     if (!(connect = (connect_t *)grab_object( hconnect )))
643     {
644         set_last_error( ERROR_INVALID_HANDLE );
645         return NULL;
646     }
647     if (connect->hdr.type != WINHTTP_HANDLE_TYPE_CONNECT)
648     {
649         release_object( &connect->hdr );
650         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
651         return NULL;
652     }
653     if (!(request = heap_alloc_zero( sizeof(request_t) )))
654     {
655         release_object( &connect->hdr );
656         return NULL;
657     }
658     request->hdr.type = WINHTTP_HANDLE_TYPE_REQUEST;
659     request->hdr.vtbl = &request_vtbl;
660     request->hdr.refs = 1;
661     request->hdr.flags = flags;
662     request->hdr.callback = connect->hdr.callback;
663     request->hdr.notify_mask = connect->hdr.notify_mask;
664     request->hdr.context = connect->hdr.context;
665
666     addref_object( &connect->hdr );
667     request->connect = connect;
668     list_add_head( &connect->hdr.children, &request->hdr.entry );
669
670     if (!netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE )) goto end;
671     request->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
672     request->send_timeout = DEFAULT_SEND_TIMEOUT;
673     request->recv_timeout = DEFAULT_RECEIVE_TIMEOUT;
674
675     if (!verb || !verb[0]) verb = getW;
676     if (!(request->verb = strdupW( verb ))) goto end;
677
678     if (object)
679     {
680         WCHAR *path, *p;
681         unsigned int len;
682
683         len = strlenW( object ) + 1;
684         if (object[0] != '/') len++;
685         if (!(p = path = heap_alloc( len * sizeof(WCHAR) ))) goto end;
686
687         if (object[0] != '/') *p++ = '/';
688         strcpyW( p, object );
689         request->path = path;
690     }
691     else if (!(request->path = strdupW( slashW ))) goto end;
692
693     if (!version || !version[0]) version = http1_1;
694     if (!(request->version = strdupW( version ))) goto end;
695
696     if (!(hrequest = alloc_handle( &request->hdr ))) goto end;
697     request->hdr.handle = hrequest;
698
699     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hrequest, sizeof(hrequest) );
700
701 end:
702     release_object( &request->hdr );
703
704     TRACE("returning %p\n", hrequest);
705     return hrequest;
706 }
707
708 /***********************************************************************
709  *          WinHttpCloseHandle (winhttp.@)
710  */
711 BOOL WINAPI WinHttpCloseHandle( HINTERNET handle )
712 {
713     object_header_t *hdr;
714
715     TRACE("%p\n", handle);
716
717     if (!(hdr = grab_object( handle )))
718     {
719         set_last_error( ERROR_INVALID_HANDLE );
720         return FALSE;
721     }
722     release_object( hdr );
723     free_handle( handle );
724     return TRUE;
725 }
726
727 static BOOL query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
728 {
729     BOOL ret = FALSE;
730
731     if (!buflen)
732     {
733         set_last_error( ERROR_INVALID_PARAMETER );
734         return FALSE;
735     }
736
737     switch (option)
738     {
739     case WINHTTP_OPTION_CONTEXT_VALUE:
740     {
741         if (!buffer || *buflen < sizeof(DWORD_PTR))
742         {
743             *buflen = sizeof(DWORD_PTR);
744             set_last_error( ERROR_INSUFFICIENT_BUFFER );
745             return FALSE;
746         }
747
748         *(DWORD_PTR *)buffer = hdr->context;
749         *buflen = sizeof(DWORD_PTR);
750         return TRUE;
751     }
752     default:
753         if (hdr->vtbl->query_option) ret = hdr->vtbl->query_option( hdr, option, buffer, buflen );
754         else
755         {
756             FIXME("unimplemented option %u\n", option);
757             set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
758             return FALSE;
759         }
760         break;
761     }
762     return ret;
763 }
764
765 /***********************************************************************
766  *          WinHttpQueryOption (winhttp.@)
767  */
768 BOOL WINAPI WinHttpQueryOption( HINTERNET handle, DWORD option, LPVOID buffer, LPDWORD buflen )
769 {
770     BOOL ret = FALSE;
771     object_header_t *hdr;
772
773     TRACE("%p, %u, %p, %p\n", handle, option, buffer, buflen);
774
775     if (!(hdr = grab_object( handle )))
776     {
777         set_last_error( ERROR_INVALID_HANDLE );
778         return FALSE;
779     }
780
781     ret = query_option( hdr, option, buffer, buflen );
782
783     release_object( hdr );
784     return ret;
785 }
786
787 static BOOL set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
788 {
789     BOOL ret = TRUE;
790
791     if (!buffer)
792     {
793         set_last_error( ERROR_INVALID_PARAMETER );
794         return FALSE;
795     }
796
797     switch (option)
798     {
799     case WINHTTP_OPTION_CONTEXT_VALUE:
800     {
801         if (buflen != sizeof(DWORD_PTR))
802         {
803             set_last_error( ERROR_INSUFFICIENT_BUFFER );
804             return FALSE;
805         }
806
807         hdr->context = *(DWORD_PTR *)buffer;
808         return TRUE;
809     }
810     default:
811         if (hdr->vtbl->set_option) ret = hdr->vtbl->set_option( hdr, option, buffer, buflen );
812         else
813         {
814             FIXME("unimplemented option %u\n", option);
815             set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
816             return FALSE;
817         }
818         break;
819     }
820     return ret;
821 }
822
823 /***********************************************************************
824  *          WinHttpSetOption (winhttp.@)
825  */
826 BOOL WINAPI WinHttpSetOption( HINTERNET handle, DWORD option, LPVOID buffer, DWORD buflen )
827 {
828     BOOL ret = FALSE;
829     object_header_t *hdr;
830
831     TRACE("%p, %u, %p, %u\n", handle, option, buffer, buflen);
832
833     if (!(hdr = grab_object( handle )))
834     {
835         set_last_error( ERROR_INVALID_HANDLE );
836         return FALSE;
837     }
838
839     ret = set_option( hdr, option, buffer, buflen );
840
841     release_object( hdr );
842     return ret;
843 }
844
845 /***********************************************************************
846  *          WinHttpDetectAutoProxyConfigUrl (winhttp.@)
847  */
848 BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, LPWSTR *url )
849 {
850     FIXME("0x%08x, %p\n", flags, url);
851
852     set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED );
853     return FALSE;
854 }
855
856 static const WCHAR Connections[] = {
857     'S','o','f','t','w','a','r','e','\\',
858     'M','i','c','r','o','s','o','f','t','\\',
859     'W','i','n','d','o','w','s','\\',
860     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
861     'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
862     'C','o','n','n','e','c','t','i','o','n','s',0 };
863 static const WCHAR WinHttpSettings[] = {
864     'W','i','n','H','t','t','p','S','e','t','t','i','n','g','s',0 };
865 static const DWORD WINHTTPSETTINGS_MAGIC = 0x18;
866 static const DWORD WINHTTP_PROXY_TYPE_DIRECT = 1;
867 static const DWORD WINHTTP_PROXY_TYPE_PROXY = 2;
868
869 struct winhttp_settings_header
870 {
871     DWORD magic;
872     DWORD unknown; /* always zero? */
873     DWORD flags;   /* one of WINHTTP_PROXY_TYPE_* */
874 };
875
876 static inline void copy_char_to_wchar_sz(const BYTE *src, DWORD len, WCHAR *dst)
877 {
878     const BYTE *begin;
879
880     for (begin = src; src - begin < len; src++, dst++)
881         *dst = *src;
882     *dst = 0;
883 }
884
885 /***********************************************************************
886  *          WinHttpGetDefaultProxyConfiguration (winhttp.@)
887  */
888 BOOL WINAPI WinHttpGetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info )
889 {
890     LONG l;
891     HKEY key;
892     BOOL got_from_reg = FALSE, direct = TRUE;
893     char *envproxy;
894
895     TRACE("%p\n", info);
896
897     l = RegOpenKeyExW( HKEY_LOCAL_MACHINE, Connections, 0, KEY_READ, &key );
898     if (!l)
899     {
900         DWORD type, size = 0;
901
902         l = RegQueryValueExW( key, WinHttpSettings, NULL, &type, NULL, &size );
903         if (!l && type == REG_BINARY &&
904             size >= sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD))
905         {
906             BYTE *buf = heap_alloc( size );
907
908             if (buf)
909             {
910                 struct winhttp_settings_header *hdr =
911                     (struct winhttp_settings_header *)buf;
912                 DWORD *len = (DWORD *)(hdr + 1);
913
914                 l = RegQueryValueExW( key, WinHttpSettings, NULL, NULL, buf,
915                     &size );
916                 if (!l && hdr->magic == WINHTTPSETTINGS_MAGIC &&
917                     hdr->unknown == 0)
918                 {
919                     if (hdr->flags & WINHTTP_PROXY_TYPE_PROXY)
920                     {
921                        BOOL sane = FALSE;
922                        LPWSTR proxy = NULL;
923                        LPWSTR proxy_bypass = NULL;
924
925                         /* Sanity-check length of proxy string */
926                         if ((BYTE *)len - buf + *len <= size)
927                         {
928                             sane = TRUE;
929                             proxy = GlobalAlloc( 0, (*len + 1) * sizeof(WCHAR) );
930                             if (proxy)
931                                 copy_char_to_wchar_sz( (BYTE *)(len + 1), *len, proxy );
932                             len = (DWORD *)((BYTE *)(len + 1) + *len);
933                         }
934                         if (sane)
935                         {
936                             /* Sanity-check length of proxy bypass string */
937                             if ((BYTE *)len - buf + *len <= size)
938                             {
939                                 proxy_bypass = GlobalAlloc( 0, (*len + 1) * sizeof(WCHAR) );
940                                 if (proxy_bypass)
941                                     copy_char_to_wchar_sz( (BYTE *)(len + 1), *len, proxy_bypass );
942                             }
943                             else
944                             {
945                                 sane = FALSE;
946                                 GlobalFree( proxy );
947                                 proxy = NULL;
948                             }
949                         }
950                         info->lpszProxy = proxy;
951                         info->lpszProxyBypass = proxy_bypass;
952                         if (sane)
953                         {
954                             got_from_reg = TRUE;
955                             direct = FALSE;
956                             info->dwAccessType =
957                                 WINHTTP_ACCESS_TYPE_NAMED_PROXY;
958                             TRACE("http proxy (from registry) = %s, bypass = %s\n",
959                                 debugstr_w(info->lpszProxy),
960                                 debugstr_w(info->lpszProxyBypass));
961                         }
962                     }
963                 }
964                 heap_free( buf );
965             }
966         }
967         RegCloseKey( key );
968     }
969     if (!got_from_reg && (envproxy = getenv( "http_proxy" )))
970     {
971         char *colon, *http_proxy;
972
973         if ((colon = strchr( envproxy, ':' )))
974         {
975             if (*(colon + 1) == '/' && *(colon + 2) == '/')
976             {
977                 static const char http[] = "http://";
978
979                 /* It's a scheme, check that it's http */
980                 if (!strncmp( envproxy, http, strlen( http ) ))
981                     http_proxy = envproxy + strlen( http );
982                 else
983                 {
984                     WARN("unsupported scheme in $http_proxy: %s\n", envproxy);
985                     http_proxy = NULL;
986                 }
987             }
988             else
989                 http_proxy = envproxy;
990         }
991         else
992             http_proxy = envproxy;
993         if (http_proxy)
994         {
995             WCHAR *http_proxyW;
996             int len;
997
998             len = MultiByteToWideChar( CP_UNIXCP, 0, http_proxy, -1, NULL, 0 );
999             if ((http_proxyW = GlobalAlloc( 0, len * sizeof(WCHAR))))
1000             {
1001                 MultiByteToWideChar( CP_UNIXCP, 0, http_proxy, -1, http_proxyW, len );
1002                 direct = FALSE;
1003                 info->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
1004                 info->lpszProxy = http_proxyW;
1005                 info->lpszProxyBypass = NULL;
1006                 TRACE("http proxy (from environment) = %s\n",
1007                     debugstr_w(info->lpszProxy));
1008             }
1009         }
1010     }
1011     if (direct)
1012     {
1013         info->dwAccessType    = WINHTTP_ACCESS_TYPE_NO_PROXY;
1014         info->lpszProxy       = NULL;
1015         info->lpszProxyBypass = NULL;
1016     }
1017     return TRUE;
1018 }
1019
1020 /***********************************************************************
1021  *          WinHttpGetIEProxyConfigForCurrentUser (winhttp.@)
1022  */
1023 BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *config )
1024 {
1025     TRACE("%p\n", config);
1026
1027     if (!config)
1028     {
1029         set_last_error( ERROR_INVALID_PARAMETER );
1030         return FALSE;
1031     }
1032
1033     /* FIXME: read from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings */
1034
1035     FIXME("returning no proxy used\n");
1036     config->fAutoDetect       = FALSE;
1037     config->lpszAutoConfigUrl = NULL;
1038     config->lpszProxy         = NULL;
1039     config->lpszProxyBypass   = NULL;
1040
1041     return TRUE;
1042 }
1043
1044 /***********************************************************************
1045  *          WinHttpGetProxyForUrl (winhttp.@)
1046  */
1047 BOOL WINAPI WinHttpGetProxyForUrl( HINTERNET hsession, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options,
1048                                    WINHTTP_PROXY_INFO *info )
1049 {
1050     FIXME("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info);
1051
1052     set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR );
1053     return FALSE;
1054 }
1055
1056 /***********************************************************************
1057  *          WinHttpSetDefaultProxyConfiguration (winhttp.@)
1058  */
1059 BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info )
1060 {
1061     LONG l;
1062     HKEY key;
1063     BOOL ret = FALSE;
1064     const WCHAR *src;
1065
1066     TRACE("%p\n", info);
1067
1068     if (!info)
1069     {
1070         set_last_error( ERROR_INVALID_PARAMETER );
1071         return FALSE;
1072     }
1073     switch (info->dwAccessType)
1074     {
1075     case WINHTTP_ACCESS_TYPE_NO_PROXY:
1076         break;
1077     case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
1078         if (!info->lpszProxy)
1079         {
1080             set_last_error( ERROR_INVALID_PARAMETER );
1081             return FALSE;
1082         }
1083         /* Only ASCII characters are allowed */
1084         for (src = info->lpszProxy; *src; src++)
1085             if (*src > 0x7f)
1086             {
1087                 set_last_error( ERROR_INVALID_PARAMETER );
1088                 return FALSE;
1089             }
1090         if (info->lpszProxyBypass)
1091         {
1092             for (src = info->lpszProxyBypass; *src; src++)
1093                 if (*src > 0x7f)
1094                 {
1095                     set_last_error( ERROR_INVALID_PARAMETER );
1096                     return FALSE;
1097                 }
1098         }
1099         break;
1100     default:
1101         set_last_error( ERROR_INVALID_PARAMETER );
1102         return FALSE;
1103     }
1104
1105     l = RegCreateKeyExW( HKEY_LOCAL_MACHINE, Connections, 0, NULL, 0,
1106         KEY_WRITE, NULL, &key, NULL );
1107     if (!l)
1108     {
1109         DWORD size = sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD);
1110         BYTE *buf;
1111
1112         if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
1113         {
1114             size += strlenW( info->lpszProxy );
1115             if (info->lpszProxyBypass)
1116                 size += strlenW( info->lpszProxyBypass );
1117         }
1118         buf = heap_alloc( size );
1119         if (buf)
1120         {
1121             struct winhttp_settings_header *hdr =
1122                 (struct winhttp_settings_header *)buf;
1123             DWORD *len = (DWORD *)(hdr + 1);
1124
1125             hdr->magic = WINHTTPSETTINGS_MAGIC;
1126             hdr->unknown = 0;
1127             if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
1128             {
1129                 BYTE *dst;
1130
1131                 hdr->flags = WINHTTP_PROXY_TYPE_PROXY;
1132                 *len++ = strlenW( info->lpszProxy );
1133                 for (dst = (BYTE *)len, src = info->lpszProxy; *src;
1134                     src++, dst++)
1135                     *dst = *src;
1136                 len = (DWORD *)dst;
1137                 if (info->lpszProxyBypass)
1138                 {
1139                     *len++ = strlenW( info->lpszProxyBypass );
1140                     for (dst = (BYTE *)len, src = info->lpszProxyBypass; *src;
1141                         src++, dst++)
1142                         *dst = *src;
1143                 }
1144                 else
1145                     *len++ = 0;
1146             }
1147             else
1148             {
1149                 hdr->flags = WINHTTP_PROXY_TYPE_DIRECT;
1150                 *len++ = 0;
1151                 *len++ = 0;
1152             }
1153             l = RegSetValueExW( key, WinHttpSettings, 0, REG_BINARY, buf, size );
1154             if (!l)
1155                 ret = TRUE;
1156             heap_free( buf );
1157         }
1158         RegCloseKey( key );
1159     }
1160     return ret;
1161 }
1162
1163 /***********************************************************************
1164  *          WinHttpSetStatusCallback (winhttp.@)
1165  */
1166 WINHTTP_STATUS_CALLBACK WINAPI WinHttpSetStatusCallback( HINTERNET handle, WINHTTP_STATUS_CALLBACK callback,
1167                                                          DWORD flags, DWORD_PTR reserved )
1168 {
1169     object_header_t *hdr;
1170     WINHTTP_STATUS_CALLBACK ret;
1171
1172     TRACE("%p, %p, 0x%08x, 0x%lx\n", handle, callback, flags, reserved);
1173
1174     if (!(hdr = grab_object( handle )))
1175     {
1176         set_last_error( ERROR_INVALID_HANDLE );
1177         return WINHTTP_INVALID_STATUS_CALLBACK;
1178     }
1179     ret = hdr->callback;
1180     hdr->callback = callback;
1181     hdr->notify_mask = flags;
1182
1183     release_object( hdr );
1184     return ret;
1185 }
1186
1187 /***********************************************************************
1188  *          WinHttpSetTimeouts (winhttp.@)
1189  */
1190 BOOL WINAPI WinHttpSetTimeouts( HINTERNET handle, int resolve, int connect, int send, int receive )
1191 {
1192     BOOL ret = TRUE;
1193     request_t *request;
1194
1195     TRACE("%p, %d, %d, %d, %d\n", handle, resolve, connect, send, receive);
1196
1197     if (resolve < -1 || connect < -1 || send < -1 || receive < -1)
1198     {
1199         set_last_error( ERROR_INVALID_PARAMETER );
1200         return FALSE;
1201     }
1202
1203     if (resolve > 0)
1204         FIXME("resolve timeout (%d) not supported\n", resolve);
1205
1206     if (!(request = (request_t *)grab_object( handle )))
1207     {
1208         set_last_error( ERROR_INVALID_HANDLE );
1209         return FALSE;
1210     }
1211
1212     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
1213     {
1214         release_object( &request->hdr );
1215         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1216         return FALSE;
1217     }
1218
1219     request->connect_timeout = connect;
1220
1221     if (send < 0) send = 0;
1222     request->send_timeout = send;
1223
1224     if (receive < 0) receive = 0;
1225     request->recv_timeout = receive;
1226
1227     if (netconn_connected( &request->netconn ))
1228     {
1229         if (netconn_set_timeout( &request->netconn, TRUE, send )) ret = FALSE;
1230         if (netconn_set_timeout( &request->netconn, FALSE, receive )) ret = FALSE;
1231     }
1232
1233     release_object( &request->hdr );
1234     return ret;
1235 }
1236
1237 static const WCHAR wkday[7][4] =
1238     {{'S','u','n', 0}, {'M','o','n', 0}, {'T','u','e', 0}, {'W','e','d', 0},
1239      {'T','h','u', 0}, {'F','r','i', 0}, {'S','a','t', 0}};
1240 static const WCHAR month[12][4] =
1241     {{'J','a','n', 0}, {'F','e','b', 0}, {'M','a','r', 0}, {'A','p','r', 0},
1242      {'M','a','y', 0}, {'J','u','n', 0}, {'J','u','l', 0}, {'A','u','g', 0},
1243      {'S','e','p', 0}, {'O','c','t', 0}, {'N','o','v', 0}, {'D','e','c', 0}};
1244
1245 /***********************************************************************
1246  *           WinHttpTimeFromSystemTime (WININET.@)
1247  */
1248 BOOL WINAPI WinHttpTimeFromSystemTime( const SYSTEMTIME *time, LPWSTR string )
1249 {
1250     static const WCHAR format[] =
1251         {'%','s',',',' ','%','0','2','d',' ','%','s',' ','%','4','d',' ','%','0',
1252          '2','d',':','%','0','2','d',':','%','0','2','d',' ','G','M','T', 0};
1253
1254     TRACE("%p, %p\n", time, string);
1255
1256     if (!time || !string) return FALSE;
1257
1258     sprintfW( string, format,
1259               wkday[time->wDayOfWeek],
1260               time->wDay,
1261               month[time->wMonth - 1],
1262               time->wYear,
1263               time->wHour,
1264               time->wMinute,
1265               time->wSecond );
1266
1267     return TRUE;
1268 }
1269
1270 /***********************************************************************
1271  *           WinHttpTimeToSystemTime (WININET.@)
1272  */
1273 BOOL WINAPI WinHttpTimeToSystemTime( LPCWSTR string, SYSTEMTIME *time )
1274 {
1275     unsigned int i;
1276     const WCHAR *s = string;
1277     WCHAR *end;
1278
1279     TRACE("%s, %p\n", debugstr_w(string), time);
1280
1281     if (!string || !time) return FALSE;
1282
1283     /* Windows does this too */
1284     GetSystemTime( time );
1285
1286     /*  Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
1287      *  a SYSTEMTIME structure.
1288      */
1289
1290     while (*s && !isalphaW( *s )) s++;
1291     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
1292     time->wDayOfWeek = 7;
1293
1294     for (i = 0; i < 7; i++)
1295     {
1296         if (toupperW( wkday[i][0] ) == toupperW( s[0] ) &&
1297             toupperW( wkday[i][1] ) == toupperW( s[1] ) &&
1298             toupperW( wkday[i][2] ) == toupperW( s[2] ) )
1299         {
1300             time->wDayOfWeek = i;
1301             break;
1302         }
1303     }
1304
1305     if (time->wDayOfWeek > 6) return TRUE;
1306     while (*s && !isdigitW( *s )) s++;
1307     time->wDay = strtolW( s, &end, 10 );
1308     s = end;
1309
1310     while (*s && !isalphaW( *s )) s++;
1311     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
1312     time->wMonth = 0;
1313
1314     for (i = 0; i < 12; i++)
1315     {
1316         if (toupperW( month[i][0]) == toupperW( s[0] ) &&
1317             toupperW( month[i][1]) == toupperW( s[1] ) &&
1318             toupperW( month[i][2]) == toupperW( s[2] ) )
1319         {
1320             time->wMonth = i + 1;
1321             break;
1322         }
1323     }
1324     if (time->wMonth == 0) return TRUE;
1325
1326     while (*s && !isdigitW( *s )) s++;
1327     if (*s == '\0') return TRUE;
1328     time->wYear = strtolW( s, &end, 10 );
1329     s = end;
1330
1331     while (*s && !isdigitW( *s )) s++;
1332     if (*s == '\0') return TRUE;
1333     time->wHour = strtolW( s, &end, 10 );
1334     s = end;
1335
1336     while (*s && !isdigitW( *s )) s++;
1337     if (*s == '\0') return TRUE;
1338     time->wMinute = strtolW( s, &end, 10 );
1339     s = end;
1340
1341     while (*s && !isdigitW( *s )) s++;
1342     if (*s == '\0') return TRUE;
1343     time->wSecond = strtolW( s, &end, 10 );
1344     s = end;
1345
1346     time->wMilliseconds = 0;
1347     return TRUE;
1348 }