7630897359378ab4ca98deae264b25b1042e7771
[platform/upstream/glib.git] / gio / gproxyaddressenumerator.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2010 Collabora, Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
21  */
22
23 #include "config.h"
24 #include "gproxyaddressenumerator.h"
25
26 #include <string.h>
27
28 #include "gasyncresult.h"
29 #include "ginetaddress.h"
30 #include "glibintl.h"
31 #include "gnetworkaddress.h"
32 #include "gnetworkingprivate.h"
33 #include "gproxy.h"
34 #include "gproxyaddress.h"
35 #include "gproxyresolver.h"
36 #include "gsimpleasyncresult.h"
37 #include "gresolver.h"
38 #include "gsocketaddress.h"
39 #include "gsocketaddressenumerator.h"
40 #include "gsocketconnectable.h"
41
42 G_DEFINE_TYPE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR);
43
44 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
45
46 enum
47 {
48   PROP_0,
49   PROP_URI,
50   PROP_CONNECTABLE
51 };
52
53 struct _GProxyAddressEnumeratorPrivate
54 {
55   /* Destination address */
56   GSocketConnectable *connectable;
57   gchar              *dest_uri;
58   gchar              *dest_hostname;
59   guint16             dest_port;
60   GList              *dest_ips;
61
62   /* Proxy enumeration */
63   gchar                   **proxies;
64   gchar                   **next_proxy;
65   GSocketAddressEnumerator *addr_enum;
66   GSocketAddress           *proxy_address;
67   gchar                    *proxy_type;
68   gchar                    *proxy_username;
69   gchar                    *proxy_password;
70   gboolean                  supports_hostname;
71   GList                    *next_dest_ip;
72
73   /* Async attributes */
74   GSimpleAsyncResult *simple;
75   GCancellable       *cancellable;
76 };
77
78 static void
79 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
80                const gchar *proxy)
81 {
82   gchar *userinfo;
83
84   if (priv->proxy_username)
85     {
86       g_free (priv->proxy_username);
87       priv->proxy_username = NULL;
88     }
89
90   if (priv->proxy_password)
91     {
92       g_free (priv->proxy_password);
93       priv->proxy_password = NULL;
94     }
95   
96   if (_g_uri_parse_authority (proxy, NULL, NULL, &userinfo))
97     {
98       if (userinfo)
99         {
100           gchar **split = g_strsplit (userinfo, ":", 2);
101
102           if (split[0] != NULL)
103             {
104               priv->proxy_username = g_uri_unescape_string (split[0], NULL);
105               if (split[1] != NULL)
106                 priv->proxy_password = g_uri_unescape_string (split[1], NULL);
107             }
108
109           g_strfreev (split);
110           g_free (userinfo);
111         }
112     }
113 }
114
115 static void
116 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
117 {
118   if (priv->proxy_address)
119     return;
120
121   while (priv->addr_enum == NULL && *priv->next_proxy)
122     {
123       GSocketConnectable *connectable = NULL;
124       const gchar *proxy_uri;
125       GProxy *proxy;
126
127       proxy_uri = *priv->next_proxy++;
128       g_free (priv->proxy_type);
129       priv->proxy_type = g_uri_parse_scheme (proxy_uri);
130
131       if (priv->proxy_type == NULL)
132         continue;
133
134       /* Assumes hostnames are supported for unkown protocols */
135       priv->supports_hostname = TRUE;
136       proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
137       if (proxy)
138         {
139           priv->supports_hostname = g_proxy_supports_hostname (proxy);
140           g_object_unref (proxy);
141         }
142
143       if (strcmp ("direct", priv->proxy_type) == 0)
144         {
145           if (priv->connectable)
146             connectable = g_object_ref (priv->connectable);
147           else
148             connectable = g_network_address_new (priv->dest_hostname,
149                                                  priv->dest_port);
150         }
151       else
152         {
153           GError *error = NULL;
154
155           connectable = g_network_address_parse_uri (proxy_uri, 0, &error);
156
157           if (error)
158             {
159               g_warning ("Invalid proxy URI '%s': %s",
160                          proxy_uri, error->message);
161               g_error_free (error);
162             }
163
164           save_userinfo (priv, proxy_uri);
165         }
166
167       if (connectable)
168         {
169           priv->addr_enum = g_socket_connectable_enumerate (connectable);
170           g_object_unref (connectable);
171         }
172     }
173 }
174
175 static GSocketAddress *
176 g_proxy_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
177                                  GCancellable              *cancellable,
178                                  GError                   **error)
179 {
180   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
181   GSocketAddress *result = NULL;
182   GError *first_error = NULL;
183
184   if (priv->proxies == NULL)
185     {
186       GProxyResolver *resolver = g_proxy_resolver_get_default ();
187       priv->proxies = g_proxy_resolver_lookup (resolver,
188                                                priv->dest_uri,
189                                                cancellable,
190                                                error);
191       priv->next_proxy = priv->proxies;
192
193       if (priv->proxies == NULL)
194         return NULL;
195     }
196
197   while (result == NULL && (*priv->next_proxy || priv->addr_enum))
198     {
199       gchar *dest_hostname;
200       GInetSocketAddress *inetsaddr;
201       GInetAddress *inetaddr;
202       guint16 port;
203
204       next_enumerator (priv);
205
206       if (!priv->addr_enum)
207         continue;
208
209       if (priv->proxy_address == NULL)
210         {
211           priv->proxy_address = g_socket_address_enumerator_next (
212                                     priv->addr_enum,
213                                     cancellable,
214                                     first_error ? NULL : &first_error);
215         }
216
217       if (priv->proxy_address == NULL)
218         {
219           g_object_unref (priv->addr_enum);
220           priv->addr_enum = NULL;
221
222           if (priv->dest_ips)
223             {
224               g_resolver_free_addresses (priv->dest_ips);
225               priv->dest_ips = NULL;
226             }
227
228           continue;
229         }
230
231       if (strcmp ("direct", priv->proxy_type) == 0)
232         {
233           result = priv->proxy_address;
234           priv->proxy_address = NULL;
235           continue;
236         }
237
238       if (!priv->supports_hostname)
239         {
240           GInetAddress *dest_ip;
241
242           if (!priv->dest_ips)
243             {
244               GResolver *resolver;
245
246               resolver = g_resolver_get_default();
247               priv->dest_ips = g_resolver_lookup_by_name (resolver,
248                                                           priv->dest_hostname,
249                                                           cancellable,
250                                                           first_error ? NULL : &first_error);
251               g_object_unref (resolver);
252
253               if (!priv->dest_ips)
254                 {
255                   g_object_unref (priv->proxy_address);
256                   priv->proxy_address = NULL;
257                   continue;
258                 }
259             }
260
261           if (!priv->next_dest_ip)
262             priv->next_dest_ip = priv->dest_ips;
263         
264           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
265           dest_hostname = g_inet_address_to_string (dest_ip);
266
267           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
268         }
269       else
270         {
271           dest_hostname = g_strdup (priv->dest_hostname);
272         }
273         
274                                   
275       g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address),
276                             NULL);
277
278       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
279       inetaddr = g_inet_socket_address_get_address (inetsaddr);
280       port = g_inet_socket_address_get_port (inetsaddr);
281
282       result = g_proxy_address_new (inetaddr, port,
283                                     priv->proxy_type,
284                                     dest_hostname, priv->dest_port,
285                                     priv->proxy_username,
286                                     priv->proxy_password);
287
288       g_free (dest_hostname);
289
290       if (priv->supports_hostname || priv->next_dest_ip == NULL)
291         {
292           g_object_unref (priv->proxy_address);
293           priv->proxy_address = NULL;
294         }
295     }
296
297   if (result == NULL && first_error)
298     g_propagate_error (error, first_error);
299   else if (first_error)
300     g_error_free (first_error);
301
302   return result;
303 }
304
305
306
307 static void
308 complete_async (GProxyAddressEnumeratorPrivate *priv)
309 {
310   GSimpleAsyncResult *simple = priv->simple;
311
312   if (priv->cancellable)
313     {
314       g_object_unref (priv->cancellable);
315       priv->cancellable = NULL;
316     }
317
318   priv->simple = NULL;
319   g_simple_async_result_complete (simple);
320   g_object_unref (simple);
321 }
322
323 static void
324 save_result (GProxyAddressEnumeratorPrivate *priv)
325 {
326   GSocketAddress *result;
327
328   if (strcmp ("direct", priv->proxy_type) == 0)
329     {
330       result = priv->proxy_address;
331       priv->proxy_address = NULL;
332     }
333   else
334     {
335       gchar *dest_hostname;
336       GInetSocketAddress *inetsaddr;
337       GInetAddress *inetaddr;
338       guint16 port;
339
340       if (!priv->supports_hostname)
341         {
342           GInetAddress *dest_ip;
343
344           if (!priv->next_dest_ip)
345             priv->next_dest_ip = priv->dest_ips;
346
347           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
348           dest_hostname = g_inet_address_to_string (dest_ip);
349
350           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
351         }
352       else
353         {
354           dest_hostname = g_strdup (priv->dest_hostname);
355         }
356
357       g_return_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address));
358
359       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
360       inetaddr = g_inet_socket_address_get_address (inetsaddr);
361       port = g_inet_socket_address_get_port (inetsaddr);
362
363       result = g_proxy_address_new (inetaddr, port,
364                                     priv->proxy_type,
365                                     dest_hostname, priv->dest_port,
366                                     priv->proxy_username,
367                                     priv->proxy_password);
368
369       g_free (dest_hostname);
370
371       if (priv->supports_hostname || priv->next_dest_ip == NULL)
372         {
373           g_object_unref (priv->proxy_address);
374           priv->proxy_address = NULL;
375         }
376     }
377
378   g_simple_async_result_set_op_res_gpointer (priv->simple,
379                                              result,
380                                              g_object_unref);
381 }
382
383 static void
384 dest_hostname_lookup_cb (GObject           *object,
385                          GAsyncResult      *result,
386                          gpointer           user_data)
387 {
388   GError *error = NULL;
389   GProxyAddressEnumeratorPrivate *priv = user_data;
390   GSimpleAsyncResult *simple = priv->simple;
391
392   priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
393                                                      result,
394                                                      &error);
395   if (priv->dest_ips)
396     {
397       save_result (priv);
398     }
399   else
400     {
401       g_simple_async_result_set_from_error (simple, error);
402       g_error_free (error);
403     }
404
405   complete_async (priv); 
406 }
407
408 static void
409 address_enumerate_cb (GObject      *object,
410                       GAsyncResult *result,
411                       gpointer      user_data)
412 {
413   GError *error = NULL;
414   GProxyAddressEnumeratorPrivate *priv = user_data;
415   GSimpleAsyncResult *simple = priv->simple;
416
417   priv->proxy_address =
418     g_socket_address_enumerator_next_finish (priv->addr_enum,
419                                              result,
420                                              &error);
421   if (priv->proxy_address)
422     {
423       if (!priv->supports_hostname && !priv->dest_ips)
424         {
425           GResolver *resolver;
426           resolver = g_resolver_get_default();
427           g_resolver_lookup_by_name_async (resolver,
428                                            priv->dest_hostname,
429                                            priv->cancellable,
430                                            dest_hostname_lookup_cb,
431                                            priv);
432           g_object_unref (resolver);
433           return;
434         }
435
436       save_result (priv);
437     }
438   else if (*priv->next_proxy)
439     {
440       g_object_unref (priv->addr_enum);
441       priv->addr_enum = NULL;
442
443       if (priv->dest_ips)
444         {
445           g_resolver_free_addresses (priv->dest_ips);
446           priv->dest_ips = NULL;
447         }
448
449       next_enumerator (priv);
450
451       if (priv->addr_enum)
452         {
453           g_socket_address_enumerator_next_async (priv->addr_enum,
454                                                   priv->cancellable,
455                                                   address_enumerate_cb,
456                                                   priv);
457           return;
458         }
459     }
460
461   if (error)
462     {
463       g_simple_async_result_set_from_error (simple, error);
464       g_error_free (error);
465     }
466
467   complete_async (priv); 
468 }
469
470 static void
471 proxy_lookup_cb (GObject      *object,
472                  GAsyncResult *result,
473                  gpointer      user_data)
474 {
475   GError *error = NULL;
476   GProxyAddressEnumeratorPrivate *priv = user_data;
477   GSimpleAsyncResult *simple = priv->simple;
478
479   priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
480                                                   result,
481                                                   &error);
482   priv->next_proxy = priv->proxies;
483
484   if (error)
485     {
486       g_simple_async_result_set_from_error (simple, error);
487       g_error_free (error);
488     }
489   else
490     {
491       next_enumerator (priv);
492       if (priv->addr_enum)
493         {
494           g_socket_address_enumerator_next_async (priv->addr_enum,
495                                                   priv->cancellable,
496                                                   address_enumerate_cb,
497                                                   priv);
498           return;
499         }
500     }
501
502   complete_async (priv); 
503 }
504
505 static void
506 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
507                                        GCancellable             *cancellable,
508                                        GAsyncReadyCallback       callback,
509                                        gpointer                  user_data)
510 {
511   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
512
513   g_return_if_fail (priv->simple == NULL);
514   g_return_if_fail (priv->cancellable == NULL);
515
516   priv->simple = g_simple_async_result_new (G_OBJECT (enumerator),
517                                             callback, user_data,
518                                             g_proxy_address_enumerator_next_async);
519
520   priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
521
522   if (priv->proxies == NULL)
523     {
524       GProxyResolver *resolver = g_proxy_resolver_get_default ();
525       g_proxy_resolver_lookup_async (resolver,
526                                      priv->dest_uri,
527                                      cancellable,
528                                      proxy_lookup_cb,
529                                      priv);
530       return;
531     }
532
533   if (priv->addr_enum)
534     {
535       if (priv->proxy_address)
536         {
537           save_result (priv);
538         }
539       else
540         {
541           g_socket_address_enumerator_next_async (priv->addr_enum,
542                                                   cancellable,
543                                                   address_enumerate_cb,
544                                                   priv);
545           return;
546         }
547     }
548
549   g_simple_async_result_complete_in_idle (priv->simple);
550
551   g_object_unref (priv->simple);
552   priv->simple = NULL;
553
554   if (priv->cancellable)
555     {
556       g_object_unref (priv->cancellable);
557       priv->cancellable = NULL;
558     }
559 }
560
561 static GSocketAddress *
562 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
563                                         GAsyncResult              *result,
564                                         GError                   **error)
565 {
566   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
567   GSocketAddress *address;
568
569   if (g_simple_async_result_propagate_error (simple, error))
570     return NULL;
571
572   address = g_simple_async_result_get_op_res_gpointer (simple);
573   if (address)
574     g_object_ref (address);
575
576   return address;
577 }
578
579 static void
580 g_proxy_address_enumerator_get_property (GObject        *object,
581                                          guint           property_id,
582                                          GValue         *value,
583                                          GParamSpec     *pspec)
584 {
585   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
586   switch (property_id)
587     {
588       case PROP_URI:
589         g_value_set_string (value, priv->dest_uri);
590         break;
591
592       case PROP_CONNECTABLE:
593         g_value_set_object (value, priv->connectable);
594         break;
595
596       default:
597         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
598     }
599 }
600
601 static void
602 g_proxy_address_enumerator_set_property (GObject        *object,
603                                          guint           property_id,
604                                          const GValue   *value,
605                                          GParamSpec     *pspec)
606 {
607   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
608   switch (property_id)
609     {
610       case PROP_URI:
611           {
612             const gchar *uri;
613
614             g_free (priv->dest_hostname);
615             priv->dest_hostname = NULL;
616             priv->dest_port = 0;
617
618             g_free (priv->dest_uri);
619             priv->dest_uri = NULL;
620
621             uri = g_value_get_string (value);
622
623             if (uri)
624               {
625                 GSocketConnectable *conn;
626
627                 conn = g_network_address_parse_uri (uri, 0, NULL);
628                 if (conn)
629                   {
630                     priv->dest_uri = g_strdup (uri);
631                     
632                     g_object_get (conn,
633                                   "hostname", &priv->dest_hostname,
634                                   "port", &priv->dest_port,
635                                   NULL);
636
637                     g_object_unref (conn);
638                   }
639                 else
640                   g_warning ("Invalid URI '%s'", uri);
641               }
642
643             break;
644           }
645
646       case PROP_CONNECTABLE:
647           priv->connectable = g_value_dup_object (value);
648           break;
649
650       default:
651         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
652     }
653 }
654
655 static void
656 g_proxy_address_enumerator_finalize (GObject *object)
657 {
658   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
659
660   if (priv->connectable)
661     g_object_unref (priv->connectable);
662
663   g_free (priv->dest_uri);
664   g_free (priv->dest_hostname);
665
666   if (priv->dest_ips)
667     g_resolver_free_addresses (priv->dest_ips);
668
669   g_strfreev (priv->proxies);
670
671   if (priv->addr_enum)
672     g_object_unref (priv->addr_enum);
673
674   g_free (priv->proxy_type);
675   g_free (priv->proxy_username);
676   g_free (priv->proxy_password);
677
678   if (priv->cancellable)
679     g_object_unref (priv->cancellable);
680
681   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
682 }
683
684 static void
685 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
686 {
687   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
688                                             G_TYPE_PROXY_ADDRESS_ENUMERATOR,
689                                             GProxyAddressEnumeratorPrivate);
690 }
691
692 static void
693 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
694 {
695   GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
696   GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
697
698   g_type_class_add_private (enumerator_class,
699                             sizeof (GProxyAddressEnumeratorPrivate));
700
701   object_class->set_property = g_proxy_address_enumerator_set_property;
702   object_class->get_property = g_proxy_address_enumerator_get_property;
703   object_class->finalize = g_proxy_address_enumerator_finalize;
704
705   enumerator_class->next = g_proxy_address_enumerator_next;
706   enumerator_class->next_async = g_proxy_address_enumerator_next_async;
707   enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
708
709   g_object_class_install_property (object_class,
710                                    PROP_URI,
711                                    g_param_spec_string ("uri",
712                                                         P_("URI"),
713                                                         P_("The destination URI, use none:// for generic socket"),
714                                                         NULL,
715                                                         G_PARAM_READWRITE |
716                                                         G_PARAM_CONSTRUCT_ONLY |
717                                                         G_PARAM_STATIC_STRINGS));
718
719   g_object_class_install_property (object_class,
720                                    PROP_CONNECTABLE,
721                                    g_param_spec_object ("connectable",
722                                                         P_("Connectable"),
723                                                         P_("The connectable being enumerated."),
724                                                         G_TYPE_SOCKET_CONNECTABLE,
725                                                         G_PARAM_READWRITE |
726                                                         G_PARAM_CONSTRUCT_ONLY |
727                                                         G_PARAM_STATIC_STRINGS));
728 }