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