Add some more cases to the app-id unit tests
[glib.git] / gio / gproxyaddressenumerator.c
blobbe2d9cb83c45d57c13c7eec9ab11139230b29dcc
1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2010 Collabora, Ltd.
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.
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.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
21 #include "config.h"
22 #include "gproxyaddressenumerator.h"
24 #include <string.h>
26 #include "gasyncresult.h"
27 #include "ginetaddress.h"
28 #include "glibintl.h"
29 #include "gnetworkaddress.h"
30 #include "gnetworkingprivate.h"
31 #include "gproxy.h"
32 #include "gproxyaddress.h"
33 #include "gproxyresolver.h"
34 #include "gtask.h"
35 #include "gresolver.h"
36 #include "gsocketaddress.h"
37 #include "gsocketaddressenumerator.h"
38 #include "gsocketconnectable.h"
40 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
42 enum
44 PROP_0,
45 PROP_URI,
46 PROP_DEFAULT_PORT,
47 PROP_CONNECTABLE,
48 PROP_PROXY_RESOLVER
51 struct _GProxyAddressEnumeratorPrivate
53 /* Destination address */
54 GSocketConnectable *connectable;
55 gchar *dest_uri;
56 guint16 default_port;
57 gchar *dest_hostname;
58 guint16 dest_port;
59 GList *dest_ips;
61 /* Proxy enumeration */
62 GProxyResolver *proxy_resolver;
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;
76 G_DEFINE_TYPE_WITH_PRIVATE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
78 static void
79 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
80 const gchar *proxy)
82 gchar *userinfo;
84 if (priv->proxy_username)
86 g_free (priv->proxy_username);
87 priv->proxy_username = NULL;
90 if (priv->proxy_password)
92 g_free (priv->proxy_password);
93 priv->proxy_password = NULL;
96 if (_g_uri_parse_authority (proxy, NULL, NULL, &userinfo))
98 if (userinfo)
100 gchar **split = g_strsplit (userinfo, ":", 2);
102 if (split[0] != NULL)
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);
109 g_strfreev (split);
110 g_free (userinfo);
115 static void
116 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
118 if (priv->proxy_address)
119 return;
121 while (priv->addr_enum == NULL && *priv->next_proxy)
123 GSocketConnectable *connectable = NULL;
124 GProxy *proxy;
126 priv->proxy_uri = *priv->next_proxy++;
127 g_free (priv->proxy_type);
128 priv->proxy_type = g_uri_parse_scheme (priv->proxy_uri);
130 if (priv->proxy_type == NULL)
131 continue;
133 /* Assumes hostnames are supported for unknown protocols */
134 priv->supports_hostname = TRUE;
135 proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
136 if (proxy)
138 priv->supports_hostname = g_proxy_supports_hostname (proxy);
139 g_object_unref (proxy);
142 if (strcmp ("direct", priv->proxy_type) == 0)
144 if (priv->connectable)
145 connectable = g_object_ref (priv->connectable);
146 else
147 connectable = g_network_address_new (priv->dest_hostname,
148 priv->dest_port);
150 else
152 GError *error = NULL;
154 connectable = g_network_address_parse_uri (priv->proxy_uri, 0, &error);
156 if (error)
158 g_warning ("Invalid proxy URI '%s': %s",
159 priv->proxy_uri, error->message);
160 g_error_free (error);
163 save_userinfo (priv, priv->proxy_uri);
166 if (connectable)
168 priv->addr_enum = g_socket_connectable_enumerate (connectable);
169 g_object_unref (connectable);
174 static GSocketAddress *
175 g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
176 GCancellable *cancellable,
177 GError **error)
179 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
180 GSocketAddress *result = NULL;
181 GError *first_error = NULL;
183 if (priv->proxies == NULL)
185 priv->proxies = g_proxy_resolver_lookup (priv->proxy_resolver,
186 priv->dest_uri,
187 cancellable,
188 error);
189 priv->next_proxy = priv->proxies;
191 if (priv->proxies == NULL)
192 return NULL;
195 while (result == NULL && (*priv->next_proxy || priv->addr_enum))
197 gchar *dest_hostname;
198 gchar *dest_protocol;
199 GInetSocketAddress *inetsaddr;
200 GInetAddress *inetaddr;
201 guint16 port;
203 next_enumerator (priv);
205 if (!priv->addr_enum)
206 continue;
208 if (priv->proxy_address == NULL)
210 priv->proxy_address = g_socket_address_enumerator_next (
211 priv->addr_enum,
212 cancellable,
213 first_error ? NULL : &first_error);
216 if (priv->proxy_address == NULL)
218 g_object_unref (priv->addr_enum);
219 priv->addr_enum = NULL;
221 if (priv->dest_ips)
223 g_resolver_free_addresses (priv->dest_ips);
224 priv->dest_ips = NULL;
227 continue;
230 if (strcmp ("direct", priv->proxy_type) == 0)
232 result = priv->proxy_address;
233 priv->proxy_address = NULL;
234 continue;
237 if (!priv->supports_hostname)
239 GInetAddress *dest_ip;
241 if (!priv->dest_ips)
243 GResolver *resolver;
245 resolver = g_resolver_get_default();
246 priv->dest_ips = g_resolver_lookup_by_name (resolver,
247 priv->dest_hostname,
248 cancellable,
249 first_error ? NULL : &first_error);
250 g_object_unref (resolver);
252 if (!priv->dest_ips)
254 g_object_unref (priv->proxy_address);
255 priv->proxy_address = NULL;
256 continue;
260 if (!priv->next_dest_ip)
261 priv->next_dest_ip = priv->dest_ips;
263 dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
264 dest_hostname = g_inet_address_to_string (dest_ip);
266 priv->next_dest_ip = g_list_next (priv->next_dest_ip);
268 else
270 dest_hostname = g_strdup (priv->dest_hostname);
272 dest_protocol = g_uri_parse_scheme (priv->dest_uri);
274 g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address),
275 NULL);
277 inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
278 inetaddr = g_inet_socket_address_get_address (inetsaddr);
279 port = g_inet_socket_address_get_port (inetsaddr);
281 result = g_object_new (G_TYPE_PROXY_ADDRESS,
282 "address", inetaddr,
283 "port", port,
284 "protocol", priv->proxy_type,
285 "destination-protocol", dest_protocol,
286 "destination-hostname", dest_hostname,
287 "destination-port", priv->dest_port,
288 "username", priv->proxy_username,
289 "password", priv->proxy_password,
290 "uri", priv->proxy_uri,
291 NULL);
292 g_free (dest_hostname);
293 g_free (dest_protocol);
295 if (priv->supports_hostname || priv->next_dest_ip == NULL)
297 g_object_unref (priv->proxy_address);
298 priv->proxy_address = NULL;
302 if (result == NULL && first_error)
303 g_propagate_error (error, first_error);
304 else if (first_error)
305 g_error_free (first_error);
307 return result;
312 static void
313 complete_async (GTask *task)
315 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
317 if (priv->last_error)
319 g_task_return_error (task, priv->last_error);
320 priv->last_error = NULL;
322 else
323 g_task_return_pointer (task, NULL, NULL);
325 g_object_unref (task);
328 static void
329 return_result (GTask *task)
331 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
332 GSocketAddress *result;
334 if (strcmp ("direct", priv->proxy_type) == 0)
336 result = priv->proxy_address;
337 priv->proxy_address = NULL;
339 else
341 gchar *dest_hostname, *dest_protocol;
342 GInetSocketAddress *inetsaddr;
343 GInetAddress *inetaddr;
344 guint16 port;
346 if (!priv->supports_hostname)
348 GInetAddress *dest_ip;
350 if (!priv->next_dest_ip)
351 priv->next_dest_ip = priv->dest_ips;
353 dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
354 dest_hostname = g_inet_address_to_string (dest_ip);
356 priv->next_dest_ip = g_list_next (priv->next_dest_ip);
358 else
360 dest_hostname = g_strdup (priv->dest_hostname);
362 dest_protocol = g_uri_parse_scheme (priv->dest_uri);
364 g_return_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address));
366 inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
367 inetaddr = g_inet_socket_address_get_address (inetsaddr);
368 port = g_inet_socket_address_get_port (inetsaddr);
370 result = g_object_new (G_TYPE_PROXY_ADDRESS,
371 "address", inetaddr,
372 "port", port,
373 "protocol", priv->proxy_type,
374 "destination-protocol", dest_protocol,
375 "destination-hostname", dest_hostname,
376 "destination-port", priv->dest_port,
377 "username", priv->proxy_username,
378 "password", priv->proxy_password,
379 "uri", priv->proxy_uri,
380 NULL);
381 g_free (dest_hostname);
382 g_free (dest_protocol);
384 if (priv->supports_hostname || priv->next_dest_ip == NULL)
386 g_object_unref (priv->proxy_address);
387 priv->proxy_address = NULL;
391 g_task_return_pointer (task, result, g_object_unref);
392 g_object_unref (task);
395 static void address_enumerate_cb (GObject *object,
396 GAsyncResult *result,
397 gpointer user_data);
399 static void
400 next_proxy (GTask *task)
402 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
404 if (*priv->next_proxy)
406 g_object_unref (priv->addr_enum);
407 priv->addr_enum = NULL;
409 if (priv->dest_ips)
411 g_resolver_free_addresses (priv->dest_ips);
412 priv->dest_ips = NULL;
415 next_enumerator (priv);
417 if (priv->addr_enum)
419 g_socket_address_enumerator_next_async (priv->addr_enum,
420 g_task_get_cancellable (task),
421 address_enumerate_cb,
422 task);
423 return;
427 complete_async (task);
430 static void
431 dest_hostname_lookup_cb (GObject *object,
432 GAsyncResult *result,
433 gpointer user_data)
435 GTask *task = user_data;
436 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
438 g_clear_error (&priv->last_error);
439 priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
440 result,
441 &priv->last_error);
442 if (priv->dest_ips)
443 return_result (task);
444 else
446 g_clear_object (&priv->proxy_address);
447 next_proxy (task);
451 static void
452 address_enumerate_cb (GObject *object,
453 GAsyncResult *result,
454 gpointer user_data)
456 GTask *task = user_data;
457 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
459 g_clear_error (&priv->last_error);
460 priv->proxy_address =
461 g_socket_address_enumerator_next_finish (priv->addr_enum,
462 result,
463 &priv->last_error);
464 if (priv->proxy_address)
466 if (!priv->supports_hostname && !priv->dest_ips)
468 GResolver *resolver;
469 resolver = g_resolver_get_default();
470 g_resolver_lookup_by_name_async (resolver,
471 priv->dest_hostname,
472 g_task_get_cancellable (task),
473 dest_hostname_lookup_cb,
474 task);
475 g_object_unref (resolver);
476 return;
479 return_result (task);
481 else
482 next_proxy (task);
485 static void
486 proxy_lookup_cb (GObject *object,
487 GAsyncResult *result,
488 gpointer user_data)
490 GTask *task = user_data;
491 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
493 g_clear_error (&priv->last_error);
494 priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
495 result,
496 &priv->last_error);
497 priv->next_proxy = priv->proxies;
499 if (priv->last_error)
501 complete_async (task);
502 return;
504 else
506 next_enumerator (priv);
507 if (priv->addr_enum)
509 g_socket_address_enumerator_next_async (priv->addr_enum,
510 g_task_get_cancellable (task),
511 address_enumerate_cb,
512 task);
513 return;
517 complete_async (task);
520 static void
521 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
522 GCancellable *cancellable,
523 GAsyncReadyCallback callback,
524 gpointer user_data)
526 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
527 GTask *task;
529 task = g_task_new (enumerator, cancellable, callback, user_data);
530 g_task_set_source_tag (task, g_proxy_address_enumerator_next_async);
531 g_task_set_task_data (task, priv, NULL);
533 if (priv->proxies == NULL)
535 g_proxy_resolver_lookup_async (priv->proxy_resolver,
536 priv->dest_uri,
537 cancellable,
538 proxy_lookup_cb,
539 task);
540 return;
543 if (priv->addr_enum)
545 if (priv->proxy_address)
547 return_result (task);
548 return;
550 else
552 g_socket_address_enumerator_next_async (priv->addr_enum,
553 cancellable,
554 address_enumerate_cb,
555 task);
556 return;
560 complete_async (task);
563 static GSocketAddress *
564 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
565 GAsyncResult *result,
566 GError **error)
568 g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
570 return g_task_propagate_pointer (G_TASK (result), error);
573 static void
574 g_proxy_address_enumerator_constructed (GObject *object)
576 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
577 GSocketConnectable *conn;
578 guint port;
580 if (priv->dest_uri)
582 conn = g_network_address_parse_uri (priv->dest_uri, priv->default_port, NULL);
583 if (conn)
585 g_object_get (conn,
586 "hostname", &priv->dest_hostname,
587 "port", &port,
588 NULL);
589 priv->dest_port = port;
591 g_object_unref (conn);
593 else
594 g_warning ("Invalid URI '%s'", priv->dest_uri);
597 G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->constructed (object);
600 static void
601 g_proxy_address_enumerator_get_property (GObject *object,
602 guint property_id,
603 GValue *value,
604 GParamSpec *pspec)
606 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
607 switch (property_id)
609 case PROP_URI:
610 g_value_set_string (value, priv->dest_uri);
611 break;
613 case PROP_DEFAULT_PORT:
614 g_value_set_uint (value, priv->default_port);
615 break;
617 case PROP_CONNECTABLE:
618 g_value_set_object (value, priv->connectable);
619 break;
621 case PROP_PROXY_RESOLVER:
622 g_value_set_object (value, priv->proxy_resolver);
623 break;
625 default:
626 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
630 static void
631 g_proxy_address_enumerator_set_property (GObject *object,
632 guint property_id,
633 const GValue *value,
634 GParamSpec *pspec)
636 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
637 switch (property_id)
639 case PROP_URI:
640 priv->dest_uri = g_value_dup_string (value);
641 break;
643 case PROP_DEFAULT_PORT:
644 priv->default_port = g_value_get_uint (value);
645 break;
647 case PROP_CONNECTABLE:
648 priv->connectable = g_value_dup_object (value);
649 break;
651 case PROP_PROXY_RESOLVER:
652 if (priv->proxy_resolver)
653 g_object_unref (priv->proxy_resolver);
654 priv->proxy_resolver = g_value_get_object (value);
655 if (!priv->proxy_resolver)
656 priv->proxy_resolver = g_proxy_resolver_get_default ();
657 g_object_ref (priv->proxy_resolver);
658 break;
660 default:
661 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
665 static void
666 g_proxy_address_enumerator_finalize (GObject *object)
668 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
670 if (priv->connectable)
671 g_object_unref (priv->connectable);
673 if (priv->proxy_resolver)
674 g_object_unref (priv->proxy_resolver);
676 g_free (priv->dest_uri);
677 g_free (priv->dest_hostname);
679 if (priv->dest_ips)
680 g_resolver_free_addresses (priv->dest_ips);
682 g_strfreev (priv->proxies);
684 if (priv->addr_enum)
685 g_object_unref (priv->addr_enum);
687 g_free (priv->proxy_type);
688 g_free (priv->proxy_username);
689 g_free (priv->proxy_password);
691 g_clear_error (&priv->last_error);
693 G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
696 static void
697 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
699 self->priv = g_proxy_address_enumerator_get_instance_private (self);
702 static void
703 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
705 GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
706 GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
708 object_class->constructed = g_proxy_address_enumerator_constructed;
709 object_class->set_property = g_proxy_address_enumerator_set_property;
710 object_class->get_property = g_proxy_address_enumerator_get_property;
711 object_class->finalize = g_proxy_address_enumerator_finalize;
713 enumerator_class->next = g_proxy_address_enumerator_next;
714 enumerator_class->next_async = g_proxy_address_enumerator_next_async;
715 enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
717 g_object_class_install_property (object_class,
718 PROP_URI,
719 g_param_spec_string ("uri",
720 P_("URI"),
721 P_("The destination URI, use none:// for generic socket"),
722 NULL,
723 G_PARAM_READWRITE |
724 G_PARAM_CONSTRUCT_ONLY |
725 G_PARAM_STATIC_STRINGS));
728 * GProxyAddressEnumerator:default-port:
730 * The default port to use if #GProxyAddressEnumerator:uri does not
731 * specify one.
733 * Since: 2.38
735 g_object_class_install_property (object_class,
736 PROP_DEFAULT_PORT,
737 g_param_spec_uint ("default-port",
738 P_("Default port"),
739 P_("The default port to use if uri does not specify one"),
740 0, 65535, 0,
741 G_PARAM_READWRITE |
742 G_PARAM_CONSTRUCT_ONLY |
743 G_PARAM_STATIC_STRINGS));
745 g_object_class_install_property (object_class,
746 PROP_CONNECTABLE,
747 g_param_spec_object ("connectable",
748 P_("Connectable"),
749 P_("The connectable being enumerated."),
750 G_TYPE_SOCKET_CONNECTABLE,
751 G_PARAM_READWRITE |
752 G_PARAM_CONSTRUCT_ONLY |
753 G_PARAM_STATIC_STRINGS));
756 * GProxyAddressEnumerator:proxy-resolver:
758 * The proxy resolver to use.
760 * Since: 2.36
762 g_object_class_install_property (object_class,
763 PROP_PROXY_RESOLVER,
764 g_param_spec_object ("proxy-resolver",
765 P_("Proxy resolver"),
766 P_("The proxy resolver to use."),
767 G_TYPE_PROXY_RESOLVER,
768 G_PARAM_READWRITE |
769 G_PARAM_CONSTRUCT |
770 G_PARAM_STATIC_STRINGS));