1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2010 Red Hat, Inc.
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
17 * <http://www.gnu.org/licenses/>.
24 #include "gproxyresolvergnome.h"
26 #include <glib/gi18n-lib.h>
27 #include <gdesktop-enums.h>
29 #define GNOME_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy"
30 #define GNOME_PROXY_MODE_KEY "mode"
31 #define GNOME_PROXY_AUTOCONFIG_URL_KEY "autoconfig-url"
32 #define GNOME_PROXY_IGNORE_HOSTS_KEY "ignore-hosts"
33 #define GNOME_PROXY_USE_SAME_PROXY_KEY "use-same-proxy"
35 #define GNOME_PROXY_HTTP_CHILD_SCHEMA "http"
36 #define GNOME_PROXY_HTTP_HOST_KEY "host"
37 #define GNOME_PROXY_HTTP_PORT_KEY "port"
38 #define GNOME_PROXY_HTTP_USE_AUTH_KEY "use-authentication"
39 #define GNOME_PROXY_HTTP_USER_KEY "authentication-user"
40 #define GNOME_PROXY_HTTP_PASSWORD_KEY "authentication-password"
42 #define GNOME_PROXY_HTTPS_CHILD_SCHEMA "https"
43 #define GNOME_PROXY_HTTPS_HOST_KEY "host"
44 #define GNOME_PROXY_HTTPS_PORT_KEY "port"
46 #define GNOME_PROXY_FTP_CHILD_SCHEMA "ftp"
47 #define GNOME_PROXY_FTP_HOST_KEY "host"
48 #define GNOME_PROXY_FTP_PORT_KEY "port"
50 #define GNOME_PROXY_SOCKS_CHILD_SCHEMA "socks"
51 #define GNOME_PROXY_SOCKS_HOST_KEY "host"
52 #define GNOME_PROXY_SOCKS_PORT_KEY "port"
58 } GProxyResolverGnomeDomain;
60 struct _GProxyResolverGnome {
61 GObject parent_instance;
63 GSettings *proxy_settings;
64 GSettings *http_settings;
65 GSettings *https_settings;
66 GSettings *ftp_settings;
67 GSettings *socks_settings;
70 GDesktopProxyMode mode;
71 gchar *autoconfig_url;
72 gboolean use_same_proxy;
74 GPtrArray *ignore_ips;
75 GProxyResolverGnomeDomain *ignore_domains;
77 gchar *http_proxy, *https_proxy;
78 gchar *ftp_proxy, *socks_authority;
80 GDBusProxy *pacrunner;
85 static void g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface);
87 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyResolverGnome,
88 g_proxy_resolver_gnome,
90 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_PROXY_RESOLVER,
91 g_proxy_resolver_gnome_iface_init))
94 g_proxy_resolver_gnome_class_finalize (GProxyResolverGnomeClass *klass)
99 free_settings (GProxyResolverGnome *resolver)
103 if (resolver->ignore_ips)
104 g_ptr_array_free (resolver->ignore_ips, TRUE);
105 if (resolver->ignore_domains)
107 for (i = 0; resolver->ignore_domains[i].name; i++)
108 g_free (resolver->ignore_domains[i].name);
109 g_free (resolver->ignore_domains);
112 g_free (resolver->http_proxy);
113 g_free (resolver->https_proxy);
114 g_free (resolver->ftp_proxy);
115 g_free (resolver->socks_authority);
116 g_free (resolver->autoconfig_url);
120 gsettings_changed (GSettings *settings,
124 GProxyResolverGnome *resolver = user_data;
126 g_mutex_lock (&resolver->lock);
127 resolver->need_update = TRUE;
128 g_mutex_unlock (&resolver->lock);
132 g_proxy_resolver_gnome_finalize (GObject *object)
134 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (object);
136 if (resolver->proxy_settings)
138 g_signal_handlers_disconnect_by_func (resolver->proxy_settings,
139 (gpointer)gsettings_changed,
141 g_object_unref (resolver->proxy_settings);
143 g_signal_handlers_disconnect_by_func (resolver->http_settings,
144 (gpointer)gsettings_changed,
146 g_object_unref (resolver->http_settings);
148 g_signal_handlers_disconnect_by_func (resolver->https_settings,
149 (gpointer)gsettings_changed,
151 g_object_unref (resolver->https_settings);
153 g_signal_handlers_disconnect_by_func (resolver->ftp_settings,
154 (gpointer)gsettings_changed,
156 g_object_unref (resolver->ftp_settings);
158 g_signal_handlers_disconnect_by_func (resolver->socks_settings,
159 (gpointer)gsettings_changed,
161 g_object_unref (resolver->socks_settings);
163 free_settings (resolver);
166 if (resolver->pacrunner)
167 g_object_unref (resolver->pacrunner);
169 g_mutex_clear (&resolver->lock);
171 G_OBJECT_CLASS (g_proxy_resolver_gnome_parent_class)->finalize (object);
175 g_proxy_resolver_gnome_init (GProxyResolverGnome *resolver)
177 g_mutex_init (&resolver->lock);
179 resolver->proxy_settings = g_settings_new (GNOME_PROXY_SETTINGS_SCHEMA);
180 g_signal_connect (resolver->proxy_settings, "changed",
181 G_CALLBACK (gsettings_changed), resolver);
182 resolver->http_settings = g_settings_get_child (resolver->proxy_settings,
183 GNOME_PROXY_HTTP_CHILD_SCHEMA);
184 g_signal_connect (resolver->http_settings, "changed",
185 G_CALLBACK (gsettings_changed), resolver);
186 resolver->https_settings = g_settings_get_child (resolver->proxy_settings,
187 GNOME_PROXY_HTTPS_CHILD_SCHEMA);
188 g_signal_connect (resolver->https_settings, "changed",
189 G_CALLBACK (gsettings_changed), resolver);
190 resolver->ftp_settings = g_settings_get_child (resolver->proxy_settings,
191 GNOME_PROXY_FTP_CHILD_SCHEMA);
192 g_signal_connect (resolver->ftp_settings, "changed",
193 G_CALLBACK (gsettings_changed), resolver);
194 resolver->socks_settings = g_settings_get_child (resolver->proxy_settings,
195 GNOME_PROXY_SOCKS_CHILD_SCHEMA);
196 g_signal_connect (resolver->socks_settings, "changed",
197 G_CALLBACK (gsettings_changed), resolver);
199 resolver->need_update = TRUE;
202 /* called with lock held */
204 update_settings (GProxyResolverGnome *resolver)
206 gchar **ignore_hosts;
211 resolver->need_update = FALSE;
213 free_settings (resolver);
216 g_settings_get_enum (resolver->proxy_settings, GNOME_PROXY_MODE_KEY);
217 resolver->autoconfig_url =
218 g_settings_get_string (resolver->proxy_settings, GNOME_PROXY_AUTOCONFIG_URL_KEY);
219 resolver->use_same_proxy =
220 g_settings_get_boolean (resolver->proxy_settings, GNOME_PROXY_USE_SAME_PROXY_KEY);
223 g_settings_get_strv (resolver->proxy_settings, GNOME_PROXY_IGNORE_HOSTS_KEY);
224 if (ignore_hosts && ignore_hosts[0])
226 GPtrArray *ignore_ips;
227 GArray *ignore_domains;
228 gchar *host, *tmp, *colon, *bracket;
230 GInetAddressMask *mask;
231 GProxyResolverGnomeDomain domain;
234 ignore_ips = g_ptr_array_new_with_free_func (g_object_unref);
235 ignore_domains = g_array_new (TRUE, FALSE, sizeof (GProxyResolverGnomeDomain));
237 for (i = 0; ignore_hosts[i]; i++)
239 host = g_strchomp (ignore_hosts[i]);
241 /* See if it's an IP address or IP/length mask */
242 mask = g_inet_address_mask_new_from_string (host, NULL);
245 g_ptr_array_add (ignore_ips, mask);
255 bracket = strchr (host, ']');
256 if (!bracket || !bracket[1] || bracket[1] != ':')
259 port = strtoul (bracket + 2, &tmp, 10);
267 colon = strchr (host, ':');
268 if (colon && !strchr (colon + 1, ':'))
270 /* hostname:port or IPv4:port */
271 port = strtoul (colon + 1, &tmp, 10);
278 iaddr = g_inet_address_new_from_string (host);
280 g_object_unref (iaddr);
283 if (g_str_has_prefix (host, "*."))
285 else if (*host == '.')
289 memset (&domain, 0, sizeof (domain));
290 domain.name = g_strdup (host);
291 domain.length = strlen (domain.name);
293 g_array_append_val (ignore_domains, domain);
297 g_warning ("Ignoring invalid ignore_hosts value '%s'", host);
301 resolver->ignore_ips = ignore_ips;
304 g_ptr_array_free (ignore_ips, TRUE);
305 resolver->ignore_ips = NULL;
308 resolver->ignore_domains = (GProxyResolverGnomeDomain *)
309 g_array_free (ignore_domains, ignore_domains->len == 0);
313 resolver->ignore_ips = NULL;
314 resolver->ignore_domains = NULL;
316 g_strfreev (ignore_hosts);
318 host = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_HOST_KEY);
319 port = g_settings_get_int (resolver->http_settings, GNOME_PROXY_HTTP_PORT_KEY);
323 if (g_settings_get_boolean (resolver->http_settings, GNOME_PROXY_HTTP_USE_AUTH_KEY))
325 gchar *user, *password;
326 gchar *enc_user, *enc_password;
328 user = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_USER_KEY);
329 enc_user = g_uri_escape_string (user, NULL, TRUE);
331 password = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_PASSWORD_KEY);
332 enc_password = g_uri_escape_string (password, NULL, TRUE);
335 resolver->http_proxy = g_strdup_printf ("http://%s:%s@%s:%u",
336 enc_user, enc_password,
339 g_free (enc_password);
342 resolver->http_proxy = g_strdup_printf ("http://%s:%u", host, port);
346 host = g_settings_get_string (resolver->https_settings, GNOME_PROXY_HTTPS_HOST_KEY);
347 port = g_settings_get_int (resolver->https_settings, GNOME_PROXY_HTTPS_PORT_KEY);
349 resolver->https_proxy = g_strdup_printf ("http://%s:%u", host, port);
352 host = g_settings_get_string (resolver->ftp_settings, GNOME_PROXY_FTP_HOST_KEY);
353 port = g_settings_get_int (resolver->ftp_settings, GNOME_PROXY_FTP_PORT_KEY);
355 resolver->ftp_proxy = g_strdup_printf ("ftp://%s:%u", host, port);
358 host = g_settings_get_string (resolver->socks_settings, GNOME_PROXY_SOCKS_HOST_KEY);
359 port = g_settings_get_int (resolver->socks_settings, GNOME_PROXY_SOCKS_PORT_KEY);
361 resolver->socks_authority = g_strdup_printf ("%s:%u", host, port);
364 if (resolver->mode == G_DESKTOP_PROXY_MODE_AUTO && !resolver->pacrunner)
366 GError *error = NULL;
367 resolver->pacrunner =
368 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
369 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
370 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
372 "org.gtk.GLib.PACRunner",
373 "/org/gtk/GLib/PACRunner",
374 "org.gtk.GLib.PACRunner",
378 g_warning ("Could not start proxy autoconfiguration helper:"
379 "\n %s\nProxy autoconfiguration will not work",
383 else if (resolver->mode != G_DESKTOP_PROXY_MODE_AUTO && resolver->pacrunner)
385 g_object_unref (resolver->pacrunner);
386 resolver->pacrunner = NULL;
391 g_proxy_resolver_gnome_is_supported (GProxyResolver *object)
393 return !g_strcmp0 (g_getenv ("DESKTOP_SESSION"), "gnome");
397 ignore_host (GProxyResolverGnome *resolver,
401 gchar *ascii_host = NULL;
402 gboolean ignore = FALSE;
403 gint i, length, offset;
405 if (resolver->ignore_ips)
409 iaddr = g_inet_address_new_from_string (host);
412 for (i = 0; i < resolver->ignore_ips->len; i++)
414 GInetAddressMask *mask = resolver->ignore_ips->pdata[i];
416 if (g_inet_address_mask_matches (mask, iaddr))
423 g_object_unref (iaddr);
429 if (g_hostname_is_non_ascii (host))
430 host = ascii_host = g_hostname_to_ascii (host);
431 length = strlen (host);
433 if (resolver->ignore_domains)
435 for (i = 0; resolver->ignore_domains[i].length; i++)
437 GProxyResolverGnomeDomain *domain = &resolver->ignore_domains[i];
439 offset = length - domain->length;
440 if ((domain->port == 0 || domain->port == port) &&
441 (offset == 0 || (offset > 0 && host[offset - 1] == '.')) &&
442 (g_ascii_strcasecmp (domain->name, host + offset) == 0))
454 static inline gchar **
455 make_proxies (const gchar *proxy)
459 proxies = g_new (gchar *, 2);
460 proxies[0] = g_strdup (proxy);
466 /* Threadsafely determines what to do with @uri; returns %FALSE if an
467 * error occurs, %TRUE and an array of proxies if the mode is NONE or
468 * MANUAL, or if @uri is covered by ignore-hosts, or %TRUE and a
469 * (transfer-full) pacrunner and autoconfig url if the mode is AUTOMATIC.
472 g_proxy_resolver_gnome_lookup_internal (GProxyResolverGnome *resolver,
474 gchar ***out_proxies,
475 GDBusProxy **out_pacrunner,
476 gchar **out_autoconfig_url,
477 GCancellable *cancellable,
480 GSocketConnectable *addr = NULL;
481 const gchar *scheme = NULL, *host = NULL;
485 *out_pacrunner = NULL;
486 *out_autoconfig_url = NULL;
488 g_mutex_lock (&resolver->lock);
489 if (resolver->need_update)
490 update_settings (resolver);
492 if (resolver->mode == G_DESKTOP_PROXY_MODE_NONE)
494 *out_proxies = make_proxies ("direct://");
498 /* FIXME: use guri when it lands... */
499 addr = g_network_address_parse_uri (uri, 0, error);
502 scheme = g_network_address_get_scheme (G_NETWORK_ADDRESS (addr));
503 host = g_network_address_get_hostname (G_NETWORK_ADDRESS (addr));
504 port = g_network_address_get_port (G_NETWORK_ADDRESS (addr));
506 if (ignore_host (resolver, host, port))
508 *out_proxies = make_proxies ("direct://");
512 if (resolver->pacrunner)
514 *out_pacrunner = g_object_ref (resolver->pacrunner);
515 *out_autoconfig_url = g_strdup (resolver->autoconfig_url);
518 else if (resolver->ftp_proxy &&
519 (!strcmp (scheme, "ftp") || !strcmp (scheme, "ftps")))
521 *out_proxies = make_proxies (resolver->ftp_proxy);
523 else if (resolver->https_proxy && !strcmp (scheme, "https"))
525 *out_proxies = make_proxies (resolver->https_proxy);
527 else if (resolver->http_proxy &&
528 (!strcmp (scheme, "http") || !strcmp (scheme, "https")))
530 *out_proxies = make_proxies (resolver->http_proxy);
532 else if (resolver->socks_authority)
534 *out_proxies = g_new (gchar *, 4);
535 *out_proxies[0] = g_strdup_printf ("socks5://%s", resolver->socks_authority);
536 *out_proxies[1] = g_strdup_printf ("socks4a://%s", resolver->socks_authority);
537 *out_proxies[2] = g_strdup_printf ("socks4://%s", resolver->socks_authority);
538 *out_proxies[3] = NULL;
540 else if (resolver->use_same_proxy && resolver->http_proxy)
542 *out_proxies = make_proxies (resolver->http_proxy);
545 *out_proxies = make_proxies ("direct://");
549 g_object_unref (addr);
550 g_mutex_unlock (&resolver->lock);
552 if (*out_proxies || *out_pacrunner)
559 g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
561 GCancellable *cancellable,
564 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
565 GDBusProxy *pacrunner;
566 gchar **proxies, *autoconfig_url;
568 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
569 &proxies, &pacrunner, &autoconfig_url,
577 vproxies = g_dbus_proxy_call_sync (pacrunner,
579 g_variant_new ("(ss)",
582 G_DBUS_CALL_FLAGS_NONE,
587 g_variant_get (vproxies, "(^as)", &proxies);
588 g_variant_unref (vproxies);
593 g_object_unref (pacrunner);
594 g_free (autoconfig_url);
601 got_autoconfig_proxies (GObject *source,
602 GAsyncResult *result,
605 GTask *task = user_data;
608 GError *error = NULL;
610 vproxies = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
614 g_variant_get (vproxies, "(^as)", &proxies);
615 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
616 g_variant_unref (vproxies);
619 g_task_return_error (task, error);
620 g_object_unref (task);
624 g_proxy_resolver_gnome_lookup_async (GProxyResolver *proxy_resolver,
626 GCancellable *cancellable,
627 GAsyncReadyCallback callback,
630 GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
632 char **proxies, *autoconfig_url;
633 GDBusProxy *pacrunner;
634 GError *error = NULL;
636 task = g_task_new (resolver, cancellable, callback, user_data);
638 if (!g_proxy_resolver_gnome_lookup_internal (resolver, uri,
639 &proxies, &pacrunner, &autoconfig_url,
640 cancellable, &error))
642 g_task_return_error (task, error);
643 g_object_unref (task);
648 g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev);
649 g_object_unref (task);
653 g_dbus_proxy_call (pacrunner,
655 g_variant_new ("(ss)",
658 G_DBUS_CALL_FLAGS_NONE,
661 got_autoconfig_proxies,
663 g_object_unref (pacrunner);
664 g_free (autoconfig_url);
668 g_proxy_resolver_gnome_lookup_finish (GProxyResolver *resolver,
669 GAsyncResult *result,
672 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
674 return g_task_propagate_pointer (G_TASK (result), error);
678 g_proxy_resolver_gnome_class_init (GProxyResolverGnomeClass *resolver_class)
680 GObjectClass *object_class;
682 object_class = G_OBJECT_CLASS (resolver_class);
683 object_class->finalize = g_proxy_resolver_gnome_finalize;
687 g_proxy_resolver_gnome_iface_init (GProxyResolverInterface *iface)
689 iface->is_supported = g_proxy_resolver_gnome_is_supported;
690 iface->lookup = g_proxy_resolver_gnome_lookup;
691 iface->lookup_async = g_proxy_resolver_gnome_lookup_async;
692 iface->lookup_finish = g_proxy_resolver_gnome_lookup_finish;
696 g_proxy_resolver_gnome_register (GIOModule *module)
698 g_proxy_resolver_gnome_register_type (G_TYPE_MODULE (module));
699 g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
700 g_proxy_resolver_gnome_get_type(),