gio: port networking classes from GSimpleAsyncResult to GTask
[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 "gtask.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   const gchar              *proxy_uri;
68   gchar                    *proxy_type;
69   gchar                    *proxy_username;
70   gchar                    *proxy_password;
71   gboolean                  supports_hostname;
72   GList                    *next_dest_ip;
73   GError                   *last_error;
74 };
75
76 static void
77 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
78                const gchar *proxy)
79 {
80   gchar *userinfo;
81
82   if (priv->proxy_username)
83     {
84       g_free (priv->proxy_username);
85       priv->proxy_username = NULL;
86     }
87
88   if (priv->proxy_password)
89     {
90       g_free (priv->proxy_password);
91       priv->proxy_password = NULL;
92     }
93   
94   if (_g_uri_parse_authority (proxy, NULL, NULL, &userinfo))
95     {
96       if (userinfo)
97         {
98           gchar **split = g_strsplit (userinfo, ":", 2);
99
100           if (split[0] != NULL)
101             {
102               priv->proxy_username = g_uri_unescape_string (split[0], NULL);
103               if (split[1] != NULL)
104                 priv->proxy_password = g_uri_unescape_string (split[1], NULL);
105             }
106
107           g_strfreev (split);
108           g_free (userinfo);
109         }
110     }
111 }
112
113 static void
114 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
115 {
116   if (priv->proxy_address)
117     return;
118
119   while (priv->addr_enum == NULL && *priv->next_proxy)
120     {
121       GSocketConnectable *connectable = NULL;
122       GProxy *proxy;
123
124       priv->proxy_uri = *priv->next_proxy++;
125       g_free (priv->proxy_type);
126       priv->proxy_type = g_uri_parse_scheme (priv->proxy_uri);
127
128       if (priv->proxy_type == NULL)
129         continue;
130
131       /* Assumes hostnames are supported for unknown protocols */
132       priv->supports_hostname = TRUE;
133       proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
134       if (proxy)
135         {
136           priv->supports_hostname = g_proxy_supports_hostname (proxy);
137           g_object_unref (proxy);
138         }
139
140       if (strcmp ("direct", priv->proxy_type) == 0)
141         {
142           if (priv->connectable)
143             connectable = g_object_ref (priv->connectable);
144           else
145             connectable = g_network_address_new (priv->dest_hostname,
146                                                  priv->dest_port);
147         }
148       else
149         {
150           GError *error = NULL;
151
152           connectable = g_network_address_parse_uri (priv->proxy_uri, 0, &error);
153
154           if (error)
155             {
156               g_warning ("Invalid proxy URI '%s': %s",
157                          priv->proxy_uri, error->message);
158               g_error_free (error);
159             }
160
161           save_userinfo (priv, priv->proxy_uri);
162         }
163
164       if (connectable)
165         {
166           priv->addr_enum = g_socket_connectable_enumerate (connectable);
167           g_object_unref (connectable);
168         }
169     }
170 }
171
172 static GSocketAddress *
173 g_proxy_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
174                                  GCancellable              *cancellable,
175                                  GError                   **error)
176 {
177   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
178   GSocketAddress *result = NULL;
179   GError *first_error = NULL;
180
181   if (priv->proxies == NULL)
182     {
183       GProxyResolver *resolver = g_proxy_resolver_get_default ();
184       priv->proxies = g_proxy_resolver_lookup (resolver,
185                                                priv->dest_uri,
186                                                cancellable,
187                                                error);
188       priv->next_proxy = priv->proxies;
189
190       if (priv->proxies == NULL)
191         return NULL;
192     }
193
194   while (result == NULL && (*priv->next_proxy || priv->addr_enum))
195     {
196       gchar *dest_hostname;
197       gchar *dest_protocol;
198       GInetSocketAddress *inetsaddr;
199       GInetAddress *inetaddr;
200       guint16 port;
201
202       next_enumerator (priv);
203
204       if (!priv->addr_enum)
205         continue;
206
207       if (priv->proxy_address == NULL)
208         {
209           priv->proxy_address = g_socket_address_enumerator_next (
210                                     priv->addr_enum,
211                                     cancellable,
212                                     first_error ? NULL : &first_error);
213         }
214
215       if (priv->proxy_address == NULL)
216         {
217           g_object_unref (priv->addr_enum);
218           priv->addr_enum = NULL;
219
220           if (priv->dest_ips)
221             {
222               g_resolver_free_addresses (priv->dest_ips);
223               priv->dest_ips = NULL;
224             }
225
226           continue;
227         }
228
229       if (strcmp ("direct", priv->proxy_type) == 0)
230         {
231           result = priv->proxy_address;
232           priv->proxy_address = NULL;
233           continue;
234         }
235
236       if (!priv->supports_hostname)
237         {
238           GInetAddress *dest_ip;
239
240           if (!priv->dest_ips)
241             {
242               GResolver *resolver;
243
244               resolver = g_resolver_get_default();
245               priv->dest_ips = g_resolver_lookup_by_name (resolver,
246                                                           priv->dest_hostname,
247                                                           cancellable,
248                                                           first_error ? NULL : &first_error);
249               g_object_unref (resolver);
250
251               if (!priv->dest_ips)
252                 {
253                   g_object_unref (priv->proxy_address);
254                   priv->proxy_address = NULL;
255                   continue;
256                 }
257             }
258
259           if (!priv->next_dest_ip)
260             priv->next_dest_ip = priv->dest_ips;
261         
262           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
263           dest_hostname = g_inet_address_to_string (dest_ip);
264
265           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
266         }
267       else
268         {
269           dest_hostname = g_strdup (priv->dest_hostname);
270         }
271       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
272                                   
273       g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address),
274                             NULL);
275
276       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
277       inetaddr = g_inet_socket_address_get_address (inetsaddr);
278       port = g_inet_socket_address_get_port (inetsaddr);
279
280       result = g_object_new (G_TYPE_PROXY_ADDRESS,
281                              "address", inetaddr,
282                              "port", port,
283                              "protocol", priv->proxy_type,
284                              "destination-protocol", dest_protocol,
285                              "destination-hostname", dest_hostname,
286                              "destination-port", priv->dest_port,
287                              "username", priv->proxy_username,
288                              "password", priv->proxy_password,
289                              "uri", priv->proxy_uri,
290                              NULL);
291       g_free (dest_hostname);
292       g_free (dest_protocol);
293
294       if (priv->supports_hostname || priv->next_dest_ip == NULL)
295         {
296           g_object_unref (priv->proxy_address);
297           priv->proxy_address = NULL;
298         }
299     }
300
301   if (result == NULL && first_error)
302     g_propagate_error (error, first_error);
303   else if (first_error)
304     g_error_free (first_error);
305
306   return result;
307 }
308
309
310
311 static void
312 complete_async (GTask *task)
313 {
314   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
315
316   if (priv->last_error)
317     {
318       g_task_return_error (task, priv->last_error);
319       priv->last_error = NULL;
320     }
321   else
322     g_task_return_pointer (task, NULL, NULL);
323
324   g_object_unref (task);
325 }
326
327 static void
328 return_result (GTask *task)
329 {
330   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
331   GSocketAddress *result;
332
333   if (strcmp ("direct", priv->proxy_type) == 0)
334     {
335       result = priv->proxy_address;
336       priv->proxy_address = NULL;
337     }
338   else
339     {
340       gchar *dest_hostname, *dest_protocol;
341       GInetSocketAddress *inetsaddr;
342       GInetAddress *inetaddr;
343       guint16 port;
344
345       if (!priv->supports_hostname)
346         {
347           GInetAddress *dest_ip;
348
349           if (!priv->next_dest_ip)
350             priv->next_dest_ip = priv->dest_ips;
351
352           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
353           dest_hostname = g_inet_address_to_string (dest_ip);
354
355           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
356         }
357       else
358         {
359           dest_hostname = g_strdup (priv->dest_hostname);
360         }
361       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
362
363       g_return_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address));
364
365       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
366       inetaddr = g_inet_socket_address_get_address (inetsaddr);
367       port = g_inet_socket_address_get_port (inetsaddr);
368
369       result = g_object_new (G_TYPE_PROXY_ADDRESS,
370                              "address", inetaddr,
371                              "port", port,
372                              "protocol", priv->proxy_type,
373                              "destination-protocol", dest_protocol,
374                              "destination-hostname", dest_hostname,
375                              "destination-port", priv->dest_port,
376                              "username", priv->proxy_username,
377                              "password", priv->proxy_password,
378                              "uri", priv->proxy_uri,
379                              NULL);
380       g_free (dest_hostname);
381       g_free (dest_protocol);
382
383       if (priv->supports_hostname || priv->next_dest_ip == NULL)
384         {
385           g_object_unref (priv->proxy_address);
386           priv->proxy_address = NULL;
387         }
388     }
389
390   g_task_return_pointer (task, result, g_object_unref);
391   g_object_unref (task);
392 }
393
394 static void address_enumerate_cb (GObject      *object,
395                                   GAsyncResult *result,
396                                   gpointer      user_data);
397
398 static void
399 next_proxy (GTask *task)
400 {
401   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
402
403   if (*priv->next_proxy)
404     {
405       g_object_unref (priv->addr_enum);
406       priv->addr_enum = NULL;
407
408       if (priv->dest_ips)
409         {
410           g_resolver_free_addresses (priv->dest_ips);
411           priv->dest_ips = NULL;
412         }
413
414       next_enumerator (priv);
415
416       if (priv->addr_enum)
417         {
418           g_socket_address_enumerator_next_async (priv->addr_enum,
419                                                   g_task_get_cancellable (task),
420                                                   address_enumerate_cb,
421                                                   task);
422           return;
423         }
424     }
425
426   complete_async (task);
427 }
428
429 static void
430 dest_hostname_lookup_cb (GObject           *object,
431                          GAsyncResult      *result,
432                          gpointer           user_data)
433 {
434   GTask *task = user_data;
435   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
436
437   g_clear_error (&priv->last_error);
438   priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
439                                                      result,
440                                                      &priv->last_error);
441   if (priv->dest_ips)
442     return_result (task);
443   else
444     {
445       g_clear_object (&priv->proxy_address);
446       next_proxy (task);
447     }
448 }
449
450 static void
451 address_enumerate_cb (GObject      *object,
452                       GAsyncResult *result,
453                       gpointer      user_data)
454 {
455   GTask *task = user_data;
456   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
457
458   g_clear_error (&priv->last_error);
459   priv->proxy_address =
460     g_socket_address_enumerator_next_finish (priv->addr_enum,
461                                              result,
462                                              &priv->last_error);
463   if (priv->proxy_address)
464     {
465       if (!priv->supports_hostname && !priv->dest_ips)
466         {
467           GResolver *resolver;
468           resolver = g_resolver_get_default();
469           g_resolver_lookup_by_name_async (resolver,
470                                            priv->dest_hostname,
471                                            g_task_get_cancellable (task),
472                                            dest_hostname_lookup_cb,
473                                            task);
474           g_object_unref (resolver);
475           return;
476         }
477
478       return_result (task);
479     }
480   else
481     next_proxy (task);
482 }
483
484 static void
485 proxy_lookup_cb (GObject      *object,
486                  GAsyncResult *result,
487                  gpointer      user_data)
488 {
489   GTask *task = user_data;
490   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
491
492   g_clear_error (&priv->last_error);
493   priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
494                                                   result,
495                                                   &priv->last_error);
496   priv->next_proxy = priv->proxies;
497
498   if (priv->last_error)
499     {
500       complete_async (task);
501       return;
502     }
503   else
504     {
505       next_enumerator (priv);
506       if (priv->addr_enum)
507         {
508           g_socket_address_enumerator_next_async (priv->addr_enum,
509                                                   g_task_get_cancellable (task),
510                                                   address_enumerate_cb,
511                                                   task);
512           return;
513         }
514     }
515
516   complete_async (task);
517 }
518
519 static void
520 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
521                                        GCancellable             *cancellable,
522                                        GAsyncReadyCallback       callback,
523                                        gpointer                  user_data)
524 {
525   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
526   GTask *task;
527
528   task = g_task_new (enumerator, cancellable, callback, user_data);
529   g_task_set_task_data (task, priv, NULL);
530
531   if (priv->proxies == NULL)
532     {
533       GProxyResolver *resolver = g_proxy_resolver_get_default ();
534       g_proxy_resolver_lookup_async (resolver,
535                                      priv->dest_uri,
536                                      cancellable,
537                                      proxy_lookup_cb,
538                                      task);
539       return;
540     }
541
542   if (priv->addr_enum)
543     {
544       if (priv->proxy_address)
545         {
546           return_result (task);
547           return;
548         }
549       else
550         {
551           g_socket_address_enumerator_next_async (priv->addr_enum,
552                                                   cancellable,
553                                                   address_enumerate_cb,
554                                                   task);
555           return;
556         }
557     }
558
559   complete_async (task);
560 }
561
562 static GSocketAddress *
563 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
564                                         GAsyncResult              *result,
565                                         GError                   **error)
566 {
567   g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
568
569   return g_task_propagate_pointer (G_TASK (result), error);
570 }
571
572 static void
573 g_proxy_address_enumerator_get_property (GObject        *object,
574                                          guint           property_id,
575                                          GValue         *value,
576                                          GParamSpec     *pspec)
577 {
578   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
579   switch (property_id)
580     {
581       case PROP_URI:
582         g_value_set_string (value, priv->dest_uri);
583         break;
584
585       case PROP_CONNECTABLE:
586         g_value_set_object (value, priv->connectable);
587         break;
588
589       default:
590         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
591     }
592 }
593
594 static void
595 g_proxy_address_enumerator_set_property (GObject        *object,
596                                          guint           property_id,
597                                          const GValue   *value,
598                                          GParamSpec     *pspec)
599 {
600   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
601   switch (property_id)
602     {
603       case PROP_URI:
604           {
605             const gchar *uri;
606
607             g_free (priv->dest_hostname);
608             priv->dest_hostname = NULL;
609             priv->dest_port = 0;
610
611             g_free (priv->dest_uri);
612             priv->dest_uri = NULL;
613
614             uri = g_value_get_string (value);
615
616             if (uri)
617               {
618                 GSocketConnectable *conn;
619
620                 conn = g_network_address_parse_uri (uri, 0, NULL);
621                 if (conn)
622                   {
623                     guint port;
624
625                     priv->dest_uri = g_strdup (uri);
626                     
627                     g_object_get (conn,
628                                   "hostname", &priv->dest_hostname,
629                                   "port", &port,
630                                   NULL);
631
632                     priv->dest_port = port;
633                     g_object_unref (conn);
634                   }
635                 else
636                   g_warning ("Invalid URI '%s'", uri);
637               }
638
639             break;
640           }
641
642       case PROP_CONNECTABLE:
643           priv->connectable = g_value_dup_object (value);
644           break;
645
646       default:
647         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
648     }
649 }
650
651 static void
652 g_proxy_address_enumerator_finalize (GObject *object)
653 {
654   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
655
656   if (priv->connectable)
657     g_object_unref (priv->connectable);
658
659   g_free (priv->dest_uri);
660   g_free (priv->dest_hostname);
661
662   if (priv->dest_ips)
663     g_resolver_free_addresses (priv->dest_ips);
664
665   g_strfreev (priv->proxies);
666
667   if (priv->addr_enum)
668     g_object_unref (priv->addr_enum);
669
670   g_free (priv->proxy_type);
671   g_free (priv->proxy_username);
672   g_free (priv->proxy_password);
673
674   g_clear_error (&priv->last_error);
675
676   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
677 }
678
679 static void
680 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
681 {
682   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
683                                             G_TYPE_PROXY_ADDRESS_ENUMERATOR,
684                                             GProxyAddressEnumeratorPrivate);
685 }
686
687 static void
688 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
689 {
690   GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
691   GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
692
693   g_type_class_add_private (enumerator_class,
694                             sizeof (GProxyAddressEnumeratorPrivate));
695
696   object_class->set_property = g_proxy_address_enumerator_set_property;
697   object_class->get_property = g_proxy_address_enumerator_get_property;
698   object_class->finalize = g_proxy_address_enumerator_finalize;
699
700   enumerator_class->next = g_proxy_address_enumerator_next;
701   enumerator_class->next_async = g_proxy_address_enumerator_next_async;
702   enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
703
704   g_object_class_install_property (object_class,
705                                    PROP_URI,
706                                    g_param_spec_string ("uri",
707                                                         P_("URI"),
708                                                         P_("The destination URI, use none:// for generic socket"),
709                                                         NULL,
710                                                         G_PARAM_READWRITE |
711                                                         G_PARAM_CONSTRUCT_ONLY |
712                                                         G_PARAM_STATIC_STRINGS));
713
714   g_object_class_install_property (object_class,
715                                    PROP_CONNECTABLE,
716                                    g_param_spec_object ("connectable",
717                                                         P_("Connectable"),
718                                                         P_("The connectable being enumerated."),
719                                                         G_TYPE_SOCKET_CONNECTABLE,
720                                                         G_PARAM_READWRITE |
721                                                         G_PARAM_CONSTRUCT_ONLY |
722                                                         G_PARAM_STATIC_STRINGS));
723 }