Merge remote-tracking branch 'gvdb/master'
[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 unknown 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     save_result (priv);
397   else
398     g_simple_async_result_take_error (simple, error);
399
400   complete_async (priv); 
401 }
402
403 static void
404 address_enumerate_cb (GObject      *object,
405                       GAsyncResult *result,
406                       gpointer      user_data)
407 {
408   GError *error = NULL;
409   GProxyAddressEnumeratorPrivate *priv = user_data;
410   GSimpleAsyncResult *simple = priv->simple;
411
412   priv->proxy_address =
413     g_socket_address_enumerator_next_finish (priv->addr_enum,
414                                              result,
415                                              &error);
416   if (priv->proxy_address)
417     {
418       if (!priv->supports_hostname && !priv->dest_ips)
419         {
420           GResolver *resolver;
421           resolver = g_resolver_get_default();
422           g_resolver_lookup_by_name_async (resolver,
423                                            priv->dest_hostname,
424                                            priv->cancellable,
425                                            dest_hostname_lookup_cb,
426                                            priv);
427           g_object_unref (resolver);
428           return;
429         }
430
431       save_result (priv);
432     }
433   else if (*priv->next_proxy)
434     {
435       g_object_unref (priv->addr_enum);
436       priv->addr_enum = NULL;
437
438       if (priv->dest_ips)
439         {
440           g_resolver_free_addresses (priv->dest_ips);
441           priv->dest_ips = NULL;
442         }
443
444       next_enumerator (priv);
445
446       if (priv->addr_enum)
447         {
448           g_socket_address_enumerator_next_async (priv->addr_enum,
449                                                   priv->cancellable,
450                                                   address_enumerate_cb,
451                                                   priv);
452           return;
453         }
454     }
455
456   if (error)
457     g_simple_async_result_take_error (simple, error);
458
459   complete_async (priv); 
460 }
461
462 static void
463 proxy_lookup_cb (GObject      *object,
464                  GAsyncResult *result,
465                  gpointer      user_data)
466 {
467   GError *error = NULL;
468   GProxyAddressEnumeratorPrivate *priv = user_data;
469   GSimpleAsyncResult *simple = priv->simple;
470
471   priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
472                                                   result,
473                                                   &error);
474   priv->next_proxy = priv->proxies;
475
476   if (error)
477     {
478       g_simple_async_result_take_error (simple, error);
479     }
480   else
481     {
482       next_enumerator (priv);
483       if (priv->addr_enum)
484         {
485           g_socket_address_enumerator_next_async (priv->addr_enum,
486                                                   priv->cancellable,
487                                                   address_enumerate_cb,
488                                                   priv);
489           return;
490         }
491     }
492
493   complete_async (priv); 
494 }
495
496 static void
497 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
498                                        GCancellable             *cancellable,
499                                        GAsyncReadyCallback       callback,
500                                        gpointer                  user_data)
501 {
502   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
503
504   g_return_if_fail (priv->simple == NULL);
505   g_return_if_fail (priv->cancellable == NULL);
506
507   priv->simple = g_simple_async_result_new (G_OBJECT (enumerator),
508                                             callback, user_data,
509                                             g_proxy_address_enumerator_next_async);
510
511   priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
512
513   if (priv->proxies == NULL)
514     {
515       GProxyResolver *resolver = g_proxy_resolver_get_default ();
516       g_proxy_resolver_lookup_async (resolver,
517                                      priv->dest_uri,
518                                      cancellable,
519                                      proxy_lookup_cb,
520                                      priv);
521       return;
522     }
523
524   if (priv->addr_enum)
525     {
526       if (priv->proxy_address)
527         {
528           save_result (priv);
529         }
530       else
531         {
532           g_socket_address_enumerator_next_async (priv->addr_enum,
533                                                   cancellable,
534                                                   address_enumerate_cb,
535                                                   priv);
536           return;
537         }
538     }
539
540   g_simple_async_result_complete_in_idle (priv->simple);
541
542   g_object_unref (priv->simple);
543   priv->simple = NULL;
544
545   if (priv->cancellable)
546     {
547       g_object_unref (priv->cancellable);
548       priv->cancellable = NULL;
549     }
550 }
551
552 static GSocketAddress *
553 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
554                                         GAsyncResult              *result,
555                                         GError                   **error)
556 {
557   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
558   GSocketAddress *address;
559
560   if (g_simple_async_result_propagate_error (simple, error))
561     return NULL;
562
563   address = g_simple_async_result_get_op_res_gpointer (simple);
564   if (address)
565     g_object_ref (address);
566
567   return address;
568 }
569
570 static void
571 g_proxy_address_enumerator_get_property (GObject        *object,
572                                          guint           property_id,
573                                          GValue         *value,
574                                          GParamSpec     *pspec)
575 {
576   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
577   switch (property_id)
578     {
579       case PROP_URI:
580         g_value_set_string (value, priv->dest_uri);
581         break;
582
583       case PROP_CONNECTABLE:
584         g_value_set_object (value, priv->connectable);
585         break;
586
587       default:
588         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
589     }
590 }
591
592 static void
593 g_proxy_address_enumerator_set_property (GObject        *object,
594                                          guint           property_id,
595                                          const GValue   *value,
596                                          GParamSpec     *pspec)
597 {
598   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
599   switch (property_id)
600     {
601       case PROP_URI:
602           {
603             const gchar *uri;
604
605             g_free (priv->dest_hostname);
606             priv->dest_hostname = NULL;
607             priv->dest_port = 0;
608
609             g_free (priv->dest_uri);
610             priv->dest_uri = NULL;
611
612             uri = g_value_get_string (value);
613
614             if (uri)
615               {
616                 GSocketConnectable *conn;
617
618                 conn = g_network_address_parse_uri (uri, 0, NULL);
619                 if (conn)
620                   {
621                     guint port;
622
623                     priv->dest_uri = g_strdup (uri);
624                     
625                     g_object_get (conn,
626                                   "hostname", &priv->dest_hostname,
627                                   "port", &port,
628                                   NULL);
629
630                     priv->dest_port = port;
631                     g_object_unref (conn);
632                   }
633                 else
634                   g_warning ("Invalid URI '%s'", uri);
635               }
636
637             break;
638           }
639
640       case PROP_CONNECTABLE:
641           priv->connectable = g_value_dup_object (value);
642           break;
643
644       default:
645         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
646     }
647 }
648
649 static void
650 g_proxy_address_enumerator_finalize (GObject *object)
651 {
652   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
653
654   if (priv->connectable)
655     g_object_unref (priv->connectable);
656
657   g_free (priv->dest_uri);
658   g_free (priv->dest_hostname);
659
660   if (priv->dest_ips)
661     g_resolver_free_addresses (priv->dest_ips);
662
663   g_strfreev (priv->proxies);
664
665   if (priv->addr_enum)
666     g_object_unref (priv->addr_enum);
667
668   g_free (priv->proxy_type);
669   g_free (priv->proxy_username);
670   g_free (priv->proxy_password);
671
672   if (priv->cancellable)
673     g_object_unref (priv->cancellable);
674
675   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
676 }
677
678 static void
679 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
680 {
681   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
682                                             G_TYPE_PROXY_ADDRESS_ENUMERATOR,
683                                             GProxyAddressEnumeratorPrivate);
684 }
685
686 static void
687 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
688 {
689   GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
690   GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
691
692   g_type_class_add_private (enumerator_class,
693                             sizeof (GProxyAddressEnumeratorPrivate));
694
695   object_class->set_property = g_proxy_address_enumerator_set_property;
696   object_class->get_property = g_proxy_address_enumerator_get_property;
697   object_class->finalize = g_proxy_address_enumerator_finalize;
698
699   enumerator_class->next = g_proxy_address_enumerator_next;
700   enumerator_class->next_async = g_proxy_address_enumerator_next_async;
701   enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
702
703   g_object_class_install_property (object_class,
704                                    PROP_URI,
705                                    g_param_spec_string ("uri",
706                                                         P_("URI"),
707                                                         P_("The destination URI, use none:// for generic socket"),
708                                                         NULL,
709                                                         G_PARAM_READWRITE |
710                                                         G_PARAM_CONSTRUCT_ONLY |
711                                                         G_PARAM_STATIC_STRINGS));
712
713   g_object_class_install_property (object_class,
714                                    PROP_CONNECTABLE,
715                                    g_param_spec_object ("connectable",
716                                                         P_("Connectable"),
717                                                         P_("The connectable being enumerated."),
718                                                         G_TYPE_SOCKET_CONNECTABLE,
719                                                         G_PARAM_READWRITE |
720                                                         G_PARAM_CONSTRUCT_ONLY |
721                                                         G_PARAM_STATIC_STRINGS));
722 }