Improve test coverage for GSubprocess
[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 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
43
44 enum
45 {
46   PROP_0,
47   PROP_URI,
48   PROP_DEFAULT_PORT,
49   PROP_CONNECTABLE,
50   PROP_PROXY_RESOLVER
51 };
52
53 struct _GProxyAddressEnumeratorPrivate
54 {
55   /* Destination address */
56   GSocketConnectable *connectable;
57   gchar              *dest_uri;
58   guint16             default_port;
59   gchar              *dest_hostname;
60   guint16             dest_port;
61   GList              *dest_ips;
62
63   /* Proxy enumeration */
64   GProxyResolver           *proxy_resolver;
65   gchar                   **proxies;
66   gchar                   **next_proxy;
67   GSocketAddressEnumerator *addr_enum;
68   GSocketAddress           *proxy_address;
69   const gchar              *proxy_uri;
70   gchar                    *proxy_type;
71   gchar                    *proxy_username;
72   gchar                    *proxy_password;
73   gboolean                  supports_hostname;
74   GList                    *next_dest_ip;
75   GError                   *last_error;
76 };
77
78 G_DEFINE_TYPE_WITH_PRIVATE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
79
80 static void
81 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
82                const gchar *proxy)
83 {
84   gchar *userinfo;
85
86   if (priv->proxy_username)
87     {
88       g_free (priv->proxy_username);
89       priv->proxy_username = NULL;
90     }
91
92   if (priv->proxy_password)
93     {
94       g_free (priv->proxy_password);
95       priv->proxy_password = NULL;
96     }
97   
98   if (_g_uri_parse_authority (proxy, NULL, NULL, &userinfo))
99     {
100       if (userinfo)
101         {
102           gchar **split = g_strsplit (userinfo, ":", 2);
103
104           if (split[0] != NULL)
105             {
106               priv->proxy_username = g_uri_unescape_string (split[0], NULL);
107               if (split[1] != NULL)
108                 priv->proxy_password = g_uri_unescape_string (split[1], NULL);
109             }
110
111           g_strfreev (split);
112           g_free (userinfo);
113         }
114     }
115 }
116
117 static void
118 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
119 {
120   if (priv->proxy_address)
121     return;
122
123   while (priv->addr_enum == NULL && *priv->next_proxy)
124     {
125       GSocketConnectable *connectable = NULL;
126       GProxy *proxy;
127
128       priv->proxy_uri = *priv->next_proxy++;
129       g_free (priv->proxy_type);
130       priv->proxy_type = g_uri_parse_scheme (priv->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 (priv->proxy_uri, 0, &error);
157
158           if (error)
159             {
160               g_warning ("Invalid proxy URI '%s': %s",
161                          priv->proxy_uri, error->message);
162               g_error_free (error);
163             }
164
165           save_userinfo (priv, 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       priv->proxies = g_proxy_resolver_lookup (priv->proxy_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       gchar *dest_protocol;
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       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
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_object_new (G_TYPE_PROXY_ADDRESS,
284                              "address", inetaddr,
285                              "port", port,
286                              "protocol", priv->proxy_type,
287                              "destination-protocol", dest_protocol,
288                              "destination-hostname", dest_hostname,
289                              "destination-port", priv->dest_port,
290                              "username", priv->proxy_username,
291                              "password", priv->proxy_password,
292                              "uri", priv->proxy_uri,
293                              NULL);
294       g_free (dest_hostname);
295       g_free (dest_protocol);
296
297       if (priv->supports_hostname || priv->next_dest_ip == NULL)
298         {
299           g_object_unref (priv->proxy_address);
300           priv->proxy_address = NULL;
301         }
302     }
303
304   if (result == NULL && first_error)
305     g_propagate_error (error, first_error);
306   else if (first_error)
307     g_error_free (first_error);
308
309   return result;
310 }
311
312
313
314 static void
315 complete_async (GTask *task)
316 {
317   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
318
319   if (priv->last_error)
320     {
321       g_task_return_error (task, priv->last_error);
322       priv->last_error = NULL;
323     }
324   else
325     g_task_return_pointer (task, NULL, NULL);
326
327   g_object_unref (task);
328 }
329
330 static void
331 return_result (GTask *task)
332 {
333   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
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, *dest_protocol;
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       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
365
366       g_return_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address));
367
368       inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
369       inetaddr = g_inet_socket_address_get_address (inetsaddr);
370       port = g_inet_socket_address_get_port (inetsaddr);
371
372       result = g_object_new (G_TYPE_PROXY_ADDRESS,
373                              "address", inetaddr,
374                              "port", port,
375                              "protocol", priv->proxy_type,
376                              "destination-protocol", dest_protocol,
377                              "destination-hostname", dest_hostname,
378                              "destination-port", priv->dest_port,
379                              "username", priv->proxy_username,
380                              "password", priv->proxy_password,
381                              "uri", priv->proxy_uri,
382                              NULL);
383       g_free (dest_hostname);
384       g_free (dest_protocol);
385
386       if (priv->supports_hostname || priv->next_dest_ip == NULL)
387         {
388           g_object_unref (priv->proxy_address);
389           priv->proxy_address = NULL;
390         }
391     }
392
393   g_task_return_pointer (task, result, g_object_unref);
394   g_object_unref (task);
395 }
396
397 static void address_enumerate_cb (GObject      *object,
398                                   GAsyncResult *result,
399                                   gpointer      user_data);
400
401 static void
402 next_proxy (GTask *task)
403 {
404   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
405
406   if (*priv->next_proxy)
407     {
408       g_object_unref (priv->addr_enum);
409       priv->addr_enum = NULL;
410
411       if (priv->dest_ips)
412         {
413           g_resolver_free_addresses (priv->dest_ips);
414           priv->dest_ips = NULL;
415         }
416
417       next_enumerator (priv);
418
419       if (priv->addr_enum)
420         {
421           g_socket_address_enumerator_next_async (priv->addr_enum,
422                                                   g_task_get_cancellable (task),
423                                                   address_enumerate_cb,
424                                                   task);
425           return;
426         }
427     }
428
429   complete_async (task);
430 }
431
432 static void
433 dest_hostname_lookup_cb (GObject           *object,
434                          GAsyncResult      *result,
435                          gpointer           user_data)
436 {
437   GTask *task = user_data;
438   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
439
440   g_clear_error (&priv->last_error);
441   priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
442                                                      result,
443                                                      &priv->last_error);
444   if (priv->dest_ips)
445     return_result (task);
446   else
447     {
448       g_clear_object (&priv->proxy_address);
449       next_proxy (task);
450     }
451 }
452
453 static void
454 address_enumerate_cb (GObject      *object,
455                       GAsyncResult *result,
456                       gpointer      user_data)
457 {
458   GTask *task = user_data;
459   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
460
461   g_clear_error (&priv->last_error);
462   priv->proxy_address =
463     g_socket_address_enumerator_next_finish (priv->addr_enum,
464                                              result,
465                                              &priv->last_error);
466   if (priv->proxy_address)
467     {
468       if (!priv->supports_hostname && !priv->dest_ips)
469         {
470           GResolver *resolver;
471           resolver = g_resolver_get_default();
472           g_resolver_lookup_by_name_async (resolver,
473                                            priv->dest_hostname,
474                                            g_task_get_cancellable (task),
475                                            dest_hostname_lookup_cb,
476                                            task);
477           g_object_unref (resolver);
478           return;
479         }
480
481       return_result (task);
482     }
483   else
484     next_proxy (task);
485 }
486
487 static void
488 proxy_lookup_cb (GObject      *object,
489                  GAsyncResult *result,
490                  gpointer      user_data)
491 {
492   GTask *task = user_data;
493   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
494
495   g_clear_error (&priv->last_error);
496   priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
497                                                   result,
498                                                   &priv->last_error);
499   priv->next_proxy = priv->proxies;
500
501   if (priv->last_error)
502     {
503       complete_async (task);
504       return;
505     }
506   else
507     {
508       next_enumerator (priv);
509       if (priv->addr_enum)
510         {
511           g_socket_address_enumerator_next_async (priv->addr_enum,
512                                                   g_task_get_cancellable (task),
513                                                   address_enumerate_cb,
514                                                   task);
515           return;
516         }
517     }
518
519   complete_async (task);
520 }
521
522 static void
523 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
524                                        GCancellable             *cancellable,
525                                        GAsyncReadyCallback       callback,
526                                        gpointer                  user_data)
527 {
528   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
529   GTask *task;
530
531   task = g_task_new (enumerator, cancellable, callback, user_data);
532   g_task_set_task_data (task, priv, NULL);
533
534   if (priv->proxies == NULL)
535     {
536       g_proxy_resolver_lookup_async (priv->proxy_resolver,
537                                      priv->dest_uri,
538                                      cancellable,
539                                      proxy_lookup_cb,
540                                      task);
541       return;
542     }
543
544   if (priv->addr_enum)
545     {
546       if (priv->proxy_address)
547         {
548           return_result (task);
549           return;
550         }
551       else
552         {
553           g_socket_address_enumerator_next_async (priv->addr_enum,
554                                                   cancellable,
555                                                   address_enumerate_cb,
556                                                   task);
557           return;
558         }
559     }
560
561   complete_async (task);
562 }
563
564 static GSocketAddress *
565 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
566                                         GAsyncResult              *result,
567                                         GError                   **error)
568 {
569   g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
570
571   return g_task_propagate_pointer (G_TASK (result), error);
572 }
573
574 static void
575 g_proxy_address_enumerator_constructed (GObject *object)
576 {
577   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
578   GSocketConnectable *conn;
579   guint port;
580
581   if (priv->dest_uri)
582     {
583       conn = g_network_address_parse_uri (priv->dest_uri, priv->default_port, NULL);
584       if (conn)
585         {
586           g_object_get (conn,
587                         "hostname", &priv->dest_hostname,
588                         "port", &port,
589                         NULL);
590           priv->dest_port = port;
591
592           g_object_unref (conn);
593         }
594       else
595         g_warning ("Invalid URI '%s'", priv->dest_uri);
596     }
597
598   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->constructed (object);
599 }
600
601 static void
602 g_proxy_address_enumerator_get_property (GObject        *object,
603                                          guint           property_id,
604                                          GValue         *value,
605                                          GParamSpec     *pspec)
606 {
607   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
608   switch (property_id)
609     {
610     case PROP_URI:
611       g_value_set_string (value, priv->dest_uri);
612       break;
613
614     case PROP_DEFAULT_PORT:
615       g_value_set_uint (value, priv->default_port);
616       break;
617
618     case PROP_CONNECTABLE:
619       g_value_set_object (value, priv->connectable);
620       break;
621
622     case PROP_PROXY_RESOLVER:
623       g_value_set_object (value, priv->proxy_resolver);
624       break;
625
626     default:
627       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
628     }
629 }
630
631 static void
632 g_proxy_address_enumerator_set_property (GObject        *object,
633                                          guint           property_id,
634                                          const GValue   *value,
635                                          GParamSpec     *pspec)
636 {
637   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
638   switch (property_id)
639     {
640     case PROP_URI:
641       priv->dest_uri = g_value_dup_string (value);
642       break;
643
644     case PROP_DEFAULT_PORT:
645       priv->default_port = g_value_get_uint (value);
646       break;
647
648     case PROP_CONNECTABLE:
649       priv->connectable = g_value_dup_object (value);
650       break;
651
652     case PROP_PROXY_RESOLVER:
653       if (priv->proxy_resolver)
654         g_object_unref (priv->proxy_resolver);
655       priv->proxy_resolver = g_value_get_object (value);
656       if (!priv->proxy_resolver)
657         priv->proxy_resolver = g_proxy_resolver_get_default ();
658       g_object_ref (priv->proxy_resolver);
659       break;
660
661     default:
662       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
663     }
664 }
665
666 static void
667 g_proxy_address_enumerator_finalize (GObject *object)
668 {
669   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
670
671   if (priv->connectable)
672     g_object_unref (priv->connectable);
673
674   if (priv->proxy_resolver)
675     g_object_unref (priv->proxy_resolver);
676
677   g_free (priv->dest_uri);
678   g_free (priv->dest_hostname);
679
680   if (priv->dest_ips)
681     g_resolver_free_addresses (priv->dest_ips);
682
683   g_strfreev (priv->proxies);
684
685   if (priv->addr_enum)
686     g_object_unref (priv->addr_enum);
687
688   g_free (priv->proxy_type);
689   g_free (priv->proxy_username);
690   g_free (priv->proxy_password);
691
692   g_clear_error (&priv->last_error);
693
694   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
695 }
696
697 static void
698 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
699 {
700   self->priv = g_proxy_address_enumerator_get_instance_private (self);
701 }
702
703 static void
704 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
705 {
706   GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
707   GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
708
709   object_class->constructed = g_proxy_address_enumerator_constructed;
710   object_class->set_property = g_proxy_address_enumerator_set_property;
711   object_class->get_property = g_proxy_address_enumerator_get_property;
712   object_class->finalize = g_proxy_address_enumerator_finalize;
713
714   enumerator_class->next = g_proxy_address_enumerator_next;
715   enumerator_class->next_async = g_proxy_address_enumerator_next_async;
716   enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
717
718   g_object_class_install_property (object_class,
719                                    PROP_URI,
720                                    g_param_spec_string ("uri",
721                                                         P_("URI"),
722                                                         P_("The destination URI, use none:// for generic socket"),
723                                                         NULL,
724                                                         G_PARAM_READWRITE |
725                                                         G_PARAM_CONSTRUCT_ONLY |
726                                                         G_PARAM_STATIC_STRINGS));
727
728   /**
729    * GProxyAddressEnumerator:default-port:
730    *
731    * The default port to use if #GProxyAddressEnumerator:uri does not
732    * specify one.
733    *
734    * Since: 2.38
735    */
736   g_object_class_install_property (object_class,
737                                    PROP_DEFAULT_PORT,
738                                    g_param_spec_uint ("default-port",
739                                                       P_("Default port"),
740                                                       P_("The default port to use if uri does not specify one"),
741                                                       0, 65535, 0,
742                                                       G_PARAM_READWRITE |
743                                                       G_PARAM_CONSTRUCT_ONLY |
744                                                       G_PARAM_STATIC_STRINGS));
745
746   g_object_class_install_property (object_class,
747                                    PROP_CONNECTABLE,
748                                    g_param_spec_object ("connectable",
749                                                         P_("Connectable"),
750                                                         P_("The connectable being enumerated."),
751                                                         G_TYPE_SOCKET_CONNECTABLE,
752                                                         G_PARAM_READWRITE |
753                                                         G_PARAM_CONSTRUCT_ONLY |
754                                                         G_PARAM_STATIC_STRINGS));
755
756   /**
757    * GProxyAddressEnumerator:proxy-resolver:
758    *
759    * The proxy resolver to use.
760    *
761    * Since: 2.36
762    */
763   g_object_class_install_property (object_class,
764                                    PROP_PROXY_RESOLVER,
765                                    g_param_spec_object ("proxy-resolver",
766                                                         P_("Proxy resolver"),
767                                                         P_("The proxy resolver to use."),
768                                                         G_TYPE_PROXY_RESOLVER,
769                                                         G_PARAM_READWRITE |
770                                                         G_PARAM_CONSTRUCT |
771                                                         G_PARAM_STATIC_STRINGS));
772 }